dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.93k stars 527 forks source link

Crash Android 12 Bluetooth Connect #7067

Closed DavidMarquezF closed 2 years ago

DavidMarquezF commented 2 years ago

Android application type

Classic Xamarin.Android (MonoAndroid12.0, etc.)

Affected platform version

VS 2022 17.0.4

Description

With an Android 12 device, after following android's bluetooth permission article when trying to call BluetoothAdapter.ConnectAsync() the app crashes with:

-       $exception  {Java.Lang.SecurityException: UID 10424 / PID 6333 lacks permission android.permission.BLUETOOTH
  at Java.Interop.JniEnvironment+InstanceMethods.CallVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <dd8770963db64939be322a2c6d46563b>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00014] in <dd8770963db64939be322a2c6d46563b>:0 
  at Android.Bluetooth.BluetoothSocket.Connect () [0x00000] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-31/mcw/Android.Bluetooth.BluetoothSocket.cs:167 
  at Android.Bluetooth.BluetoothSocket.<ConnectAsync>b__26_0 () [0x00000] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-31/mcw/Android.Bluetooth.BluetoothSocket.cs:174 
  at System.Threading.Tasks.Task.InnerInvoke () [0x0000f] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2476 
  at System.Threading.Tasks.Task.Execute () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2319 
--- End of stack trace from previous location where exception was thrown ---

As you can see the crash reports a missing permission: android.permission.BLUETOOTH. However, from what I understand this permission has been removed in Android 12 and in the docs they state that you must declare it like this:

<uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />

Steps to Reproduce

  1. Create a new app
  2. Add the following permissions in manifest:

    
    <manifest>
    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />
    
    <!-- Needed only if your app communicates with already-paired Bluetooth
         devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />


3. Request for runtime permissions for BLUETOOTH_CONNECT
4. Get Bonded devices list and try to connect to one with `ConnectAsync`
5. App crashes with the exception stated before

### Did you find any workaround?

Apparently removing `android:maxSdkVersion="30"` lets it work again

### Relevant log output

_No response_
gmck commented 2 years ago

@DavidMarquez

Add, even if you don't scan directly.

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />

DavidMarquezF commented 2 years ago

Why though? The Android docs don't say you need it for my intended case (which is only connecting to already paired components). Adding an extra permission in the Manifest just to fix this issue is not ideal since you should always try to keep them at a minimum

gmck commented 2 years ago

@DavidMarquezF

Well in my case I expected the same as you. But it appears that if you use a BluetoothAdapter as in

BluetoothManager manager = Application.Context.GetSystemService(Application.BluetoothService) as BluetoothManager;
BluetoothAdapter bluetoothAdapter = manager.Adapter;

and then get a list of paired devices as in ICollection<BluetoothDevice> pairedDevices = bluetoothAdapter.BondedDevices;

They seem to consider that as a scan. I'm presuming you offer the user a list of Bluetooth devices to choose from?

Note that Android 12 also changed from BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter to bluetoothAdapter.BondedDevices.

The other comment I had in my manifest was <!--We don't need this because - we don't scan - but we do cancel.discovery check -->

However, I do cancel.discovery in my library code, prior to Connecting. So overall I just went along with adding the extra permission.

Does adding the extra permission fix your Java.Lang.SecurityException?

From memory (it was a long time ago) I didn't get a - lacks permission android.permission.BLUETOOTH, I'm pretty sure it was about scanning or connecting. I'd have to comment out the permission code and test again if you want confirmation.

I support both Bluetooth Classic and BluetoothLE. I definitely don't scan for the BluetoothLE devices as there is no standard for these particular devices, each manufacturer uses different Guids for Services etc, so I need their documentation before I can support those devices.

grendello commented 2 years ago

@DavidMarquezF @gmck's advice is correct. Also, please not that it is not a Xamarin.Android issue and we can't fix it. The exception is thrown by the Android runtime, not by us, we simply relay it to the managed land. Xamarin.Android doesn't implement any of the APIs you call, the android-31/mcw/ fragment of the source file path in stack trace tells you that this is a generated wrapper around Java APIs, which merely "forwards" the call to the Java side using the JNI interfaces (which is what the top two frames of the stack trace shows). When the Java side throws the exception, we capture it and wrap it in a managed one.

If you think the behavior is incorrect, I'm afraid you will need to raise an issue with Android directly as there's nothing Xamarin.Android can do here :(

DavidMarquezF commented 2 years ago

Okay, thank you @grendello @gmck . I will create an issue on their side and try to get to the bottom of this.

@gmck I'm already getting the list with BondedDevices and I don't get the permission error. It is only thrown when I call the Connect function.

gmck commented 2 years ago

@DavidMarquezF ,

Yes, you are correct. I must have already selected a device to get to cancel.discovery, which is right before Connect. I'll have to check the code in my library in the morning. I'll try and recreate it so we can compare what permission is missing.

gmck commented 2 years ago

@DavidMarquezF,

My comments were wrong last night.

Today, I just commented out both permissions and then commented out the code requesting permissions.

I run code, a BottomSheetDialog just before opening the SettingsFragment and warn an Android 12 user that they will get a permission request dialog before the SettingsFragment opens. I immediately got java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for android.content.AttributionSource@afc12dc6: AdapterService getBondedDevices.

Reversed the code and added back just the BLUETOOTH_ CONNECT permission and got past it.

Later when I tried to connect to a device I get the following java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for android.content.AttributionSource@66ee80fd: AdapterService cancelDiscovery

Reversed more code and added back BLUETOOTH_SCAN to the manifest and everything was working as it was before.

I don't understand how you managed to get past AdapterService getBondedDevices.

Back to you...

DavidMarquezF commented 2 years ago

@gmck The error you got is expected and is correct since from Android 12 you must ask permissions for the Bluetooth Connect permission in the manifest and in runtime. In my code I ask and get that permission, which is why I can get the bonded devices.

However, if you look at the error in my logs, it is an android.permission.BLUETOOTH, not BLUETOOTH_CONNECT, which in theory should not exist in Andorid 12 because the bluetooth permissions have changed. Furthermore, it only happens when I call connect, not when I access the bonded devices. Calling connect and getting the bonded devices require the same level of permissions from my understanding.

Even though I understand @grendello s comment on how xamarin justs wraps android, I still find it kind of weird that the permission setup provided in the official Android Docs doesn't work. Maybe I should create a barebones java project and an xamarin project doing just this.

grendello commented 2 years ago

@DavidMarquezF recreating the issue in Java is always a good idea, indeed. If you can't reproduce it there, then there's a chance that it might be an issue with our bindings generator (mapping of some values, enumification) - in that case, do reopen this issue and we'll take a look at that.

gmck commented 2 years ago

@DavidMarquezF, @grendello

I just tried removing <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> without touching the other 3 permissions and it runs ok, so it looks as though Android 12 doesn't even care about the BLUETOOTH permission. However, that doesn't explain your original exception where it only mentions BLUETOOTH and not the others.

As @grendello mentioned, maybe Xamarin.Android is returning the wrong permission value for the exception, even though it returns the correct value for me.

Can I assume you don't also need BLUETOOTH_SCAN?

I reckon your idea of the java project is the only way to find out.

Let me know if you want me to test any other combinations. It all seemed so straightforward when I added this stuff last year. When it crashed I just added whatever permission it was failing on and each time it just worked.

DavidMarquezF commented 2 years ago

Yup, I don't need BLUETOOTH_SCAN. I hope in the next weeks I have time to go back to the project. I'll try to do the Java and Xamarin comparison just to make sure. Thanks!

DavidMarquezF commented 2 years ago

I created barebones projects with Xamarin and Kotlin. Indeed it crashes with the Kotlin project too so I guess the problem is related with them and not with Xamarin.

It's so frustrating that Android is so hard to develop on. I'm going to try to dig a bit deeper and see if I find why this is happening, since from what I understand from the docs I'm doing everything I should.

DavidMarquezF commented 2 years ago

If anyone is interested in why this is happening I created a bug report on Android.