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