Merge "Handle the race condition when calling uncrypt services." am: 42d25b5992
am: 86df8ecdf3
Change-Id: I1dc6ea14b4de99bf65f9efff4f22c0bd99e2f86f
This commit is contained in:
@@ -25,4 +25,5 @@ interface IRecoverySystem {
|
|||||||
boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
|
boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
|
||||||
boolean setupBcb(in String command);
|
boolean setupBcb(in String command);
|
||||||
boolean clearBcb();
|
boolean clearBcb();
|
||||||
|
void rebootRecoveryWithCommand(in String command);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -671,7 +671,6 @@ public class RecoverySystem {
|
|||||||
* @throws IOException if something goes wrong.
|
* @throws IOException if something goes wrong.
|
||||||
*/
|
*/
|
||||||
private static void bootCommand(Context context, String... args) throws IOException {
|
private static void bootCommand(Context context, String... args) throws IOException {
|
||||||
synchronized (sRequestLock) {
|
|
||||||
LOG_FILE.delete();
|
LOG_FILE.delete();
|
||||||
|
|
||||||
StringBuilder command = new StringBuilder();
|
StringBuilder command = new StringBuilder();
|
||||||
@@ -682,18 +681,13 @@ public class RecoverySystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the command into BCB (bootloader control block).
|
// Write the command into BCB (bootloader control block) and boot from
|
||||||
RecoverySystem rs = (RecoverySystem) context.getSystemService(
|
// there. Will not return unless failed.
|
||||||
Context.RECOVERY_SERVICE);
|
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
|
||||||
rs.setupBcb(command.toString());
|
rs.rebootRecoveryWithCommand(command.toString());
|
||||||
|
|
||||||
// Having set up the BCB, go ahead and reboot.
|
|
||||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
pm.reboot(PowerManager.REBOOT_RECOVERY);
|
|
||||||
|
|
||||||
throw new IOException("Reboot failed (no permissions?)");
|
throw new IOException("Reboot failed (no permissions?)");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Read last_install; then report time (in seconds) and I/O (in MiB) for
|
// Read last_install; then report time (in seconds) and I/O (in MiB) for
|
||||||
// this update to tron.
|
// this update to tron.
|
||||||
@@ -886,6 +880,17 @@ public class RecoverySystem {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Talks to RecoverySystemService via Binder to set up the BCB command and
|
||||||
|
* reboot into recovery accordingly.
|
||||||
|
*/
|
||||||
|
private void rebootRecoveryWithCommand(String command) {
|
||||||
|
try {
|
||||||
|
mService.rebootRecoveryWithCommand(command);
|
||||||
|
} catch (RemoteException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internally, recovery treats each line of the command file as a separate
|
* Internally, recovery treats each line of the command file as a separate
|
||||||
* argv, so we only need to protect against newlines and nulls.
|
* argv, so we only need to protect against newlines and nulls.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.net.LocalSocket;
|
|||||||
import android.net.LocalSocketAddress;
|
import android.net.LocalSocketAddress;
|
||||||
import android.os.IRecoverySystem;
|
import android.os.IRecoverySystem;
|
||||||
import android.os.IRecoverySystemProgressListener;
|
import android.os.IRecoverySystemProgressListener;
|
||||||
|
import android.os.PowerManager;
|
||||||
import android.os.RecoverySystem;
|
import android.os.RecoverySystem;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
@@ -50,8 +51,15 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
// The socket at /dev/socket/uncrypt to communicate with uncrypt.
|
// The socket at /dev/socket/uncrypt to communicate with uncrypt.
|
||||||
private static final String UNCRYPT_SOCKET = "uncrypt";
|
private static final String UNCRYPT_SOCKET = "uncrypt";
|
||||||
|
|
||||||
|
// The init services that communicate with /system/bin/uncrypt.
|
||||||
|
private static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";
|
||||||
|
private static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
|
||||||
|
private static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
|
||||||
|
|
||||||
private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
|
private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
|
||||||
|
|
||||||
|
private static final Object sRequestLock = new Object();
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
public RecoverySystemService(Context context) {
|
public RecoverySystemService(Context context) {
|
||||||
@@ -69,8 +77,15 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
|
public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
|
||||||
if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
|
if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
|
||||||
|
|
||||||
|
synchronized (sRequestLock) {
|
||||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
|
||||||
|
|
||||||
|
final boolean available = checkAndWaitForUncryptService();
|
||||||
|
if (!available) {
|
||||||
|
Slog.e(TAG, "uncrypt service is unavailable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Write the filename into UNCRYPT_PACKAGE_FILE to be read by
|
// Write the filename into UNCRYPT_PACKAGE_FILE to be read by
|
||||||
// uncrypt.
|
// uncrypt.
|
||||||
RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
|
RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
|
||||||
@@ -78,8 +93,8 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
|
try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
|
||||||
uncryptFile.write(filename + "\n");
|
uncryptFile.write(filename + "\n");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE +
|
Slog.e(TAG, "IOException when writing \"" +
|
||||||
"\": ", e);
|
RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,18 +162,71 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override // Binder call
|
@Override // Binder call
|
||||||
public boolean clearBcb() {
|
public boolean clearBcb() {
|
||||||
if (DEBUG) Slog.d(TAG, "clearBcb");
|
if (DEBUG) Slog.d(TAG, "clearBcb");
|
||||||
|
synchronized (sRequestLock) {
|
||||||
return setupOrClearBcb(false, null);
|
return setupOrClearBcb(false, null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override // Binder call
|
@Override // Binder call
|
||||||
public boolean setupBcb(String command) {
|
public boolean setupBcb(String command) {
|
||||||
if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
|
if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
|
||||||
|
synchronized (sRequestLock) {
|
||||||
return setupOrClearBcb(true, command);
|
return setupOrClearBcb(true, command);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // Binder call
|
||||||
|
public void rebootRecoveryWithCommand(String command) {
|
||||||
|
if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
|
||||||
|
synchronized (sRequestLock) {
|
||||||
|
if (!setupOrClearBcb(true, command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having set up the BCB, go ahead and reboot.
|
||||||
|
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||||
|
pm.reboot(PowerManager.REBOOT_RECOVERY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if any of the init services is still running. If so, we cannot
|
||||||
|
* start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
|
||||||
|
* it may break the socket communication since init creates / deletes
|
||||||
|
* the socket (/dev/socket/uncrypt) on service start / exit.
|
||||||
|
*/
|
||||||
|
private boolean checkAndWaitForUncryptService() {
|
||||||
|
for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
|
||||||
|
final String uncryptService = SystemProperties.get(INIT_SERVICE_UNCRYPT);
|
||||||
|
final String setupBcbService = SystemProperties.get(INIT_SERVICE_SETUP_BCB);
|
||||||
|
final String clearBcbService = SystemProperties.get(INIT_SERVICE_CLEAR_BCB);
|
||||||
|
final boolean busy = "running".equals(uncryptService) ||
|
||||||
|
"running".equals(setupBcbService) || "running".equals(clearBcbService);
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(TAG, "retry: " + retry + " busy: " + busy +
|
||||||
|
" uncrypt: [" + uncryptService + "]" +
|
||||||
|
" setupBcb: [" + setupBcbService + "]" +
|
||||||
|
" clearBcb: [" + clearBcbService + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!busy) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Slog.w(TAG, "Interrupted:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private LocalSocket connectService() {
|
private LocalSocket connectService() {
|
||||||
LocalSocket socket = new LocalSocket();
|
LocalSocket socket = new LocalSocket();
|
||||||
@@ -176,7 +244,7 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Slog.w(TAG, "Interrupted: ", e);
|
Slog.w(TAG, "Interrupted:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +258,12 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
private boolean setupOrClearBcb(boolean isSetup, String command) {
|
private boolean setupOrClearBcb(boolean isSetup, String command) {
|
||||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
|
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
|
||||||
|
|
||||||
|
final boolean available = checkAndWaitForUncryptService();
|
||||||
|
if (!available) {
|
||||||
|
Slog.e(TAG, "uncrypt service is unavailable.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isSetup) {
|
if (isSetup) {
|
||||||
SystemProperties.set("ctl.start", "setup-bcb");
|
SystemProperties.set("ctl.start", "setup-bcb");
|
||||||
} else {
|
} else {
|
||||||
@@ -232,7 +306,7 @@ public final class RecoverySystemService extends SystemService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.e(TAG, "IOException when communicating with uncrypt: ", e);
|
Slog.e(TAG, "IOException when communicating with uncrypt:", e);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
IoUtils.closeQuietly(dis);
|
IoUtils.closeQuietly(dis);
|
||||||
|
|||||||
Reference in New Issue
Block a user