Android 12+设备BLE扫描崩溃问题技术求助
Android 12目标版本下Android 11设备BLE扫描崩溃问题排查与解决
我是Haia Consultancy的实习生,使用Kotlin开发Android应用。当前应用targetSDK设为32(对应Android 12),在S21+(Android 11)设备上执行BLE扫描时应用崩溃,低版本Android设备无此问题。错误日志提示需要android.permission.BLUETOOTH_SCAN权限,我已按官方指南配置Manifest权限、申请位置权限,甚至直接使用Android 12+适配的官方示例代码,仍然报错。检查权限时,BLUETOOTH_SCAN返回-1,尝试多种方案无效。最后发现将权限检查改为BLUETOOTH_CONNECT后代码可正常运行,但不清楚原因,求问问题根源及解决方法。
错误日志
2022-12-07 16:01:45.692 28797-28797 AndroidRuntime com...e.ultrawidebandproofofconcept E FATAL EXCEPTION: main Process: com.example.ultrawidebandproofofconcept, PID: 28797 java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for AttributionSource { uid = 10366, packageName = com.example.ultrawidebandproofofconcept, attributionTag = null, token = android.os.BinderProxy@ff32c8f, next = null }: GattService registerScanner at android.os.Parcel.createExceptionOrNull(Parcel.java:2438) at android.os.Parcel.createException(Parcel.java:2422) at android.os.Parcel.readException(Parcel.java:2405) at android.os.Parcel.readException(Parcel.java:2347) at android.bluetooth.IBluetoothGatt$Stub$Proxy.registerScanner(IBluetoothGatt.java:1886) at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper.startRegistration(BluetoothLeScanner.java:519) at android.bluetooth.le.BluetoothLeScanner.startScan(BluetoothLeScanner.java:305) at android.bluetooth.le.BluetoothLeScanner.startScan(BluetoothLeScanner.java:161) at com.example.ultrawidebandproofofconcept.MainActivity.startBleScan(MainActivity.kt:96) at com.example.ultrawidebandproofofconcept.MainActivity.onCreate$lambda-0(MainActivity.kt:80) at com.example.ultrawidebandproofofconcept.MainActivity.$r8$lambda$NQsLDkI5TsYB6INH-F0JwBK_SNM(Unknown Source:0) at com.example.ultrawidebandproofofconcept.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:2) at android.view.View.performClick(View.java:7792) at android.widget.TextView.performClick(TextView.java:16112) at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219) at android.view.View.performClickInternal(View.java:7769) at android.view.View.access$3800(View.java:910) at android.view.View$PerformClick.run(View.java:30218) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8751) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
已尝试方案
- 申请
ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION权限 - 在Manifest中声明BLE扫描无需关联位置信息(添加
android:usesPermissionFlags="neverForLocation") - 使用Android官方提供的Android 12+ BLE适配示例代码
崩溃代码
if (ActivityCompat.checkSelfPermission( this, Manifest.permission.BLUETOOTH_SCAN ) != PackageManager.PERMISSION_GRANTED ) { requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_SCAN ) requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_CONNECT ) return } bleScanner.startScan(null, scanSettings, scanCallback)
可行代码
if (ActivityCompat.checkSelfPermission( this, Manifest.permission.BLUETOOTH_CONNECT ) != PackageManager.PERMISSION_GRANTED ) { requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_SCAN ) requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_CONNECT ) return } bleScanner.startScan(null, scanSettings, scanCallback)
问题根源
这是targetSDK版本与运行设备系统版本的权限兼容性冲突导致的:
BLUETOOTH_SCAN是Android 12新增的危险权限,当应用targetSDK=32时,系统要求动态申请该权限;但Android 11及以下系统中不存在这个权限,ActivityCompat.checkSelfPermission检查该权限会直接返回PERMISSION_DENIED(即-1)。- 崩溃代码中,因
BLUETOOTH_SCAN检查未通过,进入权限申请逻辑,但Android 11系统无法识别该权限,导致requestPermissionLauncher无法弹出对应的授权对话框,权限始终无法被授予,后续执行startScan时触发SecurityException。 - 可行代码中检查的
BLUETOOTH_CONNECT同样是Android 12新增权限,但在Android 11中,系统会将该权限检查映射为旧的BLUETOOTH权限(Android 11及以下中,BLE扫描仅需BLUETOOTH和位置权限)。若已在Manifest中声明旧版BLUETOOTH权限,系统会判定BLUETOOTH_CONNECT权限已授予,跳过申请逻辑直接执行扫描,此时设备已具备所需权限,扫描正常运行。
解决方法
正确的做法是根据运行设备的系统版本,区分权限检查与申请逻辑:
val isAndroid12OrHigher = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val hasRequiredPermission = if (isAndroid12OrHigher) { ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED } else { ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED } if (!hasRequiredPermission) { val permissionsToRequest = if (isAndroid12OrHigher) { arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT) } else { arrayOf(Manifest.permission.BLUETOOTH, Manifest.permission.ACCESS_FINE_LOCATION) } requestPermissionLauncher.launch(permissionsToRequest) return } bleScanner.startScan(null, scanSettings, scanCallback)
同时,确保Manifest中同时声明新旧权限,覆盖不同系统版本:
<!-- 旧版本Android权限(Android 11及以下) --> <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.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/> <!-- Android 12+权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
内容的提问来源于stack exchange,提问作者SctyGmr
相关产品推荐
相关产品推荐

