[go: nahoru, domu]

Add flag to switch between aosp and gmscore.

When there's no GMS support, androidx API will try to use the AOSP uwb backend service that are distributed to OEMs via AOSP platform (https://cs.android.com/android/platform/superproject/+/master:packages/modules/Uwb/androidx_backend/).

Test: demo app
Bug: 258009640
Change-Id: Id60067eefb3c042527e94399ebe4f72b306c1fcc
diff --git a/core/uwb/uwb-rxjava3/build.gradle b/core/uwb/uwb-rxjava3/build.gradle
index 52d84ae..2e29520 100644
--- a/core/uwb/uwb-rxjava3/build.gradle
+++ b/core/uwb/uwb-rxjava3/build.gradle
@@ -44,6 +44,7 @@
 
 android {
     defaultConfig {
+        minSdkVersion 33
         multiDexEnabled = true
     }
 
diff --git a/core/uwb/uwb/build.gradle b/core/uwb/uwb/build.gradle
index 85d148ce..24a6baa 100644
--- a/core/uwb/uwb/build.gradle
+++ b/core/uwb/uwb/build.gradle
@@ -55,6 +55,7 @@
 android {
     namespace "androidx.core.uwb"
     defaultConfig {
+        minSdkVersion 33
         multiDexEnabled = true
     }
 }
diff --git a/core/uwb/uwb/src/main/AndroidManifest.xml b/core/uwb/uwb/src/main/AndroidManifest.xml
index ef5bd56..fd303e0 100644
--- a/core/uwb/uwb/src/main/AndroidManifest.xml
+++ b/core/uwb/uwb/src/main/AndroidManifest.xml
@@ -15,5 +15,7 @@
   limitations under the License.
   -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
-
-</manifest>
\ No newline at end of file
+    <queries>
+        <package android:name="androidx.core.uwb.backend" />
+    </queries>
+</manifest>
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeAospImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeAospImpl.kt
new file mode 100644
index 0000000..fea81c2
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbClientSessionScopeAospImpl.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import android.util.Log
+import androidx.core.uwb.RangingCapabilities
+import androidx.core.uwb.RangingMeasurement
+import androidx.core.uwb.RangingParameters
+import androidx.core.uwb.RangingResult
+import androidx.core.uwb.UwbAddress
+import androidx.core.uwb.UwbControleeSessionScope
+import androidx.core.uwb.backend.IRangingSessionCallback
+import androidx.core.uwb.backend.IUwbClient
+import androidx.core.uwb.backend.RangingPosition
+import androidx.core.uwb.backend.UwbComplexChannel
+import androidx.core.uwb.backend.UwbDevice
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+internal class UwbClientSessionScopeAospImpl(
+    private val uwbClient: IUwbClient,
+    override val rangingCapabilities: RangingCapabilities,
+    override val localAddress: UwbAddress,
+) : UwbControleeSessionScope {
+    companion object {
+        private const val TAG = "UwbClientSessionScope"
+    }
+    private var sessionStarted = false
+
+    override fun prepareSession(parameters: RangingParameters) = callbackFlow {
+        if (sessionStarted) {
+            throw IllegalStateException("Ranging has already started. To initiate " +
+                "a new ranging session, create a new client session scope.")
+        }
+
+        val parametersBuilder1 = androidx.core.uwb.backend.RangingParameters()
+        parametersBuilder1.uwbConfigId = when (parameters.uwbConfigType) {
+            RangingParameters.UWB_CONFIG_ID_1 -> RangingParameters.UWB_CONFIG_ID_1
+            RangingParameters.UWB_CONFIG_ID_3 -> RangingParameters.UWB_CONFIG_ID_3
+            else -> throw IllegalArgumentException("The selected UWB Config Id is not a valid id.")
+        }
+        parametersBuilder1.rangingUpdateRate = when (parameters.updateRateType) {
+            RangingParameters.RANGING_UPDATE_RATE_AUTOMATIC ->
+                RangingParameters.RANGING_UPDATE_RATE_AUTOMATIC
+            RangingParameters.RANGING_UPDATE_RATE_FREQUENT ->
+                RangingParameters.RANGING_UPDATE_RATE_FREQUENT
+            RangingParameters.RANGING_UPDATE_RATE_INFREQUENT ->
+                RangingParameters.RANGING_UPDATE_RATE_INFREQUENT
+            else -> throw IllegalArgumentException(
+                "The selected ranging update rate is not a valid update rate.")
+        }
+        parametersBuilder1.sessionId = parameters.sessionId
+        parametersBuilder1.sessionKeyInfo = parameters.sessionKeyInfo
+        if (parameters.complexChannel != null) {
+            val channel = UwbComplexChannel()
+            channel.channel = parameters.complexChannel.channel
+            channel.preambleIndex = parameters.complexChannel.preambleIndex
+            parametersBuilder1.complexChannel = channel
+        }
+
+        val peerList = ArrayList<UwbDevice>()
+        for (peer in parameters.peerDevices) {
+            val device = UwbDevice()
+            val address = androidx.core.uwb.backend.UwbAddress()
+            address.address = peer.address.address
+            device.address = address
+            peerList.add(device)
+        }
+        parametersBuilder1.peerDevices = peerList
+        val callback =
+            object : IRangingSessionCallback.Stub() {
+                override fun onRangingInitialized(device: UwbDevice) {
+                    Log.i(TAG, "Started UWB ranging.")
+                }
+
+                override fun onRangingResult(device: UwbDevice, position: RangingPosition) {
+                    trySend(
+                        RangingResult.RangingResultPosition(
+                            androidx.core.uwb.UwbDevice(UwbAddress(device.address?.address!!)),
+                            androidx.core.uwb.RangingPosition(
+                                position.distance?.let { RangingMeasurement(it.value) },
+                                position.azimuth?.let {
+                                    RangingMeasurement(it.value)
+                                },
+                                position.elevation?.let {
+                                    RangingMeasurement(it.value)
+                                },
+                                position.elapsedRealtimeNanos
+                            )
+                        )
+                    )
+                }
+
+                override fun onRangingSuspended(device: UwbDevice, reason: Int) {
+                    trySend(
+                        RangingResult.RangingResultPeerDisconnected(
+                            androidx.core.uwb.UwbDevice(UwbAddress(device.address?.address!!))
+                        )
+                    )
+                }
+
+                override fun getInterfaceVersion(): Int {
+                    return 0
+                }
+
+                override fun getInterfaceHash(): String {
+                    return ""
+                }
+            }
+
+        try {
+            uwbClient.startRanging(parametersBuilder1, callback)
+            sessionStarted = true
+        } catch (e: Exception) {
+            throw(e)
+        }
+
+        awaitClose {
+            CoroutineScope(Dispatchers.Main.immediate).launch {
+                try {
+                    uwbClient.stopRanging(callback)
+                } catch (e: Exception) {
+                    throw(e)
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControleeSessionScopeAospImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControleeSessionScopeAospImpl.kt
new file mode 100644
index 0000000..e39bed3
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControleeSessionScopeAospImpl.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import androidx.core.uwb.RangingCapabilities
+import androidx.core.uwb.RangingParameters
+import androidx.core.uwb.RangingResult
+import androidx.core.uwb.UwbAddress
+import androidx.core.uwb.UwbControleeSessionScope
+import androidx.core.uwb.backend.IUwbClient
+import kotlinx.coroutines.flow.Flow
+
+internal class UwbControleeSessionScopeAospImpl(
+    uwbClient: IUwbClient,
+    override val rangingCapabilities: RangingCapabilities,
+    override val localAddress: UwbAddress
+) : UwbControleeSessionScope {
+    private val uwbClientSessionScope =
+        UwbClientSessionScopeAospImpl(uwbClient, rangingCapabilities, localAddress)
+
+    override fun prepareSession(parameters: RangingParameters): Flow<RangingResult> {
+        return uwbClientSessionScope.prepareSession(parameters)
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControllerSessionScopeAospImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControllerSessionScopeAospImpl.kt
new file mode 100644
index 0000000..0630d41
--- /dev/null
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbControllerSessionScopeAospImpl.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.uwb.impl
+
+import androidx.core.uwb.RangingCapabilities
+import androidx.core.uwb.RangingParameters
+import androidx.core.uwb.RangingResult
+import androidx.core.uwb.UwbAddress
+import androidx.core.uwb.UwbComplexChannel
+import androidx.core.uwb.UwbControllerSessionScope
+import androidx.core.uwb.backend.IUwbClient
+import kotlinx.coroutines.flow.Flow
+
+internal class UwbControllerSessionScopeAospImpl(
+    private val uwbClient: IUwbClient,
+    override val rangingCapabilities: RangingCapabilities,
+    override val localAddress: UwbAddress,
+    override val uwbComplexChannel: UwbComplexChannel
+) : UwbControllerSessionScope {
+    private val uwbClientSessionScope =
+        UwbClientSessionScopeAospImpl(uwbClient, rangingCapabilities, localAddress)
+    override suspend fun addControlee(address: UwbAddress) {
+        val uwbAddress = androidx.core.uwb.backend.UwbAddress()
+        uwbAddress.address = address.address
+        try {
+            uwbClient.addControlee(uwbAddress)
+        } catch (e: Exception) {
+            throw(e)
+        }
+    }
+
+    override suspend fun removeControlee(address: UwbAddress) {
+        val uwbAddress = androidx.core.uwb.backend.UwbAddress()
+        uwbAddress.address = address.address
+        try {
+            uwbClient.removeControlee(uwbAddress)
+        } catch (e: Exception) {
+            throw(e)
+        }
+    }
+
+    override fun prepareSession(parameters: RangingParameters): Flow<RangingResult> {
+        return uwbClientSessionScope.prepareSession(parameters)
+    }
+}
\ No newline at end of file
diff --git a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
index bbf9cec..84297cf 100644
--- a/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
+++ b/core/uwb/uwb/src/main/java/androidx/core/uwb/impl/UwbManagerImpl.kt
@@ -16,7 +16,12 @@
 
 package androidx.core.uwb.impl
 
+import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
+import android.util.Log
 import androidx.core.uwb.RangingCapabilities
 import androidx.core.uwb.UwbAddress
 import androidx.core.uwb.UwbClientSessionScope
@@ -24,13 +29,35 @@
 import androidx.core.uwb.UwbControleeSessionScope
 import androidx.core.uwb.UwbControllerSessionScope
 import androidx.core.uwb.UwbManager
+import androidx.core.uwb.backend.IUwb
 import com.google.android.gms.common.api.ApiException
 import com.google.android.gms.nearby.Nearby
 import kotlinx.coroutines.tasks.await
 import androidx.core.uwb.helper.checkSystemFeature
 import androidx.core.uwb.helper.handleApiException
+import com.google.android.gms.common.ConnectionResult
+import com.google.android.gms.common.GoogleApiAvailability
 
 internal class UwbManagerImpl(private val context: Context) : UwbManager {
+    companion object {
+        const val TAG = "UwbMangerImpl"
+        var iUwb: IUwb? = null
+    }
+
+    init {
+        val connection = object : ServiceConnection {
+            override fun onServiceConnected(className: ComponentName, service: IBinder) {
+                iUwb = IUwb.Stub.asInterface(service)
+                Log.i(TAG, "iUwb service created successfully.")
+            }
+            override fun onServiceDisconnected(p0: ComponentName?) {
+                iUwb = null
+            }
+        }
+        val intent = Intent("androidx.core.uwb.backend.service")
+        intent.setPackage("androidx.core.uwb.backend")
+        context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
+    }
     @Deprecated("Renamed to controleeSessionScope")
     override suspend fun clientSessionScope(): UwbClientSessionScope {
         return createClientSessionScope(false)
@@ -46,6 +73,14 @@
 
     private suspend fun createClientSessionScope(isController: Boolean): UwbClientSessionScope {
         checkSystemFeature(context)
+        val hasGmsCore = GoogleApiAvailability.getInstance()
+            .isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
+        return if (hasGmsCore) createGmsClientSessionScope(isController)
+        else createAospClientSessionScope(isController)
+    }
+
+    private suspend fun createGmsClientSessionScope(isController: Boolean): UwbClientSessionScope {
+        Log.i(TAG, "Creating Gms Client session scope")
         val uwbClient = if (isController)
             Nearby.getUwbControllerClient(context) else Nearby.getUwbControleeClient(context)
         try {
@@ -77,4 +112,41 @@
                 "up-to-date with the service backend.")
         }
     }
+
+    private fun createAospClientSessionScope(isController: Boolean): UwbClientSessionScope {
+        Log.i(TAG, "Creating Aosp Client session scope")
+        val uwbClient = if (isController)
+            iUwb?.controllerClient else iUwb?.controleeClient
+        if (uwbClient == null) {
+            Log.e(TAG, "Failed to get UwbClient. AOSP backend is not available.")
+        }
+        try {
+            val aospLocalAddress = uwbClient!!.localAddress
+            val aospRangingCapabilities = uwbClient.rangingCapabilities
+            val localAddress = aospLocalAddress?.address?.let { UwbAddress(it) }
+            val rangingCapabilities = aospRangingCapabilities?.let {
+                RangingCapabilities(
+                    it.supportsDistance,
+                    it.supportsAzimuthalAngle,
+                    it.supportsElevationAngle)
+            }
+            return if (isController) {
+                val uwbComplexChannel = uwbClient.complexChannel
+                UwbControllerSessionScopeAospImpl(
+                    uwbClient,
+                    rangingCapabilities!!,
+                    localAddress!!,
+                    UwbComplexChannel(uwbComplexChannel!!.channel, uwbComplexChannel.preambleIndex)
+                )
+            } else {
+                UwbControleeSessionScopeAospImpl(
+                    uwbClient,
+                    rangingCapabilities!!,
+                    localAddress!!
+                )
+            }
+        } catch (e: Exception) {
+            throw e
+        }
+    }
 }
\ No newline at end of file