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

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

Merge "Switch to socket communication with uncrypt." into nyc-dev

parents 96700fc0 dd3baae7
Loading
Loading
Loading
Loading
+116 −92
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server;
package com.android.server;


import android.content.Context;
import android.content.Context;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.IRecoverySystem;
import android.os.IRecoverySystem;
import android.os.IRecoverySystemProgressListener;
import android.os.IRecoverySystemProgressListener;
import android.os.RecoverySystem;
import android.os.RecoverySystem;
@@ -26,9 +28,11 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.Os;
import android.util.Slog;
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.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.IOException;


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


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

    private static final String COMMAND_FILE = "/cache/recovery/command";
    private static final int SOCKET_CONNECTION_MAX_RETRY = 30;


    private Context mContext;
    private Context mContext;


@@ -79,27 +83,22 @@ public final class RecoverySystemService extends SystemService {
                return false;
                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.
            // Trigger uncrypt via init.
            SystemProperties.set("ctl.start", "uncrypt");
            SystemProperties.set("ctl.start", "uncrypt");


            // Read the status from the pipe.
            // Connect to the uncrypt service socket.
            try (BufferedReader reader = new BufferedReader(new FileReader(UNCRYPT_STATUS_FILE))) {
            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;
                int lastStatus = Integer.MIN_VALUE;
                while (true) {
                while (true) {
                    String str = reader.readLine();
                    int status = dis.readInt();
                    try {
                        int status = Integer.parseInt(str);

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


            return true;
            return true;
@@ -150,64 +157,81 @@ public final class RecoverySystemService extends SystemService {
            return setupOrClearBcb(true, command);
            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) {
        private boolean setupOrClearBcb(boolean isSetup, String command) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);


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


            // Create the status pipe file to communicate with uncrypt.
            // Connect to the uncrypt service socket.
            new File(UNCRYPT_STATUS_FILE).delete();
            LocalSocket socket = connectService();
            try {
            if (socket == null) {
                Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
                Slog.e(TAG, "Failed to connect to uncrypt socket");
            } catch (ErrnoException e) {
                Slog.e(TAG, "ErrnoException when creating named pipe \"" +
                        UNCRYPT_STATUS_FILE + "\": " + e.getMessage());
                return false;
                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) {
                if (isSetup) {
                SystemProperties.set("ctl.start", "setup-bcb");
                    dos.writeInt(command.length());
            } else {
                    dos.writeBytes(command);
                SystemProperties.set("ctl.start", "clear-bcb");
                    dos.flush();
                }
                }


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

                    String str = reader.readLine();
                // Ack receipt of the status code. uncrypt waits for the ack so
                    try {
                // the socket won't be destroyed before we receive the code.
                        int status = Integer.parseInt(str);
                dos.writeInt(0);
                dos.flush();


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


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