fkm blog

software開発に関することを書いていきます

Google I/O 2019: Updating Your Apps for Location Permission Changes in Android Q

現地で聞いてないセッション。「位置情報に関する更新はだいじ!」と何名から言われたので早めに確認します。

youtu.be

Android Qでは、ユーザーにとって - シンプルで(simple) - 発見しやすく(discoverable) - 理解しやすい(easy to understand) 位置情報データに対するアクセスコントロールを提供。

Qより前は、ユーザーは位置情報に関して「許可」「禁止」の2つしか選べなかった。許可した場合、アプリ使用中/バックグラウンドに関わらず、位置情報が取得できた。そのため、Qでは「このアプリ使用中にのみ位置情報の取得を許可する」が追加された。

「使用中」とは? - アクティビティがフォアグラウンド - フォアグラウンドサービス

targetSdkVersionがQの場合、 バックグラウンドで位置情報を取得するには AndroidManifest.xmlACCESS_BACKGROUND_LOCATIONパーミッション追加が必要。 今までの ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION はフォアグラウンドでしか取得できない。

また、Q以前にしていた場合は自動で ACCESS_BACKGROUND_LOCATION にマップされる。これをオプトアウトする術はない。

targetSdkVersionをQにした場合、位置情報取得の許可を得る際、「このアプリ使用中のみ取得」「拒否」の2択ダイアログが出せる(「常に取得」を非表示にできる)。また、インクリメンタルに許可をとることもできる。

例えば鉄道のアプリで、ユーザーがアプリを起動中に近くの駅の情報を出すといった機能があった場合、「アプリ使用中のみ位置情報を取得する」のダイアログを表示することができる。そしてユーザーがバックグラウンドにいるときも運行情報を取得したいとしたときにはじめて「常に位置情報を使用」まで含めたダイアログを表示。その際、「アプリは現在、使用中のみ位置情報にアクセスできます」と表示される。

ユーザーが許可したかは、 checkSelfPermission() で確認できる。またユーザーは設定でいつでも「常に許可」「アプリ使用中のみ許可」の変更ができる。アプリ開発者はQでのテストを早めに行ってほしい。

ユーザーがPからQにアップグレードした場合は?Pで許可していた場合、Qでは「常に許可」としてアップグレードされる。つまりバックグラウンドでの位置情報もとれる。

フォアグラウンドサービス

ユーザーが、ユーザーの意思で位置情報を取得し続けてほしい(ホームボタンでアプリが中断されても)場合、それはフォアグラウンドサービスとして実装されるべき。

Qでは、フォアグラウンドサービスを作る場合、 AndroidManifest.xmlandoird:foregroundServiceType="location" をつける必要がある。この権限をつけ忘れ、ユーザーが「アプリ使用中」の設定にした場合、位置情報は取得できない。

通知について

位置情報の使用を許可したアプリで、ユーザーが長期間使用していない場合、システムが通知欄に「このアプリは位置情報を取得しつづけてるよ」の通知を出す。ユーザーはこの通知をタップすると、位置情報の許可レベル設定画面が表示される。

Wifi API

Q以前は、WifiのスキャンAPIと接続APIがあった。スキャンできるということは、そこからだいたいの位置が特定できてしまう。なので位置情報のパーミッションが必要だった。ユーザーにとって、「なんでWifiに繋ぐだけなのに位置情報を取得するの?」と混乱の元となっていた。

この問題を解決するため、Qでは次の2つのAPIが追加された。

  • Wifi NetworkSuggestion
  • Wifi NetworkSpecify

これらを使うと、アプリは近くのWifi情報にアクセスできなくなるので位置情報のパーミッションが不要になる。

Wifiが位置情報を出していた場合、それらを取得するには ACCESS_FINE_LOCATIONパーミッションが必要になる。

Wifi Network Suggestion

WifiNetworkSuggestion.Builder() を使って組み立てる。SSIDやパスワードの指定も可能。組み立てたら WifiManageraddaddNetworkSuggestion() に渡す。

あとはユーザーが指定したWifiの領域内に入ったら、システムが通知で「このネットワークに繋ぐ?」と出してくれる。その際、「このアプリが提案してるよ」と出してくれる。

setIsAppInteractionRequired()WifiNetworkSuggestion につけると、接続後にアプリにブロードキャストが投げられる。インテントフィルタは ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION (長いなぁ)。この機能はユーザーの位置がある程度わかっちゃうので ACCESS_FINE_LOCATION が必要。

Wifi Network Specier API

例えばIoT機器を買ってきて、セットアップが必要な場合、電源をいれてスマホからWifi経由で繋ぐというケースがある。こんなときに使えるのがWifi Network Specier APIWifi Network Suggestionと似ているが、このケースの場合、機器ごとにSSIDが違う(もちろんパターンはある)のでWifi Network Suggestionは使えない。

WifiNetworkSpecifier.Builder() を使って組み立てる。SSIDのパターンや、BSSID(MACアドレス)のパターンを登録する。

次に NetworkRequest オブジェクトを組み立てる。「インターネット接続はいらない」といった設定を付与できる。

これができたら、URL Connectivity Manager APIを使って確認するだけ(ソースなし。。)

ベストプラクティス

  • 必要ないときは位置情報をとらない
  • 必要になったときに許可をとる
  • 位置情報取得の理由をユーザーに伝えよう
  • アプリ起動直後に「位置情報使うよ」のダイアログを出さないようにしよう
  • インクリメンタルに聞こう(バックグラウンドで取得する必要がでたら、そのときに聞こう)
  • 位置情報がとれないときの処理をちゃんと書こう。ユーザーはいつでも設定を変更できる。