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

Commit 3b5f1a50 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "BinaryTransparencyService: Add package update observers."

parents 5ee13b19 22943804
Loading
Loading
Loading
Loading
+129 −25
Original line number Original line Diff line number Diff line
@@ -27,15 +27,20 @@ import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.app.job.JobService;
import android.compat.annotation.ChangeId;
import android.compat.annotation.ChangeId;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IStagedApexObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ParceledListSlice;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.Signature;
@@ -51,6 +56,7 @@ import android.hardware.face.FaceSensorProperties;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.Binder;
import android.os.Build;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
@@ -142,7 +148,6 @@ public class BinaryTransparencyService extends SystemService {
    private String mVbmetaDigest;
    private String mVbmetaDigest;
    // the system time (in ms) the last measurement was taken
    // the system time (in ms) the last measurement was taken
    private long mMeasurementsLastRecordedMs;
    private long mMeasurementsLastRecordedMs;
    private PackageManagerInternal mPackageManagerInternal;
    private BiometricLogger mBiometricLogger;
    private BiometricLogger mBiometricLogger;


    /**
    /**
@@ -1103,7 +1108,6 @@ public class BinaryTransparencyService extends SystemService {
        mServiceImpl = new BinaryTransparencyServiceImpl();
        mServiceImpl = new BinaryTransparencyServiceImpl();
        mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
        mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
        mMeasurementsLastRecordedMs = 0;
        mMeasurementsLastRecordedMs = 0;
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mBiometricLogger = biometricLogger;
        mBiometricLogger = biometricLogger;
    }
    }


@@ -1119,28 +1123,6 @@ public class BinaryTransparencyService extends SystemService {
        } catch (Throwable t) {
        } catch (Throwable t) {
            Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
            Slog.e(TAG, "Failed to start BinaryTransparencyService.", t);
        }
        }

        // register a package observer to detect updates to preloads
        mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
            @Override
            public void onPackageChanged(String packageName, int uid) {
                // check if the updated package is a preloaded app.
                PackageManager pm = mContext.getPackageManager();
                try {
                    pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(
                            PackageManager.MATCH_FACTORY_ONLY));
                } catch (PackageManager.NameNotFoundException e) {
                    // this means that this package is not a preloaded app
                    return;
                }

                Slog.d(TAG, "Preload " + packageName + " was updated. Scheduling measurement...");
                UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                        BinaryTransparencyService.this);
            }
        });

        // TODO(b/264428429): Register observer for updates to APEXs.
    }
    }


    /**
    /**
@@ -1172,6 +1154,8 @@ public class BinaryTransparencyService extends SystemService {
            Slog.i(TAG, "Scheduling measurements to be taken.");
            Slog.i(TAG, "Scheduling measurements to be taken.");
            UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
            UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                    BinaryTransparencyService.this);
                    BinaryTransparencyService.this);

            registerAllPackageUpdateObservers();
        }
        }
    }
    }


@@ -1415,6 +1399,126 @@ public class BinaryTransparencyService extends SystemService {
        FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
        FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
    }
    }


    /**
     * Listen for APK updates.
     *
     * There are two ways available to us to do this:
     * 1. Register an observer using
     * {@link PackageManagerInternal#getPackageList(PackageManagerInternal.PackageListObserver)}.
     * 2. Register broadcast receivers, listening to either {@code ACTION_PACKAGE_ADDED} or
     * {@code ACTION_PACKAGE_REPLACED}.
     *
     * After experimentation, we found that Option #1 does not catch updates to non-staged APEXs.
     * Thus, we are implementing Option #2 here. More specifically, listening to
     * {@link Intent#ACTION_PACKAGE_ADDED} allows us to capture all events we care about.
     *
     * We did not use {@link Intent#ACTION_PACKAGE_REPLACED} because it unfortunately does not
     * detect updates to non-staged APEXs. Thus, we rely on {@link Intent#EXTRA_REPLACING} to
     * filter out new installation from updates instead.
     */
    private void registerApkAndNonStagedApexUpdateListener() {
        Slog.d(TAG, "Registering APK & Non-Staged APEX updates...");
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addDataScheme("package");    // this is somehow necessary
        mContext.registerReceiver(new PackageUpdatedReceiver(), filter);
    }

    /**
     * Listen for staged-APEX updates.
     *
     * This method basically covers cases that are not caught by
     * {@link #registerApkAndNonStagedApexUpdateListener()}, namely updates to APEXs that are staged
     * for the subsequent reboot.
     */
    private void registerStagedApexUpdateObserver() {
        Slog.d(TAG, "Registering APEX updates...");
        IPackageManagerNative iPackageManagerNative = IPackageManagerNative.Stub.asInterface(
                ServiceManager.getService("package_native"));
        if (iPackageManagerNative == null) {
            Slog.e(TAG, "IPackageManagerNative is null");
            return;
        }

        try {
            iPackageManagerNative.registerStagedApexObserver(new IStagedApexObserver.Stub() {
                @Override
                public void onApexStaged(ApexStagedEvent event) throws RemoteException {
                    Slog.d(TAG, "A new APEX has been staged for update. There are currently "
                            + event.stagedApexModuleNames.length + " APEX(s) staged for update. "
                            + "Scheduling measurement...");
                    UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                            BinaryTransparencyService.this);
                }
            });
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to register a StagedApexObserver.");
        }
    }

    private boolean isPackagePreloaded(String packageName) {
        PackageManager pm = mContext.getPackageManager();
        try {
            pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(
                    PackageManager.MATCH_FACTORY_ONLY));
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
        return true;
    }

    private boolean isPackageAnApex(String packageName) {
        PackageManager pm = mContext.getPackageManager();
        try {
            PackageInfo packageInfo = pm.getPackageInfo(packageName,
                    PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX));
            return packageInfo.isApex;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private class PackageUpdatedReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
                return;
            }

            Uri data = intent.getData();
            if (data == null) {
                Slog.e(TAG, "Shouldn't happen: intent data is null!");
                return;
            }

            if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                Slog.d(TAG, "Not an update. Skipping...");
                return;
            }

            String packageName = data.getSchemeSpecificPart();
            // now we've got to check what package is this
            if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) {
                Slog.d(TAG, packageName + " was updated. Scheduling measurement...");
                UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                        BinaryTransparencyService.this);
            }
        }
    }

    /**
     * Register observers for APK and APEX updates. The current implementation breaks this process
     * into 2 cases/methods because PackageManager does not offer a unified interface to register
     * for all package updates in a universal and comprehensive manner.
     * Thus, the observers will be invoked when either
     * i) APK or non-staged APEX update; or
     * ii) APEX staging happens.
     * This will then be used as signals to schedule measurement for the relevant binaries.
     */
    private void registerAllPackageUpdateObservers() {
        registerApkAndNonStagedApexUpdateListener();
        registerStagedApexUpdateObserver();
    }

    private String translateContentDigestAlgorithmIdToString(int algorithmId) {
    private String translateContentDigestAlgorithmIdToString(int algorithmId) {
        switch (algorithmId) {
        switch (algorithmId) {
            case ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256:
            case ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256: