From d76b7bf6a4b65b3bf6e2210ecb0aada63b319f01 Mon Sep 17 00:00:00 2001 From: Billy Lau Date: Wed, 16 Feb 2022 21:16:26 -0800 Subject: [PATCH] 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 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 --- core/res/AndroidManifest.xml | 4 + .../server/BinaryTransparencyService.java | 86 ++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 80317b8f5305f..c147b5f6c8c59 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6916,6 +6916,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> + + + diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 6986d3bbe5852..1f8ef8226c320 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -18,14 +18,22 @@ package com.android.server; import android.annotation.NonNull; 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.pm.ApplicationInfo; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemProperties; @@ -42,6 +50,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; import java.util.stream.Collectors; /** @@ -49,6 +58,7 @@ import java.util.stream.Collectors; */ public class BinaryTransparencyService extends SystemService { private static final String TAG = "TransparencyService"; + private static final String EXTRA_SERVICE = "service"; @VisibleForTesting 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 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."); 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 private List getInstalledApexs() { - List results = new ArrayList(); + List results = new ArrayList<>(); PackageManager pm = mContext.getPackageManager(); if (pm == null) { Slog.e(TAG, "Error obtaining an instance of PackageManager.");