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

Commit f484543d authored by Rambo Wang's avatar Rambo Wang Committed by Automerger Merge Worker
Browse files

Merge "Support carrier privileges linger period in CarrierPrivilegesTracker" am: a236a123

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/2037886

Change-Id: I34eb67a8414e88298a4890ea46a8023165d33c6a
parents 1ca27138 a236a123
Loading
Loading
Loading
Loading
+113 −24
Original line number 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.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;
@@ -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;
@@ -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;
@@ -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.
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -683,6 +756,7 @@ public class CarrierPrivilegesTracker extends Handler {
            pw.println(
                    "CarrierPrivilegesTracker - Privileged package info: "
                            + mPrivilegedPackageInfo);
            pw.println("mSimIsReadyButNotLoaded: " + mSimIsReadyButNotLoaded);
        } finally {
            mPrivilegedPackageInfoLock.readLock().unlock();
        }
@@ -696,6 +770,7 @@ public class CarrierPrivilegesTracker extends Handler {
                                    mInstalledPackageCerts.entrySet(),
                                    e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
        }
        pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);
    }

    /**
@@ -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();
        }
@@ -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();
        }
@@ -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();
        }
@@ -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<>();
@@ -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);
+150 −2
Original line number 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_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;
@@ -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
@@ -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));
        }
    }
@@ -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(
@@ -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