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

Commit 867b133a authored by Martin Stjernholm's avatar Martin Stjernholm Committed by Android (Google) Code Review
Browse files

Merge "Add a flag to block the job scheduler from starting bg dexopt jobs."

parents 04d30fcc c4a1feea
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -147,6 +147,9 @@ public final class BackgroundDexOptService {
    // Tells whether post boot update is completed or not.
    @GuardedBy("mLock") private boolean mFinishedPostBootUpdate;

    // True if JobScheduler invocations of dexopt have been disabled.
    @GuardedBy("mLock") private boolean mDisableJobSchedulerJobs;

    @GuardedBy("mLock") @Status private int mLastExecutionStatus = STATUS_OK;

    @GuardedBy("mLock") private long mLastExecutionStartTimeMs;
@@ -227,6 +230,8 @@ public final class BackgroundDexOptService {
            writer.println(mDexOptCancellingThread);
            writer.print("mFinishedPostBootUpdate:");
            writer.println(mFinishedPostBootUpdate);
            writer.print("mDisableJobSchedulerJobs:");
            writer.println(mDisableJobSchedulerJobs);
            writer.print("mLastExecutionStatus:");
            writer.println(mLastExecutionStatus);
            writer.print("mLastExecutionStartTimeMs:");
@@ -298,6 +303,22 @@ public final class BackgroundDexOptService {
        Binder.withCleanCallingIdentity(() -> cancelDexOptAndWaitForCompletion());
    }

    /**
     * Sets a flag that disables jobs from being started from JobScheduler.
     *
     * This state is not persistent and is only retained in this service instance.
     *
     * This is intended for shell command use and only root or shell users can call it.
     *
     * @param disable True if JobScheduler invocations should be disabled, false otherwise.
     */
    public void setDisableJobSchedulerJobs(boolean disable) {
        enforceRootOrShell();
        synchronized (mLock) {
            mDisableJobSchedulerJobs = disable;
        }
    }

    /** Adds listener for package update */
    public void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
        synchronized (mLock) {
@@ -351,6 +372,10 @@ public final class BackgroundDexOptService {
        mThermalStatusCutoff = mInjector.getDexOptThermalCutoff();

        synchronized (mLock) {
            if (mDisableJobSchedulerJobs) {
                Slog.i(TAG, "JobScheduler invocations disabled");
                return false;
            }
            if (mDexOptThread != null && mDexOptThread.isAlive()) {
                // Other task is already running.
                return false;
@@ -475,7 +500,7 @@ public final class BackgroundDexOptService {
    }

    private void enforceRootOrShell() {
        int uid = Binder.getCallingUid();
        int uid = mInjector.getCallingUid();
        if (uid != Process.ROOT_UID && uid != Process.SHELL_UID) {
            throw new SecurityException("Should be shell or root user");
        }
@@ -974,6 +999,10 @@ public final class BackgroundDexOptService {
            mPackageManagerService = pm;
        }

        int getCallingUid() {
            return Binder.getCallingUid();
        }

        Context getContext() {
            return mContext;
        }
+62 −23
Original line number Diff line number Diff line
@@ -257,7 +257,7 @@ class PackageManagerShellCommand extends ShellCommand {
                case "force-dex-opt":
                    return runForceDexOpt();
                case "bg-dexopt-job":
                    return runDexoptJob();
                    return runBgDexOpt();
                case "cancel-bg-dexopt-job":
                    return cancelBgDexOptJob();
                case "delete-dexopt":
@@ -1971,16 +1971,47 @@ class PackageManagerShellCommand extends ShellCommand {
        return 0;
    }

    private int runDexoptJob() throws RemoteException {
        String arg;
    private int runBgDexOpt() throws RemoteException {
        String opt = getNextOption();

        if (opt == null) {
            List<String> packageNames = new ArrayList<>();
            String arg;
            while ((arg = getNextArg()) != null) {
                packageNames.add(arg);
            }
        boolean result = BackgroundDexOptService.getService().runBackgroundDexoptJob(
                packageNames.isEmpty() ? null : packageNames);
        getOutPrintWriter().println(result ? "Success" : "Failure");
        return result ? 0 : -1;
            if (!BackgroundDexOptService.getService().runBackgroundDexoptJob(
                    packageNames.isEmpty() ? null : packageNames)) {
                getOutPrintWriter().println("Failure");
                return -1;
            }
        } else {
            String extraArg = getNextArg();
            if (extraArg != null) {
                getErrPrintWriter().println("Invalid argument: " + extraArg);
                return -1;
            }

            switch (opt) {
                case "--cancel":
                    return cancelBgDexOptJob();

                case "--disable":
                    BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true);
                    break;

                case "--enable":
                    BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false);
                    break;

                default:
                    getErrPrintWriter().println("Unknown option: " + opt);
                    return -1;
            }
        }

        getOutPrintWriter().println("Success");
        return 0;
    }

    private int cancelBgDexOptJob() throws RemoteException {
@@ -4209,20 +4240,28 @@ class PackageManagerShellCommand extends ShellCommand {
        pw.println("  delete-dexopt PACKAGE");
        pw.println("    Delete dex optimization results for the given PACKAGE.");
        pw.println("");
        pw.println("  bg-dexopt-job [PACKAGE...]");
        pw.println("    Execute the background optimizations immediately.");
        pw.println("    Optimize only the given PACKAGEs, or all packages if none is specified.");
        pw.println("    Note that the command only runs the background optimizer logic. It will");
        pw.println("    run even if the device is not in the idle maintenance mode. If a job is");
        pw.println("    already running (including one started automatically by the system) it");
        pw.println("    will wait for it to finish before starting. A background job will not be");
        pw.println("    started automatically while one started this way is running.");
        pw.println("  bg-dexopt-job [PACKAGE... | --cancel | --disable | --enable]");
        pw.println("    Controls the background job that optimizes dex files:");
        pw.println("    Without flags, run background optimization immediately on the given");
        pw.println("    PACKAGEs, or all packages if none is specified, and wait until the job");
        pw.println("    finishes. Note that the command only runs the background optimizer logic.");
        pw.println("    It will run even if the device is not in the idle maintenance mode. If a");
        pw.println("    job is already running (including one started automatically by the");
        pw.println("    system) it will wait for it to finish before starting. A background job");
        pw.println("    will not be started automatically while one started this way is running.");
        pw.println("      --cancel: Cancels any currently running background optimization job");
        pw.println("        immediately. This cancels jobs started either automatically by the");
        pw.println("        system or through this command. Note that cancelling a currently");
        pw.println("        running bg-dexopt-job command requires running this command from a");
        pw.println("        separate adb shell.");
        pw.println("      --disable: Disables background jobs from being started by the job");
        pw.println("        scheduler. Does not affect bg-dexopt-job invocations from the shell.");
        pw.println("        Does not imply --cancel. This state will be lost when the");
        pw.println("        system_server process exits.");
        pw.println("      --enable: Enables background jobs to be started by the job scheduler");
        pw.println("        again, if previously disabled by --disable.");
        pw.println("  cancel-bg-dexopt-job");
        pw.println("    Cancels any currently running background optimization job immediately.");
        pw.println("    This cancels jobs started either automatically by the system or through");
        pw.println("    the bg-dexopt-job command. Note that cancelling a currently running");
        pw.println("    bg-dexopt-job command requires running this command from a separate adb");
        pw.println("    shell.");
        pw.println("    Same as bg-dexopt-job --cancel.");
        pw.println("");
        pw.println("  reconcile-secondary-dex-files TARGET-PACKAGE");
        pw.println("    Reconciles the package secondary dex files with the generated oat files.");
+17 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.Process;
import android.util.Log;

import com.android.internal.util.IndentingPrintWriter;
@@ -118,6 +119,7 @@ public final class BackgroundDexOptServiceUnitTest {

    @Before
    public void setUp() throws Exception {
        when(mInjector.getCallingUid()).thenReturn(Process.FIRST_APPLICATION_UID);
        when(mInjector.getContext()).thenReturn(mContext);
        when(mInjector.getDexOptHelper()).thenReturn(mDexOptHelper);
        when(mInjector.getDexManager()).thenReturn(mDexManager);
@@ -454,6 +456,21 @@ public final class BackgroundDexOptServiceUnitTest {
        assertThrows(SecurityException.class, () -> mService.cancelBackgroundDexoptJob());
    }

    @Test
    public void testDisableJobSchedulerJobs() {
        when(mInjector.getCallingUid()).thenReturn(Process.SHELL_UID);
        mService.setDisableJobSchedulerJobs(true);
        assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
        verify(mDexOptHelper, never()).performDexOpt(any());
        verify(mDexOptHelper, never()).performDexOptWithStatus(any());
    }

    @Test
    public void testSetDisableJobSchedulerJobsWithInvalidUid() {
        // Test uid cannot execute the command APIs
        assertThrows(SecurityException.class, () -> mService.setDisableJobSchedulerJobs(true));
    }

    private void initUntilBootCompleted() {
        ArgumentCaptor<BroadcastReceiver> argReceiver = ArgumentCaptor.forClass(
                BroadcastReceiver.class);