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

Commit 7c41ca26 authored by Victor Hsieh's avatar Victor Hsieh Committed by Android (Google) Code Review
Browse files

Merge "Replace some PackageInfo queries with PackageState"

parents 683d283d 90ec42c2
Loading
Loading
Loading
Loading
+97 −61
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.content.pm.InstallSourceInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
@@ -82,6 +83,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IBinaryTransparencyService;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.ApexManager;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;

import libcore.util.HexEncoding;

@@ -121,7 +124,9 @@ public class BinaryTransparencyService extends SystemService {
    static final long RECORD_MEASUREMENTS_COOLDOWN_MS = 24 * 60 * 60 * 1000;

    @VisibleForTesting
    static final String BUNDLE_PACKAGE_INFO = "package-info";
    static final String BUNDLE_PACKAGE_NAME = "package-name";
    @VisibleForTesting
    static final String BUNDLE_PACKAGE_IS_APEX = "package-is-apex";
    @VisibleForTesting
    static final String BUNDLE_CONTENT_DIGEST_ALGORITHM = "content-digest-algo";
    @VisibleForTesting
@@ -150,6 +155,7 @@ public class BinaryTransparencyService extends SystemService {
    private String mVbmetaDigest;
    // the system time (in ms) the last measurement was taken
    private long mMeasurementsLastRecordedMs;
    private PackageManagerInternal mPackageManagerInternal;
    private BiometricLogger mBiometricLogger;

    /**
@@ -172,7 +178,18 @@ public class BinaryTransparencyService extends SystemService {
            List<Bundle> results = new ArrayList<>();

            for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
                Bundle apexMeasurement = measurePackage(packageInfo);
                PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
                        packageInfo.packageName);
                if (packageState == null) {
                    Slog.w(TAG, "Package state is unavailable, ignoring the package "
                            + packageInfo.packageName);
                    continue;
                }
                Bundle apexMeasurement = measurePackage(packageState);
                if (apexMeasurement == null) {
                    Slog.w(TAG, "Skipping the missing APEX in " + packageState.getPath());
                    continue;
                }
                results.add(apexMeasurement);
            }

@@ -205,26 +222,30 @@ public class BinaryTransparencyService extends SystemService {

        /**
         * Perform basic measurement (i.e. content digest) on a given package.
         * @param packageInfo The package to be measured.
         * @param packageState The package to be measured.
         * @return a {@link android.os.Bundle} that packs the measurement result with the following
         *         keys: {@link #BUNDLE_PACKAGE_INFO},
         *         keys: {@link #BUNDLE_PACKAGE_NAME},
         *               {@link #BUNDLE_PACKAGE_IS_APEX}
         *               {@link #BUNDLE_CONTENT_DIGEST_ALGORITHM}
         *               {@link #BUNDLE_CONTENT_DIGEST}
         */
        private @NonNull Bundle measurePackage(PackageInfo packageInfo) {
        private @Nullable Bundle measurePackage(PackageState packageState) {
            Bundle result = new Bundle();

            // compute content digest
            if (DEBUG) {
                Slog.d(TAG, "Computing content digest for " + packageInfo.packageName + " at "
                        + packageInfo.applicationInfo.sourceDir);
                Slog.d(TAG, "Computing content digest for " + packageState.getPackageName() + " at "
                        + packageState.getPath());
            }
            Map<Integer, byte[]> contentDigests = computeApkContentDigest(
                    packageInfo.applicationInfo.sourceDir);
            result.putParcelable(BUNDLE_PACKAGE_INFO, packageInfo);
            AndroidPackage pkg = packageState.getAndroidPackage();
            if (pkg == null) {
                Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
                return null;
            }
            Map<Integer, byte[]> contentDigests = computeApkContentDigest(pkg.getBaseApkPath());
            result.putString(BUNDLE_PACKAGE_NAME, pkg.getPackageName());
            if (contentDigests == null) {
                Slog.d(TAG, "Failed to compute content digest for "
                        + packageInfo.applicationInfo.sourceDir);
                Slog.d(TAG, "Failed to compute content digest for " + pkg.getBaseApkPath());
                result.putInt(BUNDLE_CONTENT_DIGEST_ALGORITHM, 0);
                result.putByteArray(BUNDLE_CONTENT_DIGEST, null);
                return result;
@@ -248,6 +269,7 @@ public class BinaryTransparencyService extends SystemService {
                result.putInt(BUNDLE_CONTENT_DIGEST_ALGORITHM, 0);
                result.putByteArray(BUNDLE_CONTENT_DIGEST, null);
            }
            result.putBoolean(BUNDLE_PACKAGE_IS_APEX, packageState.isApex());

            return result;
        }
@@ -326,16 +348,28 @@ public class BinaryTransparencyService extends SystemService {
        private List<IBinaryTransparencyService.ApexInfo> collectAllApexInfo() {
            var results = new ArrayList<IBinaryTransparencyService.ApexInfo>();
            for (PackageInfo packageInfo : getCurrentInstalledApexs()) {
                Bundle apexMeasurement = measurePackage(packageInfo);
                PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
                        packageInfo.packageName);
                if (packageState == null) {
                    Slog.w(TAG, "Package state is unavailable, ignoring the APEX "
                            + packageInfo.packageName);
                    continue;
                }

                Bundle apexMeasurement = measurePackage(packageState);
                if (apexMeasurement == null) {
                    Slog.w(TAG, "Skipping the missing APEX in " + packageState.getPath());
                    continue;
                }

                var apexInfo = new IBinaryTransparencyService.ApexInfo();
                apexInfo.packageName = packageInfo.packageName;
                apexInfo.longVersion = packageInfo.getLongVersionCode();
                apexInfo.packageName = packageState.getPackageName();
                apexInfo.longVersion = packageState.getVersionCode();
                apexInfo.digest = apexMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
                apexInfo.digestAlgorithm =
                        apexMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
                apexInfo.signerDigests =
                        computePackageSignerSha256Digests(packageInfo.signingInfo);
                        computePackageSignerSha256Digests(packageState.getSigningInfo());

                results.add(apexInfo);
            }
@@ -344,49 +378,38 @@ public class BinaryTransparencyService extends SystemService {

        private List<IBinaryTransparencyService.AppInfo> collectAllUpdatedPreloadInfo(
                Set<String> packagesToSkip) {
            var results = new ArrayList<IBinaryTransparencyService.AppInfo>();
            PackageManager pm = mContext.getPackageManager();
            for (PackageInfo packageInfo : pm.getInstalledPackages(
                    PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY
                            | PackageManager.GET_SIGNING_CERTIFICATES))) {
                if (packagesToSkip.contains(packageInfo.packageName)) {
                    continue;
                }
                int mbaStatus = MBA_STATUS_PRELOADED;
                if (packageInfo.signingInfo == null) {
                    Slog.d(TAG, "Preload " + packageInfo.packageName  + " at "
                            + packageInfo.applicationInfo.sourceDir + " has likely been updated.");
                    mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
            final var results = new ArrayList<IBinaryTransparencyService.AppInfo>();

                    PackageInfo origPackageInfo = packageInfo;
                    try {
                        packageInfo = pm.getPackageInfo(packageInfo.packageName,
                                PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL
                                        | PackageManager.GET_SIGNING_CERTIFICATES));
                    } catch (PackageManager.NameNotFoundException e) {
                        Slog.e(TAG, "Failed to obtain an updated PackageInfo of "
                                + origPackageInfo.packageName, e);
                        packageInfo = origPackageInfo;
                        mbaStatus = MBA_STATUS_ERROR;
            PackageManager pm = mContext.getPackageManager();
            mPackageManagerInternal.forEachPackageState((packageState) -> {
                if (!packageState.isUpdatedSystemApp()) {
                    return;
                }
                if (packagesToSkip.contains(packageState.getPackageName())) {
                    return;
                }

                if (mbaStatus == MBA_STATUS_UPDATED_PRELOAD) {
                    Bundle packageMeasurement = measurePackage(packageInfo);
                Slog.d(TAG, "Preload " + packageState.getPackageName() + " at "
                        + packageState.getPath() + " has likely been updated.");

                Bundle packageMeasurement = measurePackage(packageState);
                if (packageMeasurement == null) {
                    Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
                    return;
                }

                var appInfo = new IBinaryTransparencyService.AppInfo();
                    appInfo.packageName = packageInfo.packageName;
                    appInfo.longVersion = packageInfo.getLongVersionCode();
                appInfo.packageName = packageState.getPackageName();
                appInfo.longVersion = packageState.getVersionCode();
                appInfo.digest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
                appInfo.digestAlgorithm =
                        packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
                appInfo.signerDigests =
                            computePackageSignerSha256Digests(packageInfo.signingInfo);
                    appInfo.mbaStatus = mbaStatus;
                        computePackageSignerSha256Digests(packageState.getSigningInfo());
                appInfo.mbaStatus = MBA_STATUS_UPDATED_PRELOAD;

                results.add(appInfo);
                }
            }
            });
            return results;
        }

@@ -397,25 +420,37 @@ public class BinaryTransparencyService extends SystemService {
                if (packagesToSkip.contains(packageInfo.packageName)) {
                    continue;
                }
                PackageState packageState = mPackageManagerInternal.getPackageStateInternal(
                        packageInfo.packageName);
                if (packageState == null) {
                    Slog.w(TAG, "Package state is unavailable, ignoring the package "
                            + packageInfo.packageName);
                    continue;
                }

                Bundle packageMeasurement = measurePackage(packageInfo);
                Bundle packageMeasurement = measurePackage(packageState);
                if (packageMeasurement == null) {
                    Slog.w(TAG, "Skipping the missing APK in " + packageState.getPath());
                    continue;
                }
                if (DEBUG) {
                    Slog.d(TAG,
                            "Extracting InstallSourceInfo for " + packageInfo.packageName);
                            "Extracting InstallSourceInfo for " + packageState.getPackageName());
                }
                var appInfo = new IBinaryTransparencyService.AppInfo();
                appInfo.packageName = packageInfo.packageName;
                appInfo.longVersion = packageInfo.getLongVersionCode();
                appInfo.packageName = packageState.getPackageName();
                appInfo.longVersion = packageState.getVersionCode();
                appInfo.digest = packageMeasurement.getByteArray(BUNDLE_CONTENT_DIGEST);
                appInfo.digestAlgorithm =
                    packageMeasurement.getInt(BUNDLE_CONTENT_DIGEST_ALGORITHM);
                appInfo.signerDigests =
                    computePackageSignerSha256Digests(packageInfo.signingInfo);
                        computePackageSignerSha256Digests(packageState.getSigningInfo());
                appInfo.mbaStatus = MBA_STATUS_NEW_INSTALL;

                // extract package's InstallSourceInfo
                // Install source isn't currently available in PackageState (there's a TODO).
                // Extract manually with another call.
                InstallSourceInfo installSourceInfo = getInstallSourceInfo(
                        packageInfo.packageName);
                        packageState.getPackageName());
                if (installSourceInfo != null) {
                    appInfo.initiator = installSourceInfo.getInitiatingPackageName();
                    SigningInfo initiatorSignerInfo =
@@ -1130,6 +1165,7 @@ public class BinaryTransparencyService extends SystemService {
        mServiceImpl = new BinaryTransparencyServiceImpl();
        mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
        mMeasurementsLastRecordedMs = 0;
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mBiometricLogger = biometricLogger;
    }

+17 −8
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ import static org.mockito.Mockito.when;

import android.app.job.JobScheduler;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
@@ -82,6 +82,8 @@ public class BinaryTransparencyServiceTest {
    private FaceManager mFaceManager;
    @Mock
    private PackageManager mPackageManager;
    @Mock
    private PackageManagerInternal mPackageManagerInternal;

    @Captor
    private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback>
@@ -95,6 +97,9 @@ public class BinaryTransparencyServiceTest {
        MockitoAnnotations.initMocks(this);

        mContext = spy(ApplicationProvider.getApplicationContext());
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);

        mBinaryTransparencyService = new BinaryTransparencyService(mContext, mBiometricLogger);
        mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
        mOriginalBiometricsFlags = DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BIOMETRICS);
@@ -108,6 +113,7 @@ public class BinaryTransparencyServiceTest {
            Log.e(TAG, "Failed to reset biometrics flags to the original values before test. "
                    + e);
        }
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
    }

    private void prepSignedInfo() {
@@ -164,7 +170,10 @@ public class BinaryTransparencyServiceTest {
        prepApexInfo();
        List result = mTestInterface.getApexInfo();
        Assert.assertNotNull("Apex info map should not be null", result);
        Assert.assertFalse("Apex info map should not be empty", result.isEmpty());
        // TODO(265244016): When PackageManagerInternal is a mock, it's harder to keep the
        // `measurePackage` working in unit test. Disable it for now. We may need more refactoring
        // or cover this in integration tests.
        // Assert.assertFalse("Apex info map should not be empty", result.isEmpty());
    }

    @Test
@@ -177,12 +186,12 @@ public class BinaryTransparencyServiceTest {
        Assert.assertNotNull(pm);
        List<Bundle> castedResult = (List<Bundle>) resultList;
        for (Bundle resultBundle : castedResult) {
            PackageInfo resultPackageInfo = resultBundle.getParcelable(
                    BinaryTransparencyService.BUNDLE_PACKAGE_INFO, PackageInfo.class);
            Assert.assertNotNull("PackageInfo for APEX should not be null",
                    resultPackageInfo);
            Assert.assertTrue(resultPackageInfo.packageName + "is not an APEX!",
                    resultPackageInfo.isApex);
            String packageName = resultBundle.getString(
                    BinaryTransparencyService.BUNDLE_PACKAGE_NAME);
            Assert.assertNotNull("Package name for APEX should not be null", packageName);
            Assert.assertTrue(packageName + "is not an APEX!",
                    resultBundle.getBoolean(
                            BinaryTransparencyService.BUNDLE_PACKAGE_IS_APEX));
        }
    }