2024年6月24日月曜日

expo48でbleの検索を使うにはAndroidManifestでneverForLocationの指定が必要


背景

2024年6月末までに新たな活動が無いgoogle開発者アカウントが閉じられるということだったので、約6年ぶりにreact-nativeでアプリを作って公開しました。
その際、bleの権限を有効にする方法が不可解な挙動をしたので、調べて設定を適用しました。

試行錯誤の内容と分かったことを記事にまとめます。

使ったもの

  • react-native + expo + shadow-cljsのAndroidアプリ
    利用しているライブラリのバージョンはこちらです。
    expo: 48.0.21
    expoは現在最新が51で50未満は非推奨の環境ですが、環境更新に手間取りそうだったので今回はこのまま検証を行いました。
    react: 18.2.0
    react-native: 0.71.14
    shadow-cljs: 2.23.3
  • react-native-ble-manager: 11.5.3
    bleライブラリの1つです。
    以後ble-managerと呼びます。
  • react-native-ble-plx: 3.2.0
    bleライブラリの1つです。
    以後ble-plxと呼びます。
  • ノートPC: ubuntu22.04
    このPCにexpoの動作環境をインストールして開発しました。
  • Android端末: Asus Zenfon9 + Andorid14
    この端末にアプリをインストールして動作確認しました。

おかしな挙動: ble-plxをパッケージとして取り込んでないとble-managerが使えない

ble-managerをインストールしてapp.jsonでBLE関係の権限を付与するのでは設定が不十分なようで、周辺端末の探索を開始しても何も見つからなかったです。
package.json
{
"dependencies": {
"react-native-ble-manager": "^11.5.5"
}
}

app.json
{
"expo": {
"android": {
"permissions": [
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT"
]
}
}
}
app.jsonの設定を反映するには、設定後にpre buildを行います。
npx expo prebuild --clean --platform android

上記コマンドを実行すると、設定が「プロジェクト/android/app/src/main/AndroidManifest.xml」に反映されます。

上記の設定では動かないものの、ble-plxをインストールするとble-managerの検索が動きました。
package.json
{
"dependencies": {
"react-native-ble-plx": "^3.2.0"
}
}

ライブラリに含まれるAndroidManifest.xmlに違いあり

プラグインやコードの記述をしなくてもpackage.jsonで依存していれば機能が有効になるのなら、ライブラリに含まれているandroidの設定ファイルが影響していると推測し調査したところble-managerとble-plxのAndroidManifest.xmlに違いを見つけました。

記事を書いている時点でのble-managerのmanifest
AndoidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.innove">

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
</manifest>

記事を書いている時点でのble-plxのmanifest
AndoidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bleplx"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission
android:name="android.permission.BLUETOOTH_CONNECT"
tools:targetApi="s" />
</manifest>

ble-plxの方はneverForLocationに対応している書き方です。
neverForLocationとはAndroid12から導入された、位置情報の許諾なしでbluetoothを使える設定です。(参考: Android 12でBluetoothは位置情報権限の呪縛から解かれる
node_modules/react-native-ble-manager/android/src/main/AndroidManifest.xmlをble-plxと同等に変更してアプリをビルドすると、ble-plx無しでble-managerの検索機能を動かせました。

ということで、その変更のpull requestをしました。
Update AndroidManifest.xml to support new options #1241

これが取り込まれたら、特に意識せずble-managerをexop48の環境で使えるようになります。

非推奨: prebuildで生成されるAndroidManifestのBLUETOOTH_SCANにneverForLocationを付けても動く

Android14でBLEの検索をするにはneverForLocationが必要なようで、prebuildで出来る下記のmanifestのBLUETOOTH_SCANに設定を付与しても動きはしました。

android/app/src/main/AndoidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
</manifest>

しかしながら、上記のファイルは.gitignoreされているためgitの追跡対象ではなく、prebuildによって設定が破棄されるので、試行錯誤の動作確認で書き換えるのは良いですが、本番運用はライブラリを更新したりmanifestを書き換えるプラグインを作るなどで対応するのが良いです。

expoのpluginでもAndroidManifest.xmlを書き換え可能らしい

ble-managerはexampleのプロジェクトに、ble-plxはライブラリ経由で利用可能な形式でプラグインが書かれています。

https://github.com/innoveit/react-native-ble-manager/tree/master/example/plugins
https://github.com/dotintent/react-native-ble-plx/tree/master/plugin

expo公式の説明書もあります。
Plugins and mods

多分ble-plxのプラグインを参考にすればneverForLocationを付与しつつBLUETOOTH_SCANを定義出来ると思うのですが、数行の設定のために理解が必要な設定が多そうだったのと、プラグインではなくライブラリのAndroidManifest.xmlの書き換えで問題は解決したので、今回は試さず終わりました。

今後expoのバージョンを50以上にしたときに情報が必要になりそうなので、上記のリンクを残します。

おわり

ble-plxに依存していないとble-managerが使えないのは、ble-managerのAndroidManifest.xmlがAndroid12から導入されたneverForLocationに対応していなかったからと分かりました。
現行最新の50系のexpoでは指定方法が変わっているかもしれませんが、espo48で期待通りにble-managerを動かす方法が分かって良かったです。

参考

react-native-ble-manager
react-native-ble-plx
Android 12でBluetoothは位置情報権限の呪縛から解かれる

この記事で分かった内容を含むpull requestです。
Update AndroidManifest.xml to support new options #1241

0 件のコメント :