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

Commit 57acd348 authored by Nathan Harold's avatar Nathan Harold
Browse files

Performance Optimizations for CarrierPrivilegesTracker

-Reduce the memory usage of CPT by only holding the hash of
 the SHA digests (a hash of hashes) in memory for each
 package. This also moves away from using hex strings in
 favor of doing byte-based comparisons, which saves the
 conversion from byte->ascii for storage and back to byte
 during the comparison in UiccPrivilegesRule. Memory saving
 should be... (Old_Size - New_Size) * Num_Phones * Num Packages

 Old_Size = (SHA1_len + SHA256_len) * Bytes_Per_AsciiHex_Byte
 - Bytes_Per_Int * 2 =
 = (20 + 32) * (2 * 2)  = 208 bytes

 New_Size = 2 * Bytes_Per_Int = 16

 Num_Packages = ~350

 (208 - 16) * 350 * 2 * 1  = 132KB / installed user

-This will also speed up searches in the event that there
 is no carrier package at the slight cost of increasing the
 lookup cost in the event that there is one or more carrier
 packages because a full comparison requires a binder call.
 Because the circumstances that cause a lookup are pretty
 rare, this feels like a very minimal regression to me.

By combining into a single cache that could operate across
multiple phones, we could further simplify; however, I believe
that would come with additional complexity that is no longer
worthwhile given that the total impact should now be only a few
KB per instance.

Bug: 162182054
Test: atest CarrierPrivilegesTrackerTest; #note: refactor-only
Flag: EXEMPT refactor
Change-Id: I9e817a2a2853891a1ee56447157db40cb808ab53
parent b563e66a
Loading
Loading
Loading
Loading
+80 −32
Original line number Diff line number Diff line
@@ -68,7 +68,6 @@ import android.util.Pair;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
@@ -80,7 +79,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -198,7 +196,7 @@ public class CarrierPrivilegesTracker extends Handler {
    @Nullable private List<UiccAccessRule> mTestOverrideRules = null;
    @Nullable private String mTestOverrideCarrierServicePackage = null;
    // Map of PackageName -> Certificate hashes for that Package
    @NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
    @NonNull private final Map<String, Set<Integer>> mInstalledPackageCertHashes = new ArrayMap<>();
    // Map of PackageName -> UIDs for that Package
    @NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>();

@@ -556,13 +554,12 @@ public class CarrierPrivilegesTracker extends Handler {
        return uiccProfile.getCarrierPrivilegeAccessRules();
    }

    private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
        if (pkgName == null) return;
    private PackageInfo getPackageInfoForPackage(@Nullable String pkgName) {
        if (pkgName == null) return null;

        PackageInfo pkg;
        try {
            pkg =
                    mFeatureFlags.supportCarrierServicesForHsum()
            return mFeatureFlags.supportCarrierServicesForHsum()
                        ? mPackageManager.getPackageInfoAsUser(
                                pkgName,
                                INSTALLED_PACKAGES_QUERY_FLAGS,
@@ -571,10 +568,15 @@ public class CarrierPrivilegesTracker extends Handler {
                                pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
        } catch (NameNotFoundException e) {
            Rlog.e(TAG, "Error getting installed package: " + pkgName, e);
            return;
            return null;
        }
    }

    private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
        PackageInfo pkg = getPackageInfoForPackage(pkgName);
        if (pkg == null) return;

        updateCertsForPackage(pkg);
        updateCertHashHashesForPackage(pkg);
        // Invalidate cache because this may be a package already on the device but getting
        // installed for a user it wasn't installed in before, which means there will be an
        // additional UID.
@@ -582,30 +584,46 @@ public class CarrierPrivilegesTracker extends Handler {
        if (VDBG) {
            Rlog.d(TAG, "Package added/replaced/changed:"
                    + " pkg=" + Rlog.pii(TAG, pkgName)
                    + " cert hashes=" + mInstalledPackageCerts.get(pkgName));
                    + " cert hashes=" + mInstalledPackageCertHashes.get(pkgName));
        }

        maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
    }

    private void updateCertsForPackage(@NonNull PackageInfo pkg) {
        Set<String> certs = new ArraySet<>(1);
    private void updateCertHashHashesForPackage(@NonNull PackageInfo pkg) {
        Set<Integer> certs = new ArraySet<>(2);
        List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
        for (Signature signature : signatures) {
            byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
            certs.add(UiccAccessRule.getCertificateHashHashCode(sha1));

            byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
            certs.add(UiccAccessRule.getCertificateHashHashCode(sha256));
        }

        mInstalledPackageCerts.put(pkg.packageName, certs);
        mInstalledPackageCertHashes.put(pkg.packageName, certs);
    }

    private Set<byte[]> getCertsForPackage(@NonNull String pkgName) {
        PackageInfo pkg = getPackageInfoForPackage(pkgName);
        if (pkg == null) return Collections.emptySet();

        List<Signature> signatures = UiccAccessRule.getSignatures(pkg);

        ArraySet<byte[]> certs = new ArraySet<>(2);
        for (Signature signature : signatures) {
            certs.add(UiccAccessRule.getCertHash(signature, SHA_1));
            certs.add(UiccAccessRule.getCertHash(signature, SHA_256));
        }

        return certs;
    }

    private void handlePackageRemovedOrDisabledByUser(@Nullable String pkgName) {
        if (pkgName == null) return;

        if (mInstalledPackageCerts.remove(pkgName) == null || mCachedUids.remove(pkgName) == null) {
        if (mInstalledPackageCertHashes.remove(pkgName) == null
                || mCachedUids.remove(pkgName) == null) {
            Rlog.e(TAG, "Unknown package was uninstalled or disabled by user: " + pkgName);
            return;
        }
@@ -637,7 +655,7 @@ public class CarrierPrivilegesTracker extends Handler {
            msg +=
                    " installed pkgs="
                            + getObfuscatedPackages(
                                    mInstalledPackageCerts.entrySet(),
                                    mInstalledPackageCertHashes.entrySet(),
                                    e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue());
        }
        mLocalLog.log(msg);
@@ -651,7 +669,7 @@ public class CarrierPrivilegesTracker extends Handler {
                                ? ActivityManager.getCurrentUser()
                                : UserHandle.SYSTEM.getIdentifier());
        for (PackageInfo pkg : installedPackages) {
            updateCertsForPackage(pkg);
            updateCertHashHashesForPackage(pkg);
            // This may be unnecessary before initialization, but invalidate the cache all the time
            // just in case to ensure consistency.
            getUidsForPackage(pkg.packageName, /* invalidateCache= */ true);
@@ -740,8 +758,12 @@ public class CarrierPrivilegesTracker extends Handler {
        Set<String> carrierServiceEligiblePackages = new ArraySet<>();
        Set<String> privilegedPackageNames = new ArraySet<>();
        Set<Integer> privilegedUids = new ArraySet<>();
        for (Map.Entry<String, Set<String>> e : mInstalledPackageCerts.entrySet()) {
            final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
        for (Map.Entry<String, Set<Integer>> e : mInstalledPackageCertHashes.entrySet()) {
            if (!isPackageMaybePrivileged(e.getKey(), e.getValue())) continue;

            Set<byte[]> fullCerts = getCertsForPackage(e.getKey());

            final int priv = getPackagePrivilegedStatus(e.getKey(), fullCerts);
            switch (priv) {
                case PACKAGE_PRIVILEGED_FROM_SIM:
                case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
@@ -760,32 +782,58 @@ public class CarrierPrivilegesTracker extends Handler {
                getCarrierService(carrierServiceEligiblePackages));
    }

    private boolean isPackageMaybePrivileged(
            @NonNull String pkgName, @NonNull Set<Integer> hashHashes) {
        for (Integer hashHash : hashHashes) {
            // Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
            if (mTestOverrideRules != null) {
                for (UiccAccessRule rule : mTestOverrideRules) {
                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
                        return true;
                    }
                }
            } else {
                for (UiccAccessRule rule : mUiccRules) {
                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
                        return true;
                    }
                }
                for (UiccAccessRule rule : mCarrierConfigRules) {
                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Returns the privilege status of the provided package.
     *
     * <p>Returned privilege status depends on whether a package matches the certificates from
     * carrier config, from test overrides or from certificates stored on the SIM.
     */
    private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<String> certs) {
    private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<byte[]> certs) {
        // Double-nested for loops, but each collection should contain at most 2 elements in nearly
        // every case.
        // TODO(b/184382310) find a way to speed this up
        for (String cert : certs) {
        for (byte[] cert : certs) {
            // Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
            if (mTestOverrideRules != null) {
                for (UiccAccessRule rule : mTestOverrideRules) {
                    if (rule.matches(cert, pkgName)) {
                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                        return PACKAGE_PRIVILEGED_FROM_SIM;
                    }
                }
            } else {
                for (UiccAccessRule rule : mUiccRules) {
                    if (rule.matches(cert, pkgName)) {
                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                        return PACKAGE_PRIVILEGED_FROM_SIM;
                    }
                }
                for (UiccAccessRule rule : mCarrierConfigRules) {
                    if (rule.matches(cert, pkgName)) {
                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                        return pkgName.equals(mTestOverrideCarrierServicePackage)
                                ? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
                                : PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
@@ -856,7 +904,7 @@ public class CarrierPrivilegesTracker extends Handler {
            pw.println(
                    "CarrierPrivilegesTracker - Obfuscated Pkgs + Certs: "
                            + getObfuscatedPackages(
                                    mInstalledPackageCerts.entrySet(),
                                    mInstalledPackageCertHashes.entrySet(),
                                    e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
        }
        pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);