Loading core/java/android/os/IRecoverySystem.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -25,4 +25,5 @@ interface IRecoverySystem { boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener); boolean setupBcb(in String command); boolean clearBcb(); void rebootRecoveryWithCommand(in String command); } core/java/android/os/RecoverySystem.java +23 −18 Original line number Diff line number Diff line Loading @@ -700,7 +700,6 @@ public class RecoverySystem { * @throws IOException if something goes wrong. */ private static void bootCommand(Context context, String... args) throws IOException { synchronized (sRequestLock) { LOG_FILE.delete(); StringBuilder command = new StringBuilder(); Loading @@ -711,18 +710,13 @@ public class RecoverySystem { } } // Write the command into BCB (bootloader control block). RecoverySystem rs = (RecoverySystem) context.getSystemService( Context.RECOVERY_SERVICE); rs.setupBcb(command.toString()); // Having set up the BCB, go ahead and reboot. PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); // Write the command into BCB (bootloader control block) and boot from // there. Will not return unless failed. RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); rs.rebootRecoveryWithCommand(command.toString()); throw new IOException("Reboot failed (no permissions?)"); } } // Read last_install; then report time (in seconds) and I/O (in MiB) for // this update to tron. Loading Loading @@ -915,6 +909,17 @@ public class RecoverySystem { 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 * argv, so we only need to protect against newlines and nulls. Loading services/core/java/com/android/server/RecoverySystemService.java +144 −70 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.IRecoverySystem; import android.os.IRecoverySystemProgressListener; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -50,8 +51,15 @@ public final class RecoverySystemService extends SystemService { // The socket at /dev/socket/uncrypt to communicate with 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 Object sRequestLock = new Object(); private Context mContext; public RecoverySystemService(Context context) { Loading @@ -69,8 +77,15 @@ public final class RecoverySystemService extends SystemService { public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) { if (DEBUG) Slog.d(TAG, "uncrypt: " + filename); synchronized (sRequestLock) { 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 // uncrypt. RecoverySystem.UNCRYPT_PACKAGE_FILE.delete(); Loading @@ -78,8 +93,8 @@ public final class RecoverySystemService extends SystemService { try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) { uncryptFile.write(filename + "\n"); } catch (IOException e) { Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE + "\": ", e); Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e); return false; } Loading Loading @@ -147,18 +162,71 @@ public final class RecoverySystemService extends SystemService { return true; } } @Override // Binder call public boolean clearBcb() { if (DEBUG) Slog.d(TAG, "clearBcb"); synchronized (sRequestLock) { return setupOrClearBcb(false, null); } } @Override // Binder call public boolean setupBcb(String command) { if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]"); synchronized (sRequestLock) { 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() { LocalSocket socket = new LocalSocket(); Loading Loading @@ -190,6 +258,12 @@ public final class RecoverySystemService extends SystemService { private boolean setupOrClearBcb(boolean isSetup, String command) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); final boolean available = checkAndWaitForUncryptService(); if (!available) { Slog.e(TAG, "uncrypt service is unavailable."); return false; } if (isSetup) { SystemProperties.set("ctl.start", "setup-bcb"); } else { Loading Loading
core/java/android/os/IRecoverySystem.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -25,4 +25,5 @@ interface IRecoverySystem { boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener); boolean setupBcb(in String command); boolean clearBcb(); void rebootRecoveryWithCommand(in String command); }
core/java/android/os/RecoverySystem.java +23 −18 Original line number Diff line number Diff line Loading @@ -700,7 +700,6 @@ public class RecoverySystem { * @throws IOException if something goes wrong. */ private static void bootCommand(Context context, String... args) throws IOException { synchronized (sRequestLock) { LOG_FILE.delete(); StringBuilder command = new StringBuilder(); Loading @@ -711,18 +710,13 @@ public class RecoverySystem { } } // Write the command into BCB (bootloader control block). RecoverySystem rs = (RecoverySystem) context.getSystemService( Context.RECOVERY_SERVICE); rs.setupBcb(command.toString()); // Having set up the BCB, go ahead and reboot. PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); // Write the command into BCB (bootloader control block) and boot from // there. Will not return unless failed. RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); rs.rebootRecoveryWithCommand(command.toString()); throw new IOException("Reboot failed (no permissions?)"); } } // Read last_install; then report time (in seconds) and I/O (in MiB) for // this update to tron. Loading Loading @@ -915,6 +909,17 @@ public class RecoverySystem { 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 * argv, so we only need to protect against newlines and nulls. Loading
services/core/java/com/android/server/RecoverySystemService.java +144 −70 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.IRecoverySystem; import android.os.IRecoverySystemProgressListener; import android.os.PowerManager; import android.os.RecoverySystem; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -50,8 +51,15 @@ public final class RecoverySystemService extends SystemService { // The socket at /dev/socket/uncrypt to communicate with 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 Object sRequestLock = new Object(); private Context mContext; public RecoverySystemService(Context context) { Loading @@ -69,8 +77,15 @@ public final class RecoverySystemService extends SystemService { public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) { if (DEBUG) Slog.d(TAG, "uncrypt: " + filename); synchronized (sRequestLock) { 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 // uncrypt. RecoverySystem.UNCRYPT_PACKAGE_FILE.delete(); Loading @@ -78,8 +93,8 @@ public final class RecoverySystemService extends SystemService { try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) { uncryptFile.write(filename + "\n"); } catch (IOException e) { Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE + "\": ", e); Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e); return false; } Loading Loading @@ -147,18 +162,71 @@ public final class RecoverySystemService extends SystemService { return true; } } @Override // Binder call public boolean clearBcb() { if (DEBUG) Slog.d(TAG, "clearBcb"); synchronized (sRequestLock) { return setupOrClearBcb(false, null); } } @Override // Binder call public boolean setupBcb(String command) { if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]"); synchronized (sRequestLock) { 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() { LocalSocket socket = new LocalSocket(); Loading Loading @@ -190,6 +258,12 @@ public final class RecoverySystemService extends SystemService { private boolean setupOrClearBcb(boolean isSetup, String command) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); final boolean available = checkAndWaitForUncryptService(); if (!available) { Slog.e(TAG, "uncrypt service is unavailable."); return false; } if (isSetup) { SystemProperties.set("ctl.start", "setup-bcb"); } else { Loading