summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();
}