Android6.0でBLEを使う(Central編)
はじめに
以前作成したBLEのサンプルを、Android6.0に合わせて更新しました。
Nexus5Xを購入したので、そのまま動くのかな~、と以前作成したCentral(Bluetooth連携におけるMaster側)のコードをそのまま動かすと、 エラーは起きていないようなのにPeripheral(Slave)の端末が見つからない現象に見舞われました。
今回はその対策についてです。
アクセス権
Android6.0の大きな変更点として、アプリのアクセス権が個別にOn/Offできるようになったことが挙げられます。
そこでSettings > Apps > BleController(サンプルのアプリ名) > Permissions を調べたところ、何もリクエストされていないことになっていました。
ん?Bluetoothはアプリからオンにできるのにパーミッションが空になってる…?
接続できないのはこれのせいか。#android #nexus5x #BLE https://t.co/hCW2ltcJhe pic.twitter.com/6NmKOZclcM
— masanori_msl(山寨版) (@masanori_msl) November 14, 2015
AndroidManifest.xmlでは次の通り、Bluetoothに関するアクセス権を記述しています。
AndroidManifest.xml
~省略~ < uses-permission android:name="android.permission.BLUETOOTH" / > < uses-permission android:name="android.permission.BLUETOOTH_ADMIN" / > ~省略~
後述する方法でアクセス権が許可されているかを確認しても、許可されていることになっていて特に変化はありませんでした。
ACCESS_COARSE_LOCATION
もしや必要なアクセス権が変わったのか?と思って調べてみたところ、Android6.0でBluetoothを利用するには、 「BLUETOOTH」、「BLUETOOTH_ADMIN」に加えて、「ACCESS_FINE_LOCATION」または「ACCESS_COARSE_LOCATION」が必要ということが判明しました。
ということで、アクセス権として「ACCESS_COARSE_LOCATION」を追加しました。
AndroidManifest.xml
~省略~ < uses-permission android:name="android.permission.BLUETOOTH" / > < uses-permission android:name="android.permission.BLUETOOTH_ADMIN" / > < uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" / > ~省略~
アクセス権のチェックとリクエスト
今までと違い、アプリのインストール後にもアクセス権のOn/Offが任意で切り替えられるようになったため、必要な権限が許可されているかを確認する必要が出てきました。
これを行うために追加されたメソッドが、「checkSelfPermission」と「requestPermissions」です。
ここではアプリの起動時にアクセス権を確認してみます。
MainActivity.java
package jp.androidblecontroller; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.view.View; import android.widget.Button; import android.widget.Toast; import jp.blecontroller.PeripheralActivity; public class MainActivity extends Activity { private Button mBtnOpenCentral; private Button mBtnOpenPeripheral; private final static int REQUEST_PERMISSIONS = 1; private final static int SDKVER_MARSHMALLOW = 23; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // デバイスがBLEに対応していなければトースト表示. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } // Android6.0以降なら権限確認. if(Build.VERSION.SDK_INT >= SDKVER_MARSHMALLOW) { this.requestBlePermission(); } mBtnOpenCentral = (Button)findViewById(R.id.btn_open_central); mBtnOpenCentral.setOnClickListener(mBtnOpenCentralClicked); mBtnOpenPeripheral = (Button)findViewById(R.id.btn_open_peripheral); mBtnOpenPeripheral.setOnClickListener(mBtnOpenPeripheralClicked); } @TargetApi(SDKVER_MARSHMALLOW) private void requestBlePermission(){ // 権限が許可されていない場合はリクエスト. if(checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){ requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION },REQUEST_PERMISSIONS); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { // 権限リクエストの結果を取得する. if (requestCode == REQUEST_PERMISSIONS) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(MainActivity.this, "Succeed", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_SHORT).show(); } }else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } ~省略~
- 「checkSelfPermission」と「requestPermissions」はAndroid6.0(API23)以上でのみ使用可能なため、バージョンの確認をしています。
こうすると、初回起動時やSettings > Apps > BleController(サンプルのアプリ名) > Permissions からアクセス権をOffにした後起動した場合に、以下のようなメッセージが表示されるようになります。
これを許可することで、無事Perpheral端末と連携することができました。
Peripheral
ずっと積み残し課題となっていたPeripheral。
Nexus7(2013)では「BluetoothAdapter」の「getBluetoothLeAdvertiser」で値が取得できなかったため伸ばし伸ばしになっていましたが、Nexus5Xでは問題なく使えそうです。
というわけで、次回はPeripheral側のサンプルを作ってみます。