Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ccd46cc6 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 8287221 from aeb0ee3e to tm-d1-release

Change-Id: Ifb906d9aef2b463efc8eb28d5f885b0757169e3c
parents 79fcf814 aeb0ee3e
Loading
Loading
Loading
Loading
+113 −24
Original line number Original line Diff line number Diff line
@@ -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.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
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.EXTRA_SIM_STATE;
import static android.telephony.TelephonyManager.SIM_STATE_ABSENT;
import static android.telephony.TelephonyManager.SIM_STATE_ABSENT;
import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY;
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 static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN;


import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -49,6 +54,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.Process;
import android.os.Registrant;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RegistrantList;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.service.carrier.CarrierService;
import android.service.carrier.CarrierService;
@@ -81,6 +87,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.Set;
import java.util.StringJoiner;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
import java.util.function.Function;
@@ -98,6 +105,12 @@ public class CarrierPrivilegesTracker extends Handler {
    private static final String SHA_1 = "SHA-1";
    private static final String SHA_1 = "SHA-1";
    private static final String SHA_256 = "SHA-256";
    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.
     * Action to register a Registrant with this Tracker.
     * obj: Registrant that will be notified of Carrier Privileged UID changes.
     * obj: Registrant that will be notified of Carrier Privileged UID changes.
@@ -147,6 +160,11 @@ public class CarrierPrivilegesTracker extends Handler {
     */
     */
    private static final int ACTION_SET_TEST_OVERRIDE_RULE = 8;
    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 Context mContext;
    private final Phone mPhone;
    private final Phone mPhone;
    private final PackageManager mPackageManager;
    private final PackageManager mPackageManager;
@@ -171,11 +189,30 @@ public class CarrierPrivilegesTracker extends Handler {
    // Map of PackageName -> UIDs for that Package
    // Map of PackageName -> UIDs for that Package
    @NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>();
    @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();
    @NonNull private final ReadWriteLock mPrivilegedPackageInfoLock = new ReentrantReadWriteLock();
    // Package names and UIDs of apps that currently hold carrier privileges.
    // 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();
    @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. */
    /** Small snapshot to hold package names and UIDs of privileged packages. */
    private static final class PrivilegedPackageInfo {
    private static final class PrivilegedPackageInfo {
        @NonNull final Set<String> mPackageNames;
        @NonNull final Set<String> mPackageNames;
@@ -252,7 +289,10 @@ public class CarrierPrivilegesTracker extends Handler {


                            if (simState != SIM_STATE_ABSENT
                            if (simState != SIM_STATE_ABSENT
                                    && simState != SIM_STATE_NOT_READY
                                    && 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));
                            sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState));
                            break;
                            break;
@@ -351,6 +391,10 @@ public class CarrierPrivilegesTracker extends Handler {
                handleSetTestOverrideRules(carrierPrivilegeRules);
                handleSetTestOverrideRules(carrierPrivilegeRules);
                break;
                break;
            }
            }
            case ACTION_CLEAR_UICC_RULES: {
                handleClearUiccRules();
                break;
            }
            default: {
            default: {
                Rlog.e(TAG, "Received unknown msg type: " + msg.what);
                Rlog.e(TAG, "Received unknown msg type: " + msg.what);
                break;
                break;
@@ -413,16 +457,45 @@ public class CarrierPrivilegesTracker extends Handler {


        List<UiccAccessRule> updatedUiccRules = Collections.EMPTY_LIST;
        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
        // Only include the UICC rules if the SIM is fully loaded
        if (simState == SIM_STATE_LOADED) {
        if (simState == SIM_STATE_LOADED) {
            mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED;
            removeMessages(ACTION_CLEAR_UICC_RULES);

            updatedUiccRules = getSimRules();
            updatedUiccRules = getSimRules();
        }


        mLocalLog.log("SIM State Changed:"
            mLocalLog.log("SIM fully loaded:"
                    + " slotId=" + slotId
                    + " slotId=" + slotId
                    + " simState=" + simState
                    + " simState=" + simState
                    + " updated SIM-loaded rules=" + updatedUiccRules);
                    + " updated SIM-loaded rules=" + updatedUiccRules);
            maybeUpdateRulesAndNotifyRegistrants(mUiccRules, 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
    @NonNull
@@ -685,6 +758,7 @@ public class CarrierPrivilegesTracker extends Handler {
            pw.println(
            pw.println(
                    "CarrierPrivilegesTracker - Privileged package info: "
                    "CarrierPrivilegesTracker - Privileged package info: "
                            + mPrivilegedPackageInfo);
                            + mPrivilegedPackageInfo);
            pw.println("mSimIsReadyButNotLoaded: " + mSimIsReadyButNotLoaded);
        } finally {
        } finally {
            mPrivilegedPackageInfoLock.readLock().unlock();
            mPrivilegedPackageInfoLock.readLock().unlock();
        }
        }
@@ -698,6 +772,7 @@ public class CarrierPrivilegesTracker extends Handler {
                                    mInstalledPackageCerts.entrySet(),
                                    mInstalledPackageCerts.entrySet(),
                                    e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
                                    e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
        }
        }
        pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);
    }
    }


    /**
    /**
@@ -768,9 +843,13 @@ public class CarrierPrivilegesTracker extends Handler {
        // errors) always supersede them unless something goes super wrong when getting CC.
        // errors) always supersede them unless something goes super wrong when getting CC.
        mPrivilegedPackageInfoLock.readLock().lock();
        mPrivilegedPackageInfoLock.readLock().lock();
        try {
        try {
            return mPrivilegedPackageInfo.mPackageNames.contains(packageName)
            if (mSimIsReadyButNotLoaded) {
                    ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
                return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
                    : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
            } else if (mPrivilegedPackageInfo.mPackageNames.contains(packageName)) {
                return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
            } else {
                return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
            }
        } finally {
        } finally {
            mPrivilegedPackageInfoLock.readLock().unlock();
            mPrivilegedPackageInfoLock.readLock().unlock();
        }
        }
@@ -781,7 +860,8 @@ public class CarrierPrivilegesTracker extends Handler {
    public Set<String> getPackagesWithCarrierPrivileges() {
    public Set<String> getPackagesWithCarrierPrivileges() {
        mPrivilegedPackageInfoLock.readLock().lock();
        mPrivilegedPackageInfoLock.readLock().lock();
        try {
        try {
            return Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames);
            return mSimIsReadyButNotLoaded ? Collections.emptySet() :
                    Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames);
        } finally {
        } finally {
            mPrivilegedPackageInfoLock.readLock().unlock();
            mPrivilegedPackageInfoLock.readLock().unlock();
        }
        }
@@ -798,9 +878,13 @@ public class CarrierPrivilegesTracker extends Handler {
        // errors) always supersede them unless something goes super wrong when getting CC.
        // errors) always supersede them unless something goes super wrong when getting CC.
        mPrivilegedPackageInfoLock.readLock().lock();
        mPrivilegedPackageInfoLock.readLock().lock();
        try {
        try {
            return ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid)
            if (mSimIsReadyButNotLoaded) {
                    ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
                return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
                    : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
            } else if (ArrayUtils.contains(mPrivilegedPackageInfo.mUids, uid)) {
                return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
            } else {
                return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
            }
        } finally {
        } finally {
            mPrivilegedPackageInfoLock.readLock().unlock();
            mPrivilegedPackageInfoLock.readLock().unlock();
        }
        }
@@ -831,6 +915,14 @@ public class CarrierPrivilegesTracker extends Handler {
     */
     */
    @NonNull
    @NonNull
    public List<String> getCarrierPackageNamesForIntent(@NonNull Intent intent) {
    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
        // 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.
        // pieces of this method and don't depend on the set of carrier apps.
        List<ResolveInfo> resolveInfos = new ArrayList<>();
        List<ResolveInfo> resolveInfos = new ArrayList<>();
@@ -842,19 +934,16 @@ public class CarrierPrivilegesTracker extends Handler {
        // Now actually check which of the resolved packages have carrier privileges.
        // Now actually check which of the resolved packages have carrier privileges.
        mPrivilegedPackageInfoLock.readLock().lock();
        mPrivilegedPackageInfoLock.readLock().lock();
        try {
        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
            Set<String> packageNames = new ArraySet<>(); // For deduping purposes
            for (ResolveInfo resolveInfo : resolveInfos) {
            for (ResolveInfo resolveInfo : resolveInfos) {
                String packageName = getPackageName(resolveInfo);
                String packageName = getPackageName(resolveInfo);
                if (packageName == null) continue;
                if (packageName != null && CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
                switch (getCarrierPrivilegeStatusForPackage(packageName)) {
                        == getCarrierPrivilegeStatusForPackage(packageName)) {
                    case TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS:
                    packageNames.add(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);
            return new ArrayList<>(packageNames);
+150 −2
Original line number Original line Diff line number Diff line
@@ -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_CERTIFICATE_STRING_ARRAY;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
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.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_LOADED;
import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY;
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.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -77,9 +81,11 @@ import org.mockito.Mock;
import java.security.MessageDigest;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashSet;
import java.util.List;
import java.util.List;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@TestableLooper.RunWithLooper
@@ -272,12 +278,12 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
                expectedPackageNames, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges());
                expectedPackageNames, mCarrierPrivilegesTracker.getPackagesWithCarrierPrivileges());
        for (String packageName : expectedPackageNames) {
        for (String packageName : expectedPackageNames) {
            assertEquals(
            assertEquals(
                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
                    CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
                    mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(packageName));
                    mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForPackage(packageName));
        }
        }
        for (int uid : expectedUids) {
        for (int uid : expectedUids) {
            assertEquals(
            assertEquals(
                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
                    CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
                    mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(uid));
                    mCarrierPrivilegesTracker.getCarrierPrivilegeStatusForUid(uid));
        }
        }
    }
    }
@@ -535,6 +541,14 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
        sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY);
        sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_NOT_READY);
        mTestableLooper.processAllMessages();
        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]);
        verifyCurrentState(Set.of(), new int[0]);
        verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
        verifyRegistrantUpdates(new int[0], 1 /* expectedUidUpdates */);
        verifyCarrierPrivilegesChangedUpdates(
        verifyCarrierPrivilegesChangedUpdates(
@@ -543,6 +557,140 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
                        new Pair<>(Set.of(), new int[0])));
                        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
    @Test
    public void testSimStateChangedExplicitPackageNames() throws Exception {
    public void testSimStateChangedExplicitPackageNames() throws Exception {
        // Start with privileges specified just by wildcard certificate hashes, verify specifying
        // Start with privileges specified just by wildcard certificate hashes, verify specifying