Merge "Fix Wi-Fi calling option does not appear" into main

This commit is contained in:
Chaohui Wang
2024-02-27 07:44:23 +00:00
committed by Android (Google) Code Review
10 changed files with 333 additions and 12 deletions

View File

@@ -27,6 +27,8 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
/**
* Controller class for querying Wifi calling status
*/
@@ -92,7 +94,9 @@ public class WifiCallingQueryImsState extends ImsQueryController {
* Check whether Wifi Calling can be perform or not on this subscription
*
* @return true when Wifi Calling can be performed, otherwise false
* @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead.
*/
@Deprecated
public boolean isReadyToWifiCalling() {
if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
return false;

View File

@@ -80,6 +80,7 @@ import com.android.settings.network.CarrierConfigCache;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.graph.SignalDrawable;
@@ -928,7 +929,10 @@ public class MobileNetworkUtils {
/**
* Copied from WifiCallingPreferenceController#isWifiCallingEnabled()
*
* @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead.
*/
@Deprecated
public static boolean isWifiCallingEnabled(Context context, int subId,
@Nullable WifiCallingQueryImsState queryImsState) {
if (queryImsState == null) {

View File

@@ -18,12 +18,16 @@ package com.android.settings.network.telephony
import android.content.Context
import android.telephony.SubscriptionManager
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
private const val TAG = "SubscriptionRepository"
fun Context.subscriptionsChangedFlow() = callbackFlow {
val subscriptionManager = getSystemService(SubscriptionManager::class.java)!!
@@ -40,4 +44,4 @@ fun Context.subscriptionsChangedFlow() = callbackFlow {
)
awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(listener) }
}.conflate().flowOn(Dispatchers.Default)
}.conflate().onEach { Log.d(TAG, "subscriptions changed") }.flowOn(Dispatchers.Default)

View File

@@ -45,7 +45,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor(
context: Context,
key: String,
private val callStateFlowFactory: (subId: Int) -> Flow<Int> = context::callStateFlow,
private val wifiCallingRepository: (subId: Int) -> WifiCallingRepository = { subId ->
private val wifiCallingRepositoryFactory: (subId: Int) -> WifiCallingRepository = { subId ->
WifiCallingRepository(context, subId)
},
) : TelephonyBasePreferenceController(context, key) {
@@ -80,15 +80,11 @@ open class WifiCallingPreferenceController @JvmOverloads constructor(
}
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
val isVisible = withContext(Dispatchers.Default) {
MobileNetworkUtils.isWifiCallingEnabled(mContext, mSubId, null)
}
preference.isVisible = isVisible
callingPreferenceCategoryController.updateChildVisible(preferenceKey, isVisible)
wifiCallingRepositoryFactory(mSubId).wifiCallingReadyFlow()
.collectLatestWithLifecycle(viewLifecycleOwner) {
preference.isVisible = it
callingPreferenceCategoryController.updateChildVisible(preferenceKey, it)
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -122,7 +118,7 @@ open class WifiCallingPreferenceController @JvmOverloads constructor(
}
private fun getSummaryForWfcMode(): String {
val resId = when (wifiCallingRepository(mSubId).getWiFiCallingMode()) {
val resId = when (wifiCallingRepositoryFactory(mSubId).getWiFiCallingMode()) {
ImsMmTelManager.WIFI_MODE_WIFI_ONLY ->
com.android.internal.R.string.wfc_mode_wifi_only_summary

View File

@@ -0,0 +1,83 @@
/*
* 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

@@ -17,14 +17,33 @@
package com.android.settings.network.telephony.ims
import android.content.Context
import android.telephony.AccessNetworkConstants
import android.telephony.ims.ImsManager
import android.telephony.ims.ImsMmTelManager
import android.telephony.ims.ImsMmTelManager.WiFiCallingMode
import android.telephony.ims.ImsStateCallback
import android.telephony.ims.feature.MmTelFeature
import android.util.Log
import kotlin.coroutines.resume
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.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
interface ImsMmTelRepository {
@WiFiCallingMode
fun getWiFiCallingMode(useRoamingMode: Boolean): Int
fun imsReadyFlow(): Flow<Boolean>
suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
): Boolean
}
class ImsMmTelRepositoryImpl(
@@ -45,6 +64,50 @@ class ImsMmTelRepositoryImpl(
ImsMmTelManager.WIFI_MODE_UNKNOWN
}
override fun imsReadyFlow(): Flow<Boolean> = callbackFlow {
val callback = object : ImsStateCallback() {
override fun onAvailable() {
Log.d(TAG, "[$subId] IMS onAvailable")
trySend(true)
}
override fun onError() {
Log.d(TAG, "[$subId] IMS onError")
trySend(false)
}
override fun onUnavailable(reason: Int) {
Log.d(TAG, "[$subId] IMS onUnavailable")
trySend(false)
}
}
imsMmTelManager.registerImsStateCallback(Dispatchers.Default.asExecutor(), callback)
awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) }
}.catch { e ->
Log.w(TAG, "[$subId] error while imsReadyFlow", e)
}.conflate().flowOn(Dispatchers.Default)
override suspend fun isSupported(
@MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int,
@AccessNetworkConstants.TransportType transportType: Int,
): Boolean = withContext(Dispatchers.Default) {
suspendCancellableCoroutine { continuation ->
try {
imsMmTelManager.isSupported(
capability,
transportType,
Dispatchers.Default.asExecutor(),
continuation::resume,
)
} catch (e: Exception) {
continuation.resume(false)
Log.w(TAG, "[$subId] isSupported failed", e)
}
}.also { Log.d(TAG, "[$subId] isSupported = $it") }
}
private companion object {
private const val TAG = "ImsMmTelRepository"
}

View File

@@ -17,12 +17,24 @@
package com.android.settings.network.telephony.wificalling
import android.content.Context
import android.telephony.AccessNetworkConstants
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.ims.ImsMmTelManager.WiFiCallingMode
import android.telephony.ims.feature.MmTelFeature
import android.telephony.ims.stub.ImsRegistrationImplBase
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.subscriptionsChangedFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
class WifiCallingRepository(
private val context: Context,
@@ -44,4 +56,30 @@ class WifiCallingRepository(
carrierConfigManager
.getConfigForSubId(subId, KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)
.getBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL)
@OptIn(ExperimentalCoroutinesApi::class)
fun wifiCallingReadyFlow(): Flow<Boolean> {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
return context.subscriptionsChangedFlow().flatMapLatest {
combine(
imsFeatureProvisionedFlow(
subId = subId,
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
),
isWifiCallingSupportedFlow(),
) { imsFeatureProvisioned, isWifiCallingSupported ->
imsFeatureProvisioned && isWifiCallingSupported
}
}
}
private fun isWifiCallingSupportedFlow(): Flow<Boolean> {
return imsMmTelRepository.imsReadyFlow().map { imsReady ->
imsReady && imsMmTelRepository.isSupported(
capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
)
}
}
}