1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.network.telephony.ims 18 19 import android.content.Context 20 import android.telephony.AccessNetworkConstants 21 import android.telephony.ims.ImsManager 22 import android.telephony.ims.ImsMmTelManager 23 import android.telephony.ims.ImsMmTelManager.WiFiCallingMode 24 import android.telephony.ims.ImsReasonInfo 25 import android.telephony.ims.ImsRegistrationAttributes 26 import android.telephony.ims.ImsStateCallback 27 import android.telephony.ims.RegistrationManager 28 import android.telephony.ims.feature.MmTelFeature 29 import android.util.Log 30 import kotlin.coroutines.resume 31 import kotlinx.coroutines.Dispatchers 32 import kotlinx.coroutines.asExecutor 33 import kotlinx.coroutines.channels.awaitClose 34 import kotlinx.coroutines.flow.Flow 35 import kotlinx.coroutines.flow.callbackFlow 36 import kotlinx.coroutines.flow.catch 37 import kotlinx.coroutines.flow.conflate 38 import kotlinx.coroutines.flow.flowOn 39 import kotlinx.coroutines.suspendCancellableCoroutine 40 import kotlinx.coroutines.withContext 41 42 interface ImsMmTelRepository { 43 @WiFiCallingMode 44 fun getWiFiCallingMode(useRoamingMode: Boolean): Int 45 46 fun imsRegisteredFlow(): Flow<Boolean> 47 48 fun imsReadyFlow(): Flow<Boolean> 49 50 suspend fun isSupported( 51 @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, 52 @AccessNetworkConstants.TransportType transportType: Int, 53 ): Boolean 54 55 suspend fun setCrossSimCallingEnabled(enabled: Boolean) 56 } 57 58 class ImsMmTelRepositoryImpl( 59 context: Context, 60 private val subId: Int, 61 private val imsMmTelManager: ImsMmTelManager = ImsManager(context).getImsMmTelManager(subId), 62 ) : ImsMmTelRepository { 63 64 @WiFiCallingMode getWiFiCallingModenull65 override fun getWiFiCallingMode(useRoamingMode: Boolean): Int = try { 66 when { 67 !imsMmTelManager.isVoWiFiSettingEnabled -> ImsMmTelManager.WIFI_MODE_UNKNOWN 68 useRoamingMode -> imsMmTelManager.getVoWiFiRoamingModeSetting() 69 else -> imsMmTelManager.getVoWiFiModeSetting() 70 } 71 } catch (e: IllegalArgumentException) { 72 Log.w(TAG, "[$subId] getWiFiCallingMode failed useRoamingMode=$useRoamingMode", e) 73 ImsMmTelManager.WIFI_MODE_UNKNOWN 74 } 75 <lambda>null76 override fun imsRegisteredFlow(): Flow<Boolean> = callbackFlow { 77 val callback = object : RegistrationManager.RegistrationCallback() { 78 override fun onRegistered(attributes: ImsRegistrationAttributes) { 79 Log.d(TAG, "[$subId] IMS onRegistered") 80 trySend(true) 81 } 82 83 override fun onRegistering(imsTransportType: Int) { 84 Log.d(TAG, "[$subId] IMS onRegistering") 85 trySend(false) 86 } 87 88 override fun onTechnologyChangeFailed(imsTransportType: Int, info: ImsReasonInfo) { 89 Log.d(TAG, "[$subId] IMS onTechnologyChangeFailed") 90 trySend(false) 91 } 92 93 override fun onUnregistered(info: ImsReasonInfo) { 94 Log.d(TAG, "[$subId] IMS onUnregistered") 95 trySend(false) 96 } 97 } 98 99 imsMmTelManager.registerImsRegistrationCallback(Dispatchers.Default.asExecutor(), callback) 100 101 awaitClose { imsMmTelManager.unregisterImsRegistrationCallback(callback) } 102 }.catch { e -> 103 Log.w(TAG, "[$subId] error while imsRegisteredFlow", e) 104 }.conflate().flowOn(Dispatchers.Default) 105 <lambda>null106 override fun imsReadyFlow(): Flow<Boolean> = callbackFlow { 107 val callback = object : ImsStateCallback() { 108 override fun onAvailable() { 109 Log.d(TAG, "[$subId] IMS onAvailable") 110 trySend(true) 111 } 112 113 override fun onError() { 114 Log.d(TAG, "[$subId] IMS onError") 115 trySend(false) 116 } 117 118 override fun onUnavailable(reason: Int) { 119 Log.d(TAG, "[$subId] IMS onUnavailable") 120 trySend(false) 121 } 122 } 123 124 imsMmTelManager.registerImsStateCallback(Dispatchers.Default.asExecutor(), callback) 125 126 awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) } 127 }.catch { e -> 128 Log.w(TAG, "[$subId] error while imsReadyFlow", e) 129 }.conflate().flowOn(Dispatchers.Default) 130 isSupportednull131 override suspend fun isSupported( 132 @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, 133 @AccessNetworkConstants.TransportType transportType: Int, 134 ): Boolean = withContext(Dispatchers.Default) { 135 val logName = "isSupported(capability=$capability,transportType=$transportType)" 136 suspendCancellableCoroutine { continuation -> 137 try { 138 imsMmTelManager.isSupported( 139 capability, 140 transportType, 141 Dispatchers.Default.asExecutor(), 142 continuation::resume, 143 ) 144 } catch (e: Exception) { 145 continuation.resume(false) 146 Log.w(TAG, "[$subId] $logName failed", e) 147 } 148 }.also { Log.d(TAG, "[$subId] $logName = $it") } 149 } 150 setCrossSimCallingEnablednull151 override suspend fun setCrossSimCallingEnabled(enabled: Boolean) { 152 try { 153 imsMmTelManager.setCrossSimCallingEnabled(enabled) 154 Log.d(TAG, "[$subId] setCrossSimCallingEnabled: $enabled") 155 } catch (e: Exception) { 156 Log.e(TAG, "[$subId] failed setCrossSimCallingEnabled to $enabled", e) 157 } 158 } 159 160 private companion object { 161 private const val TAG = "ImsMmTelRepository" 162 } 163 } 164