After the sim name has been changed, the user changed back to original one. The map did not remove the sim item, so the onboarding flow change the sim name. Bug: 355138239 Flag: EXEMPT bugfix Test: atest SimOnboardingServiceTest Change-Id: If4276320c939604b48cf2ce8c1b6b553da699c28
391 lines
14 KiB
Kotlin
391 lines
14 KiB
Kotlin
/*
|
|
* Copyright (C) 2023 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
|
|
|
|
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
|
import android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX
|
|
|
|
import android.content.Context
|
|
import android.telephony.SubscriptionInfo
|
|
import android.telephony.SubscriptionManager
|
|
import android.telephony.TelephonyManager
|
|
import android.telephony.UiccCardInfo
|
|
import android.telephony.UiccSlotInfo
|
|
import android.util.Log
|
|
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType
|
|
import com.android.settings.network.telephony.MobileDataRepository
|
|
import com.android.settings.sim.SimActivationNotifier
|
|
import com.android.settings.spa.network.setDefaultData
|
|
import com.android.settings.spa.network.setDefaultSms
|
|
import com.android.settings.spa.network.setDefaultVoice
|
|
import com.android.settings.wifi.WifiPickerTrackerHelper
|
|
import com.android.settingslib.utils.ThreadUtils
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.withContext
|
|
|
|
class SimOnboardingService {
|
|
var subscriptionManager:SubscriptionManager? = null
|
|
var telephonyManager:TelephonyManager? = null
|
|
|
|
var targetSubId: Int = INVALID_SUBSCRIPTION_ID
|
|
var targetSubInfo: SubscriptionInfo? = null
|
|
var availableSubInfoList: List<SubscriptionInfo> = listOf()
|
|
var activeSubInfoList: List<SubscriptionInfo> = listOf()
|
|
var slotInfoList: List<UiccSlotInfo> = listOf()
|
|
var uiccCardInfoList: List<UiccCardInfo> = listOf()
|
|
var targetPrimarySimCalls: Int = INVALID_SUBSCRIPTION_ID
|
|
var targetPrimarySimTexts: Int = INVALID_SUBSCRIPTION_ID
|
|
var targetPrimarySimMobileData: Int = INVALID_SUBSCRIPTION_ID
|
|
val targetPrimarySimAutoDataSwitch = MutableStateFlow(false)
|
|
var targetNonDds: Int = INVALID_SUBSCRIPTION_ID
|
|
get() {
|
|
if(targetPrimarySimMobileData == INVALID_SUBSCRIPTION_ID) {
|
|
Log.w(TAG, "No DDS")
|
|
return INVALID_SUBSCRIPTION_ID
|
|
}
|
|
return userSelectedSubInfoList
|
|
.filter { info -> info.subscriptionId != targetPrimarySimMobileData }
|
|
.map { it.subscriptionId }
|
|
.firstOrNull() ?: INVALID_SUBSCRIPTION_ID
|
|
}
|
|
var callback: (CallbackType) -> Unit = {}
|
|
|
|
var isMultipleEnabledProfilesSupported: Boolean = false
|
|
get() {
|
|
if (uiccCardInfoList.isEmpty()) {
|
|
Log.w(TAG, "UICC cards info list is empty.")
|
|
return false
|
|
}
|
|
return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
|
|
}
|
|
var isRemovablePsimProfileEnabled: Boolean = false
|
|
get() {
|
|
if(slotInfoList.isEmpty()) {
|
|
Log.w(TAG, "UICC Slot info list is empty.")
|
|
return false
|
|
}
|
|
return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
|
|
}
|
|
var isEsimProfileEnabled: Boolean = false
|
|
get() {
|
|
activeSubInfoList.stream().anyMatch { it.isEmbedded }
|
|
return false
|
|
}
|
|
var doesTargetSimActive = false
|
|
get() {
|
|
return targetSubInfo?.getSimSlotIndex() ?: INVALID_SIM_SLOT_INDEX >= 0
|
|
}
|
|
|
|
var doesTargetSimHaveEsimOperation = false
|
|
get() {
|
|
return targetSubInfo?.isEmbedded ?: false
|
|
}
|
|
|
|
var isUsableTargetSubscriptionId = false
|
|
get() {
|
|
return SubscriptionManager.isUsableSubscriptionId(targetSubId)
|
|
}
|
|
var getActiveModemCount = 0
|
|
get() {
|
|
return (telephonyManager?.getActiveModemCount() ?: 0)
|
|
}
|
|
|
|
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
|
|
var userSelectedSubInfoList : MutableList<SubscriptionInfo> = mutableListOf()
|
|
|
|
var isSimSelectionFinished = false
|
|
get() {
|
|
val activeModem = getActiveModemCount
|
|
return activeModem != 0 && userSelectedSubInfoList.size == activeModem
|
|
}
|
|
|
|
var isAllOfSlotAssigned = false
|
|
get() {
|
|
val activeModem = getActiveModemCount
|
|
if(activeModem == 0){
|
|
Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0")
|
|
return true
|
|
}
|
|
return getActiveModemCount != 0 && activeSubInfoList.size == activeModem
|
|
}
|
|
var isMultiSimEnabled = false
|
|
get() {
|
|
return getActiveModemCount > 1
|
|
}
|
|
var isMultiSimSupported = false
|
|
get() {
|
|
return telephonyManager?.isMultiSimSupported == TelephonyManager.MULTISIM_ALLOWED
|
|
}
|
|
|
|
var doesSwitchMultiSimConfigTriggerReboot = false
|
|
get() {
|
|
return telephonyManager?.doesSwitchMultiSimConfigTriggerReboot() ?: false
|
|
}
|
|
|
|
fun isValid(): Boolean {
|
|
return targetSubId != INVALID_SUBSCRIPTION_ID
|
|
&& targetSubInfo != null
|
|
&& activeSubInfoList.isNotEmpty()
|
|
&& slotInfoList.isNotEmpty()
|
|
}
|
|
|
|
fun clear() {
|
|
targetSubId = -1
|
|
targetSubInfo = null
|
|
availableSubInfoList = listOf()
|
|
activeSubInfoList = listOf()
|
|
slotInfoList = listOf()
|
|
uiccCardInfoList = listOf()
|
|
targetPrimarySimCalls = -1
|
|
targetPrimarySimTexts = -1
|
|
targetPrimarySimMobileData = -1
|
|
clearUserRecord()
|
|
}
|
|
|
|
fun clearUserRecord(){
|
|
renameMutableMap.clear()
|
|
userSelectedSubInfoList.clear()
|
|
}
|
|
|
|
fun initData(inputTargetSubId: Int,
|
|
context: Context,
|
|
callback: (CallbackType) -> Unit) {
|
|
clear()
|
|
this.callback = callback
|
|
targetSubId = inputTargetSubId
|
|
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
|
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
|
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
|
Log.d(
|
|
TAG, "startInit: targetSubId:$targetSubId, activeSubInfoList: $activeSubInfoList"
|
|
)
|
|
|
|
ThreadUtils.postOnBackgroundThread {
|
|
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
|
targetSubInfo =
|
|
availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
|
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
|
|
Log.d(TAG, "targetSubId: $targetSubId , targetSubInfo: $targetSubInfo")
|
|
slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
|
|
Log.d(TAG, "slotInfoList: $slotInfoList.")
|
|
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
|
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
|
|
|
|
targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId()
|
|
targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId()
|
|
targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId()
|
|
|
|
Log.d(
|
|
TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
|
|
", isRemovableSimEnabled: $isRemovablePsimProfileEnabled" +
|
|
", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
|
|
", targetPrimarySimCalls: $targetPrimarySimCalls" +
|
|
", targetPrimarySimTexts: $targetPrimarySimTexts" +
|
|
", targetPrimarySimMobileData: $targetPrimarySimMobileData")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the subscriptionInfo list which has
|
|
* the target subscriptionInfo + active subscriptionInfo.
|
|
*/
|
|
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
|
var list: MutableList<SubscriptionInfo> = mutableListOf()
|
|
list.addAll(activeSubInfoList)
|
|
if (!list.contains(targetSubInfo)) {
|
|
targetSubInfo?.let { list.add(it) }
|
|
}
|
|
|
|
return list.toList()
|
|
}
|
|
|
|
/**
|
|
* Return the user selected SubscriptionInfo list.
|
|
*/
|
|
fun getSelectedSubscriptionInfoList(): List<SubscriptionInfo> {
|
|
if (userSelectedSubInfoList.isEmpty()){
|
|
Log.d(TAG, "userSelectedSubInfoList is empty")
|
|
return activeSubInfoList
|
|
}
|
|
return userSelectedSubInfoList.toList()
|
|
}
|
|
|
|
fun getSelectedSubscriptionInfoListWithRenaming(): List<SubscriptionInfo> {
|
|
if (userSelectedSubInfoList.isEmpty()){
|
|
Log.d(TAG, "userSelectedSubInfoList is empty")
|
|
return activeSubInfoList
|
|
}
|
|
return userSelectedSubInfoList.map {
|
|
SubscriptionInfo.Builder(it).setDisplayName(getSubscriptionInfoDisplayName(it)).build()
|
|
}.toList()
|
|
}
|
|
|
|
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
|
|
if (subInfo.displayName == newName) {
|
|
renameMutableMap.remove(subInfo.subscriptionId)
|
|
return
|
|
}
|
|
renameMutableMap[subInfo.subscriptionId] = newName
|
|
Log.d(
|
|
TAG,
|
|
"renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap"
|
|
)
|
|
}
|
|
|
|
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
|
|
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
|
|
}
|
|
|
|
fun addCurrentItemForSelectedSim() {
|
|
if (userSelectedSubInfoList.size < getActiveModemCount) {
|
|
userSelectedSubInfoList.addAll(
|
|
activeSubInfoList.filter { !userSelectedSubInfoList.contains(it) }
|
|
)
|
|
Log.d(TAG,
|
|
"addCurrentItemForSelectedSim: userSelectedSubInfoList: $userSelectedSubInfoList"
|
|
)
|
|
}
|
|
}
|
|
|
|
fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
|
|
if (!userSelectedSubInfoList.contains(selectedSubInfo)) {
|
|
userSelectedSubInfoList.add(selectedSubInfo)
|
|
}
|
|
}
|
|
|
|
fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
|
|
if (userSelectedSubInfoList.contains(selectedSubInfo)) {
|
|
userSelectedSubInfoList.remove(selectedSubInfo)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the subscriptionInfo which will be removed in the slot during the sim onboarding.
|
|
* If return Null, then no subscriptionInfo will be removed in the slot.
|
|
*/
|
|
fun getRemovedSim():SubscriptionInfo?{
|
|
return activeSubInfoList.find { !userSelectedSubInfoList.contains(it) }
|
|
}
|
|
|
|
fun handleTogglePsimAction() {
|
|
val canDisablePhysicalSubscription =
|
|
subscriptionManager?.canDisablePhysicalSubscription() == true
|
|
if (targetSubInfo != null && canDisablePhysicalSubscription) {
|
|
// TODO: to support disable case.
|
|
subscriptionManager?.setUiccApplicationsEnabled(
|
|
targetSubInfo!!.subscriptionId, /*enabled=*/true)
|
|
} else {
|
|
Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
|
|
+ "enable the removable slot."
|
|
)
|
|
}
|
|
}
|
|
|
|
fun isDsdsConditionSatisfied(): Boolean {
|
|
if (isMultiSimEnabled) {
|
|
Log.d(
|
|
TAG,
|
|
"DSDS is already enabled. Condition not satisfied."
|
|
)
|
|
return false
|
|
}
|
|
if (!isMultiSimSupported) {
|
|
Log.d(TAG, "Hardware does not support DSDS.")
|
|
return false
|
|
}
|
|
val isActiveSim = activeSubInfoList.isNotEmpty()
|
|
if (isMultipleEnabledProfilesSupported && isActiveSim) {
|
|
Log.d(TAG,
|
|
"Device supports MEP and eSIM operation and eSIM profile is enabled."
|
|
+ " DSDS condition satisfied."
|
|
)
|
|
return true
|
|
}
|
|
|
|
if (doesTargetSimHaveEsimOperation && isRemovablePsimProfileEnabled) {
|
|
Log.d(TAG,
|
|
"eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
|
|
)
|
|
return true
|
|
}
|
|
|
|
if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) {
|
|
Log.d(TAG,
|
|
"Removable SIM operation and eSIM profile is enabled. DSDS condition"
|
|
+ " satisfied."
|
|
)
|
|
return true
|
|
}
|
|
Log.d(TAG, "DSDS condition not satisfied.")
|
|
return false
|
|
}
|
|
|
|
fun startActivatingSim(){
|
|
// TODO: start to activate sim
|
|
callback(CallbackType.CALLBACK_FINISH)
|
|
}
|
|
|
|
suspend fun startSetupName() {
|
|
withContext(Dispatchers.Default) {
|
|
renameMutableMap.forEach {
|
|
subscriptionManager?.setDisplayName(
|
|
it.value, it.key,
|
|
SubscriptionManager.NAME_SOURCE_USER_INPUT
|
|
)
|
|
}
|
|
// next action is SETUP_PRIMARY_SIM
|
|
callback(CallbackType.CALLBACK_SETUP_PRIMARY_SIM)
|
|
}
|
|
}
|
|
|
|
suspend fun startSetupPrimarySim(
|
|
context: Context,
|
|
wifiPickerTrackerHelper: WifiPickerTrackerHelper
|
|
) {
|
|
withContext(Dispatchers.Default) {
|
|
setDefaultVoice(subscriptionManager, targetPrimarySimCalls)
|
|
setDefaultSms(subscriptionManager, targetPrimarySimTexts)
|
|
setDefaultData(
|
|
context,
|
|
subscriptionManager,
|
|
wifiPickerTrackerHelper,
|
|
targetPrimarySimMobileData
|
|
)
|
|
MobileDataRepository(context).setAutoDataSwitch(
|
|
targetNonDds,
|
|
targetPrimarySimAutoDataSwitch.value
|
|
)
|
|
}
|
|
// no next action, send finish
|
|
callback(CallbackType.CALLBACK_FINISH)
|
|
}
|
|
|
|
suspend fun startEnableDsds(context: Context) {
|
|
withContext(Dispatchers.Default) {
|
|
Log.d(TAG, "User confirmed reboot to enable DSDS.")
|
|
SimActivationNotifier.setShowSimSettingsNotification(context, true)
|
|
telephonyManager?.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS)
|
|
callback(CallbackType.CALLBACK_FINISH)
|
|
}
|
|
}
|
|
|
|
companion object{
|
|
private const val TAG = "SimOnboardingService"
|
|
const val NUM_OF_SIMS_FOR_DSDS = 2
|
|
}
|
|
} |