Loading services/core/java/com/android/server/pm/BackgroundDexOptService.java +30 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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:"); Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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"); } Loading Loading @@ -974,6 +999,10 @@ public final class BackgroundDexOptService { mPackageManagerService = pm; } int getCallingUid() { return Binder.getCallingUid(); } Context getContext() { return mContext; } Loading services/core/java/com/android/server/pm/PackageManagerShellCommand.java +62 −23 Original line number Diff line number Diff line Loading @@ -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": Loading Loading @@ -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 { Loading Loading @@ -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."); Loading services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading
services/core/java/com/android/server/pm/BackgroundDexOptService.java +30 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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:"); Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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"); } Loading Loading @@ -974,6 +999,10 @@ public final class BackgroundDexOptService { mPackageManagerService = pm; } int getCallingUid() { return Binder.getCallingUid(); } Context getContext() { return mContext; } Loading
services/core/java/com/android/server/pm/PackageManagerShellCommand.java +62 −23 Original line number Diff line number Diff line Loading @@ -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": Loading Loading @@ -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 { Loading Loading @@ -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."); Loading
services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading