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

Commit a89bd8b7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Adds atomic install support to adb"

parents d75913a3 dc58b0a3
Loading
Loading
Loading
Loading
+191 −0
Original line number Diff line number Diff line
@@ -501,6 +501,197 @@ finalize_session:
    return EXIT_FAILURE;
}

int install_multi_package(int argc, const char** argv) {
    // Find all APK arguments starting at end.
    // All other arguments passed through verbatim.
    int first_apk = -1;
    for (int i = argc - 1; i >= 0; i--) {
        const char* file = argv[i];
        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
            first_apk = i;
        } else {
            break;
        }
    }

    if (first_apk == -1) error_exit("need APK file on command line");

    if (use_legacy_install()) {
        fprintf(stderr, "adb: multi-package install is not supported on this device\n");
        return EXIT_FAILURE;
    }
    std::string install_cmd = "exec:cmd package";

    std::string multi_package_cmd =
            android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());

    // Create multi-package install session
    std::string error;
    int fd = adb_connect(multi_package_cmd, &error);
    if (fd < 0) {
        fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
        return EXIT_FAILURE;
    }
    char buf[BUFSIZ];
    read_status_line(fd, buf, sizeof(buf));
    adb_close(fd);

    int parent_session_id = -1;
    if (!strncmp("Success", buf, 7)) {
        char* start = strrchr(buf, '[');
        char* end = strrchr(buf, ']');
        if (start && end) {
            *end = '\0';
            parent_session_id = strtol(start + 1, nullptr, 10);
        }
    }
    if (parent_session_id < 0) {
        fprintf(stderr, "adb: failed to create multi-package session\n");
        fputs(buf, stderr);
        return EXIT_FAILURE;
    }

    fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);

    std::vector<int> session_ids;

    // Valid session, now create the individual sessions and stream the APKs
    int success = EXIT_FAILURE;
    std::string individual_cmd =
            android::base::StringPrintf("%s install-create", install_cmd.c_str());
    std::string all_session_ids = "";
    for (int i = 1; i < first_apk; i++) {
        individual_cmd += " " + escape_arg(argv[i]);
    }
    std::string cmd = "";
    for (int i = first_apk; i < argc; i++) {
        // Create individual install session
        fd = adb_connect(individual_cmd, &error);
        if (fd < 0) {
            fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
            goto finalize_multi_package_session;
        }
        char buf[BUFSIZ];
        read_status_line(fd, buf, sizeof(buf));
        adb_close(fd);

        int session_id = -1;
        if (!strncmp("Success", buf, 7)) {
            char* start = strrchr(buf, '[');
            char* end = strrchr(buf, ']');
            if (start && end) {
                *end = '\0';
                session_id = strtol(start + 1, nullptr, 10);
            }
        }
        if (session_id < 0) {
            fprintf(stderr, "adb: failed to create multi-package session\n");
            fputs(buf, stderr);
            goto finalize_multi_package_session;
        }

        fprintf(stdout, "Created child session ID %d.\n", session_id);
        session_ids.push_back(session_id);

        const char* file = argv[i];
        struct stat sb;
        if (stat(file, &sb) == -1) {
            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
            goto finalize_multi_package_session;
        }

        std::string cmd =
                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
                                            session_id, i, android::base::Basename(file).c_str());

        int localFd = adb_open(file, O_RDONLY);
        if (localFd < 0) {
            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
            goto finalize_multi_package_session;
        }

        std::string error;
        int remoteFd = adb_connect(cmd, &error);
        if (remoteFd < 0) {
            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
            adb_close(localFd);
            goto finalize_multi_package_session;
        }

        copy_to_file(localFd, remoteFd);
        read_status_line(remoteFd, buf, sizeof(buf));

        adb_close(localFd);
        adb_close(remoteFd);

        if (strncmp("Success", buf, 7)) {
            fprintf(stderr, "adb: failed to write %s\n", file);
            fputs(buf, stderr);
            goto finalize_multi_package_session;
        }

        all_session_ids += android::base::StringPrintf(" %d", session_id);
    }

    cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
                                      parent_session_id, all_session_ids.c_str());
    fd = adb_connect(cmd, &error);
    if (fd < 0) {
        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
        goto finalize_multi_package_session;
    }
    read_status_line(fd, buf, sizeof(buf));
    adb_close(fd);

    if (strncmp("Success", buf, 7)) {
        fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
        fputs(buf, stderr);
        goto finalize_multi_package_session;
    }

    // no failures means we can proceed with the assumption of success
    success = 0;

finalize_multi_package_session:
    // Commit session if we streamed everything okay; otherwise abandon
    std::string service =
            android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
                                        success == 0 ? "commit" : "abandon", parent_session_id);
    fd = adb_connect(service, &error);
    if (fd < 0) {
        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
        return EXIT_FAILURE;
    }
    read_status_line(fd, buf, sizeof(buf));
    adb_close(fd);

    if (!strncmp("Success", buf, 7)) {
        fputs(buf, stdout);
        if (success == 0) {
            return 0;
        }
    } else {
        fprintf(stderr, "adb: failed to finalize session\n");
        fputs(buf, stderr);
    }

    // try to abandon all remaining sessions
    for (std::size_t i = 0; i < session_ids.size(); i++) {
        service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
                                              session_ids[i]);
        fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
        fd = adb_connect(service, &error);
        if (fd < 0) {
            fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
            continue;
        }
        read_status_line(fd, buf, sizeof(buf));
        adb_close(fd);
    }
    return EXIT_FAILURE;
}

int delete_device_file(const std::string& filename) {
    std::string cmd = "rm -f " + escape_arg(filename);
    return send_shell_command(cmd);
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

int install_app(int argc, const char** argv);
int install_multiple_app(int argc, const char** argv);
int install_multi_package(int argc, const char** argv);
int uninstall_app(int argc, const char** argv);

int delete_device_file(const std::string& filename);
+7 −1
Original line number Diff line number Diff line
@@ -144,8 +144,11 @@ static void help() {
        "\n"
        "app installation:\n"
        " install [-lrtsdg] [--instant] PACKAGE\n"
        "     push a single package to the device and install it\n"
        " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
        "     push package(s) to the device and install them\n"
        "     push multiple APKs to the device for a single package and install them\n"
        " install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
        "     push one or more packages to the device and install them atomically\n"
        "     -r: replace existing application\n"
        "     -t: allow test packages\n"
        "     -d: allow version code downgrade (debuggable packages only)\n"
@@ -1734,6 +1737,9 @@ int adb_commandline(int argc, const char** argv) {
    } else if (!strcmp(argv[0], "install-multiple")) {
        if (argc < 2) error_exit("install-multiple requires an argument");
        return install_multiple_app(argc, argv);
    } else if (!strcmp(argv[0], "install-multi-package")) {
        if (argc < 3) error_exit("install-multi-package requires an argument");
        return install_multi_package(argc, argv);
    } else if (!strcmp(argv[0], "uninstall")) {
        if (argc < 2) error_exit("uninstall requires an argument");
        return uninstall_app(argc, argv);