vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

Android6.0でBLEを使う(Central編)

はじめに

以前作成したBLEのサンプルを、Android6.0に合わせて更新しました。

Nexus5Xを購入したので、そのまま動くのかな~、と以前作成したCentral(Bluetooth連携におけるMaster側)のコードをそのまま動かすと、 エラーは起きていないようなのにPeripheral(Slave)の端末が見つからない現象に見舞われました。

今回はその対策についてです。

アクセス権

Android6.0の大きな変更点として、アプリのアクセス権が個別にOn/Offできるようになったことが挙げられます。

そこでSettings > Apps > BleController(サンプルのアプリ名) > Permissions を調べたところ、何もリクエストされていないことになっていました。

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にした後起動した場合に、以下のようなメッセージが表示されるようになります。

f:id:mslGt:20151115005531p:plain

これを許可することで、無事Perpheral端末と連携することができました。

Peripheral

ずっと積み残し課題となっていたPeripheral。

Nexus7(2013)では「BluetoothAdapter」の「getBluetoothLeAdvertiser」で値が取得できなかったため伸ばし伸ばしになっていましたが、Nexus5Xでは問題なく使えそうです。

というわけで、次回はPeripheral側のサンプルを作ってみます。

参考

Android6.0

パーミッション