Merge "iorap: Stop compilation when the device is not idle."
This commit is contained in:
committed by
Android (Google) Code Review
commit
0e8e23d809
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user