• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }