酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Androidアプリでバックグラウンド状態で位置情報が取得できるのか調査した

2,3年くらい前にバックグラウンド・サービスで実装されていた位置情報取得処理をフォアグラウンド・サービスに移行(移植)したことがあった。

さくさん自身、位置情報取得の処理は1年に数回書いているような気がするが、いずれもアプリがフォアグラウンドにいる状態でのワンショット取得処理ばかりで、この手の 定期的に位置情報を取得する実装 については久しく触れていなかった。

今回「バックグラウンドで位置情報取得したいが、既知のフォアグラウンド・サービス以外の方法を探して」と言われた。以前にも調査自体はしたような気がするがAndroidXが主流となって久しいため、手法が変わったのではないかと考えて、あらたに調査することになった。

フォアグラウンド・サービスを使って位置情報を取得するのが主流

ちなみに知見を求めてググってみたところ、アンサーに「フォアグラウンド・サービスを使おう!」と書かれているものばかりだ。「それバックグラウンドじゃないじゃん……」が初手で思った感想である。

Google公式にも「バックグラウンドで位置情報を取得するな。フォアグラウンド・サービスを使え*1」とアナウンスされており、2021年2月現在では定期的に位置情報を取得したい場合、フォアグラウンド・サービスを使って位置情報を取得する方法が主流となっている。

QiitaやStack Overflowにも回答を求めて検索したが、多くは「Foreground Serviceを使おう!」と書かれていた。

なお、日本語で書かれているフォアグラウンド・サービスの実装記事のなかでは、ブログの会社が書いている以下の記事が詳しかった。

はたしてバックグラウンド・サービスで位置情報を取得する方法は廃れてしまったのだろうか。

Androidに存在している「ふたつの位置情報」について

Androidには以下の「ふたつの位置情報」が存在している *2

  • フォアグラウンドでの位置情報
  • バックグラウンドでの位置情報

それぞれの特徴を書く。僕が求めているのは後者の方の情報である。

フォアグラウンドでの位置情報

アプリのアクティビティが可視状態になっている or フォアグラウンド・サービスを実行している場合には、システムはフォアグラウンドでの位置情報アクセスとみなす。

1 回のみ、あるいは指定した期間にわたって位置情報を共有または受信する機能がアプリに含まれている場合には、フォアグラウンドでの位置情報へのアクセスが必要です。以下はその一例です。

  • ナビゲーション アプリで、ターンバイターン方式の経路案内をする機能。
  • メッセージ アプリで、現在地を別のユーザーと共有する機能。

アプリの機能が次のいずれかの状況でデバイスの現在地にアクセスする場合、システムはアプリがフォアグラウンドで位置情報を使用しているとみなします。

  • アプリに属するアクティビティが可視状態にある。
  • アプリがフォアグラウンド サービスを実行している。フォアグラウンド サービスが実行されているとき、システムはユーザーに対して常に通知を表示します。ユーザーがデバイスのホームボタンを押したりディスプレイをオフにしたりしてアプリがバックグラウンドに移動しても、アクセスは継続します。
位置情報へのアクセスをリクエストする - フォアグラウンドでの位置情報

飲食店案内アプリとかで店舗の地図を表示させたりするのもこちら側。ほとんどのアプリでは、フォアグラウンドでの位置情報アクセスとなる。通常の要件ではバックグラウンドでの位置情報取得を考える必要はない。

マラソンなどのリアルタイムに位置情報を取得するアクティビティの記録アプリであれば、フォアグラウンド・サービス一択となるだろう。

バックグラウンドでの位置情報

「フォアグラウンドでの位置情報」以外の場合は、バックグラウンドでの位置情報アクセスとなる。わかりやすい。

アプリ内の機能が常に他のユーザーと位置情報を共有する場合や、Geofencing API を使用する場合、アプリはバックグラウンドでの位置情報へのアクセスを必要とします。以下はその一例です。

  • 家族間位置情報共有アプリで、家族と位置情報を継続的に共有する機能。
  • IoT アプリで、ユーザーが自宅を離れるとオフになり、帰宅すると再びオンになるようにホームデバイスを設定する機能。

フォアグラウンドでの位置情報のセクションで説明した以外の状況で、アプリがデバイスの現在地にアクセスする場合、システムはアプリがバックグラウンドで位置情報を使用しているとみなします。

位置情報へのアクセスをリクエストする - バックグラウンドでの位置情報

フォアグラウンド位置情報と違って、バックグラウンド位置情報は1時間に数回しか更新されないなど制約も存在している。これはバッテリー消費を抑えるためである。

公式ドキュメントの引用に書かれているとおり、バックグラウンド位置情報を利用するアプリの例がふたつ書かれている。

  • IoT アプリで、ユーザーが自宅を離れるとオフになる(中略)ホームデバイスを設定する機能。
    • この例は「ジオフェンス」を意識して書かれている。
  • 家族間位置情報共有アプリで、家族と位置情報を継続的に共有する機能。
    • リアルタイムで位置情報を追跡するのではなくて定期的に大まかな位置を知れたらよいケース。今回求めている条件に該当する

Googleマップアプリではフォアグラウンド・サービスを使うことなく、ユーザーの移動位置を補足して移動経路を組み立ている。

例示されている家族間位置情報共有アプリも同様に今回求めている「バックグラウンドで位置情報取得したいが、既知のフォアグラウンド・サービス以外の方法を探して」に該当する。

バックグラウンドで位置情報アクセスするアプリには特別な審査が必要

2021年2月現在、Androidアプリでバックグラウンドでの位置情報取得には強く制限がかかっており、Google Playでアプリを公開する場合には別途審査リクエストをしないとストアからアプリが削除されてしまうようになっている。

バックグラウンド位置情報にアクセスするアプリの場合、Googleによるアプリ審査を受ける必要がある。審査内容に関しては以下のページが詳しい。

この内容で本当に審査しているのか調べてみたが、審査リクエストの対処方法についての日本語情報はほとんど見つけることができなかった。唯一見つけたのがマーケティング用のSDKを提供しているFANSHIP社で紹介されている記事である。

審査に必要なものは、Google公式のバックグラウンド位置情報にアクセスするアプリの審査を円滑に進めるためのヒントにも書かれている通り以下のものを準備する必要がある。

  1. 機密情報に関わる申請 を行う ・デモ動画が必要
  2. アプリ内での位置情報使用の開示を行う (位置情報許諾ダイアログ)
  3. プライバシーポリシー にバックグラウンドで位置情報を取得する件について書く

特に2の位置情報使用の許諾フローは特殊である。FANSHIP社の記事に詳しく書かれているので読んで欲しい。

f:id:ch3cooh393:20210208114648p:plain
初回起動許諾ダイアログについてまとめ(Android11) より引用

僕が特殊であると思った点は、OS設定アプリで位置情報へのアクセスで「常に許可」をユーザーに選択してもらわないといけない点である。実装する前にしっかりと位置情報の許諾フローを練り上げておく必要がある。iOSアプリをベースにして雑に許諾フローを作成するなどは避けた方が良いだろう。

もっとも簡単に「常に許可」させないのはiOSでも同様で、許諾ダイアログから「常に許可」が削除されたようにプライバシー保護の観点からスマホOSでの常識になりつつある。

バックグラウンド位置情報を取得する実装について

2021年2月現在、バックグラウンド・サービスを使った位置情報取得方法はすでに過去のものになっているのか情報が出てこない。

バックグラウンド・サービスを使ってバックグラウンドで位置情報を取得する

Google公式ドキュメントの「現在地の更新情報をリクエストする」にも過去のリポジトリが掲載されているだけである。下記のリンクはバックグラウンド位置情報へのアクセスをリクエストするアプリの例です。

いーがりーさんに教えてもらった情報によれば、バックグラウンド・サービスはDozeスリープを越えられず基本的に死ぬようだ。

Dozeについては「Doze とアプリ スタンバイ用に最適化する」が詳しい。バッテリーの消費電力を抑えるためにOS側が裏で動いているサービスの実行を保留したり止めたりする。

GPSロガー(GPSトラッキング)機能のあるヤマコレのヘルプページには「Huaweiのデバイスはバックグラウンドで位置情報を取得しているアプリを強制的に止めてしまうことがあるのでHuaweiデバイスを避けましょう (意訳) *3」 と書かれていた。

WorkManagerを使ってバックグラウンドで位置情報を取得する

Dozeを越えるため当初はAlarmManagerを使って位置情報を取得することを検討していたが、WorkManagerがAlarmManagerを内包していることを知ってWorkManagerを使う検討を始めた。2021年2月現在、WorkManagerを使う方法の方が主流なのかもしれない。

WorkManagerは、Jetpackに含まれているライブラリだ。 WorkManager はアプリが終了した場合やデバイスが再起動した場合でも実行され、延期可能な非同期タスクのスケジュールを簡単に設定するための API である。内部的にAlarmManagerやJosScheduler、フォアグラウンドサービス などがOSバージョンによって利用されている。これらの情報はWorkManager でタスクのスケジュールを設定する」に詳しく書かれている。

WorkManagerを使ってバックグラウンドで位置情報を取得するサンプルプログラムをいくつか見つけることができた。

前述のリンクはWorkManagerを使う断片的な情報だが、LocationTracker-WorkManagerは実行な可能なサンプルアプリとして提供されている。

このサンプルアプリはSTARTボタンを押すと15分ごとに位置情報を取得して通知を出してくれる。試しに実行してみたところ、アプリをkillしていても約15分後にWorkManagerが位置情報を取得することを確認できた。

ただし動作確認ができたのはAndroid 9エミュレータで、Android 11エミュレータではアプリをkillしてしまうと動作しないようだ。このアプリの解析はしていないが、単にコピペしただけでは最新のOSバージョンでは動かなくなることは理解できた。

よくよくIssueをみてみるとAndroid 10で動かないと報告が上がっているようだ。

まとめ

昔は横行していた(かもしれない)ユーザーに黙って位置情報をぶっこ抜く的なアプリはAndroid 11以降では作成することはできない。バックグラウンドで位置情報の取得すると、OSがユーザーへ通知を出すようになった。

あくまでもユーザーの利便性に沿ってバックグラウンドでの位置情報を取得するもののみが存在を許される。Google Playのような公式なストアで公開するためには、Googleによって許可を貰わないと、文字通りGoogle Playから存在を削除される。

Androidには位置情報の取得方法がたくさんあるので、アプリの要件によって位置情報の取得方法を使い分けたい。

  • 「現在地から最寄りの店舗を探すような1回だけ位置情報を取得できればよいアプリ」や「位置情報を共有するアプリ」では、普通に位置情報を取得すればよい。
  • 「経路情報を取得するマラソンアプリ」では、リアルタイムでの位置情報取得が必要になるためフォアグラウンド・サービスを利用する。
  • 「店舗への入店/退店を監視するアプリ」や「帰宅したらエアコンをつけるアプリ」では、ジオフェンス機能を利用する。バックグラウンド位置情報利用の審査が必要になる。
  • 「家族の位置情報共有アプリ」では、WorkManagerを利用する。これはバックグラウンド位置情報利用の審査が必要になる。

このまとめを書くためになかざんさんといーがりーさんに色々と教えていただきました💪💪 ありがとうございます!