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