Move power HAL interactions to PowerManagerService.

This refactoring sets the stage for a follow-on change that
will make use additional functions of the power HAL.

Moved functionality from android.os.Power into PowerManagerService.
None of these functions make sense being called outside of the
system server.  Moving them to the PowerManagerService makes it
easier to ensure that the power HAL is initialized exactly once.

Similarly, moved ShutdownThread out of the policy package and into
the services package where it can tie into the PowerManagerService
as needed.

Bug: 6435382
Change-Id: I958241bb124fb4410d96f5d5eb00ed68d60b29e5
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 289ab2a..2cc2704 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -17,8 +17,8 @@
 package com.android.server;
 
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.app.ShutdownThread;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.pm.ShutdownThread;
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
@@ -47,7 +47,6 @@
 import android.os.IPowerManager;
 import android.os.LocalPowerManager;
 import android.os.Message;
-import android.os.Power;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -71,6 +70,7 @@
 import static android.provider.Settings.System.TRANSITION_ANIMATION_SCALE;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -84,6 +84,12 @@
     private static final String TAG = "PowerManagerService";
     static final String PARTIAL_NAME = "PowerManagerService";
 
+    // Wake lock that ensures that the CPU is running.  The screen might not be on.
+    private static final int PARTIAL_WAKE_LOCK_ID = 1;
+
+    // Wake lock that ensures that the screen is on.
+    private static final int FULL_WAKE_LOCK_ID = 2;
+
     static final boolean DEBUG_SCREEN_ON = false;
 
     private static final boolean LOG_PARTIAL_WL = false;
@@ -134,6 +140,10 @@
     // Screen brightness should always have a value, but just in case...
     private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
 
+    // Threshold for BRIGHTNESS_LOW_BATTERY (percentage)
+    // Screen will stay dim if battery level is <= LOW_BATTERY_THRESHOLD
+    private static final int LOW_BATTERY_THRESHOLD = 10;
+
     // flags for setPowerState
     private static final int ALL_LIGHTS_OFF         = 0x00000000;
     private static final int SCREEN_ON_BIT          = 0x00000001;
@@ -175,8 +185,8 @@
     // we should read them from the driver, but our current hardware returns 0
     // for the initial value.  Oops!
     static final int INITIAL_SCREEN_BRIGHTNESS = 255;
-    static final int INITIAL_BUTTON_BRIGHTNESS = Power.BRIGHTNESS_OFF;
-    static final int INITIAL_KEYBOARD_BRIGHTNESS = Power.BRIGHTNESS_OFF;
+    static final int INITIAL_BUTTON_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
+    static final int INITIAL_KEYBOARD_BRIGHTNESS = PowerManager.BRIGHTNESS_OFF;
 
     private final int MY_UID;
     private final int MY_PID;
@@ -296,6 +306,11 @@
     private native void nativeInit();
     private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
     private native void nativeStartSurfaceFlingerAnimation(int mode);
+    private static native void nativeAcquireWakeLock(int lock, String id);
+    private static native void nativeReleaseWakeLock(String id);
+    private static native int nativeSetScreenState(boolean on);
+    private static native void nativeShutdown();
+    private static native void nativeReboot(String reason) throws IOException;
 
     /*
     static PrintStream mLog;
@@ -515,14 +530,13 @@
         MY_PID = Process.myPid();
         Binder.restoreCallingIdentity(token);
 
-        // XXX remove this when the kernel doesn't timeout wake locks
-        Power.setLastUserActivityTimeout(7*24*3600*1000); // one week
-
         // assume nothing is on yet
         mUserState = mPowerState = 0;
 
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
+
+        nativeInit();
     }
 
     private ContentQueryMap mSettings;
@@ -541,11 +555,6 @@
         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
         mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));
 
-        nativeInit();
-        synchronized (mLocks) {
-            updateNativePowerStateLocked();
-        }
-
         mInitComplete = false;
         mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread",
                 Process.THREAD_PRIORITY_DISPLAY);
@@ -581,8 +590,6 @@
             }
         }
 
-        nativeInit();
-        Power.powerInitNative();
         synchronized (mLocks) {
             updateNativePowerStateLocked();
             // We make sure to start out with the screen on due to user activity.
@@ -686,6 +693,26 @@
         }
     }
 
+    /**
+     * Low-level function turn the device off immediately, without trying
+     * to be clean.  Most people should use
+     * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.
+     */
+    public static void lowLevelShutdown() {
+        nativeShutdown();
+    }
+
+    /**
+     * Low-level function to reboot the device.
+     *
+     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
+     * @throws IOException if reboot fails for some reason (eg, lack of
+     *         permission)
+     */
+    public static void lowLevelReboot(String reason) throws IOException {
+        nativeReboot(reason);
+    }
+
     private class WakeLock implements IBinder.DeathRecipient
     {
         WakeLock(int f, IBinder b, String t, int u, int p) {
@@ -926,7 +953,7 @@
                     if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 1, tag);
                 }
             }
-            Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
+            nativeAcquireWakeLock(PARTIAL_WAKE_LOCK_ID, PARTIAL_NAME);
         }
 
         if (diffsource) {
@@ -1010,7 +1037,7 @@
             mPartialCount--;
             if (mPartialCount == 0) {
                 if (LOG_PARTIAL_WL) EventLog.writeEvent(EventLogTags.POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
-                Power.releaseWakeLock(PARTIAL_NAME);
+                nativeReleaseWakeLock(PARTIAL_NAME);
             }
         }
         // Unlink the lock from the binder.
@@ -1719,10 +1746,10 @@
                             + " mSkippedScreenOn=" + mSkippedScreenOn);
                 }
                 mScreenBrightnessHandler.removeMessages(ScreenBrightnessAnimator.ANIMATE_LIGHTS);
-                mScreenBrightnessAnimator.animateTo(Power.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0);
+                mScreenBrightnessAnimator.animateTo(PowerManager.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0);
             }
         }
-        int err = Power.setScreenState(on);
+        int err = nativeSetScreenState(on);
         if (err == 0) {
             mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
             if (mUseSoftwareAutoBrightness) {
@@ -1934,7 +1961,7 @@
 
     private boolean batteryIsLow() {
         return (!mIsPowered &&
-                mBatteryService.getBatteryLevel() <= Power.LOW_BATTERY_THRESHOLD);
+                mBatteryService.getBatteryLevel() <= LOW_BATTERY_THRESHOLD);
     }
 
     private boolean shouldDeferScreenOnLocked() {
@@ -2024,7 +2051,7 @@
                         nominalCurrentValue = mScreenBrightnessDim;
                         break;
                     case 0:
-                        nominalCurrentValue = Power.BRIGHTNESS_OFF;
+                        nominalCurrentValue = PowerManager.BRIGHTNESS_OFF;
                         break;
                     case SCREEN_BRIGHT_BIT:
                     default:
@@ -2050,7 +2077,7 @@
                         // was dim
                         steps = (int)(ANIM_STEPS*ratio*scale);
                     }
-                    brightness = Power.BRIGHTNESS_OFF;
+                    brightness = PowerManager.BRIGHTNESS_OFF;
                 } else {
                     if ((oldState & SCREEN_ON_BIT) != 0) {
                         // was bright
@@ -2101,13 +2128,13 @@
 
         if (offMask != 0) {
             if (mSpew) Slog.i(TAG, "Setting brightess off: " + offMask);
-            setLightBrightness(offMask, Power.BRIGHTNESS_OFF);
+            setLightBrightness(offMask, PowerManager.BRIGHTNESS_OFF);
         }
         if (dimMask != 0) {
             int brightness = mScreenBrightnessDim;
             if ((newState & BATTERY_LOW_BIT) != 0 &&
-                    brightness > Power.BRIGHTNESS_LOW_BATTERY) {
-                brightness = Power.BRIGHTNESS_LOW_BATTERY;
+                    brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
+                brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
             }
             if (mSpew) Slog.i(TAG, "Setting brightess dim " + brightness + ": " + dimMask);
             setLightBrightness(dimMask, brightness);
@@ -2115,8 +2142,8 @@
         if (onMask != 0) {
             int brightness = getPreferredBrightness();
             if ((newState & BATTERY_LOW_BIT) != 0 &&
-                    brightness > Power.BRIGHTNESS_LOW_BATTERY) {
-                brightness = Power.BRIGHTNESS_LOW_BATTERY;
+                    brightness > PowerManager.BRIGHTNESS_LOW_BATTERY) {
+                brightness = PowerManager.BRIGHTNESS_LOW_BATTERY;
             }
             if (mSpew) Slog.i(TAG, "Setting brightess on " + brightness + ": " + onMask);
             setLightBrightness(onMask, brightness);
@@ -2198,8 +2225,8 @@
                     if (elapsed < duration) {
                         int delta = endValue - startValue;
                         newValue = startValue + delta * elapsed / duration;
-                        newValue = Math.max(Power.BRIGHTNESS_OFF, newValue);
-                        newValue = Math.min(Power.BRIGHTNESS_ON, newValue);
+                        newValue = Math.max(PowerManager.BRIGHTNESS_OFF, newValue);
+                        newValue = Math.min(PowerManager.BRIGHTNESS_ON, newValue);
                     } else {
                         newValue = endValue;
                         mInitialAnimation = false;
@@ -2249,7 +2276,7 @@
 
                 if (target != currentValue) {
                     final boolean doScreenAnim = (mask & (SCREEN_BRIGHT_BIT | SCREEN_ON_BIT)) != 0;
-                    final boolean turningOff = endValue == Power.BRIGHTNESS_OFF;
+                    final boolean turningOff = endValue == PowerManager.BRIGHTNESS_OFF;
                     if (turningOff && doScreenAnim) {
                         // Cancel all pending animations since we're turning off
                         mScreenBrightnessHandler.removeCallbacksAndMessages(null);
@@ -2353,7 +2380,7 @@
 
     private boolean isScreenTurningOffLocked() {
         return (mScreenBrightnessAnimator.isAnimating()
-                && mScreenBrightnessAnimator.endValue == Power.BRIGHTNESS_OFF);
+                && mScreenBrightnessAnimator.endValue == PowerManager.BRIGHTNESS_OFF);
     }
 
     private boolean shouldLog(long time) {
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
index c9d4d01..d85abe6 100644
--- a/services/java/com/android/server/ShutdownActivity.java
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -22,7 +22,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Slog;
-import com.android.internal.app.ShutdownThread;
+
+import com.android.server.pm.ShutdownThread;
 
 public class ShutdownActivity extends Activity {
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 849281d..d9833ab 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -46,7 +46,6 @@
 import android.util.Slog;
 import android.view.WindowManager;
 
-import com.android.internal.app.ShutdownThread;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.SamplingProfilerIntegration;
 import com.android.internal.widget.LockSettingsService;
@@ -56,6 +55,7 @@
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.ShutdownThread;
 import com.android.server.usb.UsbService;
 import com.android.server.wm.WindowManagerService;
 
diff --git a/services/java/com/android/server/pm/ShutdownThread.java b/services/java/com/android/server/pm/ShutdownThread.java
new file mode 100644
index 0000000..1d6e068
--- /dev/null
+++ b/services/java/com/android/server/pm/ShutdownThread.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ 
+package com.android.server.pm;
+
+import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.IActivityManager;
+import android.app.ProgressDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.IBluetooth;
+import android.nfc.NfcAdapter;
+import android.nfc.INfcAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Vibrator;
+import android.os.SystemVibrator;
+import android.os.storage.IMountService;
+import android.os.storage.IMountShutdownObserver;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.server.PowerManagerService;
+
+import android.util.Log;
+import android.view.WindowManager;
+
+public final class ShutdownThread extends Thread {
+    // constants
+    private static final String TAG = "ShutdownThread";
+    private static final int MAX_NUM_PHONE_STATE_READS = 24;
+    private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
+    // maximum time we wait for the shutdown broadcast before going on.
+    private static final int MAX_BROADCAST_TIME = 10*1000;
+    private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
+
+    // length of vibration before shutting down
+    private static final int SHUTDOWN_VIBRATE_MS = 500;
+    
+    // state tracking
+    private static Object sIsStartedGuard = new Object();
+    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();
+    
+    private final Object mActionDoneSync = new Object();
+    private boolean mActionDone;
+    private Context mContext;
+    private PowerManager mPowerManager;
+    private PowerManager.WakeLock mCpuWakeLock;
+    private PowerManager.WakeLock mScreenWakeLock;
+    private Handler mHandler;
+    
+    private ShutdownThread() {
+    }
+ 
+    /**
+     * Request a clean shutdown, waiting for subsystems to clean up their
+     * state etc.  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 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) {
+            if (sIsStarted) {
+                Log.d(TAG, "Request to shutdown already running, returning.");
+                return;
+            }
+        }
+
+        final int longPressBehavior = context.getResources().getInteger(
+                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
+        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(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) {
+                            beginShutdownSequence(context);
+                        }
+                    })
+                    .setNegativeButton(com.android.internal.R.string.no, null)
+                    .create();
+            closer.dialog = dialog;
+            dialog.setOnDismissListener(closer);
+            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            dialog.show();
+        } else {
+            beginShutdownSequence(context);
+        }
+    }
+
+    private static class CloseDialogReceiver extends BroadcastReceiver
+            implements DialogInterface.OnDismissListener {
+        private Context mContext;
+        public Dialog dialog;
+
+        CloseDialogReceiver(Context context) {
+            mContext = context;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+            context.registerReceiver(this, filter);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            dialog.cancel();
+        }
+
+        public void onDismiss(DialogInterface unused) {
+            mContext.unregisterReceiver(this);
+        }
+    }
+
+    /**
+     * Request a clean shutdown, waiting for subsystems to clean up their
+     * state etc.  Must be called from a Looper thread in which its UI
+     * is shown.
+     *
+     * @param context Context used to display the shutdown progress dialog.
+     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
+     * @param confirm true if user confirmation is needed before shutting down.
+     */
+    public static void reboot(final Context context, String reason, boolean confirm) {
+        mReboot = true;
+        mRebootSafeMode = false;
+        mRebootReason = reason;
+        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) {
+        synchronized (sIsStartedGuard) {
+            if (sIsStarted) {
+                Log.d(TAG, "Shutdown sequence already running, returning.");
+                return;
+            }
+            sIsStarted = true;
+        }
+
+        // throw up an indeterminate system dialog to indicate radio is
+        // shutting down.
+        ProgressDialog pd = new ProgressDialog(context);
+        pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+        pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+        pd.setIndeterminate(true);
+        pd.setCancelable(false);
+        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+
+        pd.show();
+
+        sInstance.mContext = context;
+        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+
+        // make sure we never fall asleep again
+        sInstance.mCpuWakeLock = null;
+        try {
+            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
+                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
+            sInstance.mCpuWakeLock.setReferenceCounted(false);
+            sInstance.mCpuWakeLock.acquire();
+        } catch (SecurityException e) {
+            Log.w(TAG, "No permission to acquire wake lock", e);
+            sInstance.mCpuWakeLock = null;
+        }
+
+        // also make sure the screen stays on for better user experience
+        sInstance.mScreenWakeLock = null;
+        if (sInstance.mPowerManager.isScreenOn()) {
+            try {
+                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
+                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
+                sInstance.mScreenWakeLock.setReferenceCounted(false);
+                sInstance.mScreenWakeLock.acquire();
+            } catch (SecurityException e) {
+                Log.w(TAG, "No permission to acquire wake lock", e);
+                sInstance.mScreenWakeLock = null;
+            }
+        }
+
+        // start the thread that initiates shutdown
+        sInstance.mHandler = new Handler() {
+        };
+        sInstance.start();
+    }
+
+    void actionDone() {
+        synchronized (mActionDoneSync) {
+            mActionDone = true;
+            mActionDoneSync.notifyAll();
+        }
+    }
+
+    /**
+     * Makes sure we handle the shutdown gracefully.
+     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
+     */
+    public void run() {
+        boolean nfcOff;
+        boolean bluetoothOff;
+        boolean radioOff;
+
+        BroadcastReceiver br = new BroadcastReceiver() {
+            @Override public void onReceive(Context context, Intent intent) {
+                // We don't allow apps to cancel this, so ignore the result.
+                actionDone();
+            }
+        };
+
+        /*
+         * Write a system property in case the system_server reboots before we
+         * get to the actual hardware restart. If that happens, we'll retry at
+         * the beginning of the SystemServer startup.
+         */
+        {
+            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
+            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.
+        mActionDone = false;
+        mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
+                br, mHandler, 0, null, null);
+        
+        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
+        synchronized (mActionDoneSync) {
+            while (!mActionDone) {
+                long delay = endTime - SystemClock.elapsedRealtime();
+                if (delay <= 0) {
+                    Log.w(TAG, "Shutdown broadcast timed out");
+                    break;
+                }
+                try {
+                    mActionDoneSync.wait(delay);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        
+        Log.i(TAG, "Shutting down activity manager...");
+        
+        final IActivityManager am =
+            ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
+        if (am != null) {
+            try {
+                am.shutdown(MAX_BROADCAST_TIME);
+            } catch (RemoteException e) {
+            }
+        }
+        
+        final INfcAdapter nfc =
+                INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
+        final ITelephony phone =
+                ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+        final IBluetooth bluetooth =
+                IBluetooth.Stub.asInterface(ServiceManager.checkService(
+                        BluetoothAdapter.BLUETOOTH_SERVICE));
+        final IMountService mount =
+                IMountService.Stub.asInterface(
+                        ServiceManager.checkService("mount"));
+
+        try {
+            nfcOff = nfc == null ||
+                     nfc.getState() == NfcAdapter.STATE_OFF;
+            if (!nfcOff) {
+                Log.w(TAG, "Turning off NFC...");
+                nfc.disable(false); // Don't persist new state
+            }
+        } catch (RemoteException ex) {
+	    Log.e(TAG, "RemoteException during NFC shutdown", ex);
+            nfcOff = true;
+        }
+
+        try {
+            bluetoothOff = bluetooth == null ||
+                           bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
+            if (!bluetoothOff) {
+                Log.w(TAG, "Disabling Bluetooth...");
+                bluetooth.disable(false);  // disable but don't persist new state
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
+            bluetoothOff = true;
+        }
+
+        try {
+            radioOff = phone == null || !phone.isRadioOn();
+            if (!radioOff) {
+                Log.w(TAG, "Turning off radio...");
+                phone.setRadio(false);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "RemoteException during radio shutdown", ex);
+            radioOff = true;
+        }
+
+        Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
+        
+        // Wait a max of 32 seconds for clean shutdown
+        for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) {
+            if (!bluetoothOff) {
+                try {
+                    bluetoothOff =
+                            bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
+                    bluetoothOff = true;
+                }
+            }
+            if (!radioOff) {
+                try {
+                    radioOff = !phone.isRadioOn();
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "RemoteException during radio shutdown", ex);
+                    radioOff = true;
+                }
+            }
+            if (!nfcOff) {
+                try {
+                    nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "RemoteException during NFC shutdown", ex);
+                    nfcOff = true;
+                }
+            }
+
+            if (radioOff && bluetoothOff && nfcOff) {
+                Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
+                break;
+            }
+            SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
+        }
+
+        // Shutdown MountService to ensure media is in a safe state
+        IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
+            public void onShutDownComplete(int statusCode) throws RemoteException {
+                Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
+                actionDone();
+            }
+        };
+
+        Log.i(TAG, "Shutting down MountService");
+        // Set initial variables and time out time.
+        mActionDone = false;
+        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
+        synchronized (mActionDoneSync) {
+            try {
+                if (mount != null) {
+                    mount.shutdown(observer);
+                } else {
+                    Log.w(TAG, "MountService unavailable for shutdown");
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Exception during MountService shutdown", e);
+            }
+            while (!mActionDone) {
+                long delay = endShutTime - SystemClock.elapsedRealtime();
+                if (delay <= 0) {
+                    Log.w(TAG, "Shutdown wait timed out");
+                    break;
+                }
+                try {
+                    mActionDoneSync.wait(delay);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+
+        rebootOrShutdown(mReboot, mRebootReason);
+    }
+
+    /**
+     * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
+     * or {@link #shutdown(Context, boolean)} instead.
+     *
+     * @param reboot true to reboot or false to shutdown
+     * @param reason reason for reboot
+     */
+    public static void rebootOrShutdown(boolean reboot, String reason) {
+        if (reboot) {
+            Log.i(TAG, "Rebooting, reason: " + reason);
+            try {
+                PowerManagerService.lowLevelReboot(reason);
+            } catch (Exception e) {
+                Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
+            }
+        } else if (SHUTDOWN_VIBRATE_MS > 0) {
+            // vibrate before shutting down
+            Vibrator vibrator = new SystemVibrator();
+            try {
+                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+            } catch (Exception e) {
+                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
+                Log.w(TAG, "Failed to vibrate during shutdown.", e);
+            }
+
+            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
+            try {
+                Thread.sleep(SHUTDOWN_VIBRATE_MS);
+            } catch (InterruptedException unused) {
+            }
+        }
+
+        // Shutdown power
+        Log.i(TAG, "Performing low-level shutdown...");
+        PowerManagerService.lowLevelShutdown();
+    }
+}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 2efcb8e..c90c0c7 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -34,7 +34,6 @@
 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;
@@ -48,6 +47,7 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.input.InputFilter;
 import com.android.server.input.InputManagerService;
+import com.android.server.pm.ShutdownThread;
 
 import android.Manifest;
 import android.app.ActivityManagerNative;
@@ -82,7 +82,6 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
-import android.os.Power;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -5025,6 +5024,18 @@
         return mInputManager.monitorInput(inputChannelName);
     }
 
+    // Called by window manager policy.  Not exposed externally.
+    @Override
+    public void shutdown() {
+        ShutdownThread.shutdown(mContext, true);
+    }
+
+    // Called by window manager policy.  Not exposed externally.
+    @Override
+    public void rebootSafeMode() {
+        ShutdownThread.rebootSafeMode(mContext, true);
+    }
+
     public void setInputFilter(InputFilter filter) {
         mInputManager.setInputFilter(filter);
     }
@@ -8700,13 +8711,13 @@
                 mPowerManager.setScreenBrightnessOverride(-1);
             } else {
                 mPowerManager.setScreenBrightnessOverride((int)
-                        (mInnerFields.mScreenBrightness * Power.BRIGHTNESS_ON));
+                        (mInnerFields.mScreenBrightness * PowerManager.BRIGHTNESS_ON));
             }
             if (mInnerFields.mButtonBrightness < 0 || mInnerFields.mButtonBrightness > 1.0f) {
                 mPowerManager.setButtonBrightnessOverride(-1);
             } else {
                 mPowerManager.setButtonBrightnessOverride((int)
-                        (mInnerFields.mButtonBrightness * Power.BRIGHTNESS_ON));
+                        (mInnerFields.mButtonBrightness * PowerManager.BRIGHTNESS_ON));
             }
         }
         if (mInnerFields.mHoldScreen != mHoldingScreenOn) {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index e2bd622..e0a14af 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -23,7 +23,10 @@
     frameworks/base/services \
     frameworks/base/core/jni \
     external/skia/include/core \
-    libcore/include
+    libcore/include \
+    libcore/include/libsuspend \
+	$(call include-path-for, libhardware)/hardware \
+	$(call include-path-for, libhardware_legacy)/hardware_legacy \
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
@@ -38,7 +41,8 @@
     libinput \
     libskia \
     libgui \
-    libusbhost
+    libusbhost \
+    libsuspend
 
 ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
     LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index ce80c1f..2077469 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -24,8 +24,14 @@
 #include <limits.h>
 
 #include <android_runtime/AndroidRuntime.h>
-#include <utils/Timers.h>
 #include <gui/ISurfaceComposer.h>
+#include <utils/Timers.h>
+#include <utils/misc.h>
+#include <utils/String8.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <cutils/android_reboot.h>
+#include <suspend/autosuspend.h>
 
 #include <private/gui/ComposerService.h>
 
@@ -43,6 +49,7 @@
 // ----------------------------------------------------------------------------
 
 static jobject gPowerManagerServiceObj;
+static struct power_module* gPowerModule;
 
 static Mutex gPowerManagerLock;
 static bool gScreenOn;
@@ -112,33 +119,115 @@
 
 // ----------------------------------------------------------------------------
 
-static void android_server_PowerManagerService_nativeInit(JNIEnv* env, jobject obj) {
+static void nativeInit(JNIEnv* env, jobject obj) {
     gPowerManagerServiceObj = env->NewGlobalRef(obj);
+
+    status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID,
+            (hw_module_t const**)&gPowerModule);
+    if (err) {
+        String8 msg;
+        msg.appendFormat("Couldn't load %s module (%s)",
+                POWER_HARDWARE_MODULE_ID, strerror(-err));
+        ALOGE("%s", msg.string());
+        jniThrowRuntimeException(env, msg.string());
+        return;
+    }
+
+    gPowerModule->init(gPowerModule);
 }
 
-static void android_server_PowerManagerService_nativeSetPowerState(JNIEnv* env,
+static void nativeSetPowerState(JNIEnv* env,
         jobject serviceObj, jboolean screenOn, jboolean screenBright) {
     AutoMutex _l(gPowerManagerLock);
     gScreenOn = screenOn;
     gScreenBright = screenBright;
 }
 
-static void android_server_PowerManagerService_nativeStartSurfaceFlingerAnimation(JNIEnv* env,
+static void nativeStartSurfaceFlingerAnimation(JNIEnv* env,
         jobject obj, jint mode) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     s->turnElectronBeamOff(mode);
 }
 
+static void nativeAcquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj) {
+    if (idObj == NULL) {
+        jniThrowNullPointerException(env, "id is null");
+        return;
+    }
+
+    const char *id = env->GetStringUTFChars(idObj, NULL);
+
+    acquire_wake_lock(lock, id);
+
+    env->ReleaseStringUTFChars(idObj, id);
+}
+
+static void nativeReleaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj) {
+    if (idObj == NULL) {
+        jniThrowNullPointerException(env, "id is null");
+        return ;
+    }
+
+    const char *id = env->GetStringUTFChars(idObj, NULL);
+
+    release_wake_lock(id);
+
+    env->ReleaseStringUTFChars(idObj, id);
+
+}
+
+static int nativeSetScreenState(JNIEnv *env, jobject clazz, jboolean on) {
+    if (on) {
+        autosuspend_disable();
+        if (gPowerModule) {
+            gPowerModule->setInteractive(gPowerModule, true);
+        }
+    } else {
+        if (gPowerModule) {
+            gPowerModule->setInteractive(gPowerModule, false);
+        }
+        autosuspend_enable();
+    }
+
+    return 0;
+}
+
+static void nativeShutdown(JNIEnv *env, jobject clazz) {
+    android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+}
+
+static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {
+    if (reason == NULL) {
+        android_reboot(ANDROID_RB_RESTART, 0, 0);
+    } else {
+        const char *chars = env->GetStringUTFChars(reason, NULL);
+        android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
+        env->ReleaseStringUTFChars(reason, chars);  // In case it fails.
+    }
+    jniThrowIOException(env, errno);
+}
+
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gPowerManagerServiceMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit", "()V",
-            (void*) android_server_PowerManagerService_nativeInit },
+            (void*) nativeInit },
     { "nativeSetPowerState", "(ZZ)V",
-            (void*) android_server_PowerManagerService_nativeSetPowerState },
+            (void*) nativeSetPowerState },
     { "nativeStartSurfaceFlingerAnimation", "(I)V",
-            (void*) android_server_PowerManagerService_nativeStartSurfaceFlingerAnimation },
+            (void*) nativeStartSurfaceFlingerAnimation },
+    { "nativeAcquireWakeLock", "(ILjava/lang/String;)V",
+            (void*) nativeAcquireWakeLock },
+    { "nativeReleaseWakeLock", "(Ljava/lang/String;)V",
+            (void*) nativeReleaseWakeLock },
+    { "nativeSetScreenState", "(Z)I",
+            (void*) nativeSetScreenState },
+    { "nativeShutdown", "()V",
+            (void*) nativeShutdown },
+    { "nativeReboot", "(Ljava/lang/String;)V",
+            (void*) nativeReboot },
 };
 
 #define FIND_CLASS(var, className) \