Loading src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java +113 −24 Original line number Diff line number Diff line Loading @@ -21,12 +21,17 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY; import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; import static android.telephony.TelephonyManager.EXTRA_SIM_STATE; import static android.telephony.TelephonyManager.SIM_STATE_ABSENT; import static android.telephony.TelephonyManager.SIM_STATE_LOADED; import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; Loading @@ -48,6 +53,7 @@ import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.telephony.Annotation.CarrierPrivilegeStatus; Loading Loading @@ -79,6 +85,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringJoiner; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; Loading @@ -96,6 +103,12 @@ public class CarrierPrivilegesTracker extends Handler { private static final String SHA_1 = "SHA-1"; private static final String SHA_256 = "SHA-256"; /** * Time delay to clear UICC rules after UICC is gone. * This introduces the grace period to retain carrier privileges when SIM is removed. */ private static final long CLEAR_UICC_RULES_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(30); /** * Action to register a Registrant with this Tracker. * obj: Registrant that will be notified of Carrier Privileged UID changes. Loading Loading @@ -145,6 +158,11 @@ public class CarrierPrivilegesTracker extends Handler { */ private static final int ACTION_SET_TEST_OVERRIDE_RULE = 8; /** * Action to clear UICC rules. */ private static final int ACTION_CLEAR_UICC_RULES = 9; private final Context mContext; private final Phone mPhone; private final PackageManager mPackageManager; Loading @@ -169,11 +187,30 @@ public class CarrierPrivilegesTracker extends Handler { // Map of PackageName -> UIDs for that Package @NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>(); // This should be used to guard critical section either with // mPrivilegedPackageInfoLock.readLock() or mPrivilegedPackageInfoLock.writeLock(), but never // with the mPrivilegedPackageInfoLock object itself. @NonNull private final ReadWriteLock mPrivilegedPackageInfoLock = new ReentrantReadWriteLock(); // Package names and UIDs of apps that currently hold carrier privileges. @GuardedBy({"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) @GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) @NonNull private PrivilegedPackageInfo mPrivilegedPackageInfo = new PrivilegedPackageInfo(); // Uptime in millis on when the NEXT clear-up of UiccRules are scheduled @ElapsedRealtimeLong private long mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; // Constant indicates no schedule to clear UiccRules private static final long CLEAR_UICC_RULE_NOT_SCHEDULED = -1; // Indicates SIM has reached SIM_STATE_READY but not SIM_STATE_LOADED yet. During this transient // state, all the information previously loaded from SIM may be updated soon later and thus // unreliable. For security's concern, any carrier privileges check should return // CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED (instead of neither HAS_ACCESS nor NO_ACCESS) until // SIM becomes LOADED again, or grace period specified by CLEAR_UICC_RULES_DELAY_MILLIS expires. @GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) private boolean mSimIsReadyButNotLoaded = false; /** Small snapshot to hold package names and UIDs of privileged packages. */ private static final class PrivilegedPackageInfo { @NonNull final Set<String> mPackageNames; Loading Loading @@ -250,7 +287,10 @@ public class CarrierPrivilegesTracker extends Handler { if (simState != SIM_STATE_ABSENT && simState != SIM_STATE_NOT_READY && simState != SIM_STATE_LOADED) return; && simState != SIM_STATE_READY && simState != SIM_STATE_LOADED) { return; } sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState)); break; Loading Loading @@ -349,6 +389,10 @@ public class CarrierPrivilegesTracker extends Handler { handleSetTestOverrideRules(carrierPrivilegeRules); break; } case ACTION_CLEAR_UICC_RULES: { handleClearUiccRules(); break; } default: { Rlog.e(TAG, "Received unknown msg type: " + msg.what); break; Loading Loading @@ -411,16 +455,45 @@ public class CarrierPrivilegesTracker extends Handler { List<UiccAccessRule> updatedUiccRules = Collections.EMPTY_LIST; mPrivilegedPackageInfoLock.writeLock().lock(); try { mSimIsReadyButNotLoaded = simState == SIM_STATE_READY; } finally { mPrivilegedPackageInfoLock.writeLock().unlock(); } // Only include the UICC rules if the SIM is fully loaded if (simState == SIM_STATE_LOADED) { mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; removeMessages(ACTION_CLEAR_UICC_RULES); updatedUiccRules = getSimRules(); } mLocalLog.log("SIM State Changed:" mLocalLog.log("SIM fully loaded:" + " slotId=" + slotId + " simState=" + simState + " updated SIM-loaded rules=" + updatedUiccRules); maybeUpdateRulesAndNotifyRegistrants(mUiccRules, updatedUiccRules); } else { if (!mUiccRules.isEmpty() && mClearUiccRulesUptimeMillis == CLEAR_UICC_RULE_NOT_SCHEDULED) { mClearUiccRulesUptimeMillis = SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS; sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES), mClearUiccRulesUptimeMillis); mLocalLog.log("SIM is gone. Delay " + TimeUnit.MILLISECONDS.toSeconds( CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules."); } else { mLocalLog.log( "Ignore SIM gone event while UiccRules is empty or waiting to be emptied."); } } } private void handleClearUiccRules() { mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; removeMessages(ACTION_CLEAR_UICC_RULES); maybeUpdateRulesAndNotifyRegistrants(mUiccRules, Collections.EMPTY_LIST); } @NonNull Loading Loading @@ -683,6 +756,7 @@ public class CarrierPrivilegesTracker extends Handler { pw.println( "CarrierPrivilegesTracker - Privileged package info: " + mPrivilegedPackageInfo); pw.println("mSimIsReadyButNotLoaded: " + mSimIsReadyButNotLoaded); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -696,6 +770,7 @@ public class CarrierPrivilegesTracker extends Handler { mInstalledPackageCerts.entrySet(), e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue())); } pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis); } /** Loading Loading @@ -766,9 +841,13 @@ public class CarrierPrivilegesTracker extends Handler { // errors) always supersede them unless something goes super wrong when getting CC. mPrivilegedPackageInfoLock.readLock().lock(); try { return mPrivilegedPackageInfo.mPackageNames.contains(packageName) ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; if (mSimIsReadyButNotLoaded) { return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (mPrivilegedPackageInfo.mPackageNames.contains(packageName)) { return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } else { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -779,7 +858,8 @@ public class CarrierPrivilegesTracker extends Handler { public Set<String> getPackagesWithCarrierPrivileges() { mPrivilegedPackageInfoLock.readLock().lock(); try { return Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames); return mSimIsReadyButNotLoaded ? Collections.emptySet() : Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -796,9 +876,13 @@ public class CarrierPrivilegesTracker extends Handler { // errors) always supersede them unless something goes super wrong when getting CC. mPrivilegedPackageInfoLock.readLock().lock(); try { return ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid) ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; if (mSimIsReadyButNotLoaded) { return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid)) { return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } else { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -810,6 +894,14 @@ public class CarrierPrivilegesTracker extends Handler { */ @NonNull public List<String> getCarrierPackageNamesForIntent(@NonNull Intent intent) { mPrivilegedPackageInfoLock.readLock().lock(); try { if (mSimIsReadyButNotLoaded) return Collections.emptyList(); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } // Do the PackageManager queries before we take the lock, as these are the longest-running // pieces of this method and don't depend on the set of carrier apps. List<ResolveInfo> resolveInfos = new ArrayList<>(); Loading @@ -821,19 +913,16 @@ public class CarrierPrivilegesTracker extends Handler { // Now actually check which of the resolved packages have carrier privileges. mPrivilegedPackageInfoLock.readLock().lock(); try { // Check mSimIsReadyButNotLoaded again here since the PackageManager queries above are // pretty time-consuming, mSimIsReadyButNotLoaded state may change since last check if (mSimIsReadyButNotLoaded) return Collections.emptyList(); Set<String> packageNames = new ArraySet<>(); // For deduping purposes for (ResolveInfo resolveInfo : resolveInfos) { String packageName = getPackageName(resolveInfo); if (packageName == null) continue; switch (getCarrierPrivilegeStatusForPackage(packageName)) { case TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS: if (packageName != null && CARRIER_PRIVILEGE_STATUS_HAS_ACCESS == getCarrierPrivilegeStatusForPackage(packageName)) { packageNames.add(packageName); break; case TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS: continue; default: // Any other status is considered an error. return Collections.emptyList(); } } return new ArrayList<>(packageNames); Loading tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java +150 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,13 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; import static android.telephony.TelephonyManager.EXTRA_SIM_STATE; import static android.telephony.TelephonyManager.SIM_STATE_ABSENT; import static android.telephony.TelephonyManager.SIM_STATE_LOADED; import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; Loading Loading @@ -77,9 +81,11 @@ import org.mockito.Mock; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading Loading @@ -269,12 +275,12 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { expectedPackageNames, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); for (String packageName : expectedPackageNames) { assertEquals( TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(packageName)); } for (int uid : expectedUids) { assertEquals( TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(uid)); } } Loading Loading @@ -532,6 +538,14 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY); mTestableLooper.processAllMessages(); // Immediately check current state, nothing should change verifyCurrentState(Set.of(PACKAGE_1, PACKAGE_2), new int[]{UID_1, UID_2}); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // Check again, the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( Loading @@ -540,6 +554,140 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateAbsentThenLoadedWithSameRules() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 20 seconds and the same SIM is inserted Thread.sleep(TimeUnit.SECONDS.toMillis(20)); sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); // Wait for another 20 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(20)); // verify all carrier privileges should remain, no CP change notified verifyCurrentState(Set.of(PACKAGE_1, PACKAGE_2), new int[]{UID_1, UID_2}); verifyRegistrantUpdates(null /* expectedUidUpdates */, 0 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates(List.of()); } @Test public void testSimStateChangedSimStateAbsentForever() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // verify the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateNotReadyForever() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // eSIM profile disabled and leave in state SIM_STATE_NOT_READY sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY); mTestableLooper.processAllMessages(); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // verify the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateAbsentThenLoadedWithUpdatedRules() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 20 seconds and a different SIM is inserted Thread.sleep(TimeUnit.SECONDS.toMillis(20)); setupSimLoadedRules(ruleWithHashOnly(getHash(CERT_1))); sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); // Wait for another 20 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(20)); // Carrier privileges should be updated and CP change should be notified verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1}); verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(PACKAGE_1), new int[] {UID_1}))); } @Test public void testSimStateChangedSimStateReadyThenLoaded() throws Exception { // Start with privileges (from carrier config) setupCarrierPrivilegesTrackerWithCarrierConfigUids(); ResolveInfo pkg1ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_1).build(); ResolveInfo pkg2ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_2).build(); when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn( List.of(pkg1ResolveInfo, pkg2ResolveInfo)); // SIM is READY sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_READY); mTestableLooper.processAllMessages(); assertEquals(Collections.emptyList(), mCarrierPrivilegesTracker.getCarrierPackageNamesForIntent( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE))); assertEquals(CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(UID_1)); assertEquals(Collections.EMPTY_SET, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); assertEquals(CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(PACKAGE_1)); // SIM is LOADED sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); assertEquals(List.of(PACKAGE_1, PACKAGE_2), mCarrierPrivilegesTracker.getCarrierPackageNamesForIntent( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE))); assertEquals(CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(UID_1)); assertEquals(PRIVILEGED_PACKAGES, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); assertEquals(CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(PACKAGE_1)); } @Test public void testSimStateChangedExplicitPackageNames() throws Exception { // Start with privileges specified just by wildcard certificate hashes, verify specifying Loading Loading
src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java +113 −24 Original line number Diff line number Diff line Loading @@ -21,12 +21,17 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY; import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; import static android.telephony.TelephonyManager.EXTRA_SIM_STATE; import static android.telephony.TelephonyManager.SIM_STATE_ABSENT; import static android.telephony.TelephonyManager.SIM_STATE_LOADED; import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; Loading @@ -48,6 +53,7 @@ import android.os.Message; import android.os.PersistableBundle; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.telephony.Annotation.CarrierPrivilegeStatus; Loading Loading @@ -79,6 +85,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringJoiner; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; Loading @@ -96,6 +103,12 @@ public class CarrierPrivilegesTracker extends Handler { private static final String SHA_1 = "SHA-1"; private static final String SHA_256 = "SHA-256"; /** * Time delay to clear UICC rules after UICC is gone. * This introduces the grace period to retain carrier privileges when SIM is removed. */ private static final long CLEAR_UICC_RULES_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(30); /** * Action to register a Registrant with this Tracker. * obj: Registrant that will be notified of Carrier Privileged UID changes. Loading Loading @@ -145,6 +158,11 @@ public class CarrierPrivilegesTracker extends Handler { */ private static final int ACTION_SET_TEST_OVERRIDE_RULE = 8; /** * Action to clear UICC rules. */ private static final int ACTION_CLEAR_UICC_RULES = 9; private final Context mContext; private final Phone mPhone; private final PackageManager mPackageManager; Loading @@ -169,11 +187,30 @@ public class CarrierPrivilegesTracker extends Handler { // Map of PackageName -> UIDs for that Package @NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>(); // This should be used to guard critical section either with // mPrivilegedPackageInfoLock.readLock() or mPrivilegedPackageInfoLock.writeLock(), but never // with the mPrivilegedPackageInfoLock object itself. @NonNull private final ReadWriteLock mPrivilegedPackageInfoLock = new ReentrantReadWriteLock(); // Package names and UIDs of apps that currently hold carrier privileges. @GuardedBy({"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) @GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) @NonNull private PrivilegedPackageInfo mPrivilegedPackageInfo = new PrivilegedPackageInfo(); // Uptime in millis on when the NEXT clear-up of UiccRules are scheduled @ElapsedRealtimeLong private long mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; // Constant indicates no schedule to clear UiccRules private static final long CLEAR_UICC_RULE_NOT_SCHEDULED = -1; // Indicates SIM has reached SIM_STATE_READY but not SIM_STATE_LOADED yet. During this transient // state, all the information previously loaded from SIM may be updated soon later and thus // unreliable. For security's concern, any carrier privileges check should return // CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED (instead of neither HAS_ACCESS nor NO_ACCESS) until // SIM becomes LOADED again, or grace period specified by CLEAR_UICC_RULES_DELAY_MILLIS expires. @GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()", "mPrivilegedPackageInfoLock.writeLock()"}) private boolean mSimIsReadyButNotLoaded = false; /** Small snapshot to hold package names and UIDs of privileged packages. */ private static final class PrivilegedPackageInfo { @NonNull final Set<String> mPackageNames; Loading Loading @@ -250,7 +287,10 @@ public class CarrierPrivilegesTracker extends Handler { if (simState != SIM_STATE_ABSENT && simState != SIM_STATE_NOT_READY && simState != SIM_STATE_LOADED) return; && simState != SIM_STATE_READY && simState != SIM_STATE_LOADED) { return; } sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState)); break; Loading Loading @@ -349,6 +389,10 @@ public class CarrierPrivilegesTracker extends Handler { handleSetTestOverrideRules(carrierPrivilegeRules); break; } case ACTION_CLEAR_UICC_RULES: { handleClearUiccRules(); break; } default: { Rlog.e(TAG, "Received unknown msg type: " + msg.what); break; Loading Loading @@ -411,16 +455,45 @@ public class CarrierPrivilegesTracker extends Handler { List<UiccAccessRule> updatedUiccRules = Collections.EMPTY_LIST; mPrivilegedPackageInfoLock.writeLock().lock(); try { mSimIsReadyButNotLoaded = simState == SIM_STATE_READY; } finally { mPrivilegedPackageInfoLock.writeLock().unlock(); } // Only include the UICC rules if the SIM is fully loaded if (simState == SIM_STATE_LOADED) { mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; removeMessages(ACTION_CLEAR_UICC_RULES); updatedUiccRules = getSimRules(); } mLocalLog.log("SIM State Changed:" mLocalLog.log("SIM fully loaded:" + " slotId=" + slotId + " simState=" + simState + " updated SIM-loaded rules=" + updatedUiccRules); maybeUpdateRulesAndNotifyRegistrants(mUiccRules, updatedUiccRules); } else { if (!mUiccRules.isEmpty() && mClearUiccRulesUptimeMillis == CLEAR_UICC_RULE_NOT_SCHEDULED) { mClearUiccRulesUptimeMillis = SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS; sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES), mClearUiccRulesUptimeMillis); mLocalLog.log("SIM is gone. Delay " + TimeUnit.MILLISECONDS.toSeconds( CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules."); } else { mLocalLog.log( "Ignore SIM gone event while UiccRules is empty or waiting to be emptied."); } } } private void handleClearUiccRules() { mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED; removeMessages(ACTION_CLEAR_UICC_RULES); maybeUpdateRulesAndNotifyRegistrants(mUiccRules, Collections.EMPTY_LIST); } @NonNull Loading Loading @@ -683,6 +756,7 @@ public class CarrierPrivilegesTracker extends Handler { pw.println( "CarrierPrivilegesTracker - Privileged package info: " + mPrivilegedPackageInfo); pw.println("mSimIsReadyButNotLoaded: " + mSimIsReadyButNotLoaded); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -696,6 +770,7 @@ public class CarrierPrivilegesTracker extends Handler { mInstalledPackageCerts.entrySet(), e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue())); } pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis); } /** Loading Loading @@ -766,9 +841,13 @@ public class CarrierPrivilegesTracker extends Handler { // errors) always supersede them unless something goes super wrong when getting CC. mPrivilegedPackageInfoLock.readLock().lock(); try { return mPrivilegedPackageInfo.mPackageNames.contains(packageName) ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; if (mSimIsReadyButNotLoaded) { return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (mPrivilegedPackageInfo.mPackageNames.contains(packageName)) { return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } else { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -779,7 +858,8 @@ public class CarrierPrivilegesTracker extends Handler { public Set<String> getPackagesWithCarrierPrivileges() { mPrivilegedPackageInfoLock.readLock().lock(); try { return Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames); return mSimIsReadyButNotLoaded ? Collections.emptySet() : Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -796,9 +876,13 @@ public class CarrierPrivilegesTracker extends Handler { // errors) always supersede them unless something goes super wrong when getting CC. mPrivilegedPackageInfoLock.readLock().lock(); try { return ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid) ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; if (mSimIsReadyButNotLoaded) { return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid)) { return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } else { return CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } Loading @@ -810,6 +894,14 @@ public class CarrierPrivilegesTracker extends Handler { */ @NonNull public List<String> getCarrierPackageNamesForIntent(@NonNull Intent intent) { mPrivilegedPackageInfoLock.readLock().lock(); try { if (mSimIsReadyButNotLoaded) return Collections.emptyList(); } finally { mPrivilegedPackageInfoLock.readLock().unlock(); } // Do the PackageManager queries before we take the lock, as these are the longest-running // pieces of this method and don't depend on the set of carrier apps. List<ResolveInfo> resolveInfos = new ArrayList<>(); Loading @@ -821,19 +913,16 @@ public class CarrierPrivilegesTracker extends Handler { // Now actually check which of the resolved packages have carrier privileges. mPrivilegedPackageInfoLock.readLock().lock(); try { // Check mSimIsReadyButNotLoaded again here since the PackageManager queries above are // pretty time-consuming, mSimIsReadyButNotLoaded state may change since last check if (mSimIsReadyButNotLoaded) return Collections.emptyList(); Set<String> packageNames = new ArraySet<>(); // For deduping purposes for (ResolveInfo resolveInfo : resolveInfos) { String packageName = getPackageName(resolveInfo); if (packageName == null) continue; switch (getCarrierPrivilegeStatusForPackage(packageName)) { case TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS: if (packageName != null && CARRIER_PRIVILEGE_STATUS_HAS_ACCESS == getCarrierPrivilegeStatusForPackage(packageName)) { packageNames.add(packageName); break; case TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS: continue; default: // Any other status is considered an error. return Collections.emptyList(); } } return new ArrayList<>(packageNames); Loading
tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java +150 −2 Original line number Diff line number Diff line Loading @@ -23,9 +23,13 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY; import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; import static android.telephony.TelephonyManager.EXTRA_SIM_STATE; import static android.telephony.TelephonyManager.SIM_STATE_ABSENT; import static android.telephony.TelephonyManager.SIM_STATE_LOADED; import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; Loading Loading @@ -77,9 +81,11 @@ import org.mockito.Mock; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper Loading Loading @@ -269,12 +275,12 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { expectedPackageNames, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); for (String packageName : expectedPackageNames) { assertEquals( TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(packageName)); } for (int uid : expectedUids) { assertEquals( TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(uid)); } } Loading Loading @@ -532,6 +538,14 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY); mTestableLooper.processAllMessages(); // Immediately check current state, nothing should change verifyCurrentState(Set.of(PACKAGE_1, PACKAGE_2), new int[]{UID_1, UID_2}); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // Check again, the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( Loading @@ -540,6 +554,140 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest { new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateAbsentThenLoadedWithSameRules() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 20 seconds and the same SIM is inserted Thread.sleep(TimeUnit.SECONDS.toMillis(20)); sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); // Wait for another 20 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(20)); // verify all carrier privileges should remain, no CP change notified verifyCurrentState(Set.of(PACKAGE_1, PACKAGE_2), new int[]{UID_1, UID_2}); verifyRegistrantUpdates(null /* expectedUidUpdates */, 0 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates(List.of()); } @Test public void testSimStateChangedSimStateAbsentForever() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // verify the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateNotReadyForever() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); // CPT initialization process may trigger notification, remove the interfere here reset(mTelephonyRegistryManager); // eSIM profile disabled and leave in state SIM_STATE_NOT_READY sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY); mTestableLooper.processAllMessages(); // Wait for 30 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(30)); mTestableLooper.processAllMessages(); // verify the carrier privileges should be emptied verifyCurrentState(Set.of(), new int[0]); verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(), new int[0]))); } @Test public void testSimStateChangedSimStateAbsentThenLoadedWithUpdatedRules() throws Exception { // Start with privileges setupCarrierPrivilegesTrackerWithSimLoadedUids(); reset(mTelephonyRegistryManager); // SIM is removed sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_ABSENT); mTestableLooper.processAllMessages(); // Wait for 20 seconds and a different SIM is inserted Thread.sleep(TimeUnit.SECONDS.toMillis(20)); setupSimLoadedRules(ruleWithHashOnly(getHash(CERT_1))); sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); // Wait for another 20 seconds Thread.sleep(TimeUnit.SECONDS.toMillis(20)); // Carrier privileges should be updated and CP change should be notified verifyCurrentState(Set.of(PACKAGE_1), new int[] {UID_1}); verifyRegistrantUpdates(new int[] {UID_1}, 1 /* expectedUidUpdates */); verifyCarrierPrivilegesChangedUpdates( List.of(new Pair<>(Set.of(PACKAGE_1), new int[] {UID_1}))); } @Test public void testSimStateChangedSimStateReadyThenLoaded() throws Exception { // Start with privileges (from carrier config) setupCarrierPrivilegesTrackerWithCarrierConfigUids(); ResolveInfo pkg1ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_1).build(); ResolveInfo pkg2ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_2).build(); when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn( List.of(pkg1ResolveInfo, pkg2ResolveInfo)); // SIM is READY sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_READY); mTestableLooper.processAllMessages(); assertEquals(Collections.emptyList(), mCarrierPrivilegesTracker.getCarrierPackageNamesForIntent( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE))); assertEquals(CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(UID_1)); assertEquals(Collections.EMPTY_SET, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); assertEquals(CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(PACKAGE_1)); // SIM is LOADED sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED); mTestableLooper.processAllMessages(); assertEquals(List.of(PACKAGE_1, PACKAGE_2), mCarrierPrivilegesTracker.getCarrierPackageNamesForIntent( new Intent(CarrierService.CARRIER_SERVICE_INTERFACE))); assertEquals(CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(UID_1)); assertEquals(PRIVILEGED_PACKAGES, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges()); assertEquals(CARRIER_PRIVILEGE_STATUS_HAS_ACCESS, mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(PACKAGE_1)); } @Test public void testSimStateChangedExplicitPackageNames() throws Exception { // Start with privileges specified just by wildcard certificate hashes, verify specifying Loading