blob: ac6a28e91ca8c7a24545d51417c1ab26524c4ca4 [file] [log] [blame]
Dianne Hackborn55280a92009-05-07 15:53:46 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tao Bao90237f72015-05-21 16:25:19 -070017
Jeff Brown4f8ecd82012-06-18 18:29:13 -070018package com.android.server.power;
Dianne Hackborn55280a92009-05-07 15:53:46 -070019
20import android.app.ActivityManagerNative;
Joe Onoratod208e702010-10-08 16:22:43 -040021import android.app.AlertDialog;
22import android.app.Dialog;
Dianne Hackborn55280a92009-05-07 15:53:46 -070023import android.app.IActivityManager;
24import android.app.ProgressDialog;
Nick Pellybd022f42009-08-14 18:33:38 -070025import android.bluetooth.BluetoothAdapter;
fredc0f420372012-04-12 00:02:00 -070026import android.bluetooth.IBluetoothManager;
John Spurlock7b414672014-07-18 13:02:39 -040027import android.media.AudioAttributes;
Sunil Jogi3bba8d02012-04-10 13:12:26 -070028import android.nfc.NfcAdapter;
29import android.nfc.INfcAdapter;
Dianne Hackborn55280a92009-05-07 15:53:46 -070030import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.DialogInterface;
33import android.content.Intent;
Joe Onoratod208e702010-10-08 16:22:43 -040034import android.content.IntentFilter;
Dianne Hackborn55280a92009-05-07 15:53:46 -070035import android.os.Handler;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080036import android.os.PowerManager;
Dianne Hackborn55280a92009-05-07 15:53:46 -070037import android.os.RemoteException;
Dianne Hackborn55280a92009-05-07 15:53:46 -070038import android.os.ServiceManager;
39import android.os.SystemClock;
Kenny Rootf547d672010-09-22 10:36:48 -070040import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070041import android.os.UserHandle;
Benjamin Franzbff46ba2015-03-05 18:33:51 +000042import android.os.UserManager;
Mike Lockwooda717f642010-04-01 20:01:44 -070043import android.os.Vibrator;
Jeff Brownc2346132012-04-13 01:55:38 -070044import android.os.SystemVibrator;
San Mehatb1043402010-02-05 08:26:50 -080045import android.os.storage.IMountService;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080046import android.os.storage.IMountShutdownObserver;
Tao Bao90237f72015-05-21 16:25:19 -070047import android.system.ErrnoException;
48import android.system.Os;
Dianne Hackborn568cae52009-10-07 16:13:39 -070049
Dianne Hackborn55280a92009-05-07 15:53:46 -070050import com.android.internal.telephony.ITelephony;
Brian Carlstromff1ec4d2014-03-17 15:21:35 -070051import com.android.server.pm.PackageManagerService;
Jeff Brown7304c342012-05-11 18:42:42 -070052
Dianne Hackborn55280a92009-05-07 15:53:46 -070053import android.util.Log;
54import android.view.WindowManager;
55
Tao Bao90237f72015-05-21 16:25:19 -070056import java.io.BufferedReader;
57import java.io.File;
58import java.io.FileReader;
59import java.io.IOException;
60
Dianne Hackborn55280a92009-05-07 15:53:46 -070061public final class ShutdownThread extends Thread {
62 // constants
63 private static final String TAG = "ShutdownThread";
Dianne Hackborn55280a92009-05-07 15:53:46 -070064 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
65 // maximum time we wait for the shutdown broadcast before going on.
66 private static final int MAX_BROADCAST_TIME = 10*1000;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080067 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
Jeff Brownb8203712012-05-31 17:39:13 -070068 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
Tao Bao90237f72015-05-21 16:25:19 -070069 private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
Tao Bao81dce662015-06-03 11:42:31 -070070 // constants for progress bar. the values are roughly estimated based on timeout.
71 private static final int BROADCAST_STOP_PERCENT = 2;
72 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
73 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
74 private static final int RADIO_STOP_PERCENT = 18;
75 private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
Mike Lockwooda717f642010-04-01 20:01:44 -070076
77 // length of vibration before shutting down
78 private static final int SHUTDOWN_VIBRATE_MS = 500;
Tao Bao90237f72015-05-21 16:25:19 -070079
Dianne Hackborn55280a92009-05-07 15:53:46 -070080 // state tracking
81 private static Object sIsStartedGuard = new Object();
82 private static boolean sIsStarted = false;
Tao Bao90237f72015-05-21 16:25:19 -070083
Tao Bao81dce662015-06-03 11:42:31 -070084 // uncrypt status files
Tao Bao90237f72015-05-21 16:25:19 -070085 private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
Tao Bao81dce662015-06-03 11:42:31 -070086 private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
Tao Bao90237f72015-05-21 16:25:19 -070087
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080088 private static boolean mReboot;
Dianne Hackborn19caadc2012-04-20 17:49:10 -070089 private static boolean mRebootSafeMode;
Tao Bao81dce662015-06-03 11:42:31 -070090 private static boolean mRebootUpdate;
Yusuke Sato705ffd12015-07-21 15:52:11 -070091 private static String mReason;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080092
Kenny Rootf547d672010-09-22 10:36:48 -070093 // Provides shutdown assurance in case the system_server is killed
94 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
95
Dianne Hackborn19caadc2012-04-20 17:49:10 -070096 // Indicates whether we are rebooting into safe mode
97 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
98
Dianne Hackborn55280a92009-05-07 15:53:46 -070099 // static instance of this thread
100 private static final ShutdownThread sInstance = new ShutdownThread();
John Spurlock7b414672014-07-18 13:02:39 -0400101
102 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
103 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
104 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
105 .build();
106
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800107 private final Object mActionDoneSync = new Object();
108 private boolean mActionDone;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700109 private Context mContext;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800110 private PowerManager mPowerManager;
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200111 private PowerManager.WakeLock mCpuWakeLock;
112 private PowerManager.WakeLock mScreenWakeLock;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700113 private Handler mHandler;
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700114
115 private static AlertDialog sConfirmDialog;
Tao Bao90237f72015-05-21 16:25:19 -0700116 private ProgressDialog mProgressDialog;
117
Dianne Hackborn55280a92009-05-07 15:53:46 -0700118 private ShutdownThread() {
119 }
Tao Bao90237f72015-05-21 16:25:19 -0700120
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800121 /**
Dianne Hackborn55280a92009-05-07 15:53:46 -0700122 * Request a clean shutdown, waiting for subsystems to clean up their
123 * state etc. Must be called from a Looper thread in which its UI
124 * is shown.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800125 *
Dianne Hackborn55280a92009-05-07 15:53:46 -0700126 * @param context Context used to display the shutdown progress dialog.
Yusuke Sato705ffd12015-07-21 15:52:11 -0700127 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800128 * @param confirm true if user confirmation is needed before shutting down.
Dianne Hackborn55280a92009-05-07 15:53:46 -0700129 */
Yusuke Sato705ffd12015-07-21 15:52:11 -0700130 public static void shutdown(final Context context, String reason, boolean confirm) {
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700131 mReboot = false;
132 mRebootSafeMode = false;
Yusuke Sato705ffd12015-07-21 15:52:11 -0700133 mReason = reason;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700134 shutdownInner(context, confirm);
135 }
136
137 static void shutdownInner(final Context context, boolean confirm) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700138 // ensure that only one thread is trying to power down.
139 // any additional calls are just returned
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400140 synchronized (sIsStartedGuard) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700141 if (sIsStarted) {
142 Log.d(TAG, "Request to shutdown already running, returning.");
143 return;
144 }
145 }
146
Joe Onoratod208e702010-10-08 16:22:43 -0400147 final int longPressBehavior = context.getResources().getInteger(
148 com.android.internal.R.integer.config_longPressOnPowerBehavior);
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700149 final int resourceId = mRebootSafeMode
150 ? com.android.internal.R.string.reboot_safemode_confirm
151 : (longPressBehavior == 2
152 ? com.android.internal.R.string.shutdown_confirm_question
153 : com.android.internal.R.string.shutdown_confirm);
Joe Onoratod208e702010-10-08 16:22:43 -0400154
155 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700156
157 if (confirm) {
Joe Onoratod208e702010-10-08 16:22:43 -0400158 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700159 if (sConfirmDialog != null) {
160 sConfirmDialog.dismiss();
161 }
162 sConfirmDialog = new AlertDialog.Builder(context)
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700163 .setTitle(mRebootSafeMode
164 ? com.android.internal.R.string.reboot_safemode_title
165 : com.android.internal.R.string.power_off)
Joe Onoratod208e702010-10-08 16:22:43 -0400166 .setMessage(resourceId)
Dianne Hackborn55280a92009-05-07 15:53:46 -0700167 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
168 public void onClick(DialogInterface dialog, int which) {
169 beginShutdownSequence(context);
170 }
171 })
172 .setNegativeButton(com.android.internal.R.string.no, null)
173 .create();
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700174 closer.dialog = sConfirmDialog;
175 sConfirmDialog.setOnDismissListener(closer);
176 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
177 sConfirmDialog.show();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700178 } else {
179 beginShutdownSequence(context);
180 }
181 }
182
Joe Onoratod208e702010-10-08 16:22:43 -0400183 private static class CloseDialogReceiver extends BroadcastReceiver
184 implements DialogInterface.OnDismissListener {
185 private Context mContext;
186 public Dialog dialog;
187
188 CloseDialogReceiver(Context context) {
189 mContext = context;
190 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
191 context.registerReceiver(this, filter);
192 }
193
194 @Override
195 public void onReceive(Context context, Intent intent) {
196 dialog.cancel();
197 }
198
199 public void onDismiss(DialogInterface unused) {
200 mContext.unregisterReceiver(this);
201 }
202 }
203
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800204 /**
205 * Request a clean shutdown, waiting for subsystems to clean up their
206 * state etc. Must be called from a Looper thread in which its UI
207 * is shown.
208 *
209 * @param context Context used to display the shutdown progress dialog.
210 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
211 * @param confirm true if user confirmation is needed before shutting down.
212 */
213 public static void reboot(final Context context, String reason, boolean confirm) {
214 mReboot = true;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700215 mRebootSafeMode = false;
Tao Bao81dce662015-06-03 11:42:31 -0700216 mRebootUpdate = false;
Yusuke Sato705ffd12015-07-21 15:52:11 -0700217 mReason = reason;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700218 shutdownInner(context, confirm);
219 }
220
221 /**
222 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
223 * is shown.
224 *
225 * @param context Context used to display the shutdown progress dialog.
226 * @param confirm true if user confirmation is needed before shutting down.
227 */
228 public static void rebootSafeMode(final Context context, boolean confirm) {
Benjamin Franzbff46ba2015-03-05 18:33:51 +0000229 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
230 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
231 return;
232 }
233
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700234 mReboot = true;
235 mRebootSafeMode = true;
Tao Bao81dce662015-06-03 11:42:31 -0700236 mRebootUpdate = false;
Yusuke Sato705ffd12015-07-21 15:52:11 -0700237 mReason = null;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700238 shutdownInner(context, confirm);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800239 }
240
Dianne Hackborn55280a92009-05-07 15:53:46 -0700241 private static void beginShutdownSequence(Context context) {
242 synchronized (sIsStartedGuard) {
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400243 if (sIsStarted) {
Mathias Jeppsson8534a8e2010-08-17 13:33:09 +0200244 Log.d(TAG, "Shutdown sequence already running, returning.");
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400245 return;
246 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700247 sIsStarted = true;
248 }
249
Tao Bao81dce662015-06-03 11:42:31 -0700250 // Throw up a system dialog to indicate the device is rebooting / shutting down.
Dianne Hackborn55280a92009-05-07 15:53:46 -0700251 ProgressDialog pd = new ProgressDialog(context);
Tao Bao81dce662015-06-03 11:42:31 -0700252
253 // Path 1: Reboot to recovery and install the update
Yusuke Sato705ffd12015-07-21 15:52:11 -0700254 // Condition: mReason == REBOOT_RECOVERY and mRebootUpdate == True
Tao Bao81dce662015-06-03 11:42:31 -0700255 // (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
256 // UI: progress bar
257 //
258 // Path 2: Reboot to recovery for factory reset
Yusuke Sato705ffd12015-07-21 15:52:11 -0700259 // Condition: mReason == REBOOT_RECOVERY
Tao Bao81dce662015-06-03 11:42:31 -0700260 // UI: spinning circle only (no progress bar)
261 //
262 // Path 3: Regular reboot / shutdown
263 // Condition: Otherwise
264 // UI: spinning circle only (no progress bar)
Yusuke Sato705ffd12015-07-21 15:52:11 -0700265 if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
Tao Bao81dce662015-06-03 11:42:31 -0700266 mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
267 if (mRebootUpdate) {
268 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
269 pd.setMessage(context.getText(
270 com.android.internal.R.string.reboot_to_update_prepare));
271 pd.setMax(100);
272 pd.setProgressNumberFormat(null);
273 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
274 pd.setProgress(0);
275 pd.setIndeterminate(false);
276 } else {
277 // Factory reset path. Set the dialog message accordingly.
278 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
279 pd.setMessage(context.getText(
280 com.android.internal.R.string.reboot_to_reset_message));
281 pd.setIndeterminate(true);
282 }
Tao Bao90237f72015-05-21 16:25:19 -0700283 } else {
284 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
Tao Bao81dce662015-06-03 11:42:31 -0700285 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
286 pd.setIndeterminate(true);
Tao Bao90237f72015-05-21 16:25:19 -0700287 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700288 pd.setCancelable(false);
289 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700290
291 pd.show();
292
Tao Bao90237f72015-05-21 16:25:19 -0700293 sInstance.mProgressDialog = pd;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700294 sInstance.mContext = context;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800295 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200296
297 // make sure we never fall asleep again
298 sInstance.mCpuWakeLock = null;
299 try {
300 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
301 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
302 sInstance.mCpuWakeLock.setReferenceCounted(false);
303 sInstance.mCpuWakeLock.acquire();
304 } catch (SecurityException e) {
305 Log.w(TAG, "No permission to acquire wake lock", e);
306 sInstance.mCpuWakeLock = null;
307 }
308
309 // also make sure the screen stays on for better user experience
310 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800311 if (sInstance.mPowerManager.isScreenOn()) {
312 try {
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200313 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
314 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
315 sInstance.mScreenWakeLock.setReferenceCounted(false);
316 sInstance.mScreenWakeLock.acquire();
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800317 } catch (SecurityException e) {
318 Log.w(TAG, "No permission to acquire wake lock", e);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200319 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800320 }
321 }
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200322
323 // start the thread that initiates shutdown
Dianne Hackborn55280a92009-05-07 15:53:46 -0700324 sInstance.mHandler = new Handler() {
325 };
326 sInstance.start();
327 }
328
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800329 void actionDone() {
330 synchronized (mActionDoneSync) {
331 mActionDone = true;
332 mActionDoneSync.notifyAll();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700333 }
334 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800335
Dianne Hackborn55280a92009-05-07 15:53:46 -0700336 /**
337 * Makes sure we handle the shutdown gracefully.
338 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
339 */
340 public void run() {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700341 BroadcastReceiver br = new BroadcastReceiver() {
342 @Override public void onReceive(Context context, Intent intent) {
343 // We don't allow apps to cancel this, so ignore the result.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800344 actionDone();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700345 }
346 };
Kenny Rootf547d672010-09-22 10:36:48 -0700347
348 /*
349 * Write a system property in case the system_server reboots before we
350 * get to the actual hardware restart. If that happens, we'll retry at
351 * the beginning of the SystemServer startup.
352 */
353 {
Yusuke Sato705ffd12015-07-21 15:52:11 -0700354 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
Kenny Rootf547d672010-09-22 10:36:48 -0700355 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
356 }
357
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700358 /*
359 * If we are rebooting into safe mode, write a system property
360 * indicating so.
361 */
362 if (mRebootSafeMode) {
363 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
364 }
365
Dianne Hackborn55280a92009-05-07 15:53:46 -0700366 Log.i(TAG, "Sending shutdown broadcast...");
Tao Bao90237f72015-05-21 16:25:19 -0700367
Dianne Hackborn55280a92009-05-07 15:53:46 -0700368 // First send the high-level shut down broadcast.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800369 mActionDone = false;
Martin Wallgrena81d7da2013-02-04 14:26:51 +0100370 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
371 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
372 mContext.sendOrderedBroadcastAsUser(intent,
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700373 UserHandle.ALL, null, br, mHandler, 0, null, null);
Tao Bao90237f72015-05-21 16:25:19 -0700374
Mike Lockwood098e58d2010-05-13 16:29:49 -0400375 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800376 synchronized (mActionDoneSync) {
377 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400378 long delay = endTime - SystemClock.elapsedRealtime();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700379 if (delay <= 0) {
380 Log.w(TAG, "Shutdown broadcast timed out");
381 break;
Tao Bao81dce662015-06-03 11:42:31 -0700382 } else if (mRebootUpdate) {
383 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
384 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
385 sInstance.setRebootProgress(status, null);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700386 }
387 try {
Tao Bao81dce662015-06-03 11:42:31 -0700388 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
Dianne Hackborn55280a92009-05-07 15:53:46 -0700389 } catch (InterruptedException e) {
390 }
391 }
392 }
Tao Bao81dce662015-06-03 11:42:31 -0700393 if (mRebootUpdate) {
394 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
395 }
Tao Bao90237f72015-05-21 16:25:19 -0700396
Dianne Hackborn55280a92009-05-07 15:53:46 -0700397 Log.i(TAG, "Shutting down activity manager...");
Tao Bao90237f72015-05-21 16:25:19 -0700398
Dianne Hackborn55280a92009-05-07 15:53:46 -0700399 final IActivityManager am =
400 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
401 if (am != null) {
402 try {
403 am.shutdown(MAX_BROADCAST_TIME);
404 } catch (RemoteException e) {
405 }
406 }
Tao Bao81dce662015-06-03 11:42:31 -0700407 if (mRebootUpdate) {
408 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
409 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700410
Brian Carlstromff1ec4d2014-03-17 15:21:35 -0700411 Log.i(TAG, "Shutting down package manager...");
412
413 final PackageManagerService pm = (PackageManagerService)
414 ServiceManager.getService("package");
415 if (pm != null) {
416 pm.shutdown();
417 }
Tao Bao81dce662015-06-03 11:42:31 -0700418 if (mRebootUpdate) {
419 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
420 }
Brian Carlstromff1ec4d2014-03-17 15:21:35 -0700421
Matthew Xie96313142012-06-29 16:57:31 -0700422 // Shutdown radios.
423 shutdownRadios(MAX_RADIO_WAIT_TIME);
Tao Bao81dce662015-06-03 11:42:31 -0700424 if (mRebootUpdate) {
425 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
426 }
Matthew Xie96313142012-06-29 16:57:31 -0700427
San Mehat9f7f7ca2010-01-07 11:34:59 -0800428 // Shutdown MountService to ensure media is in a safe state
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800429 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
430 public void onShutDownComplete(int statusCode) throws RemoteException {
431 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
432 actionDone();
San Mehat9f7f7ca2010-01-07 11:34:59 -0800433 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800434 };
435
436 Log.i(TAG, "Shutting down MountService");
Jeff Brownb8203712012-05-31 17:39:13 -0700437
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800438 // Set initial variables and time out time.
439 mActionDone = false;
Mike Lockwood098e58d2010-05-13 16:29:49 -0400440 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800441 synchronized (mActionDoneSync) {
442 try {
Jeff Brownb8203712012-05-31 17:39:13 -0700443 final IMountService mount = IMountService.Stub.asInterface(
444 ServiceManager.checkService("mount"));
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800445 if (mount != null) {
446 mount.shutdown(observer);
447 } else {
448 Log.w(TAG, "MountService unavailable for shutdown");
449 }
450 } catch (Exception e) {
451 Log.e(TAG, "Exception during MountService shutdown", e);
452 }
453 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400454 long delay = endShutTime - SystemClock.elapsedRealtime();
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800455 if (delay <= 0) {
456 Log.w(TAG, "Shutdown wait timed out");
457 break;
Tao Bao81dce662015-06-03 11:42:31 -0700458 } else if (mRebootUpdate) {
459 int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
460 (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
461 MAX_SHUTDOWN_WAIT_TIME);
462 status += RADIO_STOP_PERCENT;
463 sInstance.setRebootProgress(status, null);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800464 }
465 try {
Tao Bao81dce662015-06-03 11:42:31 -0700466 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800467 } catch (InterruptedException e) {
468 }
469 }
San Mehat9f7f7ca2010-01-07 11:34:59 -0800470 }
Tao Bao81dce662015-06-03 11:42:31 -0700471 if (mRebootUpdate) {
472 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
San Mehat9f7f7ca2010-01-07 11:34:59 -0800473
Tao Bao81dce662015-06-03 11:42:31 -0700474 // If it's to reboot to install update, invoke uncrypt via init service.
Tao Bao90237f72015-05-21 16:25:19 -0700475 uncrypt();
476 }
477
Yusuke Sato705ffd12015-07-21 15:52:11 -0700478 rebootOrShutdown(mContext, mReboot, mReason);
Kenny Rootf547d672010-09-22 10:36:48 -0700479 }
480
Tao Bao81dce662015-06-03 11:42:31 -0700481 private void setRebootProgress(final int progress, final CharSequence message) {
Tao Bao90237f72015-05-21 16:25:19 -0700482 mHandler.post(new Runnable() {
483 @Override
484 public void run() {
485 if (mProgressDialog != null) {
486 mProgressDialog.setProgress(progress);
Tao Bao81dce662015-06-03 11:42:31 -0700487 if (message != null) {
488 mProgressDialog.setMessage(message);
489 }
Tao Bao90237f72015-05-21 16:25:19 -0700490 }
491 }
492 });
493 }
494
Tao Bao81dce662015-06-03 11:42:31 -0700495 private void shutdownRadios(final int timeout) {
Jeff Brownb8203712012-05-31 17:39:13 -0700496 // If a radio is wedged, disabling it may hang so we do this work in another thread,
497 // just in case.
498 final long endTime = SystemClock.elapsedRealtime() + timeout;
499 final boolean[] done = new boolean[1];
500 Thread t = new Thread() {
501 public void run() {
502 boolean nfcOff;
503 boolean bluetoothOff;
504 boolean radioOff;
505
506 final INfcAdapter nfc =
507 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
508 final ITelephony phone =
509 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
Matthew Xie96313142012-06-29 16:57:31 -0700510 final IBluetoothManager bluetooth =
511 IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
512 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
Jeff Brownb8203712012-05-31 17:39:13 -0700513
514 try {
515 nfcOff = nfc == null ||
516 nfc.getState() == NfcAdapter.STATE_OFF;
517 if (!nfcOff) {
518 Log.w(TAG, "Turning off NFC...");
519 nfc.disable(false); // Don't persist new state
520 }
521 } catch (RemoteException ex) {
522 Log.e(TAG, "RemoteException during NFC shutdown", ex);
523 nfcOff = true;
524 }
525
526 try {
Matthew Xie96313142012-06-29 16:57:31 -0700527 bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
Jeff Brownb8203712012-05-31 17:39:13 -0700528 if (!bluetoothOff) {
529 Log.w(TAG, "Disabling Bluetooth...");
530 bluetooth.disable(false); // disable but don't persist new state
531 }
532 } catch (RemoteException ex) {
533 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
534 bluetoothOff = true;
535 }
536
537 try {
Naveen Kallabd772362014-08-02 01:03:42 -0700538 radioOff = phone == null || !phone.needMobileRadioShutdown();
Jeff Brownb8203712012-05-31 17:39:13 -0700539 if (!radioOff) {
Naveen Kallabd772362014-08-02 01:03:42 -0700540 Log.w(TAG, "Turning off cellular radios...");
541 phone.shutdownMobileRadios();
Jeff Brownb8203712012-05-31 17:39:13 -0700542 }
543 } catch (RemoteException ex) {
544 Log.e(TAG, "RemoteException during radio shutdown", ex);
545 radioOff = true;
546 }
547
548 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
549
Tao Bao81dce662015-06-03 11:42:31 -0700550 long delay = endTime - SystemClock.elapsedRealtime();
551 while (delay > 0) {
552 if (mRebootUpdate) {
553 int status = (int)((timeout - delay) * 1.0 *
554 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
555 status += PACKAGE_MANAGER_STOP_PERCENT;
556 sInstance.setRebootProgress(status, null);
557 }
558
Jeff Brownb8203712012-05-31 17:39:13 -0700559 if (!bluetoothOff) {
560 try {
Matthew Xie96313142012-06-29 16:57:31 -0700561 bluetoothOff = !bluetooth.isEnabled();
Jeff Brownb8203712012-05-31 17:39:13 -0700562 } catch (RemoteException ex) {
563 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
564 bluetoothOff = true;
565 }
566 if (bluetoothOff) {
567 Log.i(TAG, "Bluetooth turned off.");
568 }
569 }
570 if (!radioOff) {
571 try {
Naveen Kallabd772362014-08-02 01:03:42 -0700572 radioOff = !phone.needMobileRadioShutdown();
Jeff Brownb8203712012-05-31 17:39:13 -0700573 } catch (RemoteException ex) {
574 Log.e(TAG, "RemoteException during radio shutdown", ex);
575 radioOff = true;
576 }
577 if (radioOff) {
578 Log.i(TAG, "Radio turned off.");
579 }
580 }
581 if (!nfcOff) {
582 try {
583 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
584 } catch (RemoteException ex) {
585 Log.e(TAG, "RemoteException during NFC shutdown", ex);
586 nfcOff = true;
587 }
Naveen Kallabd772362014-08-02 01:03:42 -0700588 if (nfcOff) {
Jeff Brownb8203712012-05-31 17:39:13 -0700589 Log.i(TAG, "NFC turned off.");
590 }
591 }
592
593 if (radioOff && bluetoothOff && nfcOff) {
594 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
595 done[0] = true;
596 break;
597 }
598 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
Tao Bao81dce662015-06-03 11:42:31 -0700599
600 delay = endTime - SystemClock.elapsedRealtime();
Jeff Brownb8203712012-05-31 17:39:13 -0700601 }
602 }
603 };
604
605 t.start();
606 try {
607 t.join(timeout);
608 } catch (InterruptedException ex) {
609 }
610 if (!done[0]) {
611 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
612 }
613 }
614
Kenny Rootf547d672010-09-22 10:36:48 -0700615 /**
616 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
617 * or {@link #shutdown(Context, boolean)} instead.
618 *
riddle_hsud3b37172015-03-19 01:11:55 +0800619 * @param context Context used to vibrate or null without vibration
Kenny Rootf547d672010-09-22 10:36:48 -0700620 * @param reboot true to reboot or false to shutdown
Yusuke Sato705ffd12015-07-21 15:52:11 -0700621 * @param reason reason for reboot/shutdown
Kenny Rootf547d672010-09-22 10:36:48 -0700622 */
riddle_hsud3b37172015-03-19 01:11:55 +0800623 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
Kenny Rootf547d672010-09-22 10:36:48 -0700624 if (reboot) {
625 Log.i(TAG, "Rebooting, reason: " + reason);
Nick Kralevichdbcf2d72013-04-18 14:41:40 -0700626 PowerManagerService.lowLevelReboot(reason);
627 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
Yusuke Sato705ffd12015-07-21 15:52:11 -0700628 reason = null;
riddle_hsud3b37172015-03-19 01:11:55 +0800629 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
Mike Lockwooda717f642010-04-01 20:01:44 -0700630 // vibrate before shutting down
riddle_hsud3b37172015-03-19 01:11:55 +0800631 Vibrator vibrator = new SystemVibrator(context);
Brad Fitzpatrick26e9cf32010-10-19 09:33:09 -0700632 try {
John Spurlock7b414672014-07-18 13:02:39 -0400633 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
Brad Fitzpatrick26e9cf32010-10-19 09:33:09 -0700634 } catch (Exception e) {
635 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
636 Log.w(TAG, "Failed to vibrate during shutdown.", e);
637 }
638
Mike Lockwooda717f642010-04-01 20:01:44 -0700639 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
640 try {
641 Thread.sleep(SHUTDOWN_VIBRATE_MS);
Brad Fitzpatricke3316442010-10-14 19:40:56 -0700642 } catch (InterruptedException unused) {
Mike Lockwooda717f642010-04-01 20:01:44 -0700643 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800644 }
645
646 // Shutdown power
Dianne Hackborn55280a92009-05-07 15:53:46 -0700647 Log.i(TAG, "Performing low-level shutdown...");
Yusuke Sato705ffd12015-07-21 15:52:11 -0700648 PowerManagerService.lowLevelShutdown(reason);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700649 }
Tao Bao90237f72015-05-21 16:25:19 -0700650
651 private void uncrypt() {
652 Log.i(TAG, "Calling uncrypt and monitoring the progress...");
653
Tao Bao90237f72015-05-21 16:25:19 -0700654 final boolean[] done = new boolean[1];
655 done[0] = false;
656 Thread t = new Thread() {
657 @Override
658 public void run() {
659 // Create the status pipe file to communicate with /system/bin/uncrypt.
660 new File(UNCRYPT_STATUS_FILE).delete();
661 try {
662 Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
663 } catch (ErrnoException e) {
664 Log.w(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
665 "\": " + e.getMessage());
666 }
667
668 SystemProperties.set("ctl.start", "uncrypt");
669
670 // Read the status from the pipe.
671 try (BufferedReader reader = new BufferedReader(
672 new FileReader(UNCRYPT_STATUS_FILE))) {
673
Tao Bao81dce662015-06-03 11:42:31 -0700674 int lastStatus = Integer.MIN_VALUE;
Tao Bao90237f72015-05-21 16:25:19 -0700675 while (true) {
676 String str = reader.readLine();
677 try {
678 int status = Integer.parseInt(str);
679
680 // Avoid flooding the log with the same message.
Tao Bao81dce662015-06-03 11:42:31 -0700681 if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
Tao Bao90237f72015-05-21 16:25:19 -0700682 continue;
683 }
Tao Bao81dce662015-06-03 11:42:31 -0700684 lastStatus = status;
Tao Bao90237f72015-05-21 16:25:19 -0700685
686 if (status >= 0 && status < 100) {
687 // Update status
688 Log.d(TAG, "uncrypt read status: " + status);
Tao Bao81dce662015-06-03 11:42:31 -0700689 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
690 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
691 status += MOUNT_SERVICE_STOP_PERCENT;
692 CharSequence msg = mContext.getText(
693 com.android.internal.R.string.reboot_to_update_package);
694 sInstance.setRebootProgress(status, msg);
Tao Bao90237f72015-05-21 16:25:19 -0700695 } else if (status == 100) {
696 Log.d(TAG, "uncrypt successfully finished.");
Tao Bao81dce662015-06-03 11:42:31 -0700697 CharSequence msg = mContext.getText(
698 com.android.internal.R.string.reboot_to_update_reboot);
699 sInstance.setRebootProgress(status, msg);
Tao Bao90237f72015-05-21 16:25:19 -0700700 break;
701 } else {
702 // Error in /system/bin/uncrypt. Or it's rebooting to recovery
703 // to perform other operations (e.g. factory reset).
704 Log.d(TAG, "uncrypt failed with status: " + status);
705 break;
706 }
707 } catch (NumberFormatException unused) {
708 Log.d(TAG, "uncrypt invalid status received: " + str);
709 break;
710 }
711 }
712 } catch (IOException unused) {
713 Log.w(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
714 }
715 done[0] = true;
716 }
717 };
718 t.start();
719
720 try {
721 t.join(MAX_UNCRYPT_WAIT_TIME);
722 } catch (InterruptedException unused) {
723 }
724 if (!done[0]) {
725 Log.w(TAG, "Timed out waiting for uncrypt.");
726 }
727 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700728}