summaryrefslogtreecommitdiff
path: root/startop
diff options
context:
space:
mode:
authoryawanng <yawanng@google.com>2020-12-01 06:22:24 +0000
committeryawanng <yawanng@google.com>2020-12-03 23:59:14 +0000
commit11b592be871ea04bf634d2355ec3298bec9a94e6 (patch)
treea1257ae1eed58998e192a5fda72621ef6e73aa1b /startop
parentccfa9a66827ec5a1b1cb9c657fa07df0f84b9128 (diff)
downloadbase-11b592be871ea04bf634d2355ec3298bec9a94e6.tar.gz
iorap: Stop compilation when the device is not idle.
System service sends each package name to iorap during idle time for compilation and only sends the next one after the current one is done. When the device is not idle, stop sending the following packages. Bug: 171438036 Test: Run 'adb shell cmd jobscheduler run -f android 283673059' and 'adb shell cmd jobscheduler timeout android 283673059' and check the log. Change-Id: I39e6cf83a55ce8b4e8c3c7984391fad6dd86a4e7
Diffstat (limited to 'startop')
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java149
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java30
2 files changed, 114 insertions, 65 deletions
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 3104c7e7e0a1..df28cea30956 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -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();
+ }
- // Something went wrong on the remote side. Treat the job as being
- // 'already finished' (i.e. immediately release wake lock).
- return false;
- }
+ /** 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;
+ }
- // True -> keep the wakelock acquired until #jobFinished is called.
- return true;
+ totalSleepTimeMs += sleepIntervalMs;
+ if (totalSleepTimeMs > timeout.toMillis()) {
+ return false;
+ }
+ }
}
// 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() {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
index 2055b206dd7a..b91dd71fd28c 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
@@ -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();
}