Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e8e23d8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents cb3b982b 11b592be
Loading
Loading
Loading
Loading
+89 −60
Original line number Diff line number Diff line
@@ -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,65 +554,94 @@ public class IorapForwardingService extends SystemService {
            // Tell iorapd to start a background job.
            Log.d(TAG, "Starting background job: " + params.toString());

            mAbortIdleCompilation.set(false);
            // PackageManagerService starts before IORap service.
            PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
            List<String> pkgs = pm.getAllPackages();
            runIdleCompilationAsync(params, pkgs);
            return true;
        }

        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.
                            // 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))
            )) {
                                                params,
                                                pkg,
                                                shouldUpdateVersions)))) {
                            synchronized (mLock) {
                                mRunningJobs.remove(request); // Avoid memory leaks.
                            }
                        }

                // Something went wrong on the remote side. Treat the job as being
                // 'already finished' (i.e. immediately release wake lock).
                return false;
                        // 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();
        }

            // True -> keep the wakelock acquired until #jobFinished is called.
        /** 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;
                }

                totalSleepTimeMs += sleepIntervalMs;
                if (totalSleepTimeMs > timeout.toMillis()) {
                    return false;
                }
            }
        }

        // Called by system to prematurely stop the job.
        @Override
        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() {
+25 −5
Original line number Diff line number Diff line
@@ -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();
    }