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