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