Merge "iorap: Stop compilation when the device is not idle."

This commit is contained in:
TreeHugger Robot
2020-12-04 01:31:42 +00:00
committed by Android (Google) Code Review
2 changed files with 114 additions and 65 deletions

View File

@@ -42,15 +42,20 @@ import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.BackgroundDexOptService;
import com.android.server.pm.PackageManagerService;
import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
import java.util.HashMap;
import java.util.List;
/**
* System-server-local proxy into the {@code IIorap} native service.
@@ -65,6 +70,7 @@ public class IorapForwardingService extends SystemService {
/** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */
private static boolean WTF_CRASH = SystemProperties.getBoolean(
"iorapd.forwarding_service.wtf_crash", false);
private static final Duration TIMEOUT = Duration.ofSeconds(600L);
// "Unique" job ID from the service name. Also equal to 283673059.
public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd");
@@ -80,6 +86,12 @@ public class IorapForwardingService extends SystemService {
private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever).
private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null).
/**
* Atomics set to true if the JobScheduler requests an abort.
*/
private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false);
/**
* Initializes the system service.
* <p>
@@ -542,32 +554,86 @@ public class IorapForwardingService extends SystemService {
// Tell iorapd to start a background job.
Log.d(TAG, "Starting background job: " + params.toString());
// We wait until that job's sequence ID returns to us with 'Completed',
RequestId request;
synchronized (mLock) {
// TODO: would be cleaner if we got the request from the 'invokeRemote' function.
// Better yet, consider a Pair<RequestId, Future<TaskResult>> or similar.
request = RequestId.nextValueForSequence();
mRunningJobs.put(request, params);
}
mAbortIdleCompilation.set(false);
// PackageManagerService starts before IORap service.
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
List<String> pkgs = pm.getAllPackages();
runIdleCompilationAsync(params, pkgs);
return true;
}
if (!invokeRemote(mIorapRemote, (IIorap remote) ->
remote.onJobScheduledEvent(request,
JobScheduledEvent.createIdleMaintenance(
JobScheduledEvent.TYPE_START_JOB,
params))
)) {
synchronized (mLock) {
mRunningJobs.remove(request); // Avoid memory leaks.
private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) {
new Thread("IORap_IdleCompilation") {
@Override
public void run() {
for (int i = 0; i < pkgs.size(); i++) {
String pkg = pkgs.get(i);
if (mAbortIdleCompilation.get()) {
Log.i(TAG, "The idle compilation is aborted");
return;
}
// We wait until that job's sequence ID returns to us with 'Completed',
RequestId request;
synchronized (mLock) {
// TODO: would be cleaner if we got the request from the
// 'invokeRemote' function. Better yet, consider
// a Pair<RequestId, Future<TaskResult>> or similar.
request = RequestId.nextValueForSequence();
mRunningJobs.put(request, params);
}
Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]",
pkg, i + 1, pkgs.size()));
boolean shouldUpdateVersions = (i == 0);
if (!invokeRemote(mIorapRemote, (IIorap remote) ->
remote.onJobScheduledEvent(request,
JobScheduledEvent.createIdleMaintenance(
JobScheduledEvent.TYPE_START_JOB,
params,
pkg,
shouldUpdateVersions)))) {
synchronized (mLock) {
mRunningJobs.remove(request); // Avoid memory leaks.
}
}
// Wait until the job is complete and removed from the running jobs.
retryWithTimeout(TIMEOUT, () -> {
synchronized (mLock) {
return !mRunningJobs.containsKey(request);
}
});
}
// Finish the job after all packages are compiled.
if (mProxy != null) {
mProxy.jobFinished(params, /*reschedule*/false);
}
}
}.start();
}
/** Retry until timeout. */
private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) {
long totalSleepTimeMs = 0L;
long sleepIntervalMs = 10L;
while (true) {
if (supplier.getAsBoolean()) {
return true;
}
try {
TimeUnit.MILLISECONDS.sleep(sleepIntervalMs);
} catch (InterruptedException e) {
Log.e(TAG, e.getMessage());
return false;
}
// Something went wrong on the remote side. Treat the job as being
// 'already finished' (i.e. immediately release wake lock).
return false;
totalSleepTimeMs += sleepIntervalMs;
if (totalSleepTimeMs > timeout.toMillis()) {
return false;
}
}
// True -> keep the wakelock acquired until #jobFinished is called.
return true;
}
// Called by system to prematurely stop the job.
@@ -575,32 +641,7 @@ public class IorapForwardingService extends SystemService {
public boolean onStopJob(JobParameters params) {
// As this is unexpected behavior, print a warning.
Log.w(TAG, "onStopJob(params=" + params.toString() + ")");
// No longer track this job (avoids a memory leak).
boolean wasTracking = false;
synchronized (mLock) {
for (HashMap.Entry<RequestId, JobParameters> entry : mRunningJobs.entrySet()) {
if (entry.getValue().getJobId() == params.getJobId()) {
mRunningJobs.remove(entry.getKey());
wasTracking = true;
}
}
}
// Notify iorapd to stop (abort) the job.
if (wasTracking) {
invokeRemote(mIorapRemote, (IIorap remote) ->
remote.onJobScheduledEvent(RequestId.nextValueForSequence(),
JobScheduledEvent.createIdleMaintenance(
JobScheduledEvent.TYPE_STOP_JOB,
params))
);
} else {
// Even weirder. This could only be considered "correct" if iorapd reported success
// concurrently to the JobService requesting an onStopJob.
Log.e(TAG, "Untracked onStopJob request"); // see above Log.w for the params.
}
mAbortIdleCompilation.set(true);
// Yes, retry the job at a later time no matter what.
return true;
@@ -626,18 +667,6 @@ public class IorapForwardingService extends SystemService {
}
Log.d(TAG, "Finished background job: " + jobParameters.toString());
// Job is successful and periodic. Do not 'reschedule' according to the back-off
// criteria.
//
// This releases the wakelock that was acquired in #onStartJob.
IorapdJobServiceProxy proxy = mProxy;
if (proxy != null) {
proxy.jobFinished(jobParameters, /*reschedule*/false);
}
// Cannot call 'jobFinished' on 'this' because it was not constructed
// from the JobService, so it would get an NPE when calling mEngine.
}
public void onIorapdDisconnected() {

View File

@@ -55,6 +55,10 @@ public class JobScheduledEvent implements Parcelable {
/** @see JobParameters#getJobId() */
public final int jobId;
public final String packageName;
public final boolean shouldUpdateVersions;
/** Device is 'idle' and it's charging (plugged in). */
public static final int SORT_IDLE_MAINTENANCE = 0;
private static final int SORT_MAX = 0;
@@ -77,14 +81,22 @@ public class JobScheduledEvent implements Parcelable {
* Only the job ID is retained from {@code jobParams}, all other param info is dropped.
*/
@NonNull
public static JobScheduledEvent createIdleMaintenance(@Type int type, JobParameters jobParams) {
return new JobScheduledEvent(type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE);
public static JobScheduledEvent createIdleMaintenance(
@Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) {
return new JobScheduledEvent(
type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions);
}
private JobScheduledEvent(@Type int type, int jobId, @Sort int sort) {
private JobScheduledEvent(@Type int type,
int jobId,
@Sort int sort,
String packageName,
boolean shouldUpdateVersions) {
this.type = type;
this.jobId = jobId;
this.sort = sort;
this.packageName = packageName;
this.shouldUpdateVersions = shouldUpdateVersions;
checkConstructorArguments();
}
@@ -108,12 +120,16 @@ public class JobScheduledEvent implements Parcelable {
private boolean equals(JobScheduledEvent other) {
return type == other.type &&
jobId == other.jobId &&
sort == other.sort;
sort == other.sort &&
packageName.equals(other.packageName) &&
shouldUpdateVersions == other.shouldUpdateVersions;
}
@Override
public String toString() {
return String.format("{type: %d, jobId: %d, sort: %d}", type, jobId, sort);
return String.format(
"{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}",
type, jobId, sort, packageName, shouldUpdateVersions);
}
//<editor-fold desc="Binder boilerplate">
@@ -122,6 +138,8 @@ public class JobScheduledEvent implements Parcelable {
out.writeInt(type);
out.writeInt(jobId);
out.writeInt(sort);
out.writeString(packageName);
out.writeBoolean(shouldUpdateVersions);
// We do not parcel the entire JobParameters here because there is no C++ equivalent
// of that class [which the iorapd side of the binder interface requires].
@@ -131,6 +149,8 @@ public class JobScheduledEvent implements Parcelable {
this.type = in.readInt();
this.jobId = in.readInt();
this.sort = in.readInt();
this.packageName = in.readString();
this.shouldUpdateVersions = in.readBoolean();
checkConstructorArguments();
}