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

Commit 9bd481b3 authored by Tao Bao's avatar Tao Bao Committed by android-build-merger
Browse files

Handle the race condition when calling uncrypt services.

am: 1eabf534

Change-Id: Ia5ac4f91618c1cf4aee67d069542676cf0a5e787
parents d6846db2 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 {