Merge "Handle the race condition when calling uncrypt services." am: 42d25b5992

am: 86df8ecdf3

Change-Id: I1dc6ea14b4de99bf65f9efff4f22c0bd99e2f86f
This commit is contained in:
Tao Bao
2016-09-30 20:46:05 +00:00
committed by android-build-merger
3 changed files with 177 additions and 97 deletions

View File

@@ -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);
} }

View File

@@ -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.

View File

@@ -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);