1 /*
2  * Copyright (C) 2022 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 package com.android.settings.network;
17 
18 import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
19 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
20 
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.database.ContentObserver;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.provider.Settings;
28 import android.telephony.SubscriptionInfo;
29 import android.telephony.SubscriptionManager;
30 import android.telephony.TelephonyCallback;
31 import android.telephony.TelephonyManager;
32 import android.telephony.UiccCardInfo;
33 import android.telephony.UiccPortInfo;
34 import android.telephony.UiccSlotInfo;
35 import android.util.ArrayMap;
36 import android.util.IndentingPrintWriter;
37 import android.util.Log;
38 
39 import androidx.annotation.GuardedBy;
40 import androidx.annotation.NonNull;
41 import androidx.lifecycle.LifecycleOwner;
42 
43 import com.android.internal.telephony.flags.Flags;
44 import com.android.settings.network.telephony.MobileNetworkUtils;
45 import com.android.settings.overlay.FeatureFactory;
46 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
47 import com.android.settingslib.mobile.dataservice.MobileNetworkDatabase;
48 import com.android.settingslib.mobile.dataservice.MobileNetworkInfoDao;
49 import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
50 import com.android.settingslib.mobile.dataservice.SubscriptionInfoDao;
51 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
52 import com.android.settingslib.mobile.dataservice.UiccInfoDao;
53 import com.android.settingslib.mobile.dataservice.UiccInfoEntity;
54 
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.concurrent.CopyOnWriteArrayList;
61 import java.util.concurrent.ExecutorService;
62 import java.util.concurrent.Executors;
63 import java.util.stream.Collectors;
64 
65 public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptionsChangedListener {
66 
67     private static final String TAG = "MobileNetworkRepository";
68     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
69 
70     private static ExecutorService sExecutor = Executors.newSingleThreadExecutor();
71     private static Map<Integer, SubscriptionInfoEntity> sCacheSubscriptionInfoEntityMap =
72             new ArrayMap<>();
73     private static Map<Integer, MobileNetworkInfoEntity> sCacheMobileNetworkInfoEntityMap =
74             new ArrayMap<>();
75     private static Map<Integer, UiccInfoEntity> sCacheUiccInfoEntityMap = new ArrayMap<>();
76     private static Collection<MobileNetworkCallback> sCallbacks = new CopyOnWriteArrayList<>();
77     private static final Object sInstanceLock = new Object();
78     @GuardedBy("sInstanceLock")
79     private static MobileNetworkRepository sInstance;
80 
81     private SubscriptionManager mSubscriptionManager;
82     private MobileNetworkDatabase mMobileNetworkDatabase;
83     private SubscriptionInfoDao mSubscriptionInfoDao;
84     private UiccInfoDao mUiccInfoDao;
85     private MobileNetworkInfoDao mMobileNetworkInfoDao;
86     private List<SubscriptionInfoEntity> mAvailableSubInfoEntityList = new ArrayList<>();
87     private List<SubscriptionInfoEntity> mActiveSubInfoEntityList = new ArrayList<>();
88     private List<UiccInfoEntity> mUiccInfoEntityList = new ArrayList<>();
89     private List<MobileNetworkInfoEntity> mMobileNetworkInfoEntityList = new ArrayList<>();
90     private Context mContext;
91     private AirplaneModeObserver mAirplaneModeObserver;
92     private DataRoamingObserver mDataRoamingObserver;
93     private MetricsFeatureProvider mMetricsFeatureProvider;
94     private int mPhysicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
95     private int mLogicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
96     private int mCardState = UiccSlotInfo.CARD_STATE_INFO_ABSENT;
97     private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
98     private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
99     private boolean mIsEuicc = false;
100     private boolean mIsRemovable = false;
101     private boolean mIsActive = false;
102     private Map<Integer, SubscriptionInfo> mSubscriptionInfoMap = new ArrayMap<>();
103     private Map<Integer, TelephonyManager> mTelephonyManagerMap = new HashMap<>();
104     private Map<Integer, PhoneCallStateTelephonyCallback> mTelephonyCallbackMap = new HashMap<>();
105 
106     @NonNull
getInstance(Context context)107     public static MobileNetworkRepository getInstance(Context context) {
108         synchronized (sInstanceLock) {
109             if (sInstance != null) {
110                 return sInstance;
111             }
112             if (DEBUG) {
113                 Log.d(TAG, "Init the instance.");
114             }
115             sInstance = new MobileNetworkRepository(context);
116             return sInstance;
117         }
118     }
119 
MobileNetworkRepository(Context context)120     private MobileNetworkRepository(Context context) {
121         mContext = context;
122         mMobileNetworkDatabase = MobileNetworkDatabase.getInstance(context);
123         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
124         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_MOBILE_NETWORK_DB_CREATED);
125         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
126         mSubscriptionInfoDao = mMobileNetworkDatabase.mSubscriptionInfoDao();
127         mUiccInfoDao = mMobileNetworkDatabase.mUiccInfoDao();
128         mMobileNetworkInfoDao = mMobileNetworkDatabase.mMobileNetworkInfoDao();
129         mAirplaneModeObserver = new AirplaneModeObserver(new Handler(Looper.getMainLooper()));
130         mDataRoamingObserver = new DataRoamingObserver(new Handler(Looper.getMainLooper()));
131     }
132 
133     private class AirplaneModeObserver extends ContentObserver {
134         private Uri mAirplaneModeSettingUri =
135                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
136 
AirplaneModeObserver(Handler handler)137         AirplaneModeObserver(Handler handler) {
138             super(handler);
139         }
140 
register(Context context)141         public void register(Context context) {
142             context.getContentResolver().registerContentObserver(mAirplaneModeSettingUri, false,
143                     this);
144         }
145 
unRegister(Context context)146         public void unRegister(Context context) {
147             context.getContentResolver().unregisterContentObserver(this);
148         }
149 
150         @Override
onChange(boolean selfChange, Uri uri)151         public void onChange(boolean selfChange, Uri uri) {
152             if (uri.equals(mAirplaneModeSettingUri)) {
153                 boolean isAirplaneModeOn = isAirplaneModeOn();
154                 for (MobileNetworkCallback callback : sCallbacks) {
155                     callback.onAirplaneModeChanged(isAirplaneModeOn);
156                 }
157             }
158         }
159     }
160 
161     private class DataRoamingObserver extends ContentObserver {
162         private int mRegSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
163         private String mBaseField = Settings.Global.DATA_ROAMING;
164 
DataRoamingObserver(Handler handler)165         DataRoamingObserver(Handler handler) {
166             super(handler);
167         }
168 
register(Context context, int subId)169         public void register(Context context, int subId) {
170             mRegSubId = subId;
171             String lastField = mBaseField;
172             createTelephonyManagerBySubId(subId);
173             TelephonyManager tm = mTelephonyManagerMap.get(subId);
174             if (tm.getSimCount() != 1) {
175                 lastField += subId;
176             }
177             context.getContentResolver().registerContentObserver(
178                     Settings.Global.getUriFor(lastField), false, this);
179         }
180 
unRegister(Context context)181         public void unRegister(Context context) {
182             context.getContentResolver().unregisterContentObserver(this);
183         }
184 
185         @Override
onChange(boolean selfChange, Uri uri)186         public void onChange(boolean selfChange, Uri uri) {
187             TelephonyManager tm = mTelephonyManagerMap.get(mRegSubId);
188             if (tm == null) {
189                 return;
190             }
191             sExecutor.execute(() -> {
192                 Log.d(TAG, "DataRoamingObserver changed");
193                 insertMobileNetworkInfo(mContext, mRegSubId, tm);
194             });
195             boolean isDataRoamingEnabled = tm.isDataRoamingEnabled();
196             for (MobileNetworkCallback callback : sCallbacks) {
197                 callback.onDataRoamingChanged(mRegSubId, isDataRoamingEnabled);
198             }
199         }
200     }
201 
202     /**
203      * Register all callbacks and listener.
204      *
205      * @param lifecycleOwner The lifecycle owner.
206      * @param mobileNetworkCallback A callback to receive all MobileNetwork's changes.
207      * @param subId The subscription ID to register relevant changes and listener for specific
208      *              subscription.
209      */
addRegister(LifecycleOwner lifecycleOwner, MobileNetworkCallback mobileNetworkCallback, int subId)210     public void addRegister(LifecycleOwner lifecycleOwner,
211             MobileNetworkCallback mobileNetworkCallback, int subId) {
212         if (DEBUG) {
213             Log.d(TAG, "addRegister by SUB ID " + subId);
214         }
215         if (sCallbacks.isEmpty()) {
216             mSubscriptionManager.addOnSubscriptionsChangedListener(mContext.getMainExecutor(),
217                     this);
218             mAirplaneModeObserver.register(mContext);
219             Log.d(TAG, "addRegister done");
220         }
221         sCallbacks.add(mobileNetworkCallback);
222         observeAllSubInfo(lifecycleOwner);
223         observeAllUiccInfo(lifecycleOwner);
224         observeAllMobileNetworkInfo(lifecycleOwner);
225         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
226             createTelephonyManagerBySubId(subId);
227             mDataRoamingObserver.register(mContext, subId);
228         }
229         // When one client registers callback first time, convey the cached results to the client
230         // so that the client is aware of the content therein.
231         sendAvailableSubInfoCache(mobileNetworkCallback);
232     }
233 
createTelephonyManagerBySubId(int subId)234     private void createTelephonyManagerBySubId(int subId) {
235         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
236                 || mTelephonyCallbackMap.containsKey(subId)) {
237             if (DEBUG) {
238                 Log.d(TAG, "createTelephonyManagerBySubId: directly return for subId = " + subId);
239             }
240             return;
241         }
242         PhoneCallStateTelephonyCallback
243                 telephonyCallback = new PhoneCallStateTelephonyCallback(subId);
244         TelephonyManager telephonyManager = mContext.getSystemService(
245                 TelephonyManager.class).createForSubscriptionId(subId);
246         telephonyManager.registerTelephonyCallback(mContext.getMainExecutor(),
247                 telephonyCallback);
248         mTelephonyCallbackMap.put(subId, telephonyCallback);
249         mTelephonyManagerMap.put(subId, telephonyManager);
250     }
251 
getTelephonyManagerBySubId(Context context, int subId)252     private TelephonyManager getTelephonyManagerBySubId(Context context, int subId) {
253         TelephonyManager telephonyManager = mTelephonyManagerMap.get(subId);
254         if (telephonyManager != null) {
255             return telephonyManager;
256         }
257 
258         if (context != null) {
259             telephonyManager = context.getSystemService(TelephonyManager.class);
260             if (telephonyManager != null) {
261                 telephonyManager = telephonyManager.createForSubscriptionId(subId);
262             } else if (DEBUG) {
263                 Log.d(TAG, "Can not get TelephonyManager for subId " + subId);
264             }
265         }
266 
267         return telephonyManager;
268 
269     }
270 
removerRegisterBySubId(int subId)271     private void removerRegisterBySubId(int subId) {
272         if (mTelephonyCallbackMap.containsKey(subId)) {
273             TelephonyManager telephonyManager = getTelephonyManagerBySubId(mContext, subId);
274             if (telephonyManager != null) {
275                 PhoneCallStateTelephonyCallback callback = mTelephonyCallbackMap.get(subId);
276                 if (callback != null) {
277                     telephonyManager.unregisterTelephonyCallback(callback);
278                     mTelephonyCallbackMap.remove(subId);
279                 }
280             }
281         }
282     }
283 
removeRegister(MobileNetworkCallback mobileNetworkCallback)284     public void removeRegister(MobileNetworkCallback mobileNetworkCallback) {
285         synchronized (this) {
286             sCallbacks.remove(mobileNetworkCallback);
287         }
288         if (sCallbacks.isEmpty()) {
289             mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
290             mAirplaneModeObserver.unRegister(mContext);
291             mDataRoamingObserver.unRegister(mContext);
292 
293             mTelephonyManagerMap.forEach((id, manager) -> {
294                 TelephonyCallback callback = mTelephonyCallbackMap.get(id);
295                 if (callback != null) {
296                     manager.unregisterTelephonyCallback(callback);
297                 }
298             });
299             mTelephonyCallbackMap.clear();
300             mTelephonyManagerMap.clear();
301             Log.d(TAG, "removeRegister done");
302         }
303     }
304 
updateEntity()305     public void updateEntity() {
306         // Check the latest state after back to the UI.
307         if (sCacheSubscriptionInfoEntityMap != null || !sCacheSubscriptionInfoEntityMap.isEmpty()) {
308             sExecutor.execute(() -> {
309                 onSubscriptionsChanged();
310             });
311         }
312 
313         boolean isAirplaneModeOn = isAirplaneModeOn();
314         for (MobileNetworkCallback callback : sCallbacks) {
315             callback.onAirplaneModeChanged(isAirplaneModeOn);
316         }
317     }
318 
observeAllSubInfo(LifecycleOwner lifecycleOwner)319     private void observeAllSubInfo(LifecycleOwner lifecycleOwner) {
320         if (DEBUG) {
321             Log.d(TAG, "Observe subInfo.");
322         }
323         mMobileNetworkDatabase.queryAvailableSubInfos().observe(
324                 lifecycleOwner, this::onAvailableSubInfoChanged);
325     }
326 
observeAllUiccInfo(LifecycleOwner lifecycleOwner)327     private void observeAllUiccInfo(LifecycleOwner lifecycleOwner) {
328         if (DEBUG) {
329             Log.d(TAG, "Observe UICC info.");
330         }
331         mMobileNetworkDatabase.queryAllUiccInfo().observe(
332                 lifecycleOwner, this::onAllUiccInfoChanged);
333     }
334 
observeAllMobileNetworkInfo(LifecycleOwner lifecycleOwner)335     private void observeAllMobileNetworkInfo(LifecycleOwner lifecycleOwner) {
336         Log.d(TAG, "Observe mobile network info.");
337         mMobileNetworkDatabase.queryAllMobileNetworkInfo().observe(
338                 lifecycleOwner, this::onAllMobileNetworkInfoChanged);
339     }
340 
getAvailableSubInfoEntityList()341     public List<SubscriptionInfoEntity> getAvailableSubInfoEntityList() {
342         return mAvailableSubInfoEntityList;
343     }
344 
getActiveSubscriptionInfoList()345     public List<SubscriptionInfoEntity> getActiveSubscriptionInfoList() {
346         return mActiveSubInfoEntityList;
347     }
348 
getUiccInfoEntityList()349     public List<UiccInfoEntity> getUiccInfoEntityList() {
350         return mUiccInfoEntityList;
351     }
352 
getMobileNetworkInfoEntityList()353     public List<MobileNetworkInfoEntity> getMobileNetworkInfoEntityList() {
354         return mMobileNetworkInfoEntityList;
355     }
356 
getSubInfoById(String subId)357     public SubscriptionInfoEntity getSubInfoById(String subId) {
358         return mSubscriptionInfoDao.querySubInfoById(subId);
359     }
360 
queryMobileNetworkInfoBySubId(String subId)361     public MobileNetworkInfoEntity queryMobileNetworkInfoBySubId(String subId) {
362         return mMobileNetworkInfoDao.queryMobileNetworkInfoBySubId(subId);
363     }
364 
getUiccInfoBySubscriptionInfo(@onNull UiccSlotInfo[] uiccSlotInfos, SubscriptionInfo subInfo)365     private void getUiccInfoBySubscriptionInfo(@NonNull UiccSlotInfo[] uiccSlotInfos,
366             SubscriptionInfo subInfo) {
367         for (int i = 0; i < uiccSlotInfos.length; i++) {
368             UiccSlotInfo curSlotInfo = uiccSlotInfos[i];
369             if (curSlotInfo != null && curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT) {
370                 final int index = i;
371                 mIsEuicc = curSlotInfo.getIsEuicc();
372                 mCardState = curSlotInfo.getCardStateInfo();
373                 mIsRemovable = curSlotInfo.isRemovable();
374                 mCardId = subInfo.getCardId();
375 
376                 Collection<UiccPortInfo> uiccPortInfos = curSlotInfo.getPorts();
377                 uiccPortInfos.forEach(portInfo -> {
378                     if (portInfo.getPortIndex() == subInfo.getPortIndex()
379                             && portInfo.getLogicalSlotIndex() == subInfo.getSimSlotIndex()) {
380                         mPhysicalSlotIndex = index;
381                         mLogicalSlotIndex = portInfo.getLogicalSlotIndex();
382                         mIsActive = portInfo.isActive();
383                         mPortIndex = portInfo.getPortIndex();
384                     } else if (DEBUG) {
385                         Log.d(TAG, "Can not get port index and physicalSlotIndex for subId "
386                                 + subInfo.getSubscriptionId());
387                     }
388                 });
389                 if (mPhysicalSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
390                     break;
391                 }
392             } else if (DEBUG) {
393                 Log.d(TAG, "Can not get card state info");
394             }
395         }
396         mMetricsFeatureProvider.action(mContext,
397                 SettingsEnums.ACTION_MOBILE_NETWORK_DB_GET_UICC_INFO,
398                 subInfo.getSubscriptionId());
399     }
400 
onAvailableSubInfoChanged( List<SubscriptionInfoEntity> availableSubInfoEntityList)401     private void onAvailableSubInfoChanged(
402             List<SubscriptionInfoEntity> availableSubInfoEntityList) {
403         synchronized (this) {
404             if (mAvailableSubInfoEntityList != null
405                     && mAvailableSubInfoEntityList.size() == availableSubInfoEntityList.size()
406                     && mAvailableSubInfoEntityList.containsAll(availableSubInfoEntityList)) {
407                 Log.d(TAG, "onAvailableSubInfoChanged, duplicates = " + availableSubInfoEntityList);
408                 return;
409             }
410             mAvailableSubInfoEntityList = new ArrayList<>(availableSubInfoEntityList);
411         }
412 
413         Log.d(TAG, "onAvailableSubInfoChanged, availableSubInfoEntityList = "
414                     + availableSubInfoEntityList);
415 
416         for (MobileNetworkCallback callback : sCallbacks) {
417             callback.onAvailableSubInfoChanged(availableSubInfoEntityList);
418         }
419         mMetricsFeatureProvider.action(mContext,
420                 SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_SUB_INFO_IS_CHANGED, 0);
421         onActiveSubInfoListChanged(availableSubInfoEntityList);
422     }
423 
onActiveSubInfoListChanged( List<SubscriptionInfoEntity> availableSubInfoEntityList)424     private void onActiveSubInfoListChanged(
425             List<SubscriptionInfoEntity> availableSubInfoEntityList) {
426         List<SubscriptionInfoEntity> activeSubInfoEntityList =
427                 availableSubInfoEntityList.stream()
428                 .filter(SubscriptionInfoEntity::isActiveSubscription)
429                 .filter(SubscriptionInfoEntity::isSubscriptionVisible)
430                 .collect(Collectors.toList());
431 
432         Log.d(TAG, "onActiveSubInfoChanged, activeSubInfoEntityList = "
433                 + activeSubInfoEntityList);
434 
435         List<SubscriptionInfoEntity> tempActiveSubInfoEntityList = new ArrayList<>(
436                 activeSubInfoEntityList);
437         synchronized (this) {
438             mActiveSubInfoEntityList = activeSubInfoEntityList;
439         }
440         for (MobileNetworkCallback callback : sCallbacks) {
441             callback.onActiveSubInfoChanged(tempActiveSubInfoEntityList);
442         }
443     }
444 
sendAvailableSubInfoCache(MobileNetworkCallback callback)445     private void sendAvailableSubInfoCache(MobileNetworkCallback callback) {
446         if (callback != null) {
447              List<SubscriptionInfoEntity> availableSubInfoEntityList = null;
448              List<SubscriptionInfoEntity> activeSubInfoEntityList = null;
449              synchronized (this) {
450                  if (mAvailableSubInfoEntityList != null) {
451                      availableSubInfoEntityList = new ArrayList<>(mAvailableSubInfoEntityList);
452                  }
453                  if (mActiveSubInfoEntityList != null) {
454                      activeSubInfoEntityList = new ArrayList<>(mActiveSubInfoEntityList);
455                  }
456              }
457              if (availableSubInfoEntityList != null) {
458                  callback.onAvailableSubInfoChanged(availableSubInfoEntityList);
459              }
460              if (activeSubInfoEntityList != null) {
461                  callback.onActiveSubInfoChanged(activeSubInfoEntityList);
462              }
463         }
464     }
465 
onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList)466     private void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList) {
467         mUiccInfoEntityList = new ArrayList<>(uiccInfoEntityList);
468         for (MobileNetworkCallback callback : sCallbacks) {
469             callback.onAllUiccInfoChanged(uiccInfoEntityList);
470         }
471         mMetricsFeatureProvider.action(mContext,
472                 SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_UICC_INFO_IS_CHANGED, 0);
473     }
474 
onAllMobileNetworkInfoChanged( List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList)475     private void onAllMobileNetworkInfoChanged(
476             List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
477         mMobileNetworkInfoEntityList = new ArrayList<>(mobileNetworkInfoEntityList);
478         for (MobileNetworkCallback callback : sCallbacks) {
479             callback.onAllMobileNetworkInfoChanged(mobileNetworkInfoEntityList);
480         }
481         mMetricsFeatureProvider.action(mContext,
482                 SettingsEnums.ACTION_MOBILE_NETWORK_DB_NOTIFY_MOBILE_NETWORK_INFO_IS_CHANGED, 0);
483     }
484 
insertSubInfo(Context context, SubscriptionInfo info)485     private void insertSubInfo(Context context, SubscriptionInfo info) {
486         int subId = info.getSubscriptionId();
487         createTelephonyManagerBySubId(subId);
488         TelephonyManager telephonyManager = getTelephonyManagerBySubId(context, subId);
489         SubscriptionInfoEntity subInfoEntity =
490                 convertToSubscriptionInfoEntity(context, info, telephonyManager);
491         if (subInfoEntity != null) {
492             if (!sCacheSubscriptionInfoEntityMap.containsKey(subId)
493                     || (sCacheSubscriptionInfoEntityMap.get(subId) != null
494                     && !sCacheSubscriptionInfoEntityMap.get(subId).equals(subInfoEntity))) {
495                 sCacheSubscriptionInfoEntityMap.put(subId, subInfoEntity);
496                 if (DEBUG) {
497                     Log.d(TAG, "Convert subId " + subId + " to SubscriptionInfoEntity: "
498                             + subInfoEntity);
499                 } else {
500                     Log.d(TAG, "insertSubsInfo into SubscriptionInfoEntity");
501                 }
502                 mMobileNetworkDatabase.insertSubsInfo(subInfoEntity);
503                 mMetricsFeatureProvider.action(mContext,
504                         SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_SUB_INFO, subId);
505                 insertUiccInfo(subId, telephonyManager);
506                 insertMobileNetworkInfo(context, subId, telephonyManager);
507             }
508         } else if (DEBUG) {
509             Log.d(TAG, "Can not insert subInfo, the entity is null");
510         }
511     }
512 
deleteAllInfoBySubId(String subId)513     private void deleteAllInfoBySubId(String subId) {
514         Log.d(TAG, "deleteAllInfoBySubId, subId = " + subId);
515         mMobileNetworkDatabase.deleteSubInfoBySubId(subId);
516         mMobileNetworkDatabase.deleteUiccInfoBySubId(subId);
517         mMobileNetworkDatabase.deleteMobileNetworkInfoBySubId(subId);
518         mUiccInfoEntityList.removeIf(info -> info.subId.equals(subId));
519         mMobileNetworkInfoEntityList.removeIf(info -> info.subId.equals(subId));
520         int id = Integer.parseInt(subId);
521         removerRegisterBySubId(id);
522         mSubscriptionInfoMap.remove(id);
523         mTelephonyManagerMap.remove(id);
524         sCacheSubscriptionInfoEntityMap.remove(id);
525         sCacheUiccInfoEntityMap.remove(id);
526         sCacheMobileNetworkInfoEntityMap.remove(id);
527         mMetricsFeatureProvider.action(mContext,
528                 SettingsEnums.ACTION_MOBILE_NETWORK_DB_DELETE_DATA, id);
529     }
530 
convertToSubscriptionInfoEntity(Context context, SubscriptionInfo subInfo, TelephonyManager telephonyManager)531     private SubscriptionInfoEntity convertToSubscriptionInfoEntity(Context context,
532             SubscriptionInfo subInfo, TelephonyManager telephonyManager) {
533         int subId = subInfo.getSubscriptionId();
534         if (telephonyManager == null) {
535             if (DEBUG) {
536                 Log.d(TAG, "Can not get TelephonyManager for subId " + subId);
537             }
538             return null;
539         }
540         UiccSlotInfo[] uiccSlotInfos = telephonyManager.getUiccSlotsInfo();
541         if (uiccSlotInfos == null || uiccSlotInfos.length == 0) {
542             if (DEBUG) {
543                 Log.d(TAG, "uiccSlotInfos = null or empty");
544             }
545             return null;
546         } else {
547             getUiccInfoBySubscriptionInfo(uiccSlotInfos, subInfo);
548             SubscriptionInfo firstRemovableSubInfo = SubscriptionUtil.getFirstRemovableSubscription(
549                     context);
550             if (DEBUG) {
551                 Log.d(TAG, "convert subscriptionInfo to entity for subId = " + subId);
552             }
553             return new SubscriptionInfoEntity(String.valueOf(subId),
554                     subInfo.getSimSlotIndex(),
555                     subInfo.getCarrierId(), subInfo.getDisplayName().toString(),
556                     subInfo.getCarrierName() != null ? subInfo.getCarrierName().toString() : "",
557                     subInfo.getDataRoaming(), subInfo.getMccString(), subInfo.getMncString(),
558                     subInfo.getCountryIso(), subInfo.isEmbedded(), mCardId,
559                     subInfo.getPortIndex(), subInfo.isOpportunistic(),
560                     String.valueOf(subInfo.getGroupUuid()),
561                     subInfo.getSubscriptionType(),
562                     SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, context).toString(),
563                     SubscriptionUtil.isSubscriptionVisible(mSubscriptionManager, context, subInfo),
564                     SubscriptionUtil.getFormattedPhoneNumber(context, subInfo),
565                     firstRemovableSubInfo == null ? false
566                             : firstRemovableSubInfo.getSubscriptionId() == subId,
567                     SubscriptionUtil.isDefaultSubscription(context, subId),
568                     mSubscriptionManager.isValidSubscriptionId(subId),
569                     mSubscriptionManager.isUsableSubscriptionId(subId),
570                     mSubscriptionManager.isActiveSubscriptionId(subId),
571                     true /*availableSubInfo*/,
572                     mSubscriptionManager.getActiveDataSubscriptionId() == subId);
573         }
574     }
575 
insertUiccInfo(int subId, TelephonyManager telephonyManager)576     private void insertUiccInfo(int subId, TelephonyManager telephonyManager) {
577         UiccInfoEntity uiccInfoEntity = convertToUiccInfoEntity(subId, telephonyManager);
578         if (DEBUG) {
579             Log.d(TAG, "uiccInfoEntity = " + uiccInfoEntity);
580         }
581         if (!sCacheUiccInfoEntityMap.containsKey(subId)
582                 || !sCacheUiccInfoEntityMap.get(subId).equals(uiccInfoEntity)) {
583             sCacheUiccInfoEntityMap.put(subId, uiccInfoEntity);
584             mMobileNetworkDatabase.insertUiccInfo(uiccInfoEntity);
585             mMetricsFeatureProvider.action(mContext,
586                     SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_UICC_INFO, subId);
587         }
588     }
589 
insertMobileNetworkInfo(Context context, int subId, TelephonyManager telephonyManager)590     private void insertMobileNetworkInfo(Context context, int subId,
591             TelephonyManager telephonyManager) {
592         MobileNetworkInfoEntity mobileNetworkInfoEntity = convertToMobileNetworkInfoEntity(context,
593                 subId, telephonyManager);
594 
595 
596         Log.d(TAG, "insertMobileNetworkInfo, mobileNetworkInfoEntity = "
597                 + mobileNetworkInfoEntity);
598 
599 
600         if (mobileNetworkInfoEntity == null) {
601             return;
602         }
603 
604         if (!sCacheMobileNetworkInfoEntityMap.containsKey(subId)
605                 || !sCacheMobileNetworkInfoEntityMap.get(subId).equals(mobileNetworkInfoEntity)) {
606             sCacheMobileNetworkInfoEntityMap.put(subId, mobileNetworkInfoEntity);
607             mMobileNetworkDatabase.insertMobileNetworkInfo(mobileNetworkInfoEntity);
608             mMetricsFeatureProvider.action(mContext,
609                     SettingsEnums.ACTION_MOBILE_NETWORK_DB_INSERT_MOBILE_NETWORK_INFO, subId);
610         }
611     }
612 
convertToMobileNetworkInfoEntity(Context context, int subId, TelephonyManager telephonyManager)613     private MobileNetworkInfoEntity convertToMobileNetworkInfoEntity(Context context, int subId,
614             TelephonyManager telephonyManager) {
615         boolean isDataEnabled = false;
616         boolean isDataRoamingEnabled = false;
617         if (telephonyManager != null) {
618             isDataEnabled = telephonyManager.isDataEnabled();
619             isDataRoamingEnabled = telephonyManager.isDataRoamingEnabled();
620         } else {
621             Log.d(TAG, "TelephonyManager is null, subId = " + subId);
622         }
623 
624         return new MobileNetworkInfoEntity(String.valueOf(subId),
625                 MobileNetworkUtils.isContactDiscoveryEnabled(context, subId),
626                 MobileNetworkUtils.isContactDiscoveryVisible(context, subId),
627                 isDataEnabled,
628                 MobileNetworkUtils.isCdmaOptions(context, subId),
629                 MobileNetworkUtils.isGsmOptions(context, subId),
630                 MobileNetworkUtils.isWorldMode(context, subId),
631                 MobileNetworkUtils.shouldDisplayNetworkSelectOptions(context, subId),
632                 MobileNetworkUtils.isTdscdmaSupported(context, subId),
633                 MobileNetworkUtils.activeNetworkIsCellular(context),
634                 SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager),
635                 isDataRoamingEnabled
636         );
637     }
638 
convertToUiccInfoEntity(int subId, TelephonyManager telephonyManager)639     private UiccInfoEntity convertToUiccInfoEntity(int subId, TelephonyManager telephonyManager) {
640         return new UiccInfoEntity(String.valueOf(subId), String.valueOf(mPhysicalSlotIndex),
641                 mLogicalSlotIndex, mCardId, mIsEuicc,
642                 isMultipleEnabledProfilesSupported(telephonyManager), mCardState, mIsRemovable,
643                 mIsActive, mPortIndex
644         );
645     }
646 
isMultipleEnabledProfilesSupported(TelephonyManager telephonyManager)647     private boolean isMultipleEnabledProfilesSupported(TelephonyManager telephonyManager) {
648         if (telephonyManager == null) {
649             Log.d(TAG, "TelephonyManager is null");
650             return false;
651         }
652 
653         List<UiccCardInfo> cardInfos = telephonyManager.getUiccCardsInfo();
654         if (cardInfos == null) {
655             Log.d(TAG, "UICC card info list is empty.");
656             return false;
657         }
658         return cardInfos.stream().anyMatch(
659                 cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
660     }
661 
662     @Override
onSubscriptionsChanged()663     public void onSubscriptionsChanged() {
664         insertAvailableSubInfoToEntity(
665                 SubscriptionUtil.getSelectableSubscriptionInfoList(mContext));
666     }
667 
insertAvailableSubInfoToEntity(List<SubscriptionInfo> inputAvailableInfoList)668     private void insertAvailableSubInfoToEntity(List<SubscriptionInfo> inputAvailableInfoList) {
669         sExecutor.execute(() -> {
670             SubscriptionInfoEntity[] availableInfoArray = null;
671             int availableEntitySize = 0;
672             synchronized (this) {
673                 availableInfoArray = mAvailableSubInfoEntityList.toArray(
674                     new SubscriptionInfoEntity[0]);
675                 availableEntitySize = mAvailableSubInfoEntityList.size();
676             }
677             if ((inputAvailableInfoList == null || inputAvailableInfoList.size() == 0)
678                     && availableEntitySize != 0) {
679                 if (DEBUG) {
680                     Log.d(TAG, "availableSudInfoList from framework is empty, remove all subs");
681                 }
682 
683                 for (SubscriptionInfoEntity info : availableInfoArray) {
684                     deleteAllInfoBySubId(info.subId);
685                 }
686 
687             } else if (inputAvailableInfoList != null) {
688                 SubscriptionInfo[] inputAvailableInfoArray = inputAvailableInfoList.toArray(
689                         new SubscriptionInfo[0]);
690                 // Remove the redundant subInfo
691                 if (inputAvailableInfoList.size() <= availableEntitySize) {
692                     for (SubscriptionInfo subInfo : inputAvailableInfoArray) {
693                         int subId = subInfo.getSubscriptionId();
694                         if (mSubscriptionInfoMap.containsKey(subId)) {
695                             mSubscriptionInfoMap.remove(subId);
696                         }
697                     }
698 
699                     if (!mSubscriptionInfoMap.isEmpty()) {
700                         for (Integer key : mSubscriptionInfoMap.keySet()) {
701                             if (key != null) {
702                                 deleteAllInfoBySubId(String.valueOf(key));
703                             }
704                         }
705                     } else if (inputAvailableInfoList.size() < availableEntitySize) {
706                         // Check the subInfo between the new list from framework and old list in
707                         // the database, if the subInfo is not existed in the new list, delete it
708                         // from the database.
709                         for (SubscriptionInfoEntity info : availableInfoArray) {
710                             if (sCacheSubscriptionInfoEntityMap.containsKey(info.getSubId())) {
711                                 deleteAllInfoBySubId(info.subId);
712                             }
713                         }
714                     }
715                 }
716 
717                 // Insert all new available subInfo to database.
718                 for (SubscriptionInfo subInfo : inputAvailableInfoArray) {
719                     if (DEBUG) {
720                         Log.d(TAG, "insert subInfo to subInfoEntity, subInfo = " + subInfo);
721                     }
722                     if (subInfo.isEmbedded()
723                         && (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING
724                             || (Flags.oemEnabledSatelliteFlag()
725                             && subInfo.isOnlyNonTerrestrialNetwork()))) {
726                         if (DEBUG) {
727                             Log.d(TAG, "Do not insert the provisioning or satellite eSIM");
728                         }
729                         continue;
730                     }
731                     mSubscriptionInfoMap.put(subInfo.getSubscriptionId(), subInfo);
732                     insertSubInfo(mContext, subInfo);
733                 }
734             }
735         });
736     }
737 
isAirplaneModeOn()738     public boolean isAirplaneModeOn() {
739         return Settings.Global.getInt(mContext.getContentResolver(),
740                 Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
741     }
742 
743     private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
744             TelephonyCallback.CallStateListener,
745             TelephonyCallback.UserMobileDataStateListener {
746 
747         private int mSubId;
748 
PhoneCallStateTelephonyCallback(int subId)749         public PhoneCallStateTelephonyCallback(int subId) {
750             mSubId = subId;
751         }
752 
753         @Override
onCallStateChanged(int state)754         public void onCallStateChanged(int state) {
755             for (MobileNetworkCallback callback : sCallbacks) {
756                 callback.onCallStateChanged(state);
757             }
758         }
759 
760         @Override
onUserMobileDataStateChanged(boolean enabled)761         public void onUserMobileDataStateChanged(boolean enabled) {
762             Log.d(TAG, "onUserMobileDataStateChanged enabled " + enabled + " on SUB " + mSubId);
763             sExecutor.execute(() -> {
764                 insertMobileNetworkInfo(mContext, mSubId,
765                         getTelephonyManagerBySubId(mContext, mSubId));
766             });
767         }
768     }
769 
770     /**
771      * Callback for clients to get the latest info changes if the framework or content observers.
772      * updates the relevant info.
773      */
774     public interface MobileNetworkCallback {
onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList)775         default void onAvailableSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
776         }
777 
onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList)778         default void onActiveSubInfoChanged(List<SubscriptionInfoEntity> subInfoEntityList) {
779         }
780 
onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList)781         default void onAllUiccInfoChanged(List<UiccInfoEntity> uiccInfoEntityList) {
782         }
783 
onAllMobileNetworkInfoChanged( List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList)784         default void onAllMobileNetworkInfoChanged(
785                 List<MobileNetworkInfoEntity> mobileNetworkInfoEntityList) {
786         }
787 
onAirplaneModeChanged(boolean enabled)788         default void onAirplaneModeChanged(boolean enabled) {
789         }
790 
791         /**
792          * Notify clients data roaming changed of subscription.
793          */
onDataRoamingChanged(int subId, boolean enabled)794         default void onDataRoamingChanged(int subId, boolean enabled) {
795         }
796 
onCallStateChanged(int state)797         default void onCallStateChanged(int state) {
798         }
799     }
800 
dump(IndentingPrintWriter printwriter)801     public void dump(IndentingPrintWriter printwriter) {
802         printwriter.println(TAG + ": ");
803         printwriter.increaseIndent();
804         printwriter.println(" availableSubInfoEntityList= " + mAvailableSubInfoEntityList);
805         printwriter.println(" activeSubInfoEntityList=" + mActiveSubInfoEntityList);
806         printwriter.println(" mobileNetworkInfoEntityList= " + mMobileNetworkInfoEntityList);
807         printwriter.println(" uiccInfoEntityList= " + mUiccInfoEntityList);
808         printwriter.println(" CacheSubscriptionInfoEntityMap= " + sCacheSubscriptionInfoEntityMap);
809         printwriter.println(" SubscriptionInfoMap= " + mSubscriptionInfoMap);
810         printwriter.flush();
811         printwriter.decreaseIndent();
812     }
813 }
814