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