diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt deleted file mode 100644 index 676949845fc..00000000000 --- a/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlow.kt +++ /dev/null @@ -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 = 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 = 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) diff --git a/src/com/android/settings/network/telephony/ims/ProvisioningRepository.kt b/src/com/android/settings/network/telephony/ims/ProvisioningRepository.kt new file mode 100644 index 00000000000..521b364a1db --- /dev/null +++ b/src/com/android/settings/network/telephony/ims/ProvisioningRepository.kt @@ -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 { + 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" + } +} diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index b5cdedae96c..04e687c2d7f 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -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, diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ProvisioningRepositoryTest.kt similarity index 56% rename from tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt rename to tests/spa_unit/src/com/android/settings/network/telephony/ims/ProvisioningRepositoryTest.kt index 75f933ab7a2..7b2c940ae53 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureProvisionedFlowTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ProvisioningRepositoryTest.kt @@ -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 { - on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer { - callback = it.arguments[1] as FeatureProvisioningCallback - callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, true) + private val mockProvisioningManager = + mock { + on { registerFeatureProvisioningChangedCallback(any(), any()) } doAnswer + { + callback = it.arguments[1] as FeatureProvisioningCallback + callback?.onFeatureProvisioningChanged(CAPABILITY, TECH, true) + } } + + private val mockPackageManager = + mock { + 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 }