blob: c0d793c372171247c6dffd0839cf264bbd30e387 [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 Hamadi5b6bf942018-08-28 12:58:09 +010033struct TFastDeployConfig {
34 bool use_localagent;
35};
36
37static TFastDeployConfig& get_fastdeploy_config() {
38 TFastDeployConfig& instance = *new TFastDeployConfig;
39 return instance;
40}
41
Idries Hamadied409ea2018-01-29 16:30:36 +000042long get_agent_version() {
43 std::vector<char> versionOutputBuffer;
44 std::vector<char> versionErrorBuffer;
45
46 int statusCode = capture_shell_command("/data/local/tmp/deployagent.sh version",
47 &versionOutputBuffer, &versionErrorBuffer);
48 long version = -1;
49
50 if (statusCode == 0 && versionOutputBuffer.size() > 0) {
51 version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
52 }
53
54 return version;
55}
56
57int get_device_api_level() {
58 std::vector<char> sdkVersionOutputBuffer;
59 std::vector<char> sdkVersionErrorBuffer;
60 int api_level = -1;
61
62 int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
63 &sdkVersionErrorBuffer);
Idries Hamadi7f51e002018-08-23 17:22:21 +010064 if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
Idries Hamadied409ea2018-01-29 16:30:36 +000065 api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
66 }
67
68 return api_level;
69}
70
Idries Hamadi5b6bf942018-08-28 12:58:09 +010071void fastdeploy_init(bool use_localagent) {
72 get_fastdeploy_config().use_localagent = use_localagent;
73}
74
Idries Hamadied409ea2018-01-29 16:30:36 +000075// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
Idries Hamadi5b6bf942018-08-28 12:58:09 +010076static bool get_agent_component_host_path(const char* local_path, const char* sdk_path,
Idries Hamadied409ea2018-01-29 16:30:36 +000077 std::string* output_path) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +010078 std::string adb_dir = android::base::GetExecutableDirectory();
79 if (adb_dir.empty()) {
Idries Hamadied409ea2018-01-29 16:30:36 +000080 return false;
81 }
82
Idries Hamadi5b6bf942018-08-28 12:58:09 +010083 if (get_fastdeploy_config().use_localagent) {
Idries Hamadied409ea2018-01-29 16:30:36 +000084 const char* product_out = getenv("ANDROID_PRODUCT_OUT");
85 if (product_out == nullptr) {
86 return false;
87 }
88 *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
89 return true;
90 } else {
Idries Hamadi5b6bf942018-08-28 12:58:09 +010091 *output_path = adb_dir + sdk_path;
Idries Hamadied409ea2018-01-29 16:30:36 +000092 return true;
93 }
94 return false;
95}
96
Idries Hamadi5b6bf942018-08-28 12:58:09 +010097static bool deploy_agent(bool checkTimeStamps) {
Idries Hamadied409ea2018-01-29 16:30:36 +000098 std::vector<const char*> srcs;
Idries Hamadied409ea2018-01-29 16:30:36 +000099 std::string agent_jar_path;
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100100 if (get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar",
101 &agent_jar_path)) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000102 srcs.push_back(agent_jar_path.c_str());
103 } else {
104 return false;
105 }
106
107 std::string agent_sh_path;
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100108 if (get_agent_component_host_path("/system/bin/deployagent.sh", "/deployagent.sh",
109 &agent_sh_path)) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000110 srcs.push_back(agent_sh_path.c_str());
111 } else {
112 return false;
113 }
114
115 if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
116 // on windows the shell script might have lost execute permission
117 // so need to set this explicitly
118 const char* kChmodCommandPattern = "chmod 777 %sdeployagent.sh";
119 std::string chmodCommand =
120 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
Idries Hamadi7f51e002018-08-23 17:22:21 +0100121 int ret = send_shell_command(chmodCommand);
Idries Hamadied409ea2018-01-29 16:30:36 +0000122 return (ret == 0);
123 } else {
124 return false;
125 }
126}
127
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100128bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000129 long agent_version = get_agent_version();
130 switch (agentUpdateStrategy) {
131 case FastDeploy_AgentUpdateAlways:
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100132 if (deploy_agent(false) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000133 return false;
134 }
135 break;
136 case FastDeploy_AgentUpdateNewerTimeStamp:
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100137 if (deploy_agent(true) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000138 return false;
139 }
140 break;
141 case FastDeploy_AgentUpdateDifferentVersion:
142 if (agent_version != kRequiredAgentVersion) {
143 if (agent_version < 0) {
144 printf("Could not detect agent on device, deploying\n");
145 } else {
146 printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
147 agent_version, kRequiredAgentVersion);
148 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100149 if (deploy_agent(false) == false) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000150 return false;
151 }
152 }
153 break;
154 }
155
156 agent_version = get_agent_version();
157 return (agent_version == kRequiredAgentVersion);
158}
159
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100160static std::string get_aapt2_path() {
161 if (get_fastdeploy_config().use_localagent) {
162 // This should never happen on a Windows machine
163 const char* host_out = getenv("ANDROID_HOST_OUT");
164 if (host_out == nullptr) {
165 fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
166 }
167 return android::base::StringPrintf("%s/bin/aapt2", host_out);
Idries Hamadied409ea2018-01-29 16:30:36 +0000168 }
169
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100170 std::string adb_dir = android::base::GetExecutableDirectory();
171 if (adb_dir.empty()) {
172 fatal("Could not locate aapt2");
173 }
174 return adb_dir + "/aapt2";
175}
176
177static int system_capture(const char* cmd, std::string& output) {
178 FILE* pipe = popen(cmd, "re");
179 int fd = -1;
180
181 if (pipe != nullptr) {
182 fd = fileno(pipe);
183 }
184
185 if (fd == -1) {
186 fatal_errno("Could not create pipe for process '%s'", cmd);
187 }
188
189 if (!android::base::ReadFdToString(fd, &output)) {
190 fatal_errno("Error reading from process '%s'", cmd);
191 }
192
193 return pclose(pipe);
Idries Hamadied409ea2018-01-29 16:30:36 +0000194}
195
196// output is required to point to a valid output string (non-null)
197static bool get_packagename_from_apk(const char* apkPath, std::string* output) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100198 const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
199 std::string aapt2_path_string = get_aapt2_path();
200 std::string getPackagenameCommand = android::base::StringPrintf(
201 kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000202
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100203 if (system_capture(getPackagenameCommand.c_str(), *output) == 0) {
204 // strip any line end characters from the output
205 *output = android::base::Trim(*output);
206 return true;
Idries Hamadied409ea2018-01-29 16:30:36 +0000207 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000208 return false;
209}
210
211int extract_metadata(const char* apkPath, FILE* outputFp) {
212 std::string packageName;
213 if (get_packagename_from_apk(apkPath, &packageName) == false) {
214 return -1;
215 }
216
217 const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent.sh extract %s";
218 std::string extractCommand =
219 android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
220
221 std::vector<char> extractErrorBuffer;
222 int statusCode;
223 DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
Idries Hamadi7f51e002018-08-23 17:22:21 +0100224 int ret = send_shell_command(extractCommand, false, &cb);
Idries Hamadied409ea2018-01-29 16:30:36 +0000225
226 if (ret == 0) {
227 return cb.getBytesWritten();
228 }
229
230 return ret;
231}
232
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100233static std::string get_patch_generator_command() {
234 if (get_fastdeploy_config().use_localagent) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000235 // This should never happen on a Windows machine
Idries Hamadied409ea2018-01-29 16:30:36 +0000236 const char* host_out = getenv("ANDROID_HOST_OUT");
237 if (host_out == nullptr) {
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100238 fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
239 "defined");
Idries Hamadied409ea2018-01-29 16:30:36 +0000240 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100241 return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
242 host_out);
Idries Hamadied409ea2018-01-29 16:30:36 +0000243 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100244
245 std::string adb_dir = android::base::GetExecutableDirectory();
246 if (adb_dir.empty()) {
247 fatal("Could not locate deploypatchgenerator.jar");
248 }
249 return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
250 adb_dir.c_str());
Idries Hamadied409ea2018-01-29 16:30:36 +0000251}
252
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100253int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000254 std::string generatePatchCommand = android::base::StringPrintf(
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100255 R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
256 patchPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000257 return system(generatePatchCommand.c_str());
258}
259
260std::string get_patch_path(const char* apkPath) {
261 std::string packageName;
262 if (get_packagename_from_apk(apkPath, &packageName) == false) {
263 return "";
264 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100265
Idries Hamadied409ea2018-01-29 16:30:36 +0000266 std::string patchDevicePath =
267 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
268 return patchDevicePath;
269}
270
271int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
272 const std::string kAgentApplyCommandPattern =
273 "/data/local/tmp/deployagent.sh apply %s %s -o %s";
274
275 std::string packageName;
276 if (get_packagename_from_apk(apkPath, &packageName) == false) {
277 return -1;
278 }
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100279
Idries Hamadied409ea2018-01-29 16:30:36 +0000280 std::string patchDevicePath = get_patch_path(apkPath);
281
282 std::vector<const char*> srcs = {patchPath};
283 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
284
285 if (!push_ok) {
286 return -1;
287 }
288
289 std::string applyPatchCommand =
290 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
291 patchDevicePath.c_str(), outputPath);
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100292
Idries Hamadied409ea2018-01-29 16:30:36 +0000293 return send_shell_command(applyPatchCommand);
294}
295
296int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
297 const std::string kAgentApplyCommandPattern =
298 "/data/local/tmp/deployagent.sh apply %s %s -pm %s";
299
300 std::string packageName;
301 if (get_packagename_from_apk(apkPath, &packageName) == false) {
302 return -1;
303 }
304
305 std::vector<const char*> srcs;
306 std::string patchDevicePath =
307 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
308 srcs.push_back(patchPath);
309 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
310
311 if (!push_ok) {
312 return -1;
313 }
314
315 std::vector<unsigned char> applyOutputBuffer;
316 std::vector<unsigned char> applyErrorBuffer;
317 std::string argsString;
318
319 for (int i = 0; i < argc; i++) {
320 argsString.append(argv[i]);
321 argsString.append(" ");
322 }
323
324 std::string applyPatchCommand =
325 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
326 patchDevicePath.c_str(), argsString.c_str());
327 return send_shell_command(applyPatchCommand);
328}