| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| Idries Hamadi | 78330f0 | 2018-09-13 18:00:25 +0100 | [diff] [blame] | 17 | #include "adb_install.h" |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 18 | |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 19 | #include <fcntl.h> |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| Idries Hamadi | 78330f0 | 2018-09-13 18:00:25 +0100 | [diff] [blame] | 22 | #include <unistd.h> |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 23 | #include <algorithm> |
| 24 | #include <iostream> |
| 25 | #include <string> |
| 26 | #include <vector> |
| 27 | |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 28 | #include <android-base/file.h> |
| 29 | #include <android-base/stringprintf.h> |
| 30 | #include <android-base/strings.h> |
| 31 | |
| Idries Hamadi | 78330f0 | 2018-09-13 18:00:25 +0100 | [diff] [blame] | 32 | #include "adb.h" |
| 33 | #include "adb_client.h" |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 34 | #include "adb_unique_fd.h" |
| Idries Hamadi | 78330f0 | 2018-09-13 18:00:25 +0100 | [diff] [blame] | 35 | #include "adb_utils.h" |
| Idries Hamadi | 78330f0 | 2018-09-13 18:00:25 +0100 | [diff] [blame] | 36 | #include "client/file_sync_client.h" |
| 37 | #include "commandline.h" |
| 38 | #include "fastdeploy.h" |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 39 | #include "incremental.h" |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 40 | |
| Idries Hamadi | dc27224 | 2018-08-24 11:46:45 +0100 | [diff] [blame] | 41 | static constexpr int kFastDeployMinApi = 24; |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 42 | static constexpr int kIncrementalMinApi = 29; |
| Idries Hamadi | dc27224 | 2018-08-24 11:46:45 +0100 | [diff] [blame] | 43 | |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 44 | namespace { |
| 45 | |
| 46 | enum InstallMode { |
| 47 | INSTALL_DEFAULT, |
| 48 | INSTALL_PUSH, |
| 49 | INSTALL_STREAM, |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 50 | INSTALL_INCREMENTAL, |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 51 | }; |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 52 | } |
| 53 | |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 54 | static bool can_use_feature(const char* feature) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 55 | FeatureSet features; |
| 56 | std::string error; |
| 57 | if (!adb_get_feature_set(&features, &error)) { |
| 58 | fprintf(stderr, "error: %s\n", error.c_str()); |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 59 | return false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 60 | } |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 61 | return CanUseFeature(features, feature); |
| 62 | } |
| 63 | |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 64 | static InstallMode best_install_mode() { |
| 65 | if (can_use_feature(kFeatureCmd)) { |
| 66 | return INSTALL_STREAM; |
| 67 | } |
| 68 | return INSTALL_PUSH; |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | static bool is_apex_supported() { |
| 72 | return can_use_feature(kFeatureApex); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 73 | } |
| 74 | |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 75 | static bool is_abb_exec_supported() { |
| 76 | return can_use_feature(kFeatureAbbExec); |
| 77 | } |
| 78 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 79 | static int pm_command(int argc, const char** argv) { |
| 80 | std::string cmd = "pm"; |
| 81 | |
| 82 | while (argc-- > 0) { |
| 83 | cmd += " " + escape_arg(*argv++); |
| 84 | } |
| 85 | |
| 86 | return send_shell_command(cmd); |
| 87 | } |
| 88 | |
| 89 | static int uninstall_app_streamed(int argc, const char** argv) { |
| 90 | // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device |
| 91 | std::string cmd = "cmd package"; |
| 92 | while (argc-- > 0) { |
| 93 | // deny the '-k' option until the remaining data/cache can be removed with adb/UI |
| 94 | if (strcmp(*argv, "-k") == 0) { |
| 95 | printf("The -k option uninstalls the application while retaining the " |
| 96 | "data/cache.\n" |
| 97 | "At the moment, there is no way to remove the remaining data.\n" |
| 98 | "You will have to reinstall the application with the same " |
| 99 | "signature, and fully " |
| 100 | "uninstall it.\n" |
| 101 | "If you truly wish to continue, execute 'adb shell cmd package " |
| 102 | "uninstall -k'.\n"); |
| 103 | return EXIT_FAILURE; |
| 104 | } |
| 105 | cmd += " " + escape_arg(*argv++); |
| 106 | } |
| 107 | |
| 108 | return send_shell_command(cmd); |
| 109 | } |
| 110 | |
| 111 | static int uninstall_app_legacy(int argc, const char** argv) { |
| 112 | /* if the user choose the -k option, we refuse to do it until devices are |
| 113 | out with the option to uninstall the remaining data somehow (adb/ui) */ |
| 114 | for (int i = 1; i < argc; i++) { |
| 115 | if (!strcmp(argv[i], "-k")) { |
| 116 | printf("The -k option uninstalls the application while retaining the " |
| 117 | "data/cache.\n" |
| 118 | "At the moment, there is no way to remove the remaining data.\n" |
| 119 | "You will have to reinstall the application with the same " |
| 120 | "signature, and fully " |
| 121 | "uninstall it.\n" |
| 122 | "If you truly wish to continue, execute 'adb shell pm uninstall " |
| 123 | "-k'\n."); |
| 124 | return EXIT_FAILURE; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ |
| 129 | return pm_command(argc, argv); |
| 130 | } |
| 131 | |
| 132 | int uninstall_app(int argc, const char** argv) { |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 133 | if (best_install_mode() == INSTALL_PUSH) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 134 | return uninstall_app_legacy(argc, argv); |
| 135 | } |
| 136 | return uninstall_app_streamed(argc, argv); |
| 137 | } |
| 138 | |
| 139 | static void read_status_line(int fd, char* buf, size_t count) { |
| 140 | count--; |
| 141 | while (count > 0) { |
| 142 | int len = adb_read(fd, buf, count); |
| 143 | if (len <= 0) { |
| 144 | break; |
| 145 | } |
| 146 | |
| 147 | buf += len; |
| 148 | count -= len; |
| 149 | } |
| 150 | *buf = '\0'; |
| 151 | } |
| 152 | |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 153 | static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 154 | printf("Performing Streamed Install\n"); |
| 155 | |
| 156 | // The last argument must be the APK file |
| 157 | const char* file = argv[argc - 1]; |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 158 | if (!android::base::EndsWithIgnoreCase(file, ".apk") && |
| 159 | !android::base::EndsWithIgnoreCase(file, ".apex")) { |
| 160 | error_exit("filename doesn't end .apk or .apex: %s", file); |
| 161 | } |
| 162 | |
| 163 | bool is_apex = false; |
| 164 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { |
| 165 | is_apex = true; |
| 166 | } |
| 167 | if (is_apex && !is_apex_supported()) { |
| 168 | error_exit(".apex is not supported on the target device"); |
| 169 | } |
| 170 | |
| 171 | if (is_apex && use_fastdeploy) { |
| 172 | error_exit("--fastdeploy doesn't support .apex files"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 173 | } |
| 174 | |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 175 | if (use_fastdeploy) { |
| 176 | auto metadata = extract_metadata(file); |
| 177 | if (metadata.has_value()) { |
| 178 | // pass all but 1st (command) and last (apk path) parameters through to pm for |
| 179 | // session creation |
| 180 | std::vector<const char*> pm_args{argv + 1, argv + argc - 1}; |
| 181 | auto patchFd = install_patch(pm_args.size(), pm_args.data()); |
| 182 | return stream_patch(file, std::move(metadata.value()), std::move(patchFd)); |
| Henry Daitx | 7cdf489 | 2019-01-17 16:11:20 +0000 | [diff] [blame] | 183 | } |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 184 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 185 | |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 186 | struct stat sb; |
| 187 | if (stat(file, &sb) == -1) { |
| 188 | fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno)); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 189 | return 1; |
| 190 | } |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 191 | |
| 192 | unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC)); |
| 193 | if (local_fd < 0) { |
| 194 | fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno)); |
| 195 | return 1; |
| 196 | } |
| 197 | |
| 198 | #ifdef __linux__ |
| 199 | posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE); |
| 200 | #endif |
| 201 | |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 202 | const bool use_abb_exec = is_abb_exec_supported(); |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 203 | std::string error; |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 204 | std::vector<std::string> cmd_args = {use_abb_exec ? "package" : "exec:cmd package"}; |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 205 | cmd_args.reserve(argc + 3); |
| 206 | |
| 207 | // don't copy the APK name, but, copy the rest of the arguments as-is |
| 208 | while (argc-- > 1) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 209 | if (use_abb_exec) { |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 210 | cmd_args.push_back(*argv++); |
| 211 | } else { |
| 212 | cmd_args.push_back(escape_arg(*argv++)); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | // add size parameter [required for streaming installs] |
| 217 | // do last to override any user specified value |
| 218 | cmd_args.push_back("-S"); |
| 219 | cmd_args.push_back(android::base::StringPrintf("%" PRIu64, static_cast<uint64_t>(sb.st_size))); |
| 220 | |
| 221 | if (is_apex) { |
| 222 | cmd_args.push_back("--apex"); |
| 223 | } |
| 224 | |
| 225 | unique_fd remote_fd; |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 226 | if (use_abb_exec) { |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 227 | remote_fd = send_abb_exec_command(cmd_args, &error); |
| 228 | } else { |
| 229 | remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error)); |
| 230 | } |
| 231 | if (remote_fd < 0) { |
| 232 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); |
| 233 | return 1; |
| 234 | } |
| 235 | |
| Josh Gao | d7f1d0b | 2019-12-03 16:05:54 -0800 | [diff] [blame] | 236 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { |
| 237 | fprintf(stderr, "adb: failed to install: copy_to_file: %s: %s", file, strerror(errno)); |
| 238 | return 1; |
| 239 | } |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 240 | |
| 241 | char buf[BUFSIZ]; |
| 242 | read_status_line(remote_fd.get(), buf, sizeof(buf)); |
| Josh Gao | d7f1d0b | 2019-12-03 16:05:54 -0800 | [diff] [blame] | 243 | if (strncmp("Success", buf, 7) != 0) { |
| 244 | fprintf(stderr, "adb: failed to install %s: %s", file, buf); |
| 245 | return 1; |
| Alex Buynytskyy | bdff85c | 2019-09-13 14:19:01 -0700 | [diff] [blame] | 246 | } |
| Josh Gao | d7f1d0b | 2019-12-03 16:05:54 -0800 | [diff] [blame] | 247 | |
| 248 | fputs(buf, stdout); |
| 249 | return 0; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 250 | } |
| 251 | |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 252 | static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 253 | printf("Performing Push Install\n"); |
| 254 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 255 | // Find last APK argument. |
| 256 | // All other arguments passed through verbatim. |
| 257 | int last_apk = -1; |
| 258 | for (int i = argc - 1; i >= 0; i--) { |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 259 | if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) { |
| 260 | error_exit("APEX packages are only compatible with Streamed Install"); |
| 261 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 262 | if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) { |
| 263 | last_apk = i; |
| 264 | break; |
| 265 | } |
| 266 | } |
| 267 | |
| Elliott Hughes | 0119a91 | 2018-10-22 17:02:51 -0700 | [diff] [blame] | 268 | if (last_apk == -1) error_exit("need APK file on command line"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 269 | |
| 270 | int result = -1; |
| 271 | std::vector<const char*> apk_file = {argv[last_apk]}; |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 272 | std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 273 | argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 274 | |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 275 | if (use_fastdeploy) { |
| 276 | auto metadata = extract_metadata(apk_file[0]); |
| 277 | if (metadata.has_value()) { |
| 278 | auto patchFd = apply_patch_on_device(apk_dest.c_str()); |
| 279 | int status = stream_patch(apk_file[0], std::move(metadata.value()), std::move(patchFd)); |
| 280 | |
| 281 | result = pm_command(argc, argv); |
| 282 | delete_device_file(apk_dest); |
| 283 | |
| 284 | return status; |
| 285 | } |
| Idries Hamadi | b1702db | 2018-09-06 18:42:39 +0100 | [diff] [blame] | 286 | } |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 287 | |
| 288 | if (do_sync_push(apk_file, apk_dest.c_str(), false)) { |
| 289 | result = pm_command(argc, argv); |
| 290 | delete_device_file(apk_dest); |
| 291 | } |
| 292 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 293 | return result; |
| 294 | } |
| 295 | |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 296 | template <class TimePoint> |
| 297 | static int msBetween(TimePoint start, TimePoint end) { |
| 298 | return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); |
| 299 | } |
| 300 | |
| 301 | static int install_app_incremental(int argc, const char** argv) { |
| 302 | printf("Performing Incremental Install\n"); |
| 303 | using clock = std::chrono::high_resolution_clock; |
| 304 | const auto start = clock::now(); |
| 305 | int first_apk = -1; |
| 306 | int last_apk = -1; |
| 307 | std::string cert_path; |
| 308 | bool wait = false; |
| 309 | std::vector<std::string_view> args = {"package"}; |
| 310 | for (int i = 0; i < argc; ++i) { |
| 311 | const auto arg = std::string_view(argv[i]); |
| 312 | if (android::base::EndsWithIgnoreCase(arg, ".apk")) { |
| 313 | last_apk = i; |
| 314 | if (first_apk == -1) { |
| 315 | first_apk = i; |
| 316 | } |
| 317 | } else if (arg == "--wait") { |
| 318 | wait = true; |
| 319 | } else if (arg.starts_with("install-")) { |
| 320 | // incremental installation command on the device is the same for all its variations in |
| 321 | // the adb, e.g. install-multiple or install-multi-package |
| 322 | args.push_back("install"); |
| 323 | } else { |
| 324 | args.push_back(arg); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | if (first_apk == -1) error_exit("Need at least one APK file on command line"); |
| 329 | |
| 330 | const auto afterApk = clock::now(); |
| 331 | |
| 332 | auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1}); |
| 333 | if (!server_process) { |
| 334 | return -1; |
| 335 | } |
| 336 | |
| 337 | const auto end = clock::now(); |
| 338 | printf("Install command complete (ms: %d total, %d apk prep, %d install)\n", |
| 339 | msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end)); |
| 340 | |
| 341 | if (wait) { |
| 342 | (*server_process).wait(); |
| 343 | } |
| 344 | |
| 345 | return 0; |
| 346 | } |
| 347 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 348 | int install_app(int argc, const char** argv) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 349 | std::vector<int> processedArgIndices; |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 350 | InstallMode installMode = INSTALL_DEFAULT; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 351 | bool use_fastdeploy = false; |
| 352 | bool is_reinstall = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 353 | FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion; |
| 354 | |
| 355 | for (int i = 1; i < argc; i++) { |
| 356 | if (!strcmp(argv[i], "--streaming")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 357 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 358 | installMode = INSTALL_STREAM; |
| 359 | } else if (!strcmp(argv[i], "--no-streaming")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 360 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 361 | installMode = INSTALL_PUSH; |
| 362 | } else if (!strcmp(argv[i], "-r")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 363 | // Note that this argument is not added to processedArgIndices because it |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 364 | // must be passed through to pm |
| 365 | is_reinstall = true; |
| 366 | } else if (!strcmp(argv[i], "--fastdeploy")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 367 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 368 | use_fastdeploy = true; |
| 369 | } else if (!strcmp(argv[i], "--no-fastdeploy")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 370 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 371 | use_fastdeploy = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 372 | } else if (!strcmp(argv[i], "--force-agent")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 373 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 374 | agent_update_strategy = FastDeploy_AgentUpdateAlways; |
| 375 | } else if (!strcmp(argv[i], "--date-check-agent")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 376 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 377 | agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp; |
| 378 | } else if (!strcmp(argv[i], "--version-check-agent")) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 379 | processedArgIndices.push_back(i); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 380 | agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion; |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 381 | } else if (!strcmp(argv[i], "--incremental")) { |
| 382 | processedArgIndices.push_back(i); |
| 383 | installMode = INSTALL_INCREMENTAL; |
| 384 | } else if (!strcmp(argv[i], "--no-incremental")) { |
| 385 | processedArgIndices.push_back(i); |
| 386 | installMode = INSTALL_DEFAULT; |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | if (installMode == INSTALL_INCREMENTAL) { |
| 391 | if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) { |
| 392 | error_exit("Attempting to use incremental install on unsupported device"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 393 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 394 | } |
| 395 | |
| 396 | if (installMode == INSTALL_DEFAULT) { |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 397 | installMode = best_install_mode(); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 398 | } |
| 399 | |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 400 | if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) { |
| Elliott Hughes | 0119a91 | 2018-10-22 17:02:51 -0700 | [diff] [blame] | 401 | error_exit("Attempting to use streaming install on unsupported device"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 402 | } |
| 403 | |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 404 | if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 405 | printf("Fast Deploy is only compatible with devices of API version %d or higher, " |
| 406 | "ignoring.\n", |
| 407 | kFastDeployMinApi); |
| 408 | use_fastdeploy = false; |
| 409 | } |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 410 | fastdeploy_set_agent_update_strategy(agent_update_strategy); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 411 | |
| 412 | std::vector<const char*> passthrough_argv; |
| 413 | for (int i = 0; i < argc; i++) { |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 414 | if (std::find(processedArgIndices.begin(), processedArgIndices.end(), i) == |
| 415 | processedArgIndices.end()) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 416 | passthrough_argv.push_back(argv[i]); |
| 417 | } |
| 418 | } |
| Henry Daitx | e4cc4d9 | 2018-12-12 10:40:57 +0000 | [diff] [blame] | 419 | if (passthrough_argv.size() < 2) { |
| 420 | error_exit("install requires an apk argument"); |
| 421 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 422 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 423 | switch (installMode) { |
| 424 | case INSTALL_PUSH: |
| 425 | return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(), |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 426 | use_fastdeploy); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 427 | case INSTALL_STREAM: |
| 428 | return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(), |
| Alex Buynytskyy | 1af550e | 2019-09-16 12:10:54 -0700 | [diff] [blame] | 429 | use_fastdeploy); |
| Alex Buynytskyy | 175ce29 | 2020-02-13 06:52:04 -0800 | [diff] [blame^] | 430 | case INSTALL_INCREMENTAL: |
| 431 | return install_app_incremental(passthrough_argv.size(), passthrough_argv.data()); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 432 | case INSTALL_DEFAULT: |
| 433 | default: |
| 434 | return 1; |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | int install_multiple_app(int argc, const char** argv) { |
| 439 | // Find all APK arguments starting at end. |
| 440 | // All other arguments passed through verbatim. |
| 441 | int first_apk = -1; |
| 442 | uint64_t total_size = 0; |
| 443 | for (int i = argc - 1; i >= 0; i--) { |
| 444 | const char* file = argv[i]; |
| Dario Freni | dcb4c36 | 2018-10-04 16:26:40 +0100 | [diff] [blame] | 445 | if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) { |
| 446 | error_exit("APEX packages are not compatible with install-multiple"); |
| 447 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 448 | |
| Victor Hsieh | 88f14b8 | 2018-10-04 10:46:56 -0700 | [diff] [blame] | 449 | if (android::base::EndsWithIgnoreCase(file, ".apk") || |
| Victor Hsieh | 5517487 | 2018-10-29 16:54:23 -0700 | [diff] [blame] | 450 | android::base::EndsWithIgnoreCase(file, ".dm") || |
| 451 | android::base::EndsWithIgnoreCase(file, ".fsv_sig")) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 452 | struct stat sb; |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 453 | if (stat(file, &sb) == -1) perror_exit("failed to stat \"%s\"", file); |
| 454 | total_size += sb.st_size; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 455 | first_apk = i; |
| 456 | } else { |
| 457 | break; |
| 458 | } |
| 459 | } |
| 460 | |
| Elliott Hughes | 0119a91 | 2018-10-22 17:02:51 -0700 | [diff] [blame] | 461 | if (first_apk == -1) error_exit("need APK file on command line"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 462 | |
| 463 | std::string install_cmd; |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 464 | if (best_install_mode() == INSTALL_PUSH) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 465 | install_cmd = "exec:pm"; |
| 466 | } else { |
| 467 | install_cmd = "exec:cmd package"; |
| 468 | } |
| 469 | |
| 470 | std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, |
| 471 | install_cmd.c_str(), total_size); |
| 472 | for (int i = 1; i < first_apk; i++) { |
| 473 | cmd += " " + escape_arg(argv[i]); |
| 474 | } |
| 475 | |
| 476 | // Create install session |
| 477 | std::string error; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 478 | char buf[BUFSIZ]; |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 479 | { |
| 480 | unique_fd fd(adb_connect(cmd, &error)); |
| 481 | if (fd < 0) { |
| 482 | fprintf(stderr, "adb: connect error for create: %s\n", error.c_str()); |
| 483 | return EXIT_FAILURE; |
| 484 | } |
| 485 | read_status_line(fd.get(), buf, sizeof(buf)); |
| 486 | } |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 487 | |
| 488 | int session_id = -1; |
| 489 | if (!strncmp("Success", buf, 7)) { |
| 490 | char* start = strrchr(buf, '['); |
| 491 | char* end = strrchr(buf, ']'); |
| 492 | if (start && end) { |
| 493 | *end = '\0'; |
| 494 | session_id = strtol(start + 1, nullptr, 10); |
| 495 | } |
| 496 | } |
| 497 | if (session_id < 0) { |
| 498 | fprintf(stderr, "adb: failed to create session\n"); |
| 499 | fputs(buf, stderr); |
| 500 | return EXIT_FAILURE; |
| 501 | } |
| 502 | |
| 503 | // Valid session, now stream the APKs |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 504 | bool success = true; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 505 | for (int i = first_apk; i < argc; i++) { |
| 506 | const char* file = argv[i]; |
| 507 | struct stat sb; |
| 508 | if (stat(file, &sb) == -1) { |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 509 | fprintf(stderr, "adb: failed to stat \"%s\": %s\n", file, strerror(errno)); |
| 510 | success = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 511 | goto finalize_session; |
| 512 | } |
| 513 | |
| 514 | std::string cmd = |
| Victor Hsieh | 88f14b8 | 2018-10-04 10:46:56 -0700 | [diff] [blame] | 515 | android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -", |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 516 | install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), |
| Victor Hsieh | 88f14b8 | 2018-10-04 10:46:56 -0700 | [diff] [blame] | 517 | session_id, android::base::Basename(file).c_str()); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 518 | |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 519 | unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC)); |
| 520 | if (local_fd < 0) { |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 521 | fprintf(stderr, "adb: failed to open \"%s\": %s\n", file, strerror(errno)); |
| 522 | success = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 523 | goto finalize_session; |
| 524 | } |
| 525 | |
| 526 | std::string error; |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 527 | unique_fd remote_fd(adb_connect(cmd, &error)); |
| 528 | if (remote_fd < 0) { |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 529 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 530 | success = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 531 | goto finalize_session; |
| 532 | } |
| 533 | |
| Josh Gao | d7f1d0b | 2019-12-03 16:05:54 -0800 | [diff] [blame] | 534 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { |
| 535 | fprintf(stderr, "adb: failed to write \"%s\": %s\n", file, strerror(errno)); |
| 536 | success = false; |
| 537 | goto finalize_session; |
| 538 | } |
| 539 | |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 540 | read_status_line(remote_fd.get(), buf, sizeof(buf)); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 541 | |
| 542 | if (strncmp("Success", buf, 7)) { |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 543 | fprintf(stderr, "adb: failed to write \"%s\"\n", file); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 544 | fputs(buf, stderr); |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 545 | success = false; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 546 | goto finalize_session; |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | finalize_session: |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 551 | // Commit session if we streamed everything okay; otherwise abandon. |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 552 | std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(), |
| 553 | success ? "commit" : "abandon", session_id); |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 554 | { |
| 555 | unique_fd fd(adb_connect(service, &error)); |
| 556 | if (fd < 0) { |
| 557 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); |
| 558 | return EXIT_FAILURE; |
| 559 | } |
| 560 | read_status_line(fd.get(), buf, sizeof(buf)); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 561 | } |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 562 | if (!success) return EXIT_FAILURE; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 563 | |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 564 | if (strncmp("Success", buf, 7)) { |
| 565 | fprintf(stderr, "adb: failed to finalize session\n"); |
| 566 | fputs(buf, stderr); |
| 567 | return EXIT_FAILURE; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 568 | } |
| Elliott Hughes | b6ebbe2 | 2019-08-05 17:06:04 -0700 | [diff] [blame] | 569 | |
| 570 | fputs(buf, stdout); |
| 571 | return EXIT_SUCCESS; |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 572 | } |
| 573 | |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 574 | int install_multi_package(int argc, const char** argv) { |
| 575 | // Find all APK arguments starting at end. |
| 576 | // All other arguments passed through verbatim. |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 577 | bool apex_found = false; |
| 578 | int first_package = -1; |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 579 | for (int i = argc - 1; i >= 0; i--) { |
| 580 | const char* file = argv[i]; |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 581 | if (android::base::EndsWithIgnoreCase(file, ".apk") || |
| 582 | android::base::EndsWithIgnoreCase(file, ".apex")) { |
| 583 | first_package = i; |
| 584 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { |
| 585 | apex_found = true; |
| 586 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 587 | } else { |
| 588 | break; |
| 589 | } |
| 590 | } |
| 591 | |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 592 | if (first_package == -1) error_exit("need APK or APEX files on command line"); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 593 | |
| Yurii Zubrytskyi | e59c1bd | 2019-06-27 13:47:34 -0700 | [diff] [blame] | 594 | if (best_install_mode() == INSTALL_PUSH) { |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 595 | fprintf(stderr, "adb: multi-package install is not supported on this device\n"); |
| 596 | return EXIT_FAILURE; |
| 597 | } |
| 598 | std::string install_cmd = "exec:cmd package"; |
| 599 | |
| 600 | std::string multi_package_cmd = |
| 601 | android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str()); |
| Dario Freni | 2042fd2 | 2019-01-29 14:17:05 +0000 | [diff] [blame] | 602 | for (int i = 1; i < first_package; i++) { |
| 603 | multi_package_cmd += " " + escape_arg(argv[i]); |
| 604 | } |
| 605 | |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 606 | if (apex_found) { |
| 607 | multi_package_cmd += " --staged"; |
| 608 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 609 | |
| 610 | // Create multi-package install session |
| 611 | std::string error; |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 612 | char buf[BUFSIZ]; |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 613 | { |
| 614 | unique_fd fd(adb_connect(multi_package_cmd, &error)); |
| 615 | if (fd < 0) { |
| 616 | fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str()); |
| 617 | return EXIT_FAILURE; |
| 618 | } |
| 619 | read_status_line(fd.get(), buf, sizeof(buf)); |
| 620 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 621 | |
| 622 | int parent_session_id = -1; |
| 623 | if (!strncmp("Success", buf, 7)) { |
| 624 | char* start = strrchr(buf, '['); |
| 625 | char* end = strrchr(buf, ']'); |
| 626 | if (start && end) { |
| 627 | *end = '\0'; |
| 628 | parent_session_id = strtol(start + 1, nullptr, 10); |
| 629 | } |
| 630 | } |
| 631 | if (parent_session_id < 0) { |
| 632 | fprintf(stderr, "adb: failed to create multi-package session\n"); |
| 633 | fputs(buf, stderr); |
| 634 | return EXIT_FAILURE; |
| 635 | } |
| 636 | |
| 637 | fprintf(stdout, "Created parent session ID %d.\n", parent_session_id); |
| 638 | |
| 639 | std::vector<int> session_ids; |
| 640 | |
| 641 | // Valid session, now create the individual sessions and stream the APKs |
| 642 | int success = EXIT_FAILURE; |
| 643 | std::string individual_cmd = |
| 644 | android::base::StringPrintf("%s install-create", install_cmd.c_str()); |
| 645 | std::string all_session_ids = ""; |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 646 | for (int i = 1; i < first_package; i++) { |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 647 | individual_cmd += " " + escape_arg(argv[i]); |
| 648 | } |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 649 | if (apex_found) { |
| 650 | individual_cmd += " --staged"; |
| 651 | } |
| 652 | std::string individual_apex_cmd = individual_cmd + " --apex"; |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 653 | std::string cmd = ""; |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 654 | for (int i = first_package; i < argc; i++) { |
| 655 | const char* file = argv[i]; |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 656 | char buf[BUFSIZ]; |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 657 | { |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 658 | unique_fd fd; |
| 659 | // Create individual install session |
| 660 | if (android::base::EndsWithIgnoreCase(file, ".apex")) { |
| 661 | fd.reset(adb_connect(individual_apex_cmd, &error)); |
| 662 | } else { |
| 663 | fd.reset(adb_connect(individual_cmd, &error)); |
| 664 | } |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 665 | if (fd < 0) { |
| 666 | fprintf(stderr, "adb: connect error for create: %s\n", error.c_str()); |
| 667 | goto finalize_multi_package_session; |
| 668 | } |
| 669 | read_status_line(fd.get(), buf, sizeof(buf)); |
| 670 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 671 | |
| 672 | int session_id = -1; |
| 673 | if (!strncmp("Success", buf, 7)) { |
| 674 | char* start = strrchr(buf, '['); |
| 675 | char* end = strrchr(buf, ']'); |
| 676 | if (start && end) { |
| 677 | *end = '\0'; |
| 678 | session_id = strtol(start + 1, nullptr, 10); |
| 679 | } |
| 680 | } |
| 681 | if (session_id < 0) { |
| 682 | fprintf(stderr, "adb: failed to create multi-package session\n"); |
| 683 | fputs(buf, stderr); |
| 684 | goto finalize_multi_package_session; |
| 685 | } |
| 686 | |
| 687 | fprintf(stdout, "Created child session ID %d.\n", session_id); |
| 688 | session_ids.push_back(session_id); |
| 689 | |
| Dario Freni | 7d5a5e1 | 2019-02-25 12:00:32 +0000 | [diff] [blame] | 690 | // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument. |
| 691 | std::vector<std::string> splits = android::base::Split(file, ":"); |
| 692 | |
| 693 | for (const std::string& split : splits) { |
| 694 | struct stat sb; |
| 695 | if (stat(split.c_str(), &sb) == -1) { |
| 696 | fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno)); |
| 697 | goto finalize_multi_package_session; |
| 698 | } |
| 699 | |
| 700 | std::string cmd = android::base::StringPrintf( |
| 701 | "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(), |
| 702 | static_cast<uint64_t>(sb.st_size), session_id, i, |
| 703 | android::base::Basename(split).c_str()); |
| 704 | |
| 705 | unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC)); |
| 706 | if (local_fd < 0) { |
| 707 | fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno)); |
| 708 | goto finalize_multi_package_session; |
| 709 | } |
| 710 | |
| 711 | std::string error; |
| 712 | unique_fd remote_fd(adb_connect(cmd, &error)); |
| 713 | if (remote_fd < 0) { |
| 714 | fprintf(stderr, "adb: connect error for write: %s\n", error.c_str()); |
| 715 | goto finalize_multi_package_session; |
| 716 | } |
| 717 | |
| Josh Gao | d7f1d0b | 2019-12-03 16:05:54 -0800 | [diff] [blame] | 718 | if (!copy_to_file(local_fd.get(), remote_fd.get())) { |
| 719 | fprintf(stderr, "adb: failed to write %s: %s\n", split.c_str(), strerror(errno)); |
| 720 | goto finalize_multi_package_session; |
| 721 | } |
| 722 | |
| Dario Freni | 7d5a5e1 | 2019-02-25 12:00:32 +0000 | [diff] [blame] | 723 | read_status_line(remote_fd.get(), buf, sizeof(buf)); |
| 724 | |
| 725 | if (strncmp("Success", buf, 7)) { |
| 726 | fprintf(stderr, "adb: failed to write %s\n", split.c_str()); |
| 727 | fputs(buf, stderr); |
| 728 | goto finalize_multi_package_session; |
| 729 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 730 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 731 | all_session_ids += android::base::StringPrintf(" %d", session_id); |
| 732 | } |
| 733 | |
| 734 | cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(), |
| 735 | parent_session_id, all_session_ids.c_str()); |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 736 | { |
| 737 | unique_fd fd(adb_connect(cmd, &error)); |
| 738 | if (fd < 0) { |
| Dario Freni | 907ef68 | 2019-01-14 17:06:32 +0000 | [diff] [blame] | 739 | fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str()); |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 740 | goto finalize_multi_package_session; |
| 741 | } |
| 742 | read_status_line(fd.get(), buf, sizeof(buf)); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 743 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 744 | |
| 745 | if (strncmp("Success", buf, 7)) { |
| 746 | fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str()); |
| 747 | fputs(buf, stderr); |
| 748 | goto finalize_multi_package_session; |
| 749 | } |
| 750 | |
| 751 | // no failures means we can proceed with the assumption of success |
| 752 | success = 0; |
| 753 | |
| 754 | finalize_multi_package_session: |
| 755 | // Commit session if we streamed everything okay; otherwise abandon |
| 756 | std::string service = |
| 757 | android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(), |
| 758 | success == 0 ? "commit" : "abandon", parent_session_id); |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 759 | { |
| 760 | unique_fd fd(adb_connect(service, &error)); |
| 761 | if (fd < 0) { |
| 762 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); |
| 763 | return EXIT_FAILURE; |
| 764 | } |
| 765 | read_status_line(fd.get(), buf, sizeof(buf)); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 766 | } |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 767 | |
| 768 | if (!strncmp("Success", buf, 7)) { |
| 769 | fputs(buf, stdout); |
| 770 | if (success == 0) { |
| 771 | return 0; |
| 772 | } |
| 773 | } else { |
| 774 | fprintf(stderr, "adb: failed to finalize session\n"); |
| 775 | fputs(buf, stderr); |
| 776 | } |
| 777 | |
| Dario Freni | 7d5a5e1 | 2019-02-25 12:00:32 +0000 | [diff] [blame] | 778 | session_ids.push_back(parent_session_id); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 779 | // try to abandon all remaining sessions |
| 780 | for (std::size_t i = 0; i < session_ids.size(); i++) { |
| 781 | service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(), |
| 782 | session_ids[i]); |
| 783 | fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]); |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 784 | unique_fd fd(adb_connect(service, &error)); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 785 | if (fd < 0) { |
| 786 | fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str()); |
| 787 | continue; |
| 788 | } |
| Josh Gao | b915597 | 2019-01-11 13:13:20 -0800 | [diff] [blame] | 789 | read_status_line(fd.get(), buf, sizeof(buf)); |
| Patrick Baumann | 810ee9b | 2018-10-09 10:45:03 -0700 | [diff] [blame] | 790 | } |
| 791 | return EXIT_FAILURE; |
| 792 | } |
| 793 | |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 794 | int delete_device_file(const std::string& filename) { |
| Elliott Hughes | 203f537 | 2019-08-02 16:06:06 -0700 | [diff] [blame] | 795 | // http://b/17339227 "Sideloading a Readonly File Results in a Prompt to |
| 796 | // Delete" caused us to add `-f` here, to avoid the equivalent of the `-i` |
| 797 | // prompt that you get from BSD rm (used in Android 5) if you have a |
| 798 | // non-writable file and stdin is a tty (which is true for old versions of |
| 799 | // adbd). |
| 800 | // |
| 801 | // Unfortunately, `rm -f` requires Android 4.3, so that workaround broke |
| 802 | // earlier Android releases. This was reported as http://b/37704384 "adb |
| 803 | // install -r passes invalid argument to rm on Android 4.1" and |
| 804 | // http://b/37035817 "ADB Fails: rm failed for -f, No such file or |
| 805 | // directory". |
| 806 | // |
| 807 | // Testing on a variety of devices and emulators shows that redirecting |
| 808 | // stdin is sufficient to avoid the pseudo-`-i`, and works on toolbox, |
| 809 | // BSD, and toybox versions of rm. |
| 810 | return send_shell_command("rm " + escape_arg(filename) + " </dev/null"); |
| Idries Hamadi | 1ecee44 | 2018-01-29 16:30:36 +0000 | [diff] [blame] | 811 | } |