* commit '5f90a8c5bd689afa8403d7dce967bf96a82880c4': Wait for uncrypt to finish before rebooting
This commit is contained in:
@@ -71,6 +71,7 @@ public class RecoverySystem {
|
||||
/** Used to communicate with recovery. See bootable/recovery/recovery.c. */
|
||||
private static File RECOVERY_DIR = new File("/cache/recovery");
|
||||
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 String LAST_PREFIX = "last_";
|
||||
|
||||
@@ -333,8 +334,21 @@ public class RecoverySystem {
|
||||
public static void installPackage(Context context, File packageFile)
|
||||
throws IOException {
|
||||
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 + " !!!");
|
||||
|
||||
// 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 localeArg = "--locale=" + Locale.getDefault().toString();
|
||||
bootCommand(context, filenameArg, localeArg);
|
||||
|
||||
@@ -413,6 +413,10 @@
|
||||
<!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] -->
|
||||
<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. -->
|
||||
<string name="shutdown_progress">Shutting down\u2026</string>
|
||||
|
||||
|
||||
@@ -815,6 +815,8 @@
|
||||
<java-symbol type="string" name="mobile_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="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_title" />
|
||||
<java-symbol type="string" name="relationTypeAssistant" />
|
||||
|
||||
@@ -2518,8 +2518,7 @@ public final class PowerManagerService extends SystemService
|
||||
/**
|
||||
* Low-level function to reboot the device. On success, this
|
||||
* function doesn't return. If more than 20 seconds passes from
|
||||
* the time a reboot is requested (120 seconds for reboot to
|
||||
* recovery), this method returns.
|
||||
* the time a reboot is requested, this method returns.
|
||||
*
|
||||
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
|
||||
*/
|
||||
@@ -2527,27 +2526,21 @@ public final class PowerManagerService extends SystemService
|
||||
if (reason == null) {
|
||||
reason = "";
|
||||
}
|
||||
long duration;
|
||||
if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
|
||||
// If we are rebooting to go into recovery, instead of
|
||||
// setting sys.powerctl directly we'll start the
|
||||
// pre-recovery service which will do some preparation for
|
||||
// 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");
|
||||
duration = 300 * 1000L;
|
||||
} else {
|
||||
SystemProperties.set("sys.powerctl", "reboot," + reason);
|
||||
duration = 20 * 1000L;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(duration);
|
||||
Thread.sleep(20 * 1000L);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
|
||||
}
|
||||
|
||||
@Override // Watchdog.Monitor implementation
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package com.android.server.power;
|
||||
|
||||
import android.app.ActivityManagerNative;
|
||||
@@ -44,6 +44,8 @@ import android.os.Vibrator;
|
||||
import android.os.SystemVibrator;
|
||||
import android.os.storage.IMountService;
|
||||
import android.os.storage.IMountShutdownObserver;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import com.android.internal.telephony.ITelephony;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
@@ -51,6 +53,11 @@ import com.android.server.pm.PackageManagerService;
|
||||
import android.util.Log;
|
||||
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 {
|
||||
// constants
|
||||
private static final String TAG = "ShutdownThread";
|
||||
@@ -59,14 +66,18 @@ public final class ShutdownThread extends Thread {
|
||||
private static final int MAX_BROADCAST_TIME = 10*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_UNCRYPT_WAIT_TIME = 15*60*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;
|
||||
|
||||
|
||||
// uncrypt status file
|
||||
private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
|
||||
|
||||
private static boolean mReboot;
|
||||
private static boolean mRebootSafeMode;
|
||||
private static String mRebootReason;
|
||||
@@ -94,10 +105,11 @@ public final class ShutdownThread extends Thread {
|
||||
private Handler mHandler;
|
||||
|
||||
private static AlertDialog sConfirmDialog;
|
||||
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
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
|
||||
@@ -226,7 +238,11 @@ public final class ShutdownThread extends Thread {
|
||||
// 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));
|
||||
if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
|
||||
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.setIndeterminate(true);
|
||||
pd.setCancelable(false);
|
||||
@@ -234,6 +250,7 @@ public final class ShutdownThread extends Thread {
|
||||
|
||||
pd.show();
|
||||
|
||||
sInstance.mProgressDialog = pd;
|
||||
sInstance.mContext = context;
|
||||
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
@@ -307,14 +324,14 @@ public final class ShutdownThread extends Thread {
|
||||
}
|
||||
|
||||
Log.i(TAG, "Sending shutdown broadcast...");
|
||||
|
||||
|
||||
// First send the high-level shut down broadcast.
|
||||
mActionDone = false;
|
||||
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
mContext.sendOrderedBroadcastAsUser(intent,
|
||||
UserHandle.ALL, null, br, mHandler, 0, null, null);
|
||||
|
||||
|
||||
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
|
||||
synchronized (mActionDoneSync) {
|
||||
while (!mActionDone) {
|
||||
@@ -329,9 +346,9 @@ public final class ShutdownThread extends Thread {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Log.i(TAG, "Shutting down activity manager...");
|
||||
|
||||
|
||||
final IActivityManager am =
|
||||
ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
|
||||
if (am != null) {
|
||||
@@ -390,9 +407,55 @@ public final class ShutdownThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
// If it's to reboot into recovery, invoke uncrypt via init service.
|
||||
if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
|
||||
uncrypt();
|
||||
}
|
||||
|
||||
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) {
|
||||
// If a radio is wedged, disabling it may hang so we do this work in another thread,
|
||||
// just in case.
|
||||
@@ -537,4 +600,78 @@ public final class ShutdownThread extends Thread {
|
||||
Log.i(TAG, "Performing low-level shutdown...");
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user