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

Commit fc580395 authored by Billy Lau's avatar Billy Lau
Browse files

Schedule binary measurement job at boot completed.

Instead of waiting until first invocation of commandline,
we are scheduling the computation of SHA256 digest of
APEXs and Modules at post boot such that the job can
be run when the device is idle.

For this purpose, an inner static class that extends
JobServer is created, and a declaration of this service
in the manifest which is protected by BIND_JOB_SERVICE
is also necessary.

This allows the logging of APEX information to statsd
to still happen at least once every reboot (when the
device is idle). Otherwise, logging may never occur
if the users never make use of the commandline.

Bug: 217469982
Test: Manual.
Job is found to be triggered automatically when device
is left overnight.

Job can also be manually triggered (for test) via
`adb shell cmd jobscheduler run android <job_id>`
Job id can be found via printout in logcat after
the scheduling is successfully done.

After job is run, `adb shell cmd transparency
get apex_info` no longer has latency upon first
invocation.

Change-Id: I7cff80d53b37152639bb9aebaca50f5b61733ea7
(cherry picked from commit d76b7bf6)
parent edd81e4e
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -6926,6 +6926,10 @@
                 android:permission="android.permission.BIND_JOB_SERVICE">
                 android:permission="android.permission.BIND_JOB_SERVICE">
        </service>
        </service>


        <service android:name="com.android.server.BinaryTransparencyService$UpdateMeasurementsJobService"
                 android:permission="android.permission.BIND_JOB_SERVICE">
        </service>

        <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
        <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
            android:exported="false">
            android:exported="false">
            <intent-filter>
            <intent-filter>
+83 −3
Original line number Original line Diff line number Diff line
@@ -18,14 +18,22 @@ package com.android.server;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
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.os.Build;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.ShellCommand;
import android.os.SystemProperties;
import android.os.SystemProperties;
@@ -42,6 +50,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Collectors;


/**
/**
@@ -49,6 +58,7 @@ import java.util.stream.Collectors;
 */
 */
public class BinaryTransparencyService extends SystemService {
public class BinaryTransparencyService extends SystemService {
    private static final String TAG = "TransparencyService";
    private static final String TAG = "TransparencyService";
    private static final String EXTRA_SERVICE = "service";


    @VisibleForTesting
    @VisibleForTesting
    static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
    static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
@@ -365,10 +375,80 @@ public class BinaryTransparencyService extends SystemService {


        // we are only interested in doing things at PHASE_BOOT_COMPLETED
        // we are only interested in doing things at PHASE_BOOT_COMPLETED
        if (phase == PHASE_BOOT_COMPLETED) {
        if (phase == PHASE_BOOT_COMPLETED) {
            // due to potentially long computation that holds up boot time, apex sha computations
            // are deferred to first call
            Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
            Slog.i(TAG, "Boot completed. Getting VBMeta Digest.");
            getVBMetaDigestInformation();
            getVBMetaDigestInformation();

            // due to potentially long computation that holds up boot time, computations for
            // SHA256 digests of APEX and Module packages are scheduled here,
            // but only executed when device is idle.
            Slog.i(TAG, "Scheduling APEX and Module measurements to be updated.");
            UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
                    BinaryTransparencyService.this);
        }
    }

    /**
     * JobService to update binary measurements and update internal cache.
     */
    public static class UpdateMeasurementsJobService extends JobService {
        private static final int COMPUTE_APEX_MODULE_SHA256_JOB_ID =
                BinaryTransparencyService.UpdateMeasurementsJobService.class.hashCode();

        @Override
        public boolean onStartJob(JobParameters params) {
            Slog.d(TAG, "Job to update binary measurements started.");
            if (params.getJobId() != COMPUTE_APEX_MODULE_SHA256_JOB_ID) {
                return false;
            }

            // we'll still update the measurements via threads to be mindful of low-end devices
            // where this operation might take longer than expected, and so that we don't block
            // system_server's main thread.
            Executors.defaultThreadFactory().newThread(() -> {
                // since we can't call updateBinaryMeasurements() directly, calling
                // getApexInfo() achieves the same effect, and we simply discard the return
                // value

                IBinder b = ServiceManager.getService(Context.BINARY_TRANSPARENCY_SERVICE);
                IBinaryTransparencyService iBtsService =
                        IBinaryTransparencyService.Stub.asInterface(b);
                try {
                    iBtsService.getApexInfo();
                } catch (RemoteException e) {
                    Slog.e(TAG, "Updating binary measurements was interrupted.", e);
                    return;
                }
                jobFinished(params, false);
            }).start();

            return true;
        }

        @Override
        public boolean onStopJob(JobParameters params) {
            return false;
        }

        @SuppressLint("DefaultLocale")
        static void scheduleBinaryMeasurements(Context context, BinaryTransparencyService service) {
            Slog.i(TAG, "Scheduling APEX & Module SHA256 digest computation job");
            final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
            if (jobScheduler == null) {
                Slog.e(TAG, "Failed to obtain an instance of JobScheduler.");
                return;
            }

            final JobInfo jobInfo = new JobInfo.Builder(COMPUTE_APEX_MODULE_SHA256_JOB_ID,
                    new ComponentName(context, UpdateMeasurementsJobService.class))
                    .setRequiresDeviceIdle(true)
                    .build();
            if (jobScheduler.schedule(jobInfo) != JobScheduler.RESULT_SUCCESS) {
                Slog.e(TAG, "Failed to schedule job to update binary measurements.");
                return;
            }
            Slog.d(TAG, String.format(
                    "Job %d to update binary measurements scheduled successfully.",
                    COMPUTE_APEX_MODULE_SHA256_JOB_ID));
        }
        }
    }
    }


@@ -380,7 +460,7 @@ public class BinaryTransparencyService extends SystemService {


    @NonNull
    @NonNull
    private List<PackageInfo> getInstalledApexs() {
    private List<PackageInfo> getInstalledApexs() {
        List<PackageInfo> results = new ArrayList<PackageInfo>();
        List<PackageInfo> results = new ArrayList<>();
        PackageManager pm = mContext.getPackageManager();
        PackageManager pm = mContext.getPackageManager();
        if (pm == null) {
        if (pm == null) {
            Slog.e(TAG, "Error obtaining an instance of PackageManager.");
            Slog.e(TAG, "Error obtaining an instance of PackageManager.");