1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vcn; 18 19 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; 20 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 21 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.TargetApi; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.vcn.VcnManager; 31 import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; 32 import android.os.Build; 33 import android.os.Handler; 34 import android.os.ParcelUuid; 35 import android.os.PersistableBundle; 36 import android.telephony.CarrierConfigManager; 37 import android.telephony.SubscriptionInfo; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 40 import android.telephony.TelephonyCallback; 41 import android.telephony.TelephonyManager; 42 import android.telephony.TelephonyManager.CarrierPrivilegesCallback; 43 import android.util.ArrayMap; 44 import android.util.ArraySet; 45 import android.util.IndentingPrintWriter; 46 import android.util.Slog; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.annotations.VisibleForTesting.Visibility; 50 import com.android.modules.utils.HandlerExecutor; 51 52 import java.util.ArrayList; 53 import java.util.Collections; 54 import java.util.HashMap; 55 import java.util.Iterator; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Map.Entry; 59 import java.util.Objects; 60 import java.util.Set; 61 62 /** 63 * TelephonySubscriptionTracker provides a caching layer for tracking active subscription groups. 64 * 65 * <p>This class performs two roles: 66 * 67 * <ol> 68 * <li>De-noises subscription changes by ensuring that only changes in active and ready 69 * subscription groups are acted upon 70 * <li>Caches mapping between subIds and subscription groups 71 * </ol> 72 * 73 * <p>An subscription group is active and ready if any of its contained subIds has had BOTH the 74 * {@link CarrierConfigManager#isConfigForIdentifiedCarrier()} return true, AND the subscription is 75 * listed as active per SubscriptionManager#getAllSubscriptionInfoList(). 76 * 77 * <p>Note that due to the asynchronous nature of callbacks and broadcasts, the output of this class 78 * is (only) eventually consistent. 79 * 80 * @hide 81 */ 82 @TargetApi(Build.VERSION_CODES.BAKLAVA) 83 public class TelephonySubscriptionTracker extends BroadcastReceiver { 84 @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName(); 85 private static final boolean LOG_DBG = false; // STOPSHIP if true 86 87 @NonNull private final Context mContext; 88 @NonNull private final Handler mHandler; 89 @NonNull private final TelephonySubscriptionTrackerCallback mCallback; 90 @NonNull private final Dependencies mDeps; 91 92 @NonNull private final TelephonyManager mTelephonyManager; 93 @NonNull private final SubscriptionManager mSubscriptionManager; 94 @Nullable private final CarrierConfigManager mCarrierConfigManager; 95 96 @NonNull private final ActiveDataSubscriptionIdListener mActiveDataSubIdListener; 97 98 // TODO (Android T+): Add ability to handle multiple subIds per slot. 99 @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>(); 100 101 @NonNull 102 private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>(); 103 104 @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener; 105 106 @NonNull 107 private final List<CarrierPrivilegesCallback> mCarrierPrivilegesCallbacks = new ArrayList<>(); 108 109 @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot; 110 111 @NonNull 112 private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = 113 (int logicalSlotIndex, int subscriptionId, int carrierId, int specificCarrierId) -> 114 handleActionCarrierConfigChanged(logicalSlotIndex, subscriptionId); 115 116 TelephonySubscriptionTracker( @onNull Context context, @NonNull Handler handler, @NonNull TelephonySubscriptionTrackerCallback callback)117 public TelephonySubscriptionTracker( 118 @NonNull Context context, 119 @NonNull Handler handler, 120 @NonNull TelephonySubscriptionTrackerCallback callback) { 121 this(context, handler, callback, new Dependencies()); 122 } 123 124 @VisibleForTesting(visibility = Visibility.PRIVATE) TelephonySubscriptionTracker( @onNull Context context, @NonNull Handler handler, @NonNull TelephonySubscriptionTrackerCallback callback, @NonNull Dependencies deps)125 TelephonySubscriptionTracker( 126 @NonNull Context context, 127 @NonNull Handler handler, 128 @NonNull TelephonySubscriptionTrackerCallback callback, 129 @NonNull Dependencies deps) { 130 mContext = Objects.requireNonNull(context, "Missing context"); 131 mHandler = Objects.requireNonNull(handler, "Missing handler"); 132 mCallback = Objects.requireNonNull(callback, "Missing callback"); 133 mDeps = Objects.requireNonNull(deps, "Missing deps"); 134 135 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 136 mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); 137 mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); 138 mActiveDataSubIdListener = new ActiveDataSubscriptionIdListener(); 139 140 mSubscriptionChangedListener = 141 new OnSubscriptionsChangedListener() { 142 @Override 143 public void onSubscriptionsChanged() { 144 handleSubscriptionsChanged(); 145 } 146 }; 147 } 148 149 /** 150 * Registers the receivers, and starts tracking subscriptions. 151 * 152 * <p>Must always be run on the VcnManagementService thread. 153 */ register()154 public void register() { 155 final HandlerExecutor executor = new HandlerExecutor(mHandler); 156 final IntentFilter filter = new IntentFilter(); 157 filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED); 158 159 mContext.registerReceiver(this, filter, null, mHandler); 160 mSubscriptionManager.addOnSubscriptionsChangedListener( 161 executor, mSubscriptionChangedListener); 162 mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener); 163 if (mCarrierConfigManager != null) { 164 mCarrierConfigManager.registerCarrierConfigChangeListener(executor, 165 mCarrierConfigChangeListener); 166 } 167 168 registerCarrierPrivilegesCallbacks(); 169 } 170 registerCarrierPrivilegesCallbacks()171 private void registerCarrierPrivilegesCallbacks() { 172 final HandlerExecutor executor = new HandlerExecutor(mHandler); 173 final int modemCount = mTelephonyManager.getActiveModemCount(); 174 try { 175 for (int i = 0; i < modemCount; i++) { 176 CarrierPrivilegesCallback carrierPrivilegesCallback = 177 new CarrierPrivilegesCallback() { 178 @Override 179 public void onCarrierPrivilegesChanged( 180 @NonNull Set<String> privilegedPackageNames, 181 @NonNull Set<Integer> privilegedUids) { 182 // Re-trigger the synchronous check (which is also very cheap due 183 // to caching in CarrierPrivilegesTracker). This allows consistency 184 // with the onSubscriptionsChangedListener and broadcasts. 185 handleSubscriptionsChanged(); 186 } 187 }; 188 189 mTelephonyManager.registerCarrierPrivilegesCallback( 190 i, executor, carrierPrivilegesCallback); 191 mCarrierPrivilegesCallbacks.add(carrierPrivilegesCallback); 192 } 193 } catch (IllegalArgumentException e) { 194 Slog.wtf(TAG, "Encounted exception registering carrier privileges listeners", e); 195 } 196 } 197 198 /** 199 * Unregisters the receivers, and stops tracking subscriptions. 200 * 201 * <p>Must always be run on the VcnManagementService thread. 202 */ unregister()203 public void unregister() { 204 mContext.unregisterReceiver(this); 205 mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener); 206 mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener); 207 if (mCarrierConfigManager != null) { 208 mCarrierConfigManager.unregisterCarrierConfigChangeListener( 209 mCarrierConfigChangeListener); 210 } 211 212 unregisterCarrierPrivilegesCallbacks(); 213 } 214 unregisterCarrierPrivilegesCallbacks()215 private void unregisterCarrierPrivilegesCallbacks() { 216 for (CarrierPrivilegesCallback carrierPrivilegesCallback : 217 mCarrierPrivilegesCallbacks) { 218 mTelephonyManager.unregisterCarrierPrivilegesCallback(carrierPrivilegesCallback); 219 } 220 mCarrierPrivilegesCallbacks.clear(); 221 } 222 223 /** 224 * Handles subscription changes, correlating available subscriptions and loaded carrier configs 225 * 226 * <p>The subscription change listener is registered with a HandlerExecutor backed by mHandler, 227 * so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking. 228 */ handleSubscriptionsChanged()229 public void handleSubscriptionsChanged() { 230 final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>(); 231 final Map<Integer, SubscriptionInfo> newSubIdToInfoMap = new HashMap<>(); 232 233 final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList(); 234 if (allSubs == null) { 235 return; // Telephony crashed; no way to verify subscriptions. 236 } 237 238 // If allSubs is empty, no subscriptions exist. Cache will be cleared by virtue of no active 239 // subscriptions 240 for (SubscriptionInfo subInfo : allSubs) { 241 if (subInfo.getGroupUuid() == null) { 242 continue; 243 } 244 245 // Build subId -> subGrp cache 246 newSubIdToInfoMap.put(subInfo.getSubscriptionId(), subInfo); 247 248 // Update subscription groups that are both ready, and active. For a group to be 249 // considered active, both of the following must be true: 250 // 251 // 1. A final CARRIER_CONFIG_CHANGED (where config is for an identified carrier) 252 // broadcast must have been received for the subId 253 // 2. A active subscription (is loaded into a SIM slot) must be part of the subscription 254 // group. 255 if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX 256 && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) { 257 final TelephonyManager subIdSpecificTelephonyManager = 258 mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); 259 260 final ParcelUuid subGroup = subInfo.getGroupUuid(); 261 final Set<String> pkgs = 262 privilegedPackages.getOrDefault(subGroup, new ArraySet<>()); 263 pkgs.addAll(subIdSpecificTelephonyManager.getPackagesWithCarrierPrivileges()); 264 265 privilegedPackages.put(subGroup, pkgs); 266 } 267 } 268 269 final TelephonySubscriptionSnapshot newSnapshot = 270 new TelephonySubscriptionSnapshot( 271 mDeps.getActiveDataSubscriptionId(), 272 newSubIdToInfoMap, 273 mSubIdToCarrierConfigMap, 274 privilegedPackages); 275 276 // If snapshot was meaningfully updated, fire the callback 277 if (!newSnapshot.equals(mCurrentSnapshot)) { 278 mCurrentSnapshot = newSnapshot; 279 mHandler.post( 280 () -> { 281 mCallback.onNewSnapshot(newSnapshot); 282 }); 283 } 284 } 285 286 /** 287 * Broadcast receiver for ACTION_MULTI_SIM_CONFIG_CHANGED 288 * 289 * <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all 290 * serialized on mHandler, avoiding the need for locking. 291 */ 292 @Override onReceive(Context context, Intent intent)293 public void onReceive(Context context, Intent intent) { 294 switch (intent.getAction()) { 295 case ACTION_MULTI_SIM_CONFIG_CHANGED: 296 handleActionMultiSimConfigChanged(context, intent); 297 break; 298 default: 299 Slog.v(TAG, "Unknown intent received with action: " + intent.getAction()); 300 } 301 } 302 handleActionMultiSimConfigChanged(Context context, Intent intent)303 private void handleActionMultiSimConfigChanged(Context context, Intent intent) { 304 unregisterCarrierPrivilegesCallbacks(); 305 306 // Clear invalid slotIds from the mReadySubIdsBySlotId map. 307 final int modemCount = mTelephonyManager.getActiveModemCount(); 308 final Iterator<Integer> slotIdIterator = mReadySubIdsBySlotId.keySet().iterator(); 309 while (slotIdIterator.hasNext()) { 310 final int slotId = slotIdIterator.next(); 311 312 if (slotId >= modemCount) { 313 slotIdIterator.remove(); 314 } 315 } 316 317 registerCarrierPrivilegesCallbacks(); 318 handleSubscriptionsChanged(); 319 } 320 handleActionCarrierConfigChanged(int slotId, int subId)321 private void handleActionCarrierConfigChanged(int slotId, int subId) { 322 if (slotId == INVALID_SIM_SLOT_INDEX) { 323 return; 324 } 325 326 if (SubscriptionManager.isValidSubscriptionId(subId)) { 327 // Get only configs as needed to save memory. 328 PersistableBundle carrierConfig = new PersistableBundle(); 329 try { 330 carrierConfig = 331 mCarrierConfigManager.getConfigForSubId( 332 subId, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS); 333 334 } catch (RuntimeException exception) { 335 Slog.w(TAG, "CarrierConfigLoader is not available."); 336 } 337 338 if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) { 339 mReadySubIdsBySlotId.put(slotId, subId); 340 341 if (!carrierConfig.isEmpty()) { 342 mSubIdToCarrierConfigMap.put(subId, 343 new PersistableBundleWrapper(carrierConfig)); 344 } 345 handleSubscriptionsChanged(); 346 } 347 } else { 348 final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId); 349 if (oldSubid != null) { 350 mSubIdToCarrierConfigMap.remove(oldSubid); 351 } 352 handleSubscriptionsChanged(); 353 } 354 } 355 356 @VisibleForTesting(visibility = Visibility.PRIVATE) setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId)357 void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) { 358 mReadySubIdsBySlotId.clear(); 359 mReadySubIdsBySlotId.putAll(readySubIdsBySlotId); 360 } 361 362 @VisibleForTesting(visibility = Visibility.PRIVATE) setSubIdToCarrierConfigMap( Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap)363 void setSubIdToCarrierConfigMap( 364 Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) { 365 mSubIdToCarrierConfigMap.clear(); 366 mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap); 367 } 368 369 @VisibleForTesting(visibility = Visibility.PRIVATE) getReadySubIdsBySlotId()370 Map<Integer, Integer> getReadySubIdsBySlotId() { 371 return Collections.unmodifiableMap(mReadySubIdsBySlotId); 372 } 373 374 @VisibleForTesting(visibility = Visibility.PRIVATE) getSubIdToCarrierConfigMap()375 Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() { 376 return Collections.unmodifiableMap(mSubIdToCarrierConfigMap); 377 } 378 379 /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */ 380 public static class TelephonySubscriptionSnapshot { 381 private final int mActiveDataSubId; 382 private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap; 383 private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap; 384 private final Map<ParcelUuid, Set<String>> mPrivilegedPackages; 385 386 public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT = 387 new TelephonySubscriptionSnapshot( 388 INVALID_SUBSCRIPTION_ID, 389 Collections.emptyMap(), 390 Collections.emptyMap(), 391 Collections.emptyMap()); 392 393 @VisibleForTesting(visibility = Visibility.PRIVATE) TelephonySubscriptionSnapshot( int activeDataSubId, @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap, @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, @NonNull Map<ParcelUuid, Set<String>> privilegedPackages)394 TelephonySubscriptionSnapshot( 395 int activeDataSubId, 396 @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap, 397 @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, 398 @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) { 399 mActiveDataSubId = activeDataSubId; 400 Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null"); 401 Objects.requireNonNull(privilegedPackages, "privilegedPackages was null"); 402 Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null"); 403 404 mSubIdToInfoMap = 405 Collections.unmodifiableMap( 406 new HashMap<Integer, SubscriptionInfo>(subIdToInfoMap)); 407 mSubIdToCarrierConfigMap = 408 Collections.unmodifiableMap( 409 new HashMap<Integer, PersistableBundleWrapper>( 410 subIdToCarrierConfigMap)); 411 412 final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>(); 413 for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) { 414 unmodifiableInnerSets.put( 415 entry.getKey(), Collections.unmodifiableSet(entry.getValue())); 416 } 417 mPrivilegedPackages = Collections.unmodifiableMap(unmodifiableInnerSets); 418 } 419 420 /** Returns the active subscription ID. May be INVALID_SUBSCRIPTION_ID */ getActiveDataSubscriptionId()421 public int getActiveDataSubscriptionId() { 422 return mActiveDataSubId; 423 } 424 425 /** Returns the active subscription group */ 426 @Nullable getActiveDataSubscriptionGroup()427 public ParcelUuid getActiveDataSubscriptionGroup() { 428 final SubscriptionInfo info = mSubIdToInfoMap.get(getActiveDataSubscriptionId()); 429 if (info == null) { 430 return null; 431 } 432 433 return info.getGroupUuid(); 434 } 435 436 /** Returns the active subscription groups */ 437 @NonNull getActiveSubscriptionGroups()438 public Set<ParcelUuid> getActiveSubscriptionGroups() { 439 return mPrivilegedPackages.keySet(); 440 } 441 442 /** Returns all subscription groups */ 443 @NonNull getAllSubscriptionGroups()444 public Set<ParcelUuid> getAllSubscriptionGroups() { 445 final Set<ParcelUuid> subGroups = new ArraySet<>(); 446 for (SubscriptionInfo subInfo : mSubIdToInfoMap.values()) { 447 subGroups.add(subInfo.getGroupUuid()); 448 } 449 450 return subGroups; 451 } 452 453 /** Checks if the provided package is carrier privileged for the specified sub group. */ packageHasPermissionsForSubscriptionGroup( @onNull ParcelUuid subGrp, @NonNull String packageName)454 public boolean packageHasPermissionsForSubscriptionGroup( 455 @NonNull ParcelUuid subGrp, @NonNull String packageName) { 456 final Set<String> privilegedPackages = mPrivilegedPackages.get(subGrp); 457 458 return privilegedPackages != null && privilegedPackages.contains(packageName); 459 } 460 461 /** Returns the Subscription Group for a given subId. */ 462 @Nullable getGroupForSubId(int subId)463 public ParcelUuid getGroupForSubId(int subId) { 464 return mSubIdToInfoMap.containsKey(subId) 465 ? mSubIdToInfoMap.get(subId).getGroupUuid() 466 : null; 467 } 468 469 /** 470 * Returns all the subIds in a given group, including available, but inactive subscriptions. 471 */ 472 @NonNull getAllSubIdsInGroup(ParcelUuid subGrp)473 public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) { 474 final Set<Integer> subIds = new ArraySet<>(); 475 476 for (Entry<Integer, SubscriptionInfo> entry : mSubIdToInfoMap.entrySet()) { 477 if (subGrp.equals(entry.getValue().getGroupUuid())) { 478 subIds.add(entry.getKey()); 479 } 480 } 481 482 return subIds; 483 } 484 485 /** Checks if the requested subscription is opportunistic */ 486 @NonNull isOpportunistic(int subId)487 public boolean isOpportunistic(int subId) { 488 return mSubIdToInfoMap.containsKey(subId) 489 ? mSubIdToInfoMap.get(subId).isOpportunistic() 490 : false; 491 } 492 493 /** 494 * Retrieves a carrier config for a subscription in the provided group. 495 * 496 * <p>This method will prioritize non-opportunistic subscriptions, but will use the a 497 * carrier config for an opportunistic subscription if no other subscriptions are found. 498 */ 499 @Nullable getCarrierConfigForSubGrp(@onNull ParcelUuid subGrp)500 public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) { 501 PersistableBundleWrapper result = null; 502 503 for (int subId : getAllSubIdsInGroup(subGrp)) { 504 final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId); 505 if (config != null) { 506 result = config; 507 508 // Attempt to use (any) non-opportunistic subscription. If this subscription is 509 // opportunistic, continue and try to find a non-opportunistic subscription, 510 // using the opportunistic ones as a last resort. 511 if (!isOpportunistic(subId)) { 512 return config; 513 } 514 } 515 } 516 517 return result; 518 } 519 520 @Override hashCode()521 public int hashCode() { 522 return Objects.hash( 523 mActiveDataSubId, 524 mSubIdToInfoMap, 525 mSubIdToCarrierConfigMap, 526 mPrivilegedPackages); 527 } 528 529 @Override equals(Object obj)530 public boolean equals(Object obj) { 531 if (!(obj instanceof TelephonySubscriptionSnapshot)) { 532 return false; 533 } 534 535 final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj; 536 537 return mActiveDataSubId == other.mActiveDataSubId 538 && mSubIdToInfoMap.equals(other.mSubIdToInfoMap) 539 && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap) 540 && mPrivilegedPackages.equals(other.mPrivilegedPackages); 541 } 542 543 /** Dumps the state of this snapshot for logging and debugging purposes. */ dump(IndentingPrintWriter pw)544 public void dump(IndentingPrintWriter pw) { 545 pw.println("TelephonySubscriptionSnapshot:"); 546 pw.increaseIndent(); 547 548 pw.println("mActiveDataSubId: " + mActiveDataSubId); 549 pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap); 550 pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap); 551 pw.println("mPrivilegedPackages: " + mPrivilegedPackages); 552 553 pw.decreaseIndent(); 554 } 555 556 @Override toString()557 public String toString() { 558 return "TelephonySubscriptionSnapshot{ " 559 + "mActiveDataSubId=" + mActiveDataSubId 560 + ", mSubIdToInfoMap=" + mSubIdToInfoMap 561 + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap 562 + ", mPrivilegedPackages=" + mPrivilegedPackages 563 + " }"; 564 } 565 } 566 567 /** 568 * Interface for listening to changes in subscriptions 569 * 570 * @see TelephonySubscriptionTracker 571 */ 572 public interface TelephonySubscriptionTrackerCallback { 573 /** 574 * Called when subscription information changes, and a new subscription snapshot was taken 575 * 576 * @param snapshot the snapshot of subscription information. 577 */ onNewSnapshot(@onNull TelephonySubscriptionSnapshot snapshot)578 void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot); 579 } 580 581 private class ActiveDataSubscriptionIdListener extends TelephonyCallback 582 implements TelephonyCallback.ActiveDataSubscriptionIdListener { 583 @Override onActiveDataSubscriptionIdChanged(int subId)584 public void onActiveDataSubscriptionIdChanged(int subId) { 585 handleSubscriptionsChanged(); 586 } 587 } 588 589 /** External static dependencies for test injection */ 590 @VisibleForTesting(visibility = Visibility.PRIVATE) 591 public static class Dependencies { 592 /** Checks if the given bundle is for an identified carrier */ isConfigForIdentifiedCarrier(PersistableBundle bundle)593 public boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) { 594 return CarrierConfigManager.isConfigForIdentifiedCarrier(bundle); 595 } 596 597 /** Gets the active Subscription ID */ getActiveDataSubscriptionId()598 public int getActiveDataSubscriptionId() { 599 return SubscriptionManager.getActiveDataSubscriptionId(); 600 } 601 } 602 } 603