blob: 29148362afdd80ad3999ee6be9eda7d9e7056fcb [file] [log] [blame]
Idries Hamadied409ea2018-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 Hamadied409ea2018-01-29 16:30:36 +000017#include <libgen.h>
18#include <algorithm>
Idries Hamadi5b6bf942018-08-28 12:58:09 +010019#include <array>
Idries Hamadied409ea2018-01-29 16:30:36 +000020
Idries Hamadi5b6bf942018-08-28 12:58:09 +010021#include "android-base/file.h"
22#include "android-base/strings.h"
Idries Hamadied409ea2018-01-29 16:30:36 +000023#include "client/file_sync_client.h"
24#include "commandline.h"
25#include "fastdeploy.h"
26#include "fastdeploycallbacks.h"
27#include "utils/String16.h"
28
Elliott Hughes86ab9ff2018-09-05 12:13:11 -070029static constexpr long kRequiredAgentVersion = 0x00000001;
Idries Hamadied409ea2018-01-29 16:30:36 +000030
Elliott Hughes86ab9ff2018-09-05 12:13:11 -070031static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
Idries Hamadied409ea2018-01-29 16:30:36 +000032
Idries Hamadi7575cd92018-08-24 11:46:45 +010033static bool use_localagent = false;
Idries Hamadi5b6bf942018-08-28 12:58:09 +010034
Idries Hamadied409ea2018-01-29 16:30:36 +000035long get_agent_version() {
36 std::vector<char> versionOutputBuffer;
37 std::vector<char> versionErrorBuffer;
38
Idries Hamadi7575cd92018-08-24 11:46:45 +010039 int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
Idries Hamadied409ea2018-01-29 16:30:36 +000040 &versionOutputBuffer, &versionErrorBuffer);
41 long version = -1;
42
43 if (statusCode == 0 && versionOutputBuffer.size() > 0) {
44 version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
45 }
46
47 return version;
48}
49
50int get_device_api_level() {
51 std::vector<char> sdkVersionOutputBuffer;
52 std::vector<char> sdkVersionErrorBuffer;
53 int api_level = -1;
54
55 int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
56 &sdkVersionErrorBuffer);
Idries Hamadi7f51e002018-08-23 17:22:21 +010057 if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
Idries Hamadied409ea2018-01-29 16:30:36 +000058 api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
59 }
60
61 return api_level;
62}
63
Idries Hamadi7575cd92018-08-24 11:46:45 +010064void fastdeploy_set_local_agent(bool set_use_localagent) {
65 use_localagent = set_use_localagent;
Idries Hamadi5b6bf942018-08-28 12:58:09 +010066}
67
Idries Hamadied409ea2018-01-29 16:30:36 +000068// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
Idries Hamadi5b6bf942018-08-28 12:58:09 +010069static bool get_agent_component_host_path(const char* local_path, const char* sdk_path,
Idries Hamadied409ea2018-01-29 16:30:36 +000070 std::string* output_path) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +010071 std::string adb_dir = android::base::GetExecutableDirectory();
72 if (adb_dir.empty()) {
Idries Hamadied409ea2018-01-29 16:30:36 +000073 return false;
74 }
75
Idries Hamadi7575cd92018-08-24 11:46:45 +010076 if (use_localagent) {
Idries Hamadied409ea2018-01-29 16:30:36 +000077 const char* product_out = getenv("ANDROID_PRODUCT_OUT");
78 if (product_out == nullptr) {
79 return false;
80 }
81 *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
82 return true;
83 } else {
Idries Hamadi5b6bf942018-08-28 12:58:09 +010084 *output_path = adb_dir + sdk_path;
Idries Hamadied409ea2018-01-29 16:30:36 +000085 return true;
86 }
87 return false;
88}
89
Idries Hamadi5b6bf942018-08-28 12:58:09 +010090static bool deploy_agent(bool checkTimeStamps) {
Idries Hamadied409ea2018-01-29 16:30:36 +000091 std::vector<const char*> srcs;
Idries Hamadied409ea2018-01-29 16:30:36 +000092 std::string agent_jar_path;
Idries Hamadi5b6bf942018-08-28 12:58:09 +010093 if (get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar",
94 &agent_jar_path)) {
Idries Hamadied409ea2018-01-29 16:30:36 +000095 srcs.push_back(agent_jar_path.c_str());
96 } else {
97 return false;
98 }
99
100 std::string agent_sh_path;
Idries Hamadi7575cd92018-08-24 11:46:45 +0100101 if (get_agent_component_host_path("/system/bin/deployagent", "/deployagent", &agent_sh_path)) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000102 srcs.push_back(agent_sh_path.c_str());
103 } else {
104 return false;
105 }
106
107 if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
108 // on windows the shell script might have lost execute permission
109 // so need to set this explicitly
Idries Hamadi7575cd92018-08-24 11:46:45 +0100110 const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
Idries Hamadied409ea2018-01-29 16:30:36 +0000111 std::string chmodCommand =
112 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
Idries Hamadi7f51e002018-08-23 17:22:21 +0100113 int ret = send_shell_command(chmodCommand);
Idries Hamadied409ea2018-01-29 16:30:36 +0000114 return (ret == 0);
115 } else {
116 return false;
117 }
118}
119
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100120bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000121 long agent_version = get_agent_version();
122 switch (agentUpdateStrategy) {
123 case FastDeploy_AgentUpdateAlways:
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100124 if (deploy_agent(false) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000125 return false;
126 }
127 break;
128 case FastDeploy_AgentUpdateNewerTimeStamp:
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100129 if (deploy_agent(true) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000130 return false;
131 }
132 break;
133 case FastDeploy_AgentUpdateDifferentVersion:
134 if (agent_version != kRequiredAgentVersion) {
135 if (agent_version < 0) {
136 printf("Could not detect agent on device, deploying\n");
137 } else {
138 printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
139 agent_version, kRequiredAgentVersion);
140 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100141 if (deploy_agent(false) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000142 return false;
143 }
144 }
145 break;
146 }
147
148 agent_version = get_agent_version();
149 return (agent_version == kRequiredAgentVersion);
150}
151
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100152static std::string get_aapt2_path() {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100153 if (use_localagent) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100154 // This should never happen on a Windows machine
155 const char* host_out = getenv("ANDROID_HOST_OUT");
156 if (host_out == nullptr) {
157 fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
158 }
159 return android::base::StringPrintf("%s/bin/aapt2", host_out);
Idries Hamadied409ea2018-01-29 16:30:36 +0000160 }
161
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100162 std::string adb_dir = android::base::GetExecutableDirectory();
163 if (adb_dir.empty()) {
164 fatal("Could not locate aapt2");
165 }
166 return adb_dir + "/aapt2";
167}
168
169static int system_capture(const char* cmd, std::string& output) {
170 FILE* pipe = popen(cmd, "re");
171 int fd = -1;
172
173 if (pipe != nullptr) {
174 fd = fileno(pipe);
175 }
176
177 if (fd == -1) {
178 fatal_errno("Could not create pipe for process '%s'", cmd);
179 }
180
181 if (!android::base::ReadFdToString(fd, &output)) {
182 fatal_errno("Error reading from process '%s'", cmd);
183 }
184
185 return pclose(pipe);
Idries Hamadied409ea2018-01-29 16:30:36 +0000186}
187
188// output is required to point to a valid output string (non-null)
189static bool get_packagename_from_apk(const char* apkPath, std::string* output) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100190 const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
191 std::string aapt2_path_string = get_aapt2_path();
192 std::string getPackagenameCommand = android::base::StringPrintf(
193 kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000194
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100195 if (system_capture(getPackagenameCommand.c_str(), *output) == 0) {
196 // strip any line end characters from the output
197 *output = android::base::Trim(*output);
198 return true;
Idries Hamadied409ea2018-01-29 16:30:36 +0000199 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000200 return false;
201}
202
203int extract_metadata(const char* apkPath, FILE* outputFp) {
204 std::string packageName;
205 if (get_packagename_from_apk(apkPath, &packageName) == false) {
206 return -1;
207 }
208
Idries Hamadi7575cd92018-08-24 11:46:45 +0100209 const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
Idries Hamadied409ea2018-01-29 16:30:36 +0000210 std::string extractCommand =
211 android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
212
213 std::vector<char> extractErrorBuffer;
214 int statusCode;
215 DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
Idries Hamadi7f51e002018-08-23 17:22:21 +0100216 int ret = send_shell_command(extractCommand, false, &cb);
Idries Hamadied409ea2018-01-29 16:30:36 +0000217
218 if (ret == 0) {
219 return cb.getBytesWritten();
220 }
221
222 return ret;
223}
224
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100225static std::string get_patch_generator_command() {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100226 if (use_localagent) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000227 // This should never happen on a Windows machine
Idries Hamadied409ea2018-01-29 16:30:36 +0000228 const char* host_out = getenv("ANDROID_HOST_OUT");
229 if (host_out == nullptr) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100230 fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
231 "defined");
Idries Hamadied409ea2018-01-29 16:30:36 +0000232 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100233 return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
234 host_out);
Idries Hamadied409ea2018-01-29 16:30:36 +0000235 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100236
237 std::string adb_dir = android::base::GetExecutableDirectory();
238 if (adb_dir.empty()) {
239 fatal("Could not locate deploypatchgenerator.jar");
240 }
241 return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
242 adb_dir.c_str());
Idries Hamadied409ea2018-01-29 16:30:36 +0000243}
244
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100245int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000246 std::string generatePatchCommand = android::base::StringPrintf(
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100247 R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
248 patchPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000249 return system(generatePatchCommand.c_str());
250}
251
252std::string get_patch_path(const char* apkPath) {
253 std::string packageName;
254 if (get_packagename_from_apk(apkPath, &packageName) == false) {
255 return "";
256 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100257
Idries Hamadied409ea2018-01-29 16:30:36 +0000258 std::string patchDevicePath =
259 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
260 return patchDevicePath;
261}
262
263int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100264 const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
Idries Hamadied409ea2018-01-29 16:30:36 +0000265
266 std::string packageName;
267 if (get_packagename_from_apk(apkPath, &packageName) == false) {
268 return -1;
269 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100270
Idries Hamadied409ea2018-01-29 16:30:36 +0000271 std::string patchDevicePath = get_patch_path(apkPath);
272
273 std::vector<const char*> srcs = {patchPath};
274 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
275
276 if (!push_ok) {
277 return -1;
278 }
279
280 std::string applyPatchCommand =
281 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
282 patchDevicePath.c_str(), outputPath);
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100283
Idries Hamadied409ea2018-01-29 16:30:36 +0000284 return send_shell_command(applyPatchCommand);
285}
286
287int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100288 const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
Idries Hamadied409ea2018-01-29 16:30:36 +0000289
290 std::string packageName;
291 if (get_packagename_from_apk(apkPath, &packageName) == false) {
292 return -1;
293 }
294
295 std::vector<const char*> srcs;
296 std::string patchDevicePath =
297 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
298 srcs.push_back(patchPath);
299 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
300
301 if (!push_ok) {
302 return -1;
303 }
304
305 std::vector<unsigned char> applyOutputBuffer;
306 std::vector<unsigned char> applyErrorBuffer;
307 std::string argsString;
308
309 for (int i = 0; i < argc; i++) {
310 argsString.append(argv[i]);
311 argsString.append(" ");
312 }
313
314 std::string applyPatchCommand =
315 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
316 patchDevicePath.c_str(), argsString.c_str());
317 return send_shell_command(applyPatchCommand);
318}