blob: 022666ff3b866798f72382609f3d2c0643575fda [file] [log] [blame]
Idries Hamadi1ecee442018-01-29 16:30:36 +00001/*
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 Hamadi78330f02018-09-13 18:00:25 +010017#include "adb_install.h"
Idries Hamadi1ecee442018-01-29 16:30:36 +000018
19#include <stdio.h>
20#include <stdlib.h>
Idries Hamadi78330f02018-09-13 18:00:25 +010021#include <unistd.h>
Idries Hamadi1ecee442018-01-29 16:30:36 +000022#include <algorithm>
23#include <iostream>
24#include <string>
25#include <vector>
26
Idries Hamadi78330f02018-09-13 18:00:25 +010027#include "adb.h"
28#include "adb_client.h"
29#include "adb_utils.h"
30#include "android-base/file.h"
31#include "android-base/stringprintf.h"
32#include "android-base/strings.h"
33#include "android-base/test_utils.h"
34#include "client/file_sync_client.h"
35#include "commandline.h"
36#include "fastdeploy.h"
37#include "sysdeps.h"
Idries Hamadi1ecee442018-01-29 16:30:36 +000038
Idries Hamadidc272242018-08-24 11:46:45 +010039static constexpr int kFastDeployMinApi = 24;
40
Idries Hamadi1ecee442018-01-29 16:30:36 +000041static bool _use_legacy_install() {
42 FeatureSet features;
43 std::string error;
44 if (!adb_get_feature_set(&features, &error)) {
45 fprintf(stderr, "error: %s\n", error.c_str());
46 return true;
47 }
48 return !CanUseFeature(features, kFeatureCmd);
49}
50
51static int pm_command(int argc, const char** argv) {
52 std::string cmd = "pm";
53
54 while (argc-- > 0) {
55 cmd += " " + escape_arg(*argv++);
56 }
57
58 return send_shell_command(cmd);
59}
60
61static int uninstall_app_streamed(int argc, const char** argv) {
62 // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
63 std::string cmd = "cmd package";
64 while (argc-- > 0) {
65 // deny the '-k' option until the remaining data/cache can be removed with adb/UI
66 if (strcmp(*argv, "-k") == 0) {
67 printf("The -k option uninstalls the application while retaining the "
68 "data/cache.\n"
69 "At the moment, there is no way to remove the remaining data.\n"
70 "You will have to reinstall the application with the same "
71 "signature, and fully "
72 "uninstall it.\n"
73 "If you truly wish to continue, execute 'adb shell cmd package "
74 "uninstall -k'.\n");
75 return EXIT_FAILURE;
76 }
77 cmd += " " + escape_arg(*argv++);
78 }
79
80 return send_shell_command(cmd);
81}
82
83static int uninstall_app_legacy(int argc, const char** argv) {
84 /* if the user choose the -k option, we refuse to do it until devices are
85 out with the option to uninstall the remaining data somehow (adb/ui) */
86 for (int i = 1; i < argc; i++) {
87 if (!strcmp(argv[i], "-k")) {
88 printf("The -k option uninstalls the application while retaining the "
89 "data/cache.\n"
90 "At the moment, there is no way to remove the remaining data.\n"
91 "You will have to reinstall the application with the same "
92 "signature, and fully "
93 "uninstall it.\n"
94 "If you truly wish to continue, execute 'adb shell pm uninstall "
95 "-k'\n.");
96 return EXIT_FAILURE;
97 }
98 }
99
100 /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
101 return pm_command(argc, argv);
102}
103
104int uninstall_app(int argc, const char** argv) {
105 if (_use_legacy_install()) {
106 return uninstall_app_legacy(argc, argv);
107 }
108 return uninstall_app_streamed(argc, argv);
109}
110
111static void read_status_line(int fd, char* buf, size_t count) {
112 count--;
113 while (count > 0) {
114 int len = adb_read(fd, buf, count);
115 if (len <= 0) {
116 break;
117 }
118
119 buf += len;
120 count -= len;
121 }
122 *buf = '\0';
123}
124
125static int delete_device_patch_file(const char* apkPath) {
126 std::string patchDevicePath = get_patch_path(apkPath);
127 return delete_device_file(patchDevicePath);
128}
129
Idries Hamadi1ecee442018-01-29 16:30:36 +0000130static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
Idries Hamadi7919be22018-08-28 12:58:09 +0100131 bool use_localagent) {
Idries Hamadi1ecee442018-01-29 16:30:36 +0000132 printf("Performing Streamed Install\n");
133
134 // The last argument must be the APK file
135 const char* file = argv[argc - 1];
136 if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
Elliott Hughes0119a912018-10-22 17:02:51 -0700137 error_exit("filename doesn't end .apk: %s", file);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000138 }
139
140 if (use_fastdeploy == true) {
Idries Hamadi0a525802018-08-21 15:47:35 +0100141 TemporaryFile metadataTmpFile;
142 TemporaryFile patchTmpFile;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000143
Idries Hamadi0a525802018-08-21 15:47:35 +0100144 FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
Idries Hamadi78330f02018-09-13 18:00:25 +0100145 extract_metadata(file, metadataFile);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000146 fclose(metadataFile);
147
Idries Hamadi78330f02018-09-13 18:00:25 +0100148 create_patch(file, metadataTmpFile.path, patchTmpFile.path);
149 // pass all but 1st (command) and last (apk path) parameters through to pm for
150 // session creation
151 std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
152 install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
153 delete_device_patch_file(file);
154 return 0;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000155 } else {
156 struct stat sb;
157 if (stat(file, &sb) == -1) {
158 fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
159 return 1;
160 }
161
162 int localFd = adb_open(file, O_RDONLY);
163 if (localFd < 0) {
164 fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
165 return 1;
166 }
167
168 std::string error;
169 std::string cmd = "exec:cmd package";
170
171 // don't copy the APK name, but, copy the rest of the arguments as-is
172 while (argc-- > 1) {
173 cmd += " " + escape_arg(std::string(*argv++));
174 }
175
176 // add size parameter [required for streaming installs]
177 // do last to override any user specified value
178 cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
179
180 int remoteFd = adb_connect(cmd, &error);
181 if (remoteFd < 0) {
182 fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
183 adb_close(localFd);
184 return 1;
185 }
186
187 char buf[BUFSIZ];
188 copy_to_file(localFd, remoteFd);
189 read_status_line(remoteFd, buf, sizeof(buf));
190
191 adb_close(localFd);
192 adb_close(remoteFd);
193
194 if (!strncmp("Success", buf, 7)) {
195 fputs(buf, stdout);
196 return 0;
197 }
198 fprintf(stderr, "adb: failed to install %s: %s", file, buf);
199 return 1;
200 }
201}
202
Idries Hamadi7919be22018-08-28 12:58:09 +0100203static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
204 bool use_localagent) {
Idries Hamadi1ecee442018-01-29 16:30:36 +0000205 static const char* const DATA_DEST = "/data/local/tmp/%s";
206 static const char* const SD_DEST = "/sdcard/tmp/%s";
207 const char* where = DATA_DEST;
208
209 printf("Performing Push Install\n");
210
211 for (int i = 1; i < argc; i++) {
212 if (!strcmp(argv[i], "-s")) {
213 where = SD_DEST;
214 }
215 }
216
217 // Find last APK argument.
218 // All other arguments passed through verbatim.
219 int last_apk = -1;
220 for (int i = argc - 1; i >= 0; i--) {
221 if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
222 last_apk = i;
223 break;
224 }
225 }
226
Elliott Hughes0119a912018-10-22 17:02:51 -0700227 if (last_apk == -1) error_exit("need APK file on command line");
Idries Hamadi1ecee442018-01-29 16:30:36 +0000228
229 int result = -1;
230 std::vector<const char*> apk_file = {argv[last_apk]};
231 std::string apk_dest =
232 android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
233
Idries Hamadi1ecee442018-01-29 16:30:36 +0000234 if (use_fastdeploy == true) {
Idries Hamadib1702db2018-09-06 18:42:39 +0100235 TemporaryFile metadataTmpFile;
236 TemporaryFile patchTmpFile;
237
Idries Hamadi0a525802018-08-21 15:47:35 +0100238 FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
Idries Hamadi78330f02018-09-13 18:00:25 +0100239 extract_metadata(apk_file[0], metadataFile);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000240 fclose(metadataFile);
241
Idries Hamadi78330f02018-09-13 18:00:25 +0100242 create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
243 apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
Idries Hamadi1ecee442018-01-29 16:30:36 +0000244 } else {
245 if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
246 }
247
248 argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
249 result = pm_command(argc, argv);
250
251cleanup_apk:
Idries Hamadib1702db2018-09-06 18:42:39 +0100252 if (use_fastdeploy == true) {
253 delete_device_patch_file(apk_file[0]);
254 }
Idries Hamadi1ecee442018-01-29 16:30:36 +0000255 delete_device_file(apk_dest);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000256 return result;
257}
258
259int install_app(int argc, const char** argv) {
260 std::vector<int> processedArgIndicies;
261 enum installMode {
262 INSTALL_DEFAULT,
263 INSTALL_PUSH,
264 INSTALL_STREAM
265 } installMode = INSTALL_DEFAULT;
266 bool use_fastdeploy = false;
267 bool is_reinstall = false;
268 bool use_localagent = false;
269 FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
270
271 for (int i = 1; i < argc; i++) {
272 if (!strcmp(argv[i], "--streaming")) {
273 processedArgIndicies.push_back(i);
274 installMode = INSTALL_STREAM;
275 } else if (!strcmp(argv[i], "--no-streaming")) {
276 processedArgIndicies.push_back(i);
277 installMode = INSTALL_PUSH;
278 } else if (!strcmp(argv[i], "-r")) {
279 // Note that this argument is not added to processedArgIndicies because it
280 // must be passed through to pm
281 is_reinstall = true;
282 } else if (!strcmp(argv[i], "--fastdeploy")) {
283 processedArgIndicies.push_back(i);
284 use_fastdeploy = true;
285 } else if (!strcmp(argv[i], "--no-fastdeploy")) {
286 processedArgIndicies.push_back(i);
287 use_fastdeploy = false;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000288 } else if (!strcmp(argv[i], "--force-agent")) {
289 processedArgIndicies.push_back(i);
290 agent_update_strategy = FastDeploy_AgentUpdateAlways;
291 } else if (!strcmp(argv[i], "--date-check-agent")) {
292 processedArgIndicies.push_back(i);
293 agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
294 } else if (!strcmp(argv[i], "--version-check-agent")) {
295 processedArgIndicies.push_back(i);
296 agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
297#ifndef _WIN32
298 } else if (!strcmp(argv[i], "--local-agent")) {
299 processedArgIndicies.push_back(i);
300 use_localagent = true;
301#endif
302 }
Idries Hamadi1ecee442018-01-29 16:30:36 +0000303 }
304
305 if (installMode == INSTALL_DEFAULT) {
306 if (_use_legacy_install()) {
307 installMode = INSTALL_PUSH;
308 } else {
309 installMode = INSTALL_STREAM;
310 }
311 }
312
313 if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
Elliott Hughes0119a912018-10-22 17:02:51 -0700314 error_exit("Attempting to use streaming install on unsupported device");
Idries Hamadi1ecee442018-01-29 16:30:36 +0000315 }
316
317 if (use_fastdeploy == true && is_reinstall == false) {
318 printf("Fast Deploy is only available with -r.\n");
319 use_fastdeploy = false;
320 }
321
322 if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
323 printf("Fast Deploy is only compatible with devices of API version %d or higher, "
324 "ignoring.\n",
325 kFastDeployMinApi);
326 use_fastdeploy = false;
327 }
328
329 std::vector<const char*> passthrough_argv;
330 for (int i = 0; i < argc; i++) {
331 if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
332 processedArgIndicies.end()) {
333 passthrough_argv.push_back(argv[i]);
334 }
335 }
336
Idries Hamadi1ecee442018-01-29 16:30:36 +0000337 if (use_fastdeploy == true) {
Idries Hamadidc272242018-08-24 11:46:45 +0100338 fastdeploy_set_local_agent(use_localagent);
Idries Hamadib1702db2018-09-06 18:42:39 +0100339 update_agent(agent_update_strategy);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000340 }
341
342 switch (installMode) {
343 case INSTALL_PUSH:
344 return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
Idries Hamadi7919be22018-08-28 12:58:09 +0100345 use_fastdeploy, use_localagent);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000346 case INSTALL_STREAM:
347 return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
Idries Hamadi7919be22018-08-28 12:58:09 +0100348 use_fastdeploy, use_localagent);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000349 case INSTALL_DEFAULT:
350 default:
351 return 1;
352 }
353}
354
355int install_multiple_app(int argc, const char** argv) {
356 // Find all APK arguments starting at end.
357 // All other arguments passed through verbatim.
358 int first_apk = -1;
359 uint64_t total_size = 0;
360 for (int i = argc - 1; i >= 0; i--) {
361 const char* file = argv[i];
362
Victor Hsieh88f14b82018-10-04 10:46:56 -0700363 if (android::base::EndsWithIgnoreCase(file, ".apk") ||
364 android::base::EndsWithIgnoreCase(file, ".dm")) {
Idries Hamadi1ecee442018-01-29 16:30:36 +0000365 struct stat sb;
366 if (stat(file, &sb) != -1) total_size += sb.st_size;
367 first_apk = i;
368 } else {
369 break;
370 }
371 }
372
Elliott Hughes0119a912018-10-22 17:02:51 -0700373 if (first_apk == -1) error_exit("need APK file on command line");
Idries Hamadi1ecee442018-01-29 16:30:36 +0000374
375 std::string install_cmd;
376 if (_use_legacy_install()) {
377 install_cmd = "exec:pm";
378 } else {
379 install_cmd = "exec:cmd package";
380 }
381
382 std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
383 install_cmd.c_str(), total_size);
384 for (int i = 1; i < first_apk; i++) {
385 cmd += " " + escape_arg(argv[i]);
386 }
387
388 // Create install session
389 std::string error;
390 int fd = adb_connect(cmd, &error);
391 if (fd < 0) {
392 fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
393 return EXIT_FAILURE;
394 }
395 char buf[BUFSIZ];
396 read_status_line(fd, buf, sizeof(buf));
397 adb_close(fd);
398
399 int session_id = -1;
400 if (!strncmp("Success", buf, 7)) {
401 char* start = strrchr(buf, '[');
402 char* end = strrchr(buf, ']');
403 if (start && end) {
404 *end = '\0';
405 session_id = strtol(start + 1, nullptr, 10);
406 }
407 }
408 if (session_id < 0) {
409 fprintf(stderr, "adb: failed to create session\n");
410 fputs(buf, stderr);
411 return EXIT_FAILURE;
412 }
413
414 // Valid session, now stream the APKs
415 int success = 1;
416 for (int i = first_apk; i < argc; i++) {
417 const char* file = argv[i];
418 struct stat sb;
419 if (stat(file, &sb) == -1) {
420 fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
421 success = 0;
422 goto finalize_session;
423 }
424
425 std::string cmd =
Victor Hsieh88f14b82018-10-04 10:46:56 -0700426 android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %s -",
Idries Hamadi1ecee442018-01-29 16:30:36 +0000427 install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
Victor Hsieh88f14b82018-10-04 10:46:56 -0700428 session_id, android::base::Basename(file).c_str());
Idries Hamadi1ecee442018-01-29 16:30:36 +0000429
430 int localFd = adb_open(file, O_RDONLY);
431 if (localFd < 0) {
432 fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
433 success = 0;
434 goto finalize_session;
435 }
436
437 std::string error;
438 int remoteFd = adb_connect(cmd, &error);
439 if (remoteFd < 0) {
440 fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
441 adb_close(localFd);
442 success = 0;
443 goto finalize_session;
444 }
445
446 copy_to_file(localFd, remoteFd);
447 read_status_line(remoteFd, buf, sizeof(buf));
448
449 adb_close(localFd);
450 adb_close(remoteFd);
451
452 if (strncmp("Success", buf, 7)) {
453 fprintf(stderr, "adb: failed to write %s\n", file);
454 fputs(buf, stderr);
455 success = 0;
456 goto finalize_session;
457 }
458 }
459
460finalize_session:
461 // Commit session if we streamed everything okay; otherwise abandon
462 std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
463 success ? "commit" : "abandon", session_id);
464 fd = adb_connect(service, &error);
465 if (fd < 0) {
466 fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
467 return EXIT_FAILURE;
468 }
469 read_status_line(fd, buf, sizeof(buf));
470 adb_close(fd);
471
472 if (!strncmp("Success", buf, 7)) {
473 fputs(buf, stdout);
474 return 0;
475 }
476 fprintf(stderr, "adb: failed to finalize session\n");
477 fputs(buf, stderr);
478 return EXIT_FAILURE;
479}
480
481int delete_device_file(const std::string& filename) {
482 std::string cmd = "rm -f " + escape_arg(filename);
483 return send_shell_command(cmd);
484}