Distinguish user-requested shutdown from power-related ones

With this patch, when the user requested shutdown,
PowerManagerService sets sys.powerctl is set to
"shutdown,userrequested", and init runs fsck on shutdown.

When shutdown is triggered due to a low power state etc.,
the service sets the property to "shutdown,", and init
immediately shuts down the system without running the
command.

This is a follow-up CL for http://r.android.com/158525.

Bug: 21853106
Change-Id: Iae72990130fe9aa479c802f77301438190dbbfb3
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index da8586c..e11d278 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -93,7 +93,7 @@
                 } else if ("shutdown".equals(args[1])) {
                     try {
                         // no confirm, wait till device is off
-                        pm.shutdown(false, true);
+                        pm.shutdown(false, null, true);
                     } catch (RemoteException e) {
                         System.err.println("Failed to shutdown.");
                     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2fe727c..cafccbf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2018,7 +2018,9 @@
     /**
      * Activity Action:  Start this activity to request system shutdown.
      * The optional boolean extra field {@link #EXTRA_KEY_CONFIRM} can be set to true
-     * to request confirmation from the user before shutting down.
+     * to request confirmation from the user before shutting down. The optional boolean
+     * extra field {@link #EXTRA_USER_REQUESTED_SHUTDOWN} can be set to true to
+     * indicate that the shutdown is requested by the user.
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
@@ -3229,6 +3231,15 @@
     public static final String EXTRA_KEY_CONFIRM = "android.intent.extra.KEY_CONFIRM";
 
     /**
+     * Set to true in {@link #ACTION_REQUEST_SHUTDOWN} to indicate that the shutdown is
+     * requested by the user.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_USER_REQUESTED_SHUTDOWN =
+            "android.intent.extra.USER_REQUESTED_SHUTDOWN";
+
+    /**
      * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
      * {@link android.content.Intent#ACTION_PACKAGE_CHANGED} intents to override the default action
      * of restarting the application.
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 16dac7d..4d6241b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -45,7 +45,7 @@
     boolean setPowerSaveMode(boolean mode);
 
     void reboot(boolean confirm, String reason, boolean wait);
-    void shutdown(boolean confirm, boolean wait);
+    void shutdown(boolean confirm, String reason, boolean wait);
     void crash(String message);
 
     void setStayOnSetting(int val);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d52dd30..4b284ce 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -367,7 +367,13 @@
      * @hide
      */
     public static final String REBOOT_RECOVERY = "recovery";
-    
+
+    /**
+     * The value to pass as the 'reason' argument to android_reboot().
+     * @hide
+     */
+    public static final String SHUTDOWN_USER_REQUESTED = "userrequested";
+
     final Context mContext;
     final IPowerManager mService;
     final Handler mHandler;
@@ -838,13 +844,14 @@
      * Turn off the device.
      *
      * @param confirm If true, shows a shutdown confirmation dialog.
+     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
      * @param wait If true, this call waits for the shutdown to complete and does not return.
      *
      * @hide
      */
-    public void shutdown(boolean confirm, boolean wait) {
+    public void shutdown(boolean confirm, String reason, boolean wait) {
         try {
-            mService.shutdown(confirm, wait);
+            mService.shutdown(confirm, reason, wait);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/com/android/internal/app/ShutdownActivity.java b/core/java/com/android/internal/app/ShutdownActivity.java
index 97521cf..745d28f 100644
--- a/core/java/com/android/internal/app/ShutdownActivity.java
+++ b/core/java/com/android/internal/app/ShutdownActivity.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.IPowerManager;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Slog;
@@ -30,6 +31,7 @@
     private static final String TAG = "ShutdownActivity";
     private boolean mReboot;
     private boolean mConfirm;
+    private boolean mUserRequested;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -38,6 +40,7 @@
         Intent intent = getIntent();
         mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
         mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+        mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false);
         Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
 
         Thread thr = new Thread("ShutdownActivity") {
@@ -49,7 +52,9 @@
                     if (mReboot) {
                         pm.reboot(mConfirm, null, false);
                     } else {
-                        pm.shutdown(mConfirm, false);
+                        pm.shutdown(mConfirm,
+                                    mUserRequested ? PowerManager.SHUTDOWN_USER_REQUESTED : null,
+                                    false);
                     }
                 } catch (RemoteException e) {
                 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 32c0fb8..01e36e1 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2180,7 +2180,7 @@
             public void run() {
                 synchronized (this) {
                     if (shutdown) {
-                        ShutdownThread.shutdown(mContext, confirm);
+                        ShutdownThread.shutdown(mContext, reason, confirm);
                     } else {
                         ShutdownThread.reboot(mContext, reason, confirm);
                     }
@@ -2349,9 +2349,14 @@
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
+     *
+     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
      */
-    public static void lowLevelShutdown() {
-        SystemProperties.set("sys.powerctl", "shutdown");
+    public static void lowLevelShutdown(String reason) {
+        if (reason == null) {
+            reason = "";
+        }
+        SystemProperties.set("sys.powerctl", "shutdown," + reason);
     }
 
     /**
@@ -3056,12 +3061,12 @@
          * @param wait If true, this call waits for the shutdown to complete and does not return.
          */
         @Override // Binder call
-        public void shutdown(boolean confirm, boolean wait) {
+        public void shutdown(boolean confirm, String reason, boolean wait) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                shutdownOrRebootInternal(true, confirm, null, wait);
+                shutdownOrRebootInternal(true, confirm, reason, wait);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 4c14c47..8f041a5 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -87,7 +87,7 @@
     private static boolean mReboot;
     private static boolean mRebootSafeMode;
     private static boolean mRebootUpdate;
-    private static String mRebootReason;
+    private static String mReason;
 
     // Provides shutdown assurance in case the system_server is killed
     public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
@@ -123,11 +123,13 @@
      * is shown.
      *
      * @param context Context used to display the shutdown progress dialog.
+     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
      * @param confirm true if user confirmation is needed before shutting down.
      */
-    public static void shutdown(final Context context, boolean confirm) {
+    public static void shutdown(final Context context, String reason, boolean confirm) {
         mReboot = false;
         mRebootSafeMode = false;
+        mReason = reason;
         shutdownInner(context, confirm);
     }
 
@@ -211,7 +213,7 @@
         mReboot = true;
         mRebootSafeMode = false;
         mRebootUpdate = false;
-        mRebootReason = reason;
+        mReason = reason;
         shutdownInner(context, confirm);
     }
 
@@ -226,7 +228,7 @@
         mReboot = true;
         mRebootSafeMode = true;
         mRebootUpdate = false;
-        mRebootReason = null;
+        mReason = null;
         shutdownInner(context, confirm);
     }
 
@@ -243,18 +245,18 @@
         ProgressDialog pd = new ProgressDialog(context);
 
         // Path 1: Reboot to recovery and install the update
-        //   Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
+        //   Condition: mReason == REBOOT_RECOVERY and mRebootUpdate == True
         //   (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
         //   UI: progress bar
         //
         // Path 2: Reboot to recovery for factory reset
-        //   Condition: mRebootReason == REBOOT_RECOVERY
+        //   Condition: mReason == REBOOT_RECOVERY
         //   UI: spinning circle only (no progress bar)
         //
         // Path 3: Regular reboot / shutdown
         //   Condition: Otherwise
         //   UI: spinning circle only (no progress bar)
-        if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
+        if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
             mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
             if (mRebootUpdate) {
                 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
@@ -343,7 +345,7 @@
          * the beginning of the SystemServer startup.
          */
         {
-            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
+            String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
             SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
         }
 
@@ -467,7 +469,7 @@
             uncrypt();
         }
 
-        rebootOrShutdown(mContext, mReboot, mRebootReason);
+        rebootOrShutdown(mContext, mReboot, mReason);
     }
 
     private void setRebootProgress(final int progress, final CharSequence message) {
@@ -610,13 +612,14 @@
      *
      * @param context Context used to vibrate or null without vibration
      * @param reboot true to reboot or false to shutdown
-     * @param reason reason for reboot
+     * @param reason reason for reboot/shutdown
      */
     public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
         if (reboot) {
             Log.i(TAG, "Rebooting, reason: " + reason);
             PowerManagerService.lowLevelReboot(reason);
             Log.e(TAG, "Reboot failed, will attempt shutdown instead");
+            reason = null;
         } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
             // vibrate before shutting down
             Vibrator vibrator = new SystemVibrator(context);
@@ -636,7 +639,7 @@
 
         // Shutdown power
         Log.i(TAG, "Performing low-level shutdown...");
-        PowerManagerService.lowLevelShutdown();
+        PowerManagerService.lowLevelShutdown(reason);
     }
 
     private void uncrypt() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 001a6f8..85b2a41 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5679,7 +5679,7 @@
     // Called by window manager policy.  Not exposed externally.
     @Override
     public void shutdown(boolean confirm) {
-        ShutdownThread.shutdown(mContext, confirm);
+        ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
     }
 
     // Called by window manager policy.  Not exposed externally.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 39ebdfc..46aa117 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -86,7 +86,7 @@
     }
 
     @Override
-    public void shutdown(boolean confirm, boolean wait) {
+    public void shutdown(boolean confirm, String reason, boolean wait) {
         // pass for now.
     }