web-dev-qa-db-ja.com

Bluetooth LEスキャンは、AndroidバックグラウンドでM

次のコードは、Android 5.1.1(Build LMY48M)を実行しているNexus 9ではうまく機能しますが、Android 6.0(ビルドMPA44l)

List<ScanFilter> filters = new ArrayList<ScanFilter>();
ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)).build();
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setManufacturerData((int) 0x0118, new byte[]{(byte) 0xbe, (byte) 0xac}, new byte[]{(byte) 0xff, (byte)0xff});
ScanFilter scanFilter = builder.build();
filters.add(scanFilter);
mBluetoothLeScanner.startScan(filters, settings, new ScanCallback() {
  ...
});

On Android 5.x、スキャンフィルターに一致する製造元の広告が表示された場合、上記のコードはコールバックを生成します。以下のLogcat出力の例を参照してください。)スキャンフィルターをコメントアウトすると、Nexus 9でコールバックが正常に受信されます。

09-22 00:07:28.050    1748-1796/org.altbeacon.beaconreference D/BluetoothLeScanner﹕ onScanResult() - ScanResult{mDevice=00:07:80:03:89:8C, mScanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=null, mManufacturerSpecificData={280=[-66, -84, 47, 35, 68, 84, -49, 109, 74, 15, -83, -14, -12, -111, 27, -87, -1, -90, 0, 1, 0, 1, -66, 0]}, mServiceData={}, mTxPowerLevel=-2147483648, mDeviceName=null], mRssi=-64, mTimestampNanos=61272522487278}

Android M?

23
davidgyoung

問題はスキャンフィルターではなく、アプリがバックグラウンドにあるときにのみスキャンフィルターが使用されていたことです。 Android Mから始まり、アプリに次の2つの権限のいずれかがない限り、バックグラウンドでのBluetooth LEスキャンはブロックされます。

Android.permission.ACCESS_COARSE_LOCATION
Android.permission.ACCESS_FINE_LOCATION

私がテストしていたアプリはこれらの権限のいずれも要求しなかったため、Android M.問題。

Logcatで次の行を見たので、これが問題だと気付きました。

09-22 22:35:20.152  5158  5254 E BluetoothUtils: Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results

詳細はこちらをご覧ください: https://code.google.com/p/Android-developer-preview/issues/detail?id=2964

19
davidgyoung

Bluetoothに接続するアプリで同様の問題が発生しました。 LE ScanFilterではありませんが、OPが持っていたのと同じように権限の問題でした。

根本的な原因は、SDK 23以降、ActivityrequestPermissions()メソッドを使用して、実行時にユーザーにアクセス許可を求める必要があることです。

ここに私のために働いたものがあります:

  1. ルートノード内の_AndroidManifest.xml_に次の2行のいずれかを追加します。

    _<uses-permission Android:name="Android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission Android:name="Android.permission.ACCESS_COARSE_LOCATION" />
    _
  2. アクティビティで、bluetoothへの接続を試みる前に、ActivityrequestPermissions()メソッドを呼び出します。これにより、システムダイアログが開き、ユーザーに許可を求めるプロンプトが表示されます。アクセス許可ダイアログは別のスレッドで開きます。そのため、Bluetoothへの接続を試みる前に結果を待ってください。

  3. ActivityonRequestPermissionsResult()をオーバーライドして、結果を処理します。このメソッドは、ユーザーが許可を拒否した場合にのみ実際に何かをする必要があり、アプリがbluetoothアクティビティを実行できないことをユーザーに伝えます。

このブログ投稿 には、AlertDialogsを使用してユーザーに何が起こっているかを伝えるサンプルコードがあります。良い出発点ですが、いくつかの欠点があります。

  • requestPermissions()スレッドが終了するまで待機しません。
  • requestPermissions()への呼び出しをラップするAlertDialogは、私には無関係のようです。 requestPermissions()への裸の呼び出しで十分です。
31
dinosaur

BLEとともに場所の許可を追加する

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

コピーこのメソッドを貼り付けて、場所のアクセス許可を要求および付与します

  @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
                Map<String, Integer> perms = new HashMap<String, Integer>();
                // Initial
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);


                // Fill with results
                for (int i = 0; i < permissions.length; i++)
                    perms.put(permissions[i], grantResults[i]);

                // Check for ACCESS_FINE_LOCATION
                if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED

                        ) {
                    // All Permissions Granted

                    // Permission Denied
                    Toast.makeText(ScanningActivity.this, "All Permission GRANTED !! Thank You :)", Toast.LENGTH_SHORT)
                            .show();


                } else {
                    // Permission Denied
                    Toast.makeText(ScanningActivity.this, "One or More Permissions are DENIED Exiting App :(", Toast.LENGTH_SHORT)
                            .show();

                    finish();
                }
            }
            break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }


    @TargetApi(Build.VERSION_CODES.M)
    private void fuckMarshMallow() {
        List<String> permissionsNeeded = new ArrayList<String>();

        final List<String> permissionsList = new ArrayList<String>();
        if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
            permissionsNeeded.add("Show Location");

        if (permissionsList.size() > 0) {
            if (permissionsNeeded.size() > 0) {

                // Need Rationale
                String message = "App need access to " + permissionsNeeded.get(0);

                for (int i = 1; i < permissionsNeeded.size(); i++)
                    message = message + ", " + permissionsNeeded.get(i);

                showMessageOKCancel(message,
                        new DialogInterface.OnClickListener() {

                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                            }
                        });
                return;
            }
            requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
            return;
        }

        Toast.makeText(ScanningActivity.this, "No new Permission Required- Launching App .You are Awesome!!", Toast.LENGTH_SHORT)
                .show();
    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(ScanningActivity.this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

    @TargetApi(Build.VERSION_CODES.M)
    private boolean addPermission(List<String> permissionsList, String permission) {

        if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsList.add(permission);
            // Check for Rationale Option
            if (!shouldShowRequestPermissionRationale(permission))
                return false;
        }
        return true;
    }

そして、onCreateで許可のチェック

 if (Build.VERSION.SDK_INT >= 23) {
            // Marshmallow+ Permission APIs
            fuckMarshMallow();
        }

時間を節約してください。

10
Hitesh Sahu

アプリがAndroid Qの場合、大まかな場所だけでは不十分です。細かい場所を使用する必要があります。そうしないと、このエラーが発生します。

E/BluetoothUtils: Permission denial: Need ACCESS_FINE_LOCATION permission to get scan results

公式ソースについては https://developer.Android.com/preview/privacy/camera-connectivity#fine-location-telephony-wifi-bt をご覧ください。

0
Emil