Merge "Wait for uncrypt to finish before rebooting"

This commit is contained in:
Tao Bao
2015-06-10 00:43:46 +00:00
committed by Gerrit Code Review
5 changed files with 170 additions and 20 deletions

View File

@@ -71,6 +71,7 @@ public class RecoverySystem {
/** Used to communicate with recovery. See bootable/recovery/recovery.c. */ /** Used to communicate with recovery. See bootable/recovery/recovery.c. */
private static File RECOVERY_DIR = new File("/cache/recovery"); private static File RECOVERY_DIR = new File("/cache/recovery");
private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
private static File UNCRYPT_FILE = new File(RECOVERY_DIR, "uncrypt_file");
private static File LOG_FILE = new File(RECOVERY_DIR, "log"); private static File LOG_FILE = new File(RECOVERY_DIR, "log");
private static String LAST_PREFIX = "last_"; private static String LAST_PREFIX = "last_";
@@ -333,8 +334,21 @@ public class RecoverySystem {
public static void installPackage(Context context, File packageFile) public static void installPackage(Context context, File packageFile)
throws IOException { throws IOException {
String filename = packageFile.getCanonicalPath(); String filename = packageFile.getCanonicalPath();
FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE);
try {
uncryptFile.write(filename + "\n");
} finally {
uncryptFile.close();
}
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
// If the package is on the /data partition, write the block map file
// into COMMAND_FILE instead.
if (filename.startsWith("/data/")) {
filename = "@/cache/recovery/block.map";
}
final String filenameArg = "--update_package=" + filename; final String filenameArg = "--update_package=" + filename;
final String localeArg = "--locale=" + Locale.getDefault().toString(); final String localeArg = "--locale=" + Locale.getDefault().toString();
bootCommand(context, filenameArg, localeArg); bootCommand(context, filenameArg, localeArg);

View File

@@ -393,6 +393,10 @@
<!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] --> <!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] -->
<string name="silent_mode_ring">Ringer on</string> <string name="silent_mode_ring">Ringer on</string>
<!-- Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery. -->
<string name="reboot_to_recovery_title">Prepare for update</string>
<string name="reboot_to_recovery_progress">Processing the update package\u2026</string>
<!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. --> <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
<string name="shutdown_progress">Shutting down\u2026</string> <string name="shutdown_progress">Shutting down\u2026</string>

View File

@@ -780,6 +780,8 @@
<java-symbol type="string" name="mobile_provisioning_url" /> <java-symbol type="string" name="mobile_provisioning_url" />
<java-symbol type="string" name="mobile_redirected_provisioning_url" /> <java-symbol type="string" name="mobile_redirected_provisioning_url" />
<java-symbol type="string" name="quick_contacts_not_available" /> <java-symbol type="string" name="quick_contacts_not_available" />
<java-symbol type="string" name="reboot_to_recovery_progress" />
<java-symbol type="string" name="reboot_to_recovery_title" />
<java-symbol type="string" name="reboot_safemode_confirm" /> <java-symbol type="string" name="reboot_safemode_confirm" />
<java-symbol type="string" name="reboot_safemode_title" /> <java-symbol type="string" name="reboot_safemode_title" />
<java-symbol type="string" name="relationTypeAssistant" /> <java-symbol type="string" name="relationTypeAssistant" />

View File

@@ -2357,8 +2357,7 @@ public final class PowerManagerService extends SystemService
/** /**
* Low-level function to reboot the device. On success, this * Low-level function to reboot the device. On success, this
* function doesn't return. If more than 20 seconds passes from * function doesn't return. If more than 20 seconds passes from
* the time a reboot is requested (120 seconds for reboot to * the time a reboot is requested, this method returns.
* recovery), this method returns.
* *
* @param reason code to pass to the kernel (e.g. "recovery"), or null. * @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/ */
@@ -2366,27 +2365,21 @@ public final class PowerManagerService extends SystemService
if (reason == null) { if (reason == null) {
reason = ""; reason = "";
} }
long duration;
if (reason.equals(PowerManager.REBOOT_RECOVERY)) { if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
// If we are rebooting to go into recovery, instead of // If we are rebooting to go into recovery, instead of
// setting sys.powerctl directly we'll start the // setting sys.powerctl directly we'll start the
// pre-recovery service which will do some preparation for // pre-recovery service which will do some preparation for
// recovery and then reboot for us. // recovery and then reboot for us.
//
// This preparation can take more than 20 seconds if
// there's a very large update package, so lengthen the
// timeout. We have seen 750MB packages take 3-4 minutes
SystemProperties.set("ctl.start", "pre-recovery"); SystemProperties.set("ctl.start", "pre-recovery");
duration = 300 * 1000L;
} else { } else {
SystemProperties.set("sys.powerctl", "reboot," + reason); SystemProperties.set("sys.powerctl", "reboot," + reason);
duration = 20 * 1000L;
} }
try { try {
Thread.sleep(duration); Thread.sleep(20 * 1000L);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
} }
@Override // Watchdog.Monitor implementation @Override // Watchdog.Monitor implementation

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.server.power; package com.android.server.power;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
@@ -43,6 +43,8 @@ import android.os.Vibrator;
import android.os.SystemVibrator; import android.os.SystemVibrator;
import android.os.storage.IMountService; import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver; import android.os.storage.IMountShutdownObserver;
import android.system.ErrnoException;
import android.system.Os;
import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerService;
@@ -50,6 +52,11 @@ import com.android.server.pm.PackageManagerService;
import android.util.Log; import android.util.Log;
import android.view.WindowManager; import android.view.WindowManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public final class ShutdownThread extends Thread { public final class ShutdownThread extends Thread {
// constants // constants
private static final String TAG = "ShutdownThread"; private static final String TAG = "ShutdownThread";
@@ -58,14 +65,18 @@ public final class ShutdownThread extends Thread {
private static final int MAX_BROADCAST_TIME = 10*1000; private static final int MAX_BROADCAST_TIME = 10*1000;
private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000; private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
private static final int MAX_RADIO_WAIT_TIME = 12*1000; private static final int MAX_RADIO_WAIT_TIME = 12*1000;
private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
// length of vibration before shutting down // length of vibration before shutting down
private static final int SHUTDOWN_VIBRATE_MS = 500; private static final int SHUTDOWN_VIBRATE_MS = 500;
// state tracking // state tracking
private static Object sIsStartedGuard = new Object(); private static Object sIsStartedGuard = new Object();
private static boolean sIsStarted = false; private static boolean sIsStarted = false;
// uncrypt status file
private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
private static boolean mReboot; private static boolean mReboot;
private static boolean mRebootSafeMode; private static boolean mRebootSafeMode;
private static String mRebootReason; private static String mRebootReason;
@@ -93,10 +104,11 @@ public final class ShutdownThread extends Thread {
private Handler mHandler; private Handler mHandler;
private static AlertDialog sConfirmDialog; private static AlertDialog sConfirmDialog;
private ProgressDialog mProgressDialog;
private ShutdownThread() { private ShutdownThread() {
} }
/** /**
* Request a clean shutdown, waiting for subsystems to clean up their * Request a clean shutdown, waiting for subsystems to clean up their
* state etc. Must be called from a Looper thread in which its UI * state etc. Must be called from a Looper thread in which its UI
@@ -220,7 +232,11 @@ public final class ShutdownThread extends Thread {
// throw up an indeterminate system dialog to indicate radio is // throw up an indeterminate system dialog to indicate radio is
// shutting down. // shutting down.
ProgressDialog pd = new ProgressDialog(context); ProgressDialog pd = new ProgressDialog(context);
pd.setTitle(context.getText(com.android.internal.R.string.power_off)); if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title));
} else {
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
}
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true); pd.setIndeterminate(true);
pd.setCancelable(false); pd.setCancelable(false);
@@ -228,6 +244,7 @@ public final class ShutdownThread extends Thread {
pd.show(); pd.show();
sInstance.mProgressDialog = pd;
sInstance.mContext = context; sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -301,14 +318,14 @@ public final class ShutdownThread extends Thread {
} }
Log.i(TAG, "Sending shutdown broadcast..."); Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast. // First send the high-level shut down broadcast.
mActionDone = false; mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN); Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent, mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null); UserHandle.ALL, null, br, mHandler, 0, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) { synchronized (mActionDoneSync) {
while (!mActionDone) { while (!mActionDone) {
@@ -323,9 +340,9 @@ public final class ShutdownThread extends Thread {
} }
} }
} }
Log.i(TAG, "Shutting down activity manager..."); Log.i(TAG, "Shutting down activity manager...");
final IActivityManager am = final IActivityManager am =
ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
if (am != null) { if (am != null) {
@@ -384,9 +401,55 @@ public final class ShutdownThread extends Thread {
} }
} }
// If it's to reboot into recovery, invoke uncrypt via init service.
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
uncrypt();
}
rebootOrShutdown(mContext, mReboot, mRebootReason); rebootOrShutdown(mContext, mReboot, mRebootReason);
} }
private void prepareUncryptProgress() {
// Reset the dialog message to show the decrypt process.
mHandler.post(new Runnable() {
@Override
public void run() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
// It doesn't work to change the style of the existing
// one. Have to create a new one.
ProgressDialog pd = new ProgressDialog(mContext);
pd.setTitle(mContext.getText(
com.android.internal.R.string.reboot_to_recovery_title));
pd.setMessage(mContext.getText(
com.android.internal.R.string.reboot_to_recovery_progress));
pd.setIndeterminate(false);
pd.setMax(100);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setProgressNumberFormat(null);
pd.setProgress(0);
mProgressDialog = pd;
mProgressDialog.show();
}
});
}
private void setUncryptProgress(final int progress) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mProgressDialog != null) {
mProgressDialog.setProgress(progress);
}
}
});
}
private void shutdownRadios(int timeout) { private void shutdownRadios(int timeout) {
// If a radio is wedged, disabling it may hang so we do this work in another thread, // If a radio is wedged, disabling it may hang so we do this work in another thread,
// just in case. // just in case.
@@ -531,4 +594,78 @@ public final class ShutdownThread extends Thread {
Log.i(TAG, "Performing low-level shutdown..."); Log.i(TAG, "Performing low-level shutdown...");
PowerManagerService.lowLevelShutdown(); PowerManagerService.lowLevelShutdown();
} }
private void uncrypt() {
Log.i(TAG, "Calling uncrypt and monitoring the progress...");
// Update the ProcessDialog message and style.
sInstance.prepareUncryptProgress();
final boolean[] done = new boolean[1];
done[0] = false;
Thread t = new Thread() {
@Override
public void run() {
// Create the status pipe file to communicate with /system/bin/uncrypt.
new File(UNCRYPT_STATUS_FILE).delete();
try {
Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
} catch (ErrnoException e) {
Log.w(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
"\": " + e.getMessage());
}
SystemProperties.set("ctl.start", "uncrypt");
// Read the status from the pipe.
try (BufferedReader reader = new BufferedReader(
new FileReader(UNCRYPT_STATUS_FILE))) {
int last_status = Integer.MIN_VALUE;
while (true) {
String str = reader.readLine();
try {
int status = Integer.parseInt(str);
// Avoid flooding the log with the same message.
if (status == last_status && last_status != Integer.MIN_VALUE) {
continue;
}
last_status = status;
if (status >= 0 && status < 100) {
// Update status
Log.d(TAG, "uncrypt read status: " + status);
sInstance.setUncryptProgress(status);
} else if (status == 100) {
Log.d(TAG, "uncrypt successfully finished.");
sInstance.setUncryptProgress(status);
break;
} else {
// Error in /system/bin/uncrypt. Or it's rebooting to recovery
// to perform other operations (e.g. factory reset).
Log.d(TAG, "uncrypt failed with status: " + status);
break;
}
} catch (NumberFormatException unused) {
Log.d(TAG, "uncrypt invalid status received: " + str);
break;
}
}
} catch (IOException unused) {
Log.w(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
}
done[0] = true;
}
};
t.start();
try {
t.join(MAX_UNCRYPT_WAIT_TIME);
} catch (InterruptedException unused) {
}
if (!done[0]) {
Log.w(TAG, "Timed out waiting for uncrypt.");
}
}
} }