Create ProvisioningRepository

Also check if has IMS feature before calling ProvisioningManager.

Bug: 358000881
Flag: EXEMPT refactor
Test: manual - on Mobile Settings
Test: atest ProvisioningRepositoryTest
Change-Id: I5fffff6c63af8c67d666b0e855f026633bb36651
This commit is contained in:
Chaohui Wang
2024-08-12 13:28:56 +08:00
parent a6678ad6f6
commit f245b3e04f
4 changed files with 142 additions and 94 deletions

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2024 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 com.android.settings.network.telephony.ims
import android.telephony.ims.ProvisioningManager
import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback
import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability
import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech
import android.util.Log
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
private const val TAG = "ImsFeatureProvisioned"
fun imsFeatureProvisionedFlow(
subId: Int,
@MmTelCapability capability: Int,
@ImsRegistrationTech tech: Int,
): Flow<Boolean> = imsFeatureProvisionedFlow(
subId = subId,
capability = capability,
tech = tech,
provisioningManager = ProvisioningManager.createForSubscriptionId(subId),
)
@VisibleForTesting
fun imsFeatureProvisionedFlow(
subId: Int,
@MmTelCapability capability: Int,
@ImsRegistrationTech tech: Int,
provisioningManager : ProvisioningManager,
): Flow<Boolean> = callbackFlow {
val callback = object : FeatureProvisioningCallback() {
override fun onFeatureProvisioningChanged(
receivedCapability: Int,
receivedTech: Int,
isProvisioned: Boolean,
) {
if (capability == receivedCapability && tech == receivedTech) trySend(isProvisioned)
}
override fun onRcsFeatureProvisioningChanged(
capability: Int,
tech: Int,
isProvisioned: Boolean,
) {
}
}
provisioningManager.registerFeatureProvisioningChangedCallback(
Dispatchers.Default.asExecutor(),
callback,
)
trySend(provisioningManager.getProvisioningStatusForCapability(capability, tech))
awaitClose { provisioningManager.unregisterFeatureProvisioningChangedCallback(callback) }
}.catch { e ->
Log.w(TAG, "[$subId] error while imsFeatureProvisionedFlow", e)
}.conflate().onEach {
Log.d(TAG, "[$subId] changed: capability=$capability tech=$tech isProvisioned=$it")
}.flowOn(Dispatchers.Default)

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2024 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 com.android.settings.network.telephony.ims
import android.content.Context
import android.content.pm.PackageManager
import android.telephony.SubscriptionManager
import android.telephony.ims.ProvisioningManager
import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback
import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability
import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
class ProvisioningRepository(
private val context: Context,
private val provisioningManagerFactory: (Int) -> ProvisioningManager =
ProvisioningManager::createForSubscriptionId,
) {
fun imsFeatureProvisionedFlow(
subId: Int,
@MmTelCapability capability: Int,
@ImsRegistrationTech tech: Int,
): Flow<Boolean> {
if (!SubscriptionManager.isValidSubscriptionId(subId) ||
!context.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
return flowOf(false)
}
val provisioningManager = provisioningManagerFactory(subId)
return callbackFlow {
val callback =
object : FeatureProvisioningCallback() {
override fun onFeatureProvisioningChanged(
receivedCapability: Int,
receivedTech: Int,
isProvisioned: Boolean,
) {
if (capability == receivedCapability && tech == receivedTech)
trySend(isProvisioned)
}
override fun onRcsFeatureProvisioningChanged(
capability: Int,
tech: Int,
isProvisioned: Boolean,
) {}
}
provisioningManager.registerFeatureProvisioningChangedCallback(
Dispatchers.Default.asExecutor(),
callback,
)
trySend(provisioningManager.getProvisioningStatusForCapability(capability, tech))
awaitClose {
provisioningManager.unregisterFeatureProvisioningChangedCallback(callback)
}
}
.catch { e ->
Log.w(TAG, "[$subId] error while imsFeatureProvisionedFlow", e)
emit(false)
}
.distinctUntilChanged()
.conflate()
.onEach {
Log.d(TAG, "[$subId] changed: capability=$capability tech=$tech isProvisioned=$it")
}
.flowOn(Dispatchers.Default)
}
companion object {
private const val TAG = "ProvisioningRepository"
}
}

View File

@@ -27,7 +27,7 @@ import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.lifecycle.LifecycleOwner
import com.android.settings.network.telephony.ims.ImsMmTelRepository
import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow
import com.android.settings.network.telephony.ims.ProvisioningRepository
import com.android.settings.network.telephony.subscriptionsChangedFlow
import com.android.settings.network.telephony.telephonyManager
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
@@ -54,6 +54,7 @@ constructor(
) : IWifiCallingRepository {
private val telephonyManager = context.telephonyManager(subId)
private val provisioningRepository = ProvisioningRepository(context)
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
@WiFiCallingMode
@@ -80,7 +81,7 @@ constructor(
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
return context.subscriptionsChangedFlow().flatMapLatest {
combine(
imsFeatureProvisionedFlow(
provisioningRepository.imsFeatureProvisionedFlow(
subId = subId,
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,

View File

@@ -16,10 +16,13 @@
package com.android.settings.network.telephony.ims
import android.content.Context
import android.content.pm.PackageManager
import android.telephony.ims.ProvisioningManager
import android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback
import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.toListWithTimeout
import com.google.common.truth.Truth.assertThat
@@ -31,23 +34,52 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class ImsFeatureProvisionedFlowTest {
class ProvisioningRepositoryTest {
private var callback: FeatureProvisioningCallback? = null
private val mockProvisioningManager = mock<ProvisioningManager> {
on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer {
private val mockProvisioningManager =
mock<ProvisioningManager> {
on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer
{
callback = it.arguments[1] as FeatureProvisioningCallback
callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, true)
}
}
private val mockPackageManager =
mock<PackageManager> {
on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS) } doReturn true
}
private val context: Context =
spy(ApplicationProvider.getApplicationContext()) {
on { packageManager } doReturn mockPackageManager
}
private val repository = ProvisioningRepository(context) { mockProvisioningManager }
@Test
fun imsFeatureProvisionedFlow_hasNotIms_returnFalse() = runBlocking {
mockPackageManager.stub {
on { hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS) } doReturn false
}
val flow = repository.imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH)
val state = flow.first()
assertThat(state).isFalse()
}
@Test
fun imsFeatureProvisionedFlow_sendInitialValue() = runBlocking {
val flow = imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager)
val flow = repository.imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH)
val state = flow.first()
@@ -57,8 +89,7 @@ class ImsFeatureProvisionedFlowTest {
@Test
fun imsFeatureProvisionedFlow_changed(): Unit = runBlocking {
val listDeferred = async {
imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH, mockProvisioningManager)
.toListWithTimeout()
repository.imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH).toListWithTimeout()
}
delay(100)
@@ -68,7 +99,7 @@ class ImsFeatureProvisionedFlowTest {
}
private companion object {
const val SUB_ID = 1
const val SUB_ID = 10
const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE
const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
}