蓝牙 LE 扫描在后台失败 - 权限 [英] Bluetooth LE Scan fails in the background - permissions

查看:61
本文介绍了蓝牙 LE 扫描在后台失败 - 权限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码在运行 Android 5.1.1 (Build LMY48M) 的 Nexus 9 上运行良好,但不适用于运行 Android 6.0 (Build MPA44l) 的 Nexus 9

The following code works great on my Nexus 9 running Android 5.1.1 (Build LMY48M), but won't work on a Nexus 9 running Android 6.0 (Build 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() {
  ...
});

在 Android 5.x 上,当看到与扫描过滤器匹配的制造商广告时,上述代码会产生回调.(请参阅下面的示例 Logcat 输出.)在带有 MPA44l 的 Nexus 9 上,没有收到回调.如果您注释掉扫描过滤器,则 Nexus 9 将成功接收回调.

On Android 5.x, the above code yields a callback when a manufacturer advertisement matching the scan filter is seen. (See example Logcat output below.) On the Nexus 9 with MPA44l, no callbacks are received. If you comment out the scan filter, callbacks are received successfully on the 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}

有人见过 ScanFilters 在 Android M 上工作吗?

Has anybody seen ScanFilters work on Android M?

推荐答案

问题不是扫描过滤器,而是后台权限.

The problem was not the scan filter, but background permissions.

Android 10-11:

为了在后台检测 BLE 设备,您必须在清单中拥有多个权限.将以下内容放在您的 AndroidManifest.xml 中:

In order to detect BLE devices in the background, you must have several permissions in the manifest. Place the following in your AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

然后将如下代码添加到您的 Activity 以动态地向用户请求这些权限:

Then add code like follows to your Activity to dynamically request these permissions from the user:

    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
    private static final int PERMISSION_REQUEST_BACKGROUND_LOCATION = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...



        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                if (this.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                        != PackageManager.PERMISSION_GRANTED) {
                    if (this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
                        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        builder.setTitle("This app needs background location access");
                        builder.setMessage("Please grant location access so this app can detect beacons in the background.");
                        builder.setPositiveButton(android.R.string.ok, null);
                        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                            @TargetApi(23)
                            @Override
                            public void onDismiss(DialogInterface dialog) {
                                requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                                        PERMISSION_REQUEST_BACKGROUND_LOCATION);
                            }

                        });
                        builder.show();
                    }
                    else {
                        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        builder.setTitle("Functionality limited");
                        builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons in the background.  Please go to Settings -> Applications -> Permissions and grant background location access to this app.");
                        builder.setPositiveButton(android.R.string.ok, null);
                        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                            @Override
                            public void onDismiss(DialogInterface dialog) {
                            }

                        });
                        builder.show();
                    }

                }
            } else {
                if (!this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                    requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                                    Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                            PERMISSION_REQUEST_FINE_LOCATION);
                }
                else {
                    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("Functionality limited");
                    builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons.  Please go to Settings -> Applications -> Permissions and grant location access to this app.");
                    builder.setPositiveButton(android.R.string.ok, null);
                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                        @Override
                        public void onDismiss(DialogInterface dialog) {
                        }

                    });
                    builder.show();
                }

            }
        }
    }

当您提示用户提供位置权限时,操作系统对话框将让他们选择将该权限请求降级为仅在使用应用程序时允许";与始终允许".如果用户选择第一个选项,即使设置了上述所有其他内容,您也不会在后台进行检测.

When you prompt the user for location permission, the OS dialog will give them the option to downgrade that permission request to "Allow Only While Using the App" vs. "Allow All the Time". If the user chooses the first option, you will not get detections in the background, even if everything else above is set up.

在 Android 11 上,事情变得更加复杂,因为操作系统提供了另一个仅此一次"选项.对于许可请求.如果您的应用以 SDK 30 (Android 11) 为目标,它甚至不会为用户提供始终允许"选项,并且用户必须转到设置"作为单独的步骤以打开所有时间访问.请参阅此处,了解有关其工作方式的更多详细信息安卓 11.

On Android 11, things get more complex still, as the OS offers yet another option of "Only this time" for the permission request. If your app targets SDK 30 (Android 11), it won't even offer the user the option for "Allow All the Time", and the user will have to go to Settings as a separate step to turn on all the time access. See here for more details on the way this works on Android 11.

有关权限提示演变的更广泛讨论,请参阅我的博客文章此处.

For a broader discussion of the evolution of permissions prompting, see my blog post here.

Android 10 之前:

从 Android M 开始,除非应用具有以下两个权限之一,否则后台的低功耗蓝牙扫描将被阻止:

Starting with Android M, Bluetooth LE scanning in the background is blocked unless the app has one of the following two permissions:

android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION

我正在测试的应用没有请求这些权限中的任何一个,因此它在 Android M 上无法在后台运行(扫描过滤器处于活动状态的唯一时间).添加第一个解决了问题.

The app I was testing did not request either of these permissions, so it did not work in the background (the only time the scan filter was active) on Android M. Adding the first one solved the problem.

我意识到这是问题所在,因为我在 Logcat 中看到了以下行:

I realized this was the problem because I saw the following line in 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

这篇关于蓝牙 LE 扫描在后台失败 - 权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆