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

Commit c982e4e3 authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Android (Google) Code Review
Browse files

Merge "Multi-file streaming installation."

parents 52bf7227 43dd0b9a
Loading
Loading
Loading
Loading
+86 −41
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ import java.util.concurrent.TimeUnit;
class PackageManagerShellCommand extends ShellCommand {
    /** Path for streaming APK content */
    private static final String STDIN_PATH = "-";
    private static final byte[] STDIN_PATH_BYTES = "-".getBytes(StandardCharsets.UTF_8);
    /** Path where ART profiles snapshots are dumped for the shell user */
    private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
    private static final int DEFAULT_WAIT_MS = 60 * 1000;
@@ -472,6 +473,7 @@ class PackageManagerShellCommand extends ShellCommand {
                }
            }
        }

        params.sessionParams.setSize(sessionSize);
    }
    /**
@@ -1170,59 +1172,54 @@ class PackageManagerShellCommand extends ShellCommand {

    private int doRunInstall(final InstallParams params) throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        final boolean streaming = params.sessionParams.dataLoaderParams != null;

        ArrayList<String> inPaths = getRemainingArgs();
        if (inPaths.isEmpty()) {
            inPaths.add(STDIN_PATH);
        }
        final boolean isStreaming = params.sessionParams.dataLoaderParams != null;
        final boolean isApex =
                (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;

        final boolean hasSplits = inPaths.size() > 1;
        ArrayList<String> args = getRemainingArgs();

        if (STDIN_PATH.equals(inPaths.get(0))) {
            if (hasSplits) {
                pw.println("Error: can't specify SPLIT(s) along with STDIN");
                return 1;
            }
            if (params.sessionParams.sizeBytes == -1) {
        final boolean fromStdIn = args.isEmpty() || STDIN_PATH.equals(args.get(0));
        final boolean hasSplits = args.size() > 1;

        if (fromStdIn && params.sessionParams.sizeBytes == -1) {
            pw.println("Error: must either specify a package size or an APK file");
            return 1;
        }
        }

        final boolean isApex =
                (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
        if (isApex && hasSplits) {
            pw.println("Error: can't specify SPLIT(s) for APEX");
            return 1;
        }

        if (!streaming) {
            setParamsSize(params, inPaths);
        if (!isStreaming) {
            if (fromStdIn && hasSplits) {
                pw.println("Error: can't specify SPLIT(s) along with STDIN");
                return 1;
            }

            if (args.isEmpty()) {
                args.add(STDIN_PATH);
            } else {
                setParamsSize(params, args);
            }
        }

        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);
        boolean abandonSession = true;
        try {
            for (String inPath : inPaths) {
                if (streaming) {
                    String name = new File(inPath).getName();
                    byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
                    if (doAddFile(sessionId, name, params.sessionParams.sizeBytes, metadata,
                            false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
            if (isStreaming) {
                if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)
                        != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
            } else {
                    String splitName = hasSplits ? new File(inPath).getName()
                            : "base." + (isApex ? "apex" : "apk");

                    if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
                            false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                if (doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex)
                        != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
            }
            }
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
@@ -2956,23 +2953,71 @@ class PackageManagerShellCommand extends ShellCommand {
        return sessionId;
    }

    private int doAddFile(int sessionId, String name, long sizeBytes, byte[] metadata,
            boolean logSuccess) throws RemoteException {
    private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
            boolean isApex) throws RemoteException {
        PackageInstaller.Session session = new PackageInstaller.Session(
                mInterface.getPackageInstaller().openSession(sessionId));
        try {
            session.addFile(name, sizeBytes, metadata);
            // 1. Single file from stdin.
            if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
                String name = "base." + (isApex ? "apex" : "apk");
                session.addFile(name, sessionSizeBytes, STDIN_PATH_BYTES);
                return 0;
            }

            if (logSuccess) {
                getOutPrintWriter().println("Success");
            for (String arg : args) {
                final int delimLocation = arg.indexOf(':');

                // 2. File with specified size read from stdin.
                if (delimLocation != -1) {
                    String name = arg.substring(0, delimLocation);
                    String sizeStr = arg.substring(delimLocation + 1);
                    long sizeBytes;

                    if (TextUtils.isEmpty(name)) {
                        getErrPrintWriter().println("Empty file name in: " + arg);
                        return 1;
                    }
                    try {
                        sizeBytes = Long.parseUnsignedLong(sizeStr);
                    } catch (NumberFormatException e) {
                        getErrPrintWriter().println("Unable to parse size from: " + arg);
                        return 1;
                    }

                    session.addFile(name, sizeBytes, STDIN_PATH_BYTES);
                    continue;
                }

                // 3. Local file.
                final String inPath = arg;

                String name = new File(inPath).getName();
                byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);

                session.addFile(name, -1, metadata);
            }
            return 0;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

    private int doWriteSplits(int sessionId, ArrayList<String> splitPaths, long sessionSizeBytes,
            boolean isApex) throws RemoteException {
        final boolean multipleSplits = splitPaths.size() > 1;
        for (String splitPath : splitPaths) {
            String splitName = multipleSplits ? new File(splitPath).getName()
                    : "base." + (isApex ? "apex" : "apk");

            if (doWriteSplit(sessionId, splitPath, sessionSizeBytes, splitName,
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
        }
        return 0;
    }

    private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
            boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;