Loading services/core/java/com/android/server/am/ActiveServices.java +88 −20 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; Loading Loading @@ -242,6 +243,12 @@ public final class ActiveServices { @GuardedBy("mAm") private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>(); /** * The list of packages with the service restart backoff disabled. */ @GuardedBy("mAm") private final ArraySet<String> mRestartBackoffDisabledPackages = new ArraySet<>(); /** * For keeping ActiveForegroundApps retaining state while the screen is off. */ Loading Loading @@ -3298,17 +3305,19 @@ public final class ActiveServices { } } if (isServiceRestartBackoffEnabledLocked(r.packageName)) { r.nextRestartTime = now + r.restartDelay; // Make sure that we don't end up restarting a bunch of services // all at the same time. boolean repeat; final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; do { repeat = false; final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; for (int i = mRestartingServices.size() - 1; i >= 0; i--) { ServiceRecord r2 = mRestartingServices.get(i); if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-restartTimeBetween) final ServiceRecord r2 = mRestartingServices.get(i); if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween) && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) { r.nextRestartTime = r2.nextRestartTime + restartTimeBetween; r.restartDelay = r.nextRestartTime - now; Loading @@ -3317,6 +3326,11 @@ public final class ActiveServices { } } } while (repeat); } else { // It's been forced to ignore the restart backoff, fix the delay here. r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION; r.nextRestartTime = now + r.restartDelay; } } else { // Persistent processes are immediately restarted, so there is no Loading @@ -3336,15 +3350,22 @@ public final class ActiveServices { cancelForegroundNotificationLocked(r); performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis()); return true; } @VisibleForTesting @GuardedBy("mAm") void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling, @NonNull String reason, @UptimeMillisLong long now) { mAm.mHandler.removeCallbacks(r.restarter); mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Slog.w(TAG, "Scheduling restart of crashed service " r.nextRestartTime = now + r.restartDelay; Slog.w(TAG, scheduling + " restart of crashed service " + r.shortInstanceName + " in " + r.restartDelay + "ms for " + reason); EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, r.userId, r.shortInstanceName, r.restartDelay); return true; } final void performServiceRestartLocked(ServiceRecord r) { Loading Loading @@ -3409,6 +3430,52 @@ public final class ActiveServices { } } /** * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}. */ @GuardedBy("mAm") void setServiceRestartBackoffEnabledLocked(@NonNull String packageName, boolean enable, @NonNull String reason) { if (!enable) { if (mRestartBackoffDisabledPackages.contains(packageName)) { // Already disabled, do nothing. return; } mRestartBackoffDisabledPackages.add(packageName); final long now = SystemClock.uptimeMillis(); for (int i = 0, size = mRestartingServices.size(); i < size; i++) { final ServiceRecord r = mRestartingServices.get(i); if (TextUtils.equals(r.packageName, packageName)) { final long remaining = r.nextRestartTime - now; if (remaining > mAm.mConstants.SERVICE_RESTART_DURATION) { r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION; r.nextRestartTime = now + r.restartDelay; performScheduleRestartLocked(r, "Rescheduling", reason, now); } } } } else { removeServiceRestartBackoffEnabledLocked(packageName); // For the simplicity, we are not going to reschedule its pending restarts // when we turn the backoff policy back on. } } @GuardedBy("mAm") private void removeServiceRestartBackoffEnabledLocked(@NonNull String packageName) { mRestartBackoffDisabledPackages.remove(packageName); } /** * @return {@code false} if the given package has been disable from enforcing the service * restart backoff policy, used by {@link ActivityManagerShellCommand}. */ @GuardedBy("mAm") boolean isServiceRestartBackoffEnabledLocked(@NonNull String packageName) { return !mRestartBackoffDisabledPackages.contains(packageName); } private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, boolean enqueueOomAdj) Loading Loading @@ -4457,6 +4524,7 @@ public final class ActiveServices { mPendingBringups.removeAt(i); } } removeServiceRestartBackoffEnabledLocked(packageName); } void cleanUpServices(int userId, ComponentName component, Intent baseIntent) { Loading services/core/java/com/android/server/am/ActivityManagerService.java +20 −0 Original line number Diff line number Diff line Loading @@ -8395,6 +8395,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}. */ void setServiceRestartBackoffEnabled(@NonNull String packageName, boolean enable, @NonNull String reason) { synchronized (this) { mServices.setServiceRestartBackoffEnabledLocked(packageName, enable, reason); } } /** * @return {@code false} if the given package has been disable from enforcing the service * restart backoff policy, used by {@link ActivityManagerShellCommand}. */ boolean isServiceRestartBackoffEnabled(@NonNull String packageName) { synchronized (this) { return mServices.isServiceRestartBackoffEnabledLocked(packageName); } } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading services/core/java/com/android/server/am/ActivityManagerShellCommand.java +29 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runRefreshSettingsCache(); case "memory-factor": return runMemoryFactor(pw); case "service-restart-backoff": return runServiceRestartBackoff(pw); default: return handleDefaultCommands(cmd); } Loading Loading @@ -3095,6 +3097,28 @@ final class ActivityManagerShellCommand extends ShellCommand { } } private int runServiceRestartBackoff(PrintWriter pw) throws RemoteException { mInternal.enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "runServiceRestartBackoff()"); final String opt = getNextArgRequired(); switch (opt) { case "enable": mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), true, "shell"); return 0; case "disable": mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), false, "shell"); return 0; case "show": pw.println(mInternal.isServiceRestartBackoffEnabled(getNextArgRequired()) ? "enabled" : "disabled"); return 0; default: getErrPrintWriter().println("Error: unknown command '" + opt + "'"); return -1; } } private Resources getResources(PrintWriter pw) throws RemoteException { // system resources does not contain all the device configuration, construct it manually. Configuration config = mInterface.getConfiguration(); Loading Loading @@ -3418,6 +3442,11 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Shows the existing memory pressure factor"); pw.println(" reset"); pw.println(" Removes existing override for memory pressure factor"); pw.println(" service-restart-backoff <COMMAND> [...]: sub-commands to toggle service restart backoff policy."); pw.println(" enable|disable <PACKAGE_NAME>"); pw.println(" Toggles the restart backoff policy on/off for <PACKAGE_NAME>."); pw.println(" show <PACKAGE_NAME>"); pw.println(" Shows the restart backoff policy state for <PACKAGE_NAME>."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } Loading services/tests/servicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/> <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/> <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/> <uses-permission android:name="android.permission.KILL_UID"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" Loading services/tests/servicestests/AndroidTest.xml +3 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ <option name="test-file-name" value="JobTestApp.apk" /> <option name="test-file-name" value="ConnTestApp.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp1.apk" /> <option name="test-file-name" value="SimpleServiceTestApp2.apk" /> <option name="test-file-name" value="SimpleServiceTestApp3.apk" /> </target_preparer> <option name="test-tag" value="FrameworksServicesTests" /> Loading Loading
services/core/java/com/android/server/am/ActiveServices.java +88 −20 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; Loading Loading @@ -242,6 +243,12 @@ public final class ActiveServices { @GuardedBy("mAm") private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>(); /** * The list of packages with the service restart backoff disabled. */ @GuardedBy("mAm") private final ArraySet<String> mRestartBackoffDisabledPackages = new ArraySet<>(); /** * For keeping ActiveForegroundApps retaining state while the screen is off. */ Loading Loading @@ -3298,17 +3305,19 @@ public final class ActiveServices { } } if (isServiceRestartBackoffEnabledLocked(r.packageName)) { r.nextRestartTime = now + r.restartDelay; // Make sure that we don't end up restarting a bunch of services // all at the same time. boolean repeat; final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; do { repeat = false; final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; for (int i = mRestartingServices.size() - 1; i >= 0; i--) { ServiceRecord r2 = mRestartingServices.get(i); if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-restartTimeBetween) final ServiceRecord r2 = mRestartingServices.get(i); if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween) && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) { r.nextRestartTime = r2.nextRestartTime + restartTimeBetween; r.restartDelay = r.nextRestartTime - now; Loading @@ -3317,6 +3326,11 @@ public final class ActiveServices { } } } while (repeat); } else { // It's been forced to ignore the restart backoff, fix the delay here. r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION; r.nextRestartTime = now + r.restartDelay; } } else { // Persistent processes are immediately restarted, so there is no Loading @@ -3336,15 +3350,22 @@ public final class ActiveServices { cancelForegroundNotificationLocked(r); performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis()); return true; } @VisibleForTesting @GuardedBy("mAm") void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling, @NonNull String reason, @UptimeMillisLong long now) { mAm.mHandler.removeCallbacks(r.restarter); mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Slog.w(TAG, "Scheduling restart of crashed service " r.nextRestartTime = now + r.restartDelay; Slog.w(TAG, scheduling + " restart of crashed service " + r.shortInstanceName + " in " + r.restartDelay + "ms for " + reason); EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, r.userId, r.shortInstanceName, r.restartDelay); return true; } final void performServiceRestartLocked(ServiceRecord r) { Loading Loading @@ -3409,6 +3430,52 @@ public final class ActiveServices { } } /** * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}. */ @GuardedBy("mAm") void setServiceRestartBackoffEnabledLocked(@NonNull String packageName, boolean enable, @NonNull String reason) { if (!enable) { if (mRestartBackoffDisabledPackages.contains(packageName)) { // Already disabled, do nothing. return; } mRestartBackoffDisabledPackages.add(packageName); final long now = SystemClock.uptimeMillis(); for (int i = 0, size = mRestartingServices.size(); i < size; i++) { final ServiceRecord r = mRestartingServices.get(i); if (TextUtils.equals(r.packageName, packageName)) { final long remaining = r.nextRestartTime - now; if (remaining > mAm.mConstants.SERVICE_RESTART_DURATION) { r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION; r.nextRestartTime = now + r.restartDelay; performScheduleRestartLocked(r, "Rescheduling", reason, now); } } } } else { removeServiceRestartBackoffEnabledLocked(packageName); // For the simplicity, we are not going to reschedule its pending restarts // when we turn the backoff policy back on. } } @GuardedBy("mAm") private void removeServiceRestartBackoffEnabledLocked(@NonNull String packageName) { mRestartBackoffDisabledPackages.remove(packageName); } /** * @return {@code false} if the given package has been disable from enforcing the service * restart backoff policy, used by {@link ActivityManagerShellCommand}. */ @GuardedBy("mAm") boolean isServiceRestartBackoffEnabledLocked(@NonNull String packageName) { return !mRestartBackoffDisabledPackages.contains(packageName); } private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, boolean enqueueOomAdj) Loading Loading @@ -4457,6 +4524,7 @@ public final class ActiveServices { mPendingBringups.removeAt(i); } } removeServiceRestartBackoffEnabledLocked(packageName); } void cleanUpServices(int userId, ComponentName component, Intent baseIntent) { Loading
services/core/java/com/android/server/am/ActivityManagerService.java +20 −0 Original line number Diff line number Diff line Loading @@ -8395,6 +8395,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * Toggle service restart backoff policy, used by {@link ActivityManagerShellCommand}. */ void setServiceRestartBackoffEnabled(@NonNull String packageName, boolean enable, @NonNull String reason) { synchronized (this) { mServices.setServiceRestartBackoffEnabledLocked(packageName, enable, reason); } } /** * @return {@code false} if the given package has been disable from enforcing the service * restart backoff policy, used by {@link ActivityManagerShellCommand}. */ boolean isServiceRestartBackoffEnabled(@NonNull String packageName) { synchronized (this) { return mServices.isServiceRestartBackoffEnabledLocked(packageName); } } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading
services/core/java/com/android/server/am/ActivityManagerShellCommand.java +29 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runRefreshSettingsCache(); case "memory-factor": return runMemoryFactor(pw); case "service-restart-backoff": return runServiceRestartBackoff(pw); default: return handleDefaultCommands(cmd); } Loading Loading @@ -3095,6 +3097,28 @@ final class ActivityManagerShellCommand extends ShellCommand { } } private int runServiceRestartBackoff(PrintWriter pw) throws RemoteException { mInternal.enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "runServiceRestartBackoff()"); final String opt = getNextArgRequired(); switch (opt) { case "enable": mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), true, "shell"); return 0; case "disable": mInternal.setServiceRestartBackoffEnabled(getNextArgRequired(), false, "shell"); return 0; case "show": pw.println(mInternal.isServiceRestartBackoffEnabled(getNextArgRequired()) ? "enabled" : "disabled"); return 0; default: getErrPrintWriter().println("Error: unknown command '" + opt + "'"); return -1; } } private Resources getResources(PrintWriter pw) throws RemoteException { // system resources does not contain all the device configuration, construct it manually. Configuration config = mInterface.getConfiguration(); Loading Loading @@ -3418,6 +3442,11 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Shows the existing memory pressure factor"); pw.println(" reset"); pw.println(" Removes existing override for memory pressure factor"); pw.println(" service-restart-backoff <COMMAND> [...]: sub-commands to toggle service restart backoff policy."); pw.println(" enable|disable <PACKAGE_NAME>"); pw.println(" Toggles the restart backoff policy on/off for <PACKAGE_NAME>."); pw.println(" show <PACKAGE_NAME>"); pw.println(" Shows the restart backoff policy state for <PACKAGE_NAME>."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } Loading
services/tests/servicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/> <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/> <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/> <uses-permission android:name="android.permission.KILL_UID"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" Loading
services/tests/servicestests/AndroidTest.xml +3 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ <option name="test-file-name" value="JobTestApp.apk" /> <option name="test-file-name" value="ConnTestApp.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp1.apk" /> <option name="test-file-name" value="SimpleServiceTestApp2.apk" /> <option name="test-file-name" value="SimpleServiceTestApp3.apk" /> </target_preparer> <option name="test-tag" value="FrameworksServicesTests" /> Loading