1 /* <lambda>null2 * Copyright (C) 2023 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 18 19 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID 20 import android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX 21 22 import android.content.Context 23 import android.telephony.SubscriptionInfo 24 import android.telephony.SubscriptionManager 25 import android.telephony.TelephonyManager 26 import android.telephony.UiccCardInfo 27 import android.util.Log 28 import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType 29 import com.android.settings.network.telephony.MobileDataRepository 30 import com.android.settings.network.telephony.UiccSlotRepository 31 import com.android.settings.sim.SimActivationNotifier 32 import com.android.settings.spa.network.setDefaultData 33 import com.android.settings.spa.network.setDefaultSms 34 import com.android.settings.spa.network.setDefaultVoice 35 import com.android.settings.wifi.WifiPickerTrackerHelper 36 import com.android.settingslib.utils.ThreadUtils 37 import kotlinx.coroutines.Dispatchers 38 import kotlinx.coroutines.flow.MutableStateFlow 39 import kotlinx.coroutines.withContext 40 41 class SimOnboardingService { 42 var subscriptionManager:SubscriptionManager? = null 43 var telephonyManager:TelephonyManager? = null 44 45 var targetSubId: Int = INVALID_SUBSCRIPTION_ID 46 var targetSubInfo: SubscriptionInfo? = null 47 var availableSubInfoList: List<SubscriptionInfo> = listOf() 48 var activeSubInfoList: List<SubscriptionInfo> = listOf() 49 var uiccCardInfoList: List<UiccCardInfo> = listOf() 50 var targetPrimarySimCalls: Int = INVALID_SUBSCRIPTION_ID 51 var targetPrimarySimTexts: Int = INVALID_SUBSCRIPTION_ID 52 var targetPrimarySimMobileData: Int = INVALID_SUBSCRIPTION_ID 53 val targetPrimarySimAutoDataSwitch = MutableStateFlow(false) 54 var targetNonDds: Int = INVALID_SUBSCRIPTION_ID 55 get() { 56 if(targetPrimarySimMobileData == INVALID_SUBSCRIPTION_ID) { 57 Log.w(TAG, "No DDS") 58 return INVALID_SUBSCRIPTION_ID 59 } 60 return userSelectedSubInfoList 61 .filter { info -> info.subscriptionId != targetPrimarySimMobileData } 62 .map { it.subscriptionId } 63 .firstOrNull() ?: INVALID_SUBSCRIPTION_ID 64 } 65 var callback: (CallbackType) -> Unit = {} 66 67 var isMultipleEnabledProfilesSupported: Boolean = false 68 get() { 69 if (uiccCardInfoList.isEmpty()) { 70 Log.w(TAG, "UICC cards info list is empty.") 71 return false 72 } 73 return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported } 74 } 75 var isEsimProfileEnabled: Boolean = false 76 get() { 77 return activeSubInfoList.stream().anyMatch { it.isEmbedded } 78 } 79 var isRemovableSimProfileEnabled: Boolean = false 80 get() { 81 return activeSubInfoList.stream().anyMatch { !it.isEmbedded } 82 } 83 var doesTargetSimActive = false 84 get() { 85 return targetSubInfo?.getSimSlotIndex() ?: INVALID_SIM_SLOT_INDEX >= 0 86 } 87 88 var doesTargetSimHaveEsimOperation = false 89 get() { 90 return targetSubInfo?.isEmbedded ?: false 91 } 92 93 var isUsableTargetSubscriptionId = false 94 get() { 95 return SubscriptionManager.isUsableSubscriptionId(targetSubId) 96 } 97 var getActiveModemCount = 0 98 get() { 99 return (telephonyManager?.getActiveModemCount() ?: 0) 100 } 101 102 var renameMutableMap : MutableMap<Int, String> = mutableMapOf() 103 var userSelectedSubInfoList : MutableList<SubscriptionInfo> = mutableListOf() 104 105 var isSimSelectionFinished = false 106 get() { 107 val activeModem = getActiveModemCount 108 return activeModem != 0 && userSelectedSubInfoList.size == activeModem 109 } 110 111 var isAllOfSlotAssigned = false 112 get() { 113 val activeModem = getActiveModemCount 114 if(activeModem == 0){ 115 Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0") 116 return true 117 } 118 return getActiveModemCount != 0 && activeSubInfoList.size == activeModem 119 } 120 var isMultiSimEnabled = false 121 get() { 122 return getActiveModemCount > 1 123 } 124 var isMultiSimSupported = false 125 get() { 126 return telephonyManager?.isMultiSimSupported == TelephonyManager.MULTISIM_ALLOWED 127 } 128 129 var doesSwitchMultiSimConfigTriggerReboot = false 130 get() { 131 return telephonyManager?.doesSwitchMultiSimConfigTriggerReboot() ?: false 132 } 133 134 fun clear() { 135 targetSubId = -1 136 targetSubInfo = null 137 availableSubInfoList = listOf() 138 activeSubInfoList = listOf() 139 uiccCardInfoList = listOf() 140 targetPrimarySimCalls = -1 141 targetPrimarySimTexts = -1 142 targetPrimarySimMobileData = -1 143 clearUserRecord() 144 } 145 146 fun clearUserRecord(){ 147 renameMutableMap.clear() 148 userSelectedSubInfoList.clear() 149 } 150 151 fun initData(inputTargetSubId: Int, 152 context: Context, 153 callback: (CallbackType) -> Unit) { 154 clear() 155 this.callback = callback 156 targetSubId = inputTargetSubId 157 subscriptionManager = context.getSystemService(SubscriptionManager::class.java) 158 telephonyManager = context.getSystemService(TelephonyManager::class.java) 159 activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager) 160 Log.d( 161 TAG, "startInit: targetSubId:$targetSubId, activeSubInfoList: $activeSubInfoList" 162 ) 163 164 ThreadUtils.postOnBackgroundThread { 165 availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context) 166 targetSubInfo = 167 availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId } 168 targetSubInfo?.let { userSelectedSubInfoList.add(it) } 169 Log.d(TAG, "targetSubId: $targetSubId , targetSubInfo: $targetSubInfo") 170 uiccCardInfoList = telephonyManager?.uiccCardsInfo!! 171 Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList") 172 173 targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId() 174 targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId() 175 targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId() 176 177 Log.d( 178 TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" + 179 ", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" + 180 ", targetPrimarySimCalls: $targetPrimarySimCalls" + 181 ", targetPrimarySimTexts: $targetPrimarySimTexts" + 182 ", targetPrimarySimMobileData: $targetPrimarySimMobileData") 183 } 184 } 185 186 /** 187 * Return the subscriptionInfo list which has 188 * the target subscriptionInfo + active subscriptionInfo. 189 */ 190 fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> { 191 var list: MutableList<SubscriptionInfo> = mutableListOf() 192 list.addAll(activeSubInfoList) 193 if (!list.contains(targetSubInfo)) { 194 targetSubInfo?.let { list.add(it) } 195 } 196 197 return list.toList() 198 } 199 200 /** 201 * Return the user selected SubscriptionInfo list. 202 */ 203 fun getSelectedSubscriptionInfoList(): List<SubscriptionInfo> { 204 if (userSelectedSubInfoList.isEmpty()){ 205 Log.d(TAG, "userSelectedSubInfoList is empty") 206 return activeSubInfoList 207 } 208 return userSelectedSubInfoList.toList() 209 } 210 211 fun getSelectedSubscriptionInfoListWithRenaming(): List<SubscriptionInfo> { 212 if (userSelectedSubInfoList.isEmpty()){ 213 Log.d(TAG, "userSelectedSubInfoList is empty") 214 return activeSubInfoList 215 } 216 return userSelectedSubInfoList.map { 217 SubscriptionInfo.Builder(it).setDisplayName(getSubscriptionInfoDisplayName(it)).build() 218 }.toList() 219 } 220 221 fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) { 222 if (subInfo.displayName == newName) { 223 renameMutableMap.remove(subInfo.subscriptionId) 224 return 225 } 226 renameMutableMap[subInfo.subscriptionId] = newName 227 Log.d( 228 TAG, 229 "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap" 230 ) 231 } 232 233 fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String { 234 return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString() 235 } 236 237 fun addCurrentItemForSelectedSim() { 238 if (userSelectedSubInfoList.size < getActiveModemCount) { 239 userSelectedSubInfoList.addAll( 240 activeSubInfoList.filter { !userSelectedSubInfoList.contains(it) } 241 ) 242 Log.d(TAG, 243 "addCurrentItemForSelectedSim: userSelectedSubInfoList: $userSelectedSubInfoList" 244 ) 245 } 246 } 247 248 fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) { 249 if (!userSelectedSubInfoList.contains(selectedSubInfo)) { 250 userSelectedSubInfoList.add(selectedSubInfo) 251 } 252 } 253 254 fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) { 255 if (userSelectedSubInfoList.contains(selectedSubInfo)) { 256 userSelectedSubInfoList.remove(selectedSubInfo) 257 } 258 } 259 260 /** 261 * Return the subscriptionInfo which will be removed in the slot during the sim onboarding. 262 * If return Null, then no subscriptionInfo will be removed in the slot. 263 */ 264 fun getRemovedSim():SubscriptionInfo?{ 265 return activeSubInfoList.find { !userSelectedSubInfoList.contains(it) } 266 } 267 268 fun handleTogglePsimAction() { 269 val canDisablePhysicalSubscription = 270 subscriptionManager?.canDisablePhysicalSubscription() == true 271 if (targetSubInfo != null && canDisablePhysicalSubscription) { 272 // TODO: to support disable case. 273 subscriptionManager?.setUiccApplicationsEnabled( 274 targetSubInfo!!.subscriptionId, /*enabled=*/true) 275 } else { 276 Log.i(TAG, "The device does not support toggling pSIM. It is enough to just " 277 + "enable the removable slot." 278 ) 279 } 280 } 281 282 fun isDsdsConditionSatisfied(): Boolean { 283 if (isMultiSimEnabled) { 284 Log.d( 285 TAG, 286 "DSDS is already enabled. Condition not satisfied." 287 ) 288 return false 289 } 290 if (!isMultiSimSupported) { 291 Log.d(TAG, "Hardware does not support DSDS.") 292 return false 293 } 294 val anyActiveSim = activeSubInfoList.isNotEmpty() 295 if (isMultipleEnabledProfilesSupported && anyActiveSim) { 296 Log.d(TAG, 297 "Device supports MEP and eSIM operation and eSIM profile is enabled." 298 + " DSDS condition satisfied." 299 ) 300 return true 301 } 302 303 if (doesTargetSimHaveEsimOperation && isRemovableSimProfileEnabled) { 304 Log.d(TAG, 305 "eSIM operation and removable PSIM is enabled. DSDS condition satisfied." 306 ) 307 return true 308 } 309 if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) { 310 Log.d(TAG, 311 "Removable SIM operation and eSIM profile is enabled. DSDS condition" 312 + " satisfied." 313 ) 314 return true 315 } 316 Log.d(TAG, "DSDS condition not satisfied.") 317 return false 318 } 319 320 fun startActivatingSim(){ 321 // TODO: start to activate sim 322 callback(CallbackType.CALLBACK_FINISH) 323 } 324 325 suspend fun startSetupName() { 326 withContext(Dispatchers.Default) { 327 renameMutableMap.forEach { 328 subscriptionManager?.setDisplayName( 329 it.value, it.key, 330 SubscriptionManager.NAME_SOURCE_USER_INPUT 331 ) 332 } 333 // next action is SETUP_PRIMARY_SIM 334 callback(CallbackType.CALLBACK_SETUP_PRIMARY_SIM) 335 } 336 } 337 338 suspend fun startSetupPrimarySim( 339 context: Context, 340 wifiPickerTrackerHelper: WifiPickerTrackerHelper 341 ) { 342 withContext(Dispatchers.Default) { 343 setDefaultVoice(subscriptionManager, targetPrimarySimCalls) 344 setDefaultSms(subscriptionManager, targetPrimarySimTexts) 345 setDefaultData( 346 context, 347 subscriptionManager, 348 wifiPickerTrackerHelper, 349 targetPrimarySimMobileData 350 ) 351 MobileDataRepository(context).setAutoDataSwitch( 352 targetNonDds, 353 targetPrimarySimAutoDataSwitch.value 354 ) 355 } 356 // no next action, send finish 357 callback(CallbackType.CALLBACK_FINISH) 358 } 359 360 suspend fun startEnableDsds(context: Context) { 361 withContext(Dispatchers.Default) { 362 Log.d(TAG, "User confirmed reboot to enable DSDS.") 363 SimActivationNotifier.setShowSimSettingsNotification(context, true) 364 telephonyManager?.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS) 365 callback(CallbackType.CALLBACK_FINISH) 366 } 367 } 368 369 companion object{ 370 private const val TAG = "SimOnboardingService" 371 const val NUM_OF_SIMS_FOR_DSDS = 2 372 } 373 }