diff options
-rw-r--r-- | startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java | 149 | ||||
-rw-r--r-- | startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java | 30 |
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(); } |