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

Commit 470d4401 authored by Tao Bao's avatar Tao Bao Committed by android-build-merger
Browse files

Merge "Switch to socket communication with uncrypt." into nyc-dev am: 2e54da0d

am: 4af9a55b

* commit '4af9a55b':
  Switch to socket communication with uncrypt.
parents 3a68e97e 4af9a55b
Loading
Loading
Loading
Loading
+116 −92
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server;

import android.content.Context;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.IRecoverySystem;
import android.os.IRecoverySystemProgressListener;
import android.os.RecoverySystem;
@@ -26,9 +28,11 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;

import java.io.BufferedReader;
import libcore.io.IoUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

@@ -43,10 +47,10 @@ public final class RecoverySystemService extends SystemService {
    private static final String TAG = "RecoverySystemService";
    private static final boolean DEBUG = false;

    // A pipe file to monitor the uncrypt progress.
    private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
    // Temporary command file to communicate between the system server and uncrypt.
    private static final String COMMAND_FILE = "/cache/recovery/command";
    // The socket at /dev/socket/uncrypt to communicate with uncrypt.
    private static final String UNCRYPT_SOCKET = "uncrypt";

    private static final int SOCKET_CONNECTION_MAX_RETRY = 30;

    private Context mContext;

@@ -79,27 +83,22 @@ public final class RecoverySystemService extends SystemService {
                return false;
            }

            // Create the status pipe file to communicate with uncrypt.
            new File(UNCRYPT_STATUS_FILE).delete();
            try {
                Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
            } catch (ErrnoException e) {
                Slog.e(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
                        "\": " + e.getMessage());
                return false;
            }

            // Trigger uncrypt via init.
            SystemProperties.set("ctl.start", "uncrypt");

            // Read the status from the pipe.
            try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) {
            // Connect to the uncrypt service socket.
            LocalSocket socket = connectService();
            if (socket == null) {
                Slog.e(TAG, "Failed to connect to uncrypt socket");
                return false;
            }

            // Read the status from the socket.
            try (DataInputStream dis = new DataInputStream(socket.getInputStream());
                    DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
                int lastStatus = Integer.MIN_VALUE;
                while (true) {
                    String str = reader.readLine();
                    try {
                        int status = Integer.parseInt(str);

                    int status = dis.readInt();
                    // Avoid flooding the log with the same message.
                    if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
                        continue;
@@ -118,21 +117,29 @@ public final class RecoverySystemService extends SystemService {
                        }
                        if (status == 100) {
                            Slog.i(TAG, "uncrypt successfully finished.");
                            // Ack receipt of the final status code. uncrypt
                            // waits for the ack so the socket won't be
                            // destroyed before we receive the code.
                            dos.writeInt(0);
                            dos.flush();
                            break;
                        }
                    } else {
                        // Error in /system/bin/uncrypt.
                        Slog.e(TAG, "uncrypt failed with status: " + status);
                            return false;
                        }
                    } catch (NumberFormatException unused) {
                        Slog.e(TAG, "uncrypt invalid status received: " + str);
                        // Ack receipt of the final status code. uncrypt waits
                        // for the ack so the socket won't be destroyed before
                        // we receive the code.
                        dos.writeInt(0);
                        dos.flush();
                        return false;
                    }
                }
            } catch (IOException unused) {
                Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
            } catch (IOException e) {
                Slog.e(TAG, "IOException when reading status: " + e);
                return false;
            } finally {
                IoUtils.closeQuietly(socket);
            }

            return true;
@@ -150,64 +157,81 @@ public final class RecoverySystemService extends SystemService {
            return setupOrClearBcb(true, command);
        }

        private LocalSocket connectService() {
            LocalSocket socket = new LocalSocket();
            boolean done = false;
            // The uncrypt socket will be created by init upon receiving the
            // service request. It may not be ready by this point. So we will
            // keep retrying until success or reaching timeout.
            for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
                try {
                    socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
                            LocalSocketAddress.Namespace.RESERVED));
                    done = true;
                    break;
                } catch (IOException unused) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        Slog.w(TAG, "Interrupted: " + e);
                    }
                }
            }
            if (!done) {
                Slog.e(TAG, "Timed out connecting to uncrypt socket");
                return null;
            }
            return socket;
        }

        private boolean setupOrClearBcb(boolean isSetup, String command) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);

            if (isSetup) {
                // Set up the command file to be read by uncrypt.
                try (FileWriter commandFile = new FileWriter(COMMAND_FILE)) {
                    commandFile.write(command + "\n");
                } catch (IOException e) {
                    Slog.e(TAG, "IOException when writing \"" + COMMAND_FILE +
                            "\": " + e.getMessage());
                    return false;
                }
                SystemProperties.set("ctl.start", "setup-bcb");
            } else {
                SystemProperties.set("ctl.start", "clear-bcb");
            }

            // Create the status pipe file to communicate with uncrypt.
            new File(UNCRYPT_STATUS_FILE).delete();
            try {
                Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
            } catch (ErrnoException e) {
                Slog.e(TAG, "ErrnoException when creating named pipe \"" +
                        UNCRYPT_STATUS_FILE + "\": " + e.getMessage());
            // Connect to the uncrypt service socket.
            LocalSocket socket = connectService();
            if (socket == null) {
                Slog.e(TAG, "Failed to connect to uncrypt socket");
                return false;
            }

            try (DataInputStream dis = new DataInputStream(socket.getInputStream());
                    DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
                // Send the BCB commands if it's to setup BCB.
                if (isSetup) {
                SystemProperties.set("ctl.start", "setup-bcb");
            } else {
                SystemProperties.set("ctl.start", "clear-bcb");
                    dos.writeInt(command.length());
                    dos.writeBytes(command);
                    dos.flush();
                }

            // Read the status from the pipe.
            try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) {
                while (true) {
                    String str = reader.readLine();
                    try {
                        int status = Integer.parseInt(str);
                // Read the status from the socket.
                int status = dis.readInt();

                // Ack receipt of the status code. uncrypt waits for the ack so
                // the socket won't be destroyed before we receive the code.
                dos.writeInt(0);
                dos.flush();

                if (status == 100) {
                    Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
                            " bcb successfully finished.");
                            break;
                } else {
                    // Error in /system/bin/uncrypt.
                    Slog.e(TAG, "uncrypt failed with status: " + status);
                    return false;
                }
                    } catch (NumberFormatException unused) {
                        Slog.e(TAG, "uncrypt invalid status received: " + str);
                        return false;
                    }
                }
            } catch (IOException unused) {
                Slog.e(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
            } catch (IOException e) {
                Slog.e(TAG, "IOException when getting output stream: " + e);
                return false;
            } finally {
                IoUtils.closeQuietly(socket);
            }

            // Delete the command file as we don't need it anymore.
            new File(COMMAND_FILE).delete();
            return true;
        }
    }