Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 473b048c authored by Tao Bao's avatar Tao Bao Committed by Android (Google) Code Review
Browse files

Merge "Handle the race condition when calling uncrypt services." into nyc-mr1-dev

parents 00515f8e 1eabf534
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
}
+23 −18
Original line number Diff line number Diff line
@@ -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();
@@ -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.
@@ -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.
+144 −70
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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();
@@ -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;
                }

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