You can now long press on the power off menu to reboot to safe mode.

Change-Id: I3aa575285058cf5a3d89486d6b944f6ea13d645c
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index c03694f..d867ff9 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -64,11 +64,15 @@
     private static boolean sIsStarted = false;
     
     private static boolean mReboot;
+    private static boolean mRebootSafeMode;
     private static String mRebootReason;
 
     // Provides shutdown assurance in case the system_server is killed
     public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
 
+    // Indicates whether we are rebooting into safe mode
+    public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
+
     // static instance of this thread
     private static final ShutdownThread sInstance = new ShutdownThread();
     
@@ -92,6 +96,12 @@
      * @param confirm true if user confirmation is needed before shutting down.
      */
     public static void shutdown(final Context context, boolean confirm) {
+        mReboot = false;
+        mRebootSafeMode = false;
+        shutdownInner(context, confirm);
+    }
+
+    static void shutdownInner(final Context context, boolean confirm) {
         // ensure that only one thread is trying to power down.
         // any additional calls are just returned
         synchronized (sIsStartedGuard) {
@@ -103,16 +113,20 @@
 
         final int longPressBehavior = context.getResources().getInteger(
                         com.android.internal.R.integer.config_longPressOnPowerBehavior);
-        final int resourceId = longPressBehavior == 2
-                ? com.android.internal.R.string.shutdown_confirm_question
-                : com.android.internal.R.string.shutdown_confirm;
+        final int resourceId = mRebootSafeMode
+                ? com.android.internal.R.string.reboot_safemode_confirm
+                : (longPressBehavior == 2
+                        ? com.android.internal.R.string.shutdown_confirm_question
+                        : com.android.internal.R.string.shutdown_confirm);
 
         Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
 
         if (confirm) {
             final CloseDialogReceiver closer = new CloseDialogReceiver(context);
             final AlertDialog dialog = new AlertDialog.Builder(context)
-                    .setTitle(com.android.internal.R.string.power_off)
+                    .setTitle(mRebootSafeMode
+                            ? com.android.internal.R.string.reboot_safemode_title
+                            : com.android.internal.R.string.power_off)
                     .setMessage(resourceId)
                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
@@ -162,8 +176,23 @@
      */
     public static void reboot(final Context context, String reason, boolean confirm) {
         mReboot = true;
+        mRebootSafeMode = false;
         mRebootReason = reason;
-        shutdown(context, confirm);
+        shutdownInner(context, confirm);
+    }
+
+    /**
+     * Request a reboot into safe mode.  Must be called from a Looper thread in which its UI
+     * is shown.
+     *
+     * @param context Context used to display the shutdown progress dialog.
+     * @param confirm true if user confirmation is needed before shutting down.
+     */
+    public static void rebootSafeMode(final Context context, boolean confirm) {
+        mReboot = true;
+        mRebootSafeMode = true;
+        mRebootReason = null;
+        shutdownInner(context, confirm);
     }
 
     private static void beginShutdownSequence(Context context) {
@@ -254,6 +283,14 @@
             SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
         }
 
+        /*
+         * If we are rebooting into safe mode, write a system property
+         * indicating so.
+         */
+        if (mRebootSafeMode) {
+            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
+        }
+
         Log.i(TAG, "Sending shutdown broadcast...");
         
         // First send the high-level shut down broadcast.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 57a4b29..505c3f9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -708,6 +708,8 @@
   <java-symbol type="string" name="preposition_for_time" />
   <java-symbol type="string" name="progress_erasing" />
   <java-symbol type="string" name="progress_unmounting" />
+  <java-symbol type="string" name="reboot_safemode_confirm" />
+  <java-symbol type="string" name="reboot_safemode_title" />
   <java-symbol type="string" name="relationTypeAssistant" />
   <java-symbol type="string" name="relationTypeBrother" />
   <java-symbol type="string" name="relationTypeChild" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 149a78c..f5cea9c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -315,6 +315,15 @@
          power off dialog instead of the global actions menu. -->
     <string name="shutdown_confirm_question">Do you want to shut down?</string>
 
+    <!-- Title of dialog to confirm rebooting into safe mode. [CHAR LIMIT=50] -->
+    <string name="reboot_safemode_title">Reboot to safe mode</string>
+
+    <!-- Shutdown Confirmation Dialog.  Message in the confirmation dialog
+         when the user asks to reboot into safe mode. [CHAR LIMIT=NONE] -->
+    <string name="reboot_safemode_confirm">Do you want to reboot into safe mode?
+         This will disable all third party applications you have installed.
+         They will be restored when you reboot again.</string>
+
     <!-- Recent Tasks dialog: title
      TODO: this should move to SystemUI.apk, but the code for the old
             recent dialog is still in the framework
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index aeb518c..cd6da85 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -48,6 +48,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -181,6 +182,11 @@
                     ShutdownThread.shutdown(mContext, true);
                 }
 
+                public boolean onLongPress() {
+                    ShutdownThread.rebootSafeMode(mContext, true);
+                    return true;
+                }
+
                 public boolean showDuringKeyguard() {
                     return true;
                 }
@@ -242,6 +248,15 @@
 
         final AlertDialog dialog = ab.create();
         dialog.getListView().setItemsCanFocus(true);
+        dialog.getListView().setLongClickable(true);
+        dialog.getListView().setOnItemLongClickListener(
+                new AdapterView.OnItemLongClickListener() {
+                    @Override
+                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
+                            long id) {
+                        return mAdapter.getItem(position).onLongPress();
+                    }
+        });
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
 
         dialog.setOnDismissListener(this);
@@ -365,6 +380,8 @@
 
         void onPress();
 
+        public boolean onLongPress();
+
         /**
          * @return whether this action should appear in the dialog when the keygaurd
          *    is showing.
@@ -406,6 +423,10 @@
 
         abstract public void onPress();
 
+        public boolean onLongPress() {
+            return false;
+        }
+
         public View create(
                 Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
             View v = inflater.inflate(R.layout.global_actions_item, parent, false);
@@ -530,6 +551,10 @@
             changeStateFromPress(nowOn);
         }
 
+        public boolean onLongPress() {
+            return false;
+        }
+
         public boolean isEnabled() {
             return !mState.inTransition();
         }
@@ -599,6 +624,10 @@
         public void onPress() {
         }
 
+        public boolean onLongPress() {
+            return false;
+        }
+
         public boolean showDuringKeyguard() {
             return true;
         }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index f698fbc..36a0a67 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -34,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import com.android.internal.app.IBatteryStats;
+import com.android.internal.app.ShutdownThread;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
 import com.android.internal.view.IInputContext;
@@ -6507,6 +6508,13 @@
                 KeyEvent.KEYCODE_VOLUME_DOWN);
         mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                 || volumeDownState > 0;
+        try {
+            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
+                mSafeMode = true;
+                SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
+            }
+        } catch (IllegalArgumentException e) {
+        }
         if (mSafeMode) {
             Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                     + " dpad=" + dpadState + " trackball=" + trackballState + ")");