Merge "Fix compatible errors in SDK 21" into androidx-main
diff --git a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/BluetoothAdapterTest.kt b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/BluetoothAdapterTest.kt
index 4e138e1..492753f 100644
--- a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/BluetoothAdapterTest.kt
+++ b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/BluetoothAdapterTest.kt
@@ -112,6 +112,8 @@
         val fwkBluetoothAdapter =
             BluetoothManager(ApplicationProvider.getApplicationContext()).getAdapter()
         val bluetoothAdapter = BluetoothAdapter(fwkBluetoothAdapter)
+        assertTrue("Bluetooth is not enabled", bluetoothAdapter.isEnabled)
+
         // Check correct name
         val testName = "Bluetooth:Test-name_?~"
         val originalName = bluetoothAdapter.name
@@ -127,12 +129,9 @@
 
     private fun waitForAdapterNameChange(): Boolean {
         try {
-            Log.e("BluetoothAdapterTest", "trying")
             adapterNameChangedlock.lock()
             // Wait for the Adapter name to be changed
-            Log.e("BluetoothAdapterTest", "waiting")
             conditionAdapterNameChanged.await(5000, TimeUnit.MILLISECONDS)
-            Log.e("BluetoothAdapterTest", "finish waiting")
         } catch (e: InterruptedException) {
             Log.e("BluetoothAdapterTest", "waitForAdapterNameChange: interrrupted")
         } finally {
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattCharacteristic.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattCharacteristic.kt
index bd6c937..0422d55 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattCharacteristic.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattCharacteristic.kt
@@ -16,9 +16,10 @@
 
 package androidx.bluetooth.core
 
-import android.bluetooth.BluetoothGattCharacteristic as FwkBluetoothGattCharacteristic
 import android.os.Build
 import android.os.Bundle
+import android.bluetooth.BluetoothGattCharacteristic as FwkBluetoothGattCharacteristic
+import android.annotation.SuppressLint
 import androidx.annotation.RequiresApi
 import java.util.UUID
 
@@ -298,6 +299,7 @@
 
             val CREATOR: Bundleable.Creator<BluetoothGattCharacteristic> =
                 object : Bundleable.Creator<BluetoothGattCharacteristic> {
+                    @SuppressLint("SoonBlockedPrivateApi")
                     override fun fromBundle(bundle: Bundle): BluetoothGattCharacteristic {
                         val uuid = bundle.getString(keyForField(FIELD_FWK_CHARACTERISTIC_UUID))
                             ?: throw IllegalArgumentException("Bundle doesn't include uuid")
@@ -324,25 +326,24 @@
                             throw IllegalArgumentException("Bundle doesn't include keySize")
                         }
 
-                        val fwkCharacteristic =
-                            FwkBluetoothGattCharacteristic::class.java.getConstructor(
-                                UUID::class.java,
-                                Integer.TYPE,
-                                Integer.TYPE,
-                                Integer.TYPE,
-                            ).newInstance(
-                                UUID.fromString(uuid),
-                                instanceId,
-                                properties,
-                                permissions,
-                            )
-                        fwkCharacteristic.writeType = writeType
+                        val fwkCharacteristicWithoutPrivateField = FwkBluetoothGattCharacteristic(
+                            UUID.fromString(uuid),
+                            properties,
+                            permissions,
+                        )
 
-                        // Asserted, will always be true
-                        if (Build.VERSION.SDK_INT < 24) {
-                            fwkCharacteristic.javaClass.getDeclaredField("mKeySize")
-                                .setInt(fwkCharacteristic, keySize)
-                        }
+                        val fwkCharacteristic = fwkCharacteristicWithoutPrivateField.runCatching {
+                                this.writeType = writeType
+                                this.javaClass.getDeclaredField("mKeySize").let {
+                                    it.isAccessible = true
+                                    it.setInt(this, keySize)
+                                }
+                                this.javaClass.getDeclaredField("mInstanceId").let {
+                                    it.isAccessible = true
+                                    it.setInt(this, instanceId)
+                                }
+                                this
+                            }.getOrDefault(fwkCharacteristicWithoutPrivateField)
 
                         val gattCharacteristic = BluetoothGattCharacteristic(fwkCharacteristic)
 
@@ -417,10 +418,14 @@
             if (Build.VERSION.SDK_INT < 24) {
                 bundle.putInt(
                     keyForField(FIELD_FWK_CHARACTERISTIC_KEY_SIZE),
-                    fwkCharacteristic.javaClass.getDeclaredField("mKeySize")
-                        .getInt(fwkCharacteristic)
+                    fwkCharacteristic.javaClass.getDeclaredField("mKeySize").runCatching {
+                        this.isAccessible = true
+                        this.getInt(fwkCharacteristic)
+                    }.getOrDefault(16) // 16 is the default value in framework
+
                 )
             }
+
             val descriptorBundles = ArrayList(descriptors.map { it.toBundle() })
             bundle.putParcelableArrayList(
                 keyForField(FIELD_FWK_CHARACTERISTIC_DESCRIPTORS),
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattDescriptor.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattDescriptor.kt
index 612cdb0..c7800a7 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattDescriptor.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattDescriptor.kt
@@ -16,9 +16,9 @@
 
 package androidx.bluetooth.core
 
-import android.bluetooth.BluetoothGattDescriptor as FwkBluetoothGattDescriptor
 import android.os.Build
 import android.os.Bundle
+import android.bluetooth.BluetoothGattDescriptor as FwkBluetoothGattDescriptor
 import androidx.annotation.RequiresApi
 import java.util.UUID
 
@@ -168,15 +168,16 @@
                             throw IllegalArgumentException("Bundle doesn't include permission")
                         }
 
-                        val descriptor = FwkBluetoothGattDescriptor::class.java.getConstructor(
-                            UUID::class.java,
-                            Integer.TYPE,
-                            Integer.TYPE
-                        ).newInstance(
-                            UUID.fromString(uuid),
-                            instanceId,
-                            permissions
-                        )
+                        val descriptorWithoutInstanceId =
+                            FwkBluetoothGattDescriptor(UUID.fromString(uuid), permissions)
+                        val descriptor: FwkBluetoothGattDescriptor = descriptorWithoutInstanceId
+                            .runCatching {
+                                this::class.java.getDeclaredField("mInstance").let {
+                                    it.isAccessible = true
+                                    it.setInt(this, instanceId)
+                                    this
+                                }
+                            }.getOrDefault(descriptorWithoutInstanceId)
 
                         return BluetoothGattDescriptor(descriptor)
                     }
@@ -193,8 +194,10 @@
             val bundle = Bundle()
             bundle.putString(keyForField(FIELD_FWK_DESCRIPTOR_UUID), uuid.toString())
             bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_PERMISSIONS), permissions)
-            val instanceId: Int =
-                fwkDescriptor.javaClass.getDeclaredField("mInstance").getInt(fwkDescriptor)
+            val instanceId = fwkDescriptor.javaClass.getDeclaredField("mInstance").runCatching {
+                this.isAccessible = true
+                this.getInt(fwkDescriptor)
+            }.getOrDefault(0) // constructor will set instanceId to 0 by default
             bundle.putInt(keyForField(FIELD_FWK_DESCRIPTOR_INSTANCE), instanceId)
             return bundle
         }
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattService.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattService.kt
index b6b75b3..05e8963 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattService.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/BluetoothGattService.kt
@@ -17,6 +17,7 @@
 package androidx.bluetooth.core
 
 import android.bluetooth.BluetoothGattService as FwkBluetoothGattService
+import android.annotation.SuppressLint
 import android.os.Build
 import android.os.Bundle
 import androidx.annotation.RequiresApi
@@ -203,6 +204,7 @@
 
             val CREATOR: Bundleable.Creator<BluetoothGattService> =
                 object : Bundleable.Creator<BluetoothGattService> {
+                    @SuppressLint("SoonBlockedPrivateApi")
                     override fun fromBundle(bundle: Bundle): BluetoothGattService {
                         val uuid = bundle.getString(keyForField(FIELD_FWK_SERVICE_UUID))
                             ?: throw IllegalArgumentException("Bundle doesn't include uuid")
@@ -213,11 +215,15 @@
                             throw IllegalArgumentException("Bundle doesn't include service type")
                         }
 
-                        val fwkService = FwkBluetoothGattService::class.java.getConstructor(
-                            UUID::class.java,
-                            Integer.TYPE,
-                            Integer.TYPE,
-                        ).newInstance(UUID.fromString(uuid), instanceId, type)
+                        val fwkServiceWithoutInstanceId =
+                            FwkBluetoothGattService(UUID.fromString(uuid), type)
+                        val fwkService = fwkServiceWithoutInstanceId.runCatching {
+                            this.javaClass.getDeclaredField("mInstanceId").let {
+                                it.isAccessible = true
+                                it.setInt(this, instanceId)
+                            }
+                            this
+                        }.getOrDefault(fwkServiceWithoutInstanceId)
 
                         val gattService = BluetoothGattService(fwkService)