BinaryTransparencyService: Schedule measurement job on demand.

Initially, a binary measurement job is scheduled in a recurring
24h cadence.

We are now changing the scheduling of the measurement job
from one that is time based to one that is on-demand, based
on specific events and capping the measurement to be done at
most once within a 24h time window. Qualifying events include:
1. the update of a preloaded app
2. the update of an APEX

However, this change only cover event #1.

Bug: 259590636
Test: Manual.
	1. Manually update a preloaded app
	   - logcat: measurement was attempted, but not rescheduled
	2. Manually install a new app
	   - nothing is triggered
	3. Manuall update a new app
	   - nothing is triggered
Change-Id: I11ecf3588f263638a074f737487758b37ee27f5a
This commit is contained in:
Billy Lau
2023-01-04 11:44:03 -06:00
parent b95f2b215e
commit f023554fab

View File

@@ -35,6 +35,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;
@@ -76,6 +77,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
@@ -127,6 +129,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;
/**
* Guards whether or not measurements of MBA to be performed. When this change is enabled,
@@ -1016,6 +1019,7 @@ public class BinaryTransparencyService extends SystemService {
mServiceImpl = new BinaryTransparencyServiceImpl();
mVbmetaDigest = VBMETA_DIGEST_UNINITIALIZED;
mMeasurementsLastRecordedMs = 0;
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
/**
@@ -1030,6 +1034,38 @@ public class BinaryTransparencyService extends SystemService {
} catch (Throwable 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 onPackageAdded(String packageName, int uid) {
}
@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);
}
@Override
public void onPackageRemoved(String packageName, int uid) {
}
});
// TODO(b/264428429): Register observer for updates to APEXs.
}
/**
@@ -1059,6 +1095,8 @@ public class BinaryTransparencyService extends SystemService {
* JobService to measure all covered binaries and record result to Westworld.
*/
public static class UpdateMeasurementsJobService extends JobService {
private static AtomicBoolean sScheduled = new AtomicBoolean();
private static long sTimeLastRanMs = 0;
private static final int DO_BINARY_MEASUREMENTS_JOB_ID =
UpdateMeasurementsJobService.class.hashCode();
@@ -1085,6 +1123,8 @@ public class BinaryTransparencyService extends SystemService {
Slog.e(TAG, "Taking binary measurements was interrupted.", e);
return;
}
sTimeLastRanMs = System.currentTimeMillis();
sScheduled.set(false);
jobFinished(params, false);
}).start();
@@ -1105,16 +1145,33 @@ public class BinaryTransparencyService extends SystemService {
return;
}
if (sScheduled.get()) {
Slog.d(TAG, "A measurement job has already been scheduled.");
return;
}
long minWaitingPeriodMs = 0;
if (sTimeLastRanMs != 0) {
minWaitingPeriodMs = RECORD_MEASUREMENTS_COOLDOWN_MS
- (System.currentTimeMillis() - sTimeLastRanMs);
// bound the range of minWaitingPeriodMs in the case where > 24h has elapsed
minWaitingPeriodMs = Math.max(0,
Math.min(minWaitingPeriodMs, RECORD_MEASUREMENTS_COOLDOWN_MS));
Slog.d(TAG, "Scheduling the next measurement to be done at least "
+ minWaitingPeriodMs + "ms from now.");
}
final JobInfo jobInfo = new JobInfo.Builder(DO_BINARY_MEASUREMENTS_JOB_ID,
new ComponentName(context, UpdateMeasurementsJobService.class))
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setPeriodic(RECORD_MEASUREMENTS_COOLDOWN_MS)
.setMinimumLatency(minWaitingPeriodMs)
.build();
if (jobScheduler.schedule(jobInfo) != JobScheduler.RESULT_SUCCESS) {
Slog.e(TAG, "Failed to schedule job to measure binaries.");
return;
}
sScheduled.set(true);
Slog.d(TAG, TextUtils.formatSimple(
"Job %d to measure binaries was scheduled successfully.",
DO_BINARY_MEASUREMENTS_JOB_ID));