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

Commit 12f92eb6 authored by Santos Cordon's avatar Santos Cordon
Browse files

Add force suspend API to PowerManager.

Add a hidden system API (protected by DEVICE_POWER) that forces suspend,
ignoring any existing wakelock.  Add a shell cmd to trigger the API
to run.

Bug: 111991113
Test: 'adb shell svc power forcesuspend'
Change-Id: I5a258e1b7c8b1391fe1baf3930dd9d9af47235c9
parent b68b8a84
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5327,6 +5327,7 @@ package android.os {
  public final class PowerManager {
    method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
    method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveMode();
    method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
    method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+27 −1
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.os.SystemClock;
import android.os.SystemProperties;

public class PowerCommand extends Svc.Command {
    private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0;

    public PowerCommand() {
        super("power");
    }
@@ -42,7 +44,17 @@ public class PowerCommand extends Svc.Command {
                + "       svc power reboot [reason]\n"
                + "         Perform a runtime shutdown and reboot device with specified reason.\n"
                + "       svc power shutdown\n"
                + "         Perform a runtime shutdown and power off the device.\n";
                + "         Perform a runtime shutdown and power off the device.\n"
                + "       svc power forcesuspend [t]\n"
                + "         Force the system into suspend, ignoring all wakelocks.\n"
                + "         t - Number of milliseconds to wait before issuing force-suspend.\n"
                + "             Helps with devices that can't suspend while plugged in.\n"
                + "             Defaults to " + FORCE_SUSPEND_DELAY_DEFAULT_MILLIS + ".\n"
                + "             When using a delay, you must use the nohup shell modifier:\n"
                + "             'adb shell nohup svc power forcesuspend [time]'\n"
                + "         Use caution; this is dangerous. It puts the device to sleep\n"
                + "         immediately without giving apps or the system an opportunity to\n"
                + "         save their state.\n";
    }

    public void run(String[] args) {
@@ -101,6 +113,20 @@ public class PowerCommand extends Svc.Command {
                        maybeLogRemoteException("Failed to shutdown.");
                    }
                    return;
                } else if ("forcesuspend".equals(args[1])) {
                    int delayMillis = args.length > 2
                            ? Integer.parseInt(args[2]) : FORCE_SUSPEND_DELAY_DEFAULT_MILLIS;
                    try {
                        Thread.sleep(delayMillis);
                        if (!pm.forceSuspend()) {
                            System.err.println("Failed to force suspend.");
                        }
                    } catch (InterruptedException e) {
                        System.err.println("Failed to force suspend: " + e);
                    } catch (RemoteException e) {
                        maybeLogRemoteException("Failed to force-suspend with exception: " + e);
                    }
                    return;
                }
            }
        }
+3 −0
Original line number Diff line number Diff line
@@ -74,4 +74,7 @@ interface IPowerManager

    // controls whether PowerManager should doze after the screen turns off or not
    void setDozeAfterScreenOff(boolean on);

    // Forces the system to suspend even if there are held wakelocks.
    boolean forceSuspend();
}
+44 −1
Original line number Diff line number Diff line
@@ -362,11 +362,16 @@ public final class PowerManager {
    @SystemApi
    public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1;

    /**
     * @hide
     */
    public static final int GO_TO_SLEEP_REASON_MIN = 0;

    /**
     * Go to sleep reason code: Going to sleep due by application request.
     * @hide
     */
    public static final int GO_TO_SLEEP_REASON_APPLICATION = 0;
    public static final int GO_TO_SLEEP_REASON_APPLICATION = GO_TO_SLEEP_REASON_MIN;

    /**
     * Go to sleep reason code: Going to sleep due by request of the
@@ -411,6 +416,17 @@ public final class PowerManager {
     */
    public static final int GO_TO_SLEEP_REASON_ACCESSIBILITY = 7;

    /**
     * Go to sleep reason code: Going to sleep due to force-suspend.
     * @hide
     */
    public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8;

    /**
     * @hide
     */
    public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_FORCE_SUSPEND;

    /**
     * @hide
     */
@@ -424,6 +440,7 @@ public final class PowerManager {
            case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
            case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
            case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
            case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
            default: return Integer.toString(sleepReason);
        }
    }
@@ -1852,6 +1869,32 @@ public final class PowerManager {
        }
    }

    /**
     * Forces the device to go to suspend, even if there are currently wakelocks being held.
     * <b>Caution</b>
     * This is a very dangerous command as it puts the device to sleep immediately. Apps and parts
     * of the system will not be notified and will not have an opportunity to save state prior to
     * the device going to suspend.
     * This method should only be used in very rare circumstances where the device is intended
     * to appear as completely off to the user and they have a well understood, reliable way of
     * re-enabling it.
     * </p><p>
     * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
     * </p>
     *
     * @return true on success, false otherwise.
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
    public boolean forceSuspend() {
        try {
            return mService.forceSuspend();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
     * This broadcast is only sent to registered receivers.
+66 −35
Original line number Diff line number Diff line
@@ -538,6 +538,9 @@ public final class PowerManagerService extends SystemService
    // True if we are currently in VR Mode.
    private boolean mIsVrModeEnabled;

    // True if we in the process of performing a forceSuspend
    private boolean mForceSuspendActive;

    private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
        @Override
        public void onUserSwitching(int newUserId) throws RemoteException {}
@@ -684,6 +687,11 @@ public final class PowerManagerService extends SystemService
        public void nativeSetFeature(int featureId, int data) {
            PowerManagerService.nativeSetFeature(featureId, data);
        }

        /** Wrapper for PowerManager.nativeForceSuspend */
        public boolean nativeForceSuspend() {
            return PowerManagerService.nativeForceSuspend();
        }
    }

    @VisibleForTesting
@@ -718,6 +726,7 @@ public final class PowerManagerService extends SystemService
    private static native void nativeSetAutoSuspend(boolean enable);
    private static native void nativeSendPowerHint(int hintId, int data);
    private static native void nativeSetFeature(int featureId, int data);
    private static native boolean nativeForceSuspend();

    public PowerManagerService(Context context) {
        this(context, new Injector());
@@ -1427,7 +1436,7 @@ public final class PowerManagerService extends SystemService
        }

        if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
                || !mBootCompleted || !mSystemReady) {
                || !mBootCompleted || !mSystemReady || mForceSuspendActive) {
            return false;
        }

@@ -1463,8 +1472,13 @@ public final class PowerManagerService extends SystemService
        }
    }

    // This method is called goToSleep for historical reasons but we actually start
    // dozing before really going to sleep.
    /**
     * Puts the system in doze.
     *
     * This method is called goToSleep for historical reasons but actually attempts to DOZE,
     * and only tucks itself in to SLEEP if requested with the flag
     * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}.
     */
    @SuppressWarnings("deprecation")
    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
        if (DEBUG_SPEW) {
@@ -1481,35 +1495,10 @@ public final class PowerManagerService extends SystemService

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
        try {
            switch (reason) {
                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                    Slog.i(TAG, "Going to sleep due to device administration policy "
            reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
                    Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
            Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
                    + " (uid " + uid + ")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                    Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
                    Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
                    Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
                    Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_HDMI:
                    Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY:
                    Slog.i(TAG, "Going to sleep by an accessibility service request (uid "
                            + uid +")...");
                    break;
                default:
                    Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
                    reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
                    break;
            }

            mLastSleepTime = eventTime;
            mLastSleepReason = reason;
@@ -3063,10 +3052,10 @@ public final class PowerManagerService extends SystemService
            if (appid >= Process.FIRST_APPLICATION_UID) {
                // Cached inactive processes are never allowed to hold wake locks.
                if (mConstants.NO_CACHED_WAKE_LOCKS) {
                    disabled = !wakeLock.mUidState.mActive &&
                            wakeLock.mUidState.mProcState
                    disabled = mForceSuspendActive
                            || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState
                                    != ActivityManager.PROCESS_STATE_NONEXISTENT &&
                            wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER;
                            wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER);
                }
                if (mDeviceIdleMode) {
                    // If we are in idle mode, we will also ignore all partial wake locks that are
@@ -3241,6 +3230,34 @@ public final class PowerManagerService extends SystemService
        }
    }

    private boolean forceSuspendInternal(int uid) {
        try {
            synchronized (mLock) {
                mForceSuspendActive = true;
                // Place the system in an non-interactive state
                goToSleepInternal(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND,
                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid);

                // Disable all the partial wake locks as well
                updateWakeLockDisabledStatesLocked();
            }

            Slog.i(TAG, "Force-Suspending (uid " + uid + ")...");
            boolean success = mNativeWrapper.nativeForceSuspend();
            if (!success) {
                Slog.i(TAG, "Force-Suspending failed in native.");
            }
            return success;
        } finally {
            synchronized (mLock) {
                mForceSuspendActive = false;
                // Re-enable wake locks once again.
                updateWakeLockDisabledStatesLocked();
            }
        }
    }

    /**
     * Low-level function turn the device off immediately, without trying
     * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -4743,6 +4760,20 @@ public final class PowerManagerService extends SystemService
            }
        }

        @Override // binder call
        public boolean forceSuspend() {
            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                return forceSuspendInternal(uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override // Binder call
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Loading