blob: cd42b560094e696703b4ea530e45ed34215378a4 [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
17#include <androidfw/ResourceTypes.h>
18#include <androidfw/ZipFileRO.h>
19#include <libgen.h>
20#include <algorithm>
21
22#include "client/file_sync_client.h"
23#include "commandline.h"
24#include "fastdeploy.h"
25#include "fastdeploycallbacks.h"
26#include "utils/String16.h"
27
28const long kRequiredAgentVersion = 0x00000001;
29
30const char* kDeviceAgentPath = "/data/local/tmp/";
31
32long get_agent_version() {
33 std::vector<char> versionOutputBuffer;
34 std::vector<char> versionErrorBuffer;
35
36 int statusCode = capture_shell_command("/data/local/tmp/deployagent.sh version",
37 &versionOutputBuffer, &versionErrorBuffer);
38 long version = -1;
39
40 if (statusCode == 0 && versionOutputBuffer.size() > 0) {
41 version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
42 }
43
44 return version;
45}
46
47int get_device_api_level() {
48 std::vector<char> sdkVersionOutputBuffer;
49 std::vector<char> sdkVersionErrorBuffer;
50 int api_level = -1;
51
52 int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
53 &sdkVersionErrorBuffer);
54 if (statusCode == 0 && statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
55 api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
56 }
57
58 return api_level;
59}
60
61// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
62static bool get_agent_component_host_path(bool use_localagent, const char* adb_path,
63 const char* local_path, const char* sdk_path,
64 std::string* output_path) {
65 std::string mutable_adb_path = adb_path;
66 const char* adb_dir = dirname(&mutable_adb_path[0]);
67 if (adb_dir == nullptr) {
68 return false;
69 }
70
71 if (use_localagent) {
72 const char* product_out = getenv("ANDROID_PRODUCT_OUT");
73 if (product_out == nullptr) {
74 return false;
75 }
76 *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
77 return true;
78 } else {
79 *output_path = android::base::StringPrintf("%s%s", adb_dir, sdk_path);
80 return true;
81 }
82 return false;
83}
84
85static bool deploy_agent(bool checkTimeStamps, bool use_localagent, const char* adb_path) {
86 std::vector<const char*> srcs;
87
88 std::string agent_jar_path;
89 if (get_agent_component_host_path(use_localagent, adb_path, "/system/framework/deployagent.jar",
90 "/deployagent.jar", &agent_jar_path)) {
91 srcs.push_back(agent_jar_path.c_str());
92 } else {
93 return false;
94 }
95
96 std::string agent_sh_path;
97 if (get_agent_component_host_path(use_localagent, adb_path, "/system/bin/deployagent.sh",
98 "/deployagent.sh", &agent_sh_path)) {
99 srcs.push_back(agent_sh_path.c_str());
100 } else {
101 return false;
102 }
103
104 if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
105 // on windows the shell script might have lost execute permission
106 // so need to set this explicitly
107 const char* kChmodCommandPattern = "chmod 777 %sdeployagent.sh";
108 std::string chmodCommand =
109 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
110 int ret = send_shell_command(chmodCommand.c_str());
111 return (ret == 0);
112 } else {
113 return false;
114 }
115}
116
117bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
118 const char* adb_path) {
119 long agent_version = get_agent_version();
120 switch (agentUpdateStrategy) {
121 case FastDeploy_AgentUpdateAlways:
122 if (deploy_agent(false, use_localagent, adb_path) == false) {
123 return false;
124 }
125 break;
126 case FastDeploy_AgentUpdateNewerTimeStamp:
127 if (deploy_agent(true, use_localagent, adb_path) == false) {
128 return false;
129 }
130 break;
131 case FastDeploy_AgentUpdateDifferentVersion:
132 if (agent_version != kRequiredAgentVersion) {
133 if (agent_version < 0) {
134 printf("Could not detect agent on device, deploying\n");
135 } else {
136 printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
137 agent_version, kRequiredAgentVersion);
138 }
139 if (deploy_agent(false, use_localagent, adb_path) == false) {
140 return false;
141 }
142 }
143 break;
144 }
145
146 agent_version = get_agent_version();
147 return (agent_version == kRequiredAgentVersion);
148}
149
150static std::string get_string_from_utf16(const char16_t* input, int input_len) {
151 ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
152 if (utf8_length <= 0) {
153 return {};
154 }
155
156 std::string utf8;
157 utf8.resize(utf8_length);
158 utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
159 return utf8;
160}
161
162// output is required to point to a valid output string (non-null)
163static bool get_packagename_from_apk(const char* apkPath, std::string* output) {
164 using namespace android;
165
166 ZipFileRO* zipFile = ZipFileRO::open(apkPath);
167 if (zipFile == nullptr) {
168 return false;
169 }
170
171 ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
172 if (entry == nullptr) {
173 return false;
174 }
175
176 uint32_t manifest_len = 0;
177 if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
178 return false;
179 }
180
181 std::vector<char> manifest_data(manifest_len);
182 if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
183 return false;
184 }
185
186 ResXMLTree tree;
187 status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
188 if (setto_status != NO_ERROR) {
189 return false;
190 }
191
192 ResXMLParser::event_code_t code;
193 while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
194 code != ResXMLParser::END_DOCUMENT) {
195 switch (code) {
196 case ResXMLParser::START_TAG: {
197 size_t element_name_length;
198 const char16_t* element_name = tree.getElementName(&element_name_length);
199 if (element_name == nullptr) {
200 continue;
201 }
202
203 std::u16string element_name_string(element_name, element_name_length);
204 if (element_name_string == u"manifest") {
205 for (int i = 0; i < (int)tree.getAttributeCount(); i++) {
206 size_t attribute_name_length;
207 const char16_t* attribute_name_text =
208 tree.getAttributeName(i, &attribute_name_length);
209 if (attribute_name_text == nullptr) {
210 continue;
211 }
212 std::u16string attribute_name_string(attribute_name_text,
213 attribute_name_length);
214
215 if (attribute_name_string == u"package") {
216 size_t attribute_value_length;
217 const char16_t* attribute_value_text =
218 tree.getAttributeStringValue(i, &attribute_value_length);
219 if (attribute_value_text == nullptr) {
220 continue;
221 }
222 *output = get_string_from_utf16(attribute_value_text,
223 attribute_value_length);
224 return true;
225 }
226 }
227 }
228 break;
229 }
230 default:
231 break;
232 }
233 }
234
235 return false;
236}
237
238int extract_metadata(const char* apkPath, FILE* outputFp) {
239 std::string packageName;
240 if (get_packagename_from_apk(apkPath, &packageName) == false) {
241 return -1;
242 }
243
244 const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent.sh extract %s";
245 std::string extractCommand =
246 android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
247
248 std::vector<char> extractErrorBuffer;
249 int statusCode;
250 DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
251 int ret = send_shell_command(extractCommand.c_str(), false, &cb);
252
253 if (ret == 0) {
254 return cb.getBytesWritten();
255 }
256
257 return ret;
258}
259
260// output is required to point to a valid output string (non-null)
261static bool patch_generator_command(bool use_localagent, const char* adb_path,
262 std::string* output) {
263 if (use_localagent) {
264 // This should never happen on a Windows machine
265 const char* kGeneratorCommandPattern = "java -jar %s/framework/deploypatchgenerator.jar";
266 const char* host_out = getenv("ANDROID_HOST_OUT");
267 if (host_out == nullptr) {
268 return false;
269 }
270 *output = android::base::StringPrintf(kGeneratorCommandPattern, host_out, host_out);
271 return true;
272 } else {
273 const char* kGeneratorCommandPattern = R"(java -jar "%s/deploypatchgenerator.jar")";
274 std::string mutable_adb_path = adb_path;
275 const char* adb_dir = dirname(&mutable_adb_path[0]);
276 if (adb_dir == nullptr) {
277 return false;
278 }
279
280 *output = android::base::StringPrintf(kGeneratorCommandPattern, adb_dir, adb_dir);
281 return true;
282 }
283 return false;
284}
285
286int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath,
287 bool use_localagent, const char* adb_path) {
288 const char* kGeneratePatchCommandPattern = R"(%s "%s" "%s" > "%s")";
289 std::string patch_generator_command_string;
290 if (patch_generator_command(use_localagent, adb_path, &patch_generator_command_string) ==
291 false) {
292 return 1;
293 }
294 std::string generatePatchCommand = android::base::StringPrintf(
295 kGeneratePatchCommandPattern, patch_generator_command_string.c_str(), apkPath,
296 metadataPath, patchPath);
297 return system(generatePatchCommand.c_str());
298}
299
300std::string get_patch_path(const char* apkPath) {
301 std::string packageName;
302 if (get_packagename_from_apk(apkPath, &packageName) == false) {
303 return "";
304 }
305 std::string patchDevicePath =
306 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
307 return patchDevicePath;
308}
309
310int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
311 const std::string kAgentApplyCommandPattern =
312 "/data/local/tmp/deployagent.sh apply %s %s -o %s";
313
314 std::string packageName;
315 if (get_packagename_from_apk(apkPath, &packageName) == false) {
316 return -1;
317 }
318 std::string patchDevicePath = get_patch_path(apkPath);
319
320 std::vector<const char*> srcs = {patchPath};
321 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
322
323 if (!push_ok) {
324 return -1;
325 }
326
327 std::string applyPatchCommand =
328 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
329 patchDevicePath.c_str(), outputPath);
330 return send_shell_command(applyPatchCommand);
331}
332
333int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
334 const std::string kAgentApplyCommandPattern =
335 "/data/local/tmp/deployagent.sh apply %s %s -pm %s";
336
337 std::string packageName;
338 if (get_packagename_from_apk(apkPath, &packageName) == false) {
339 return -1;
340 }
341
342 std::vector<const char*> srcs;
343 std::string patchDevicePath =
344 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
345 srcs.push_back(patchPath);
346 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
347
348 if (!push_ok) {
349 return -1;
350 }
351
352 std::vector<unsigned char> applyOutputBuffer;
353 std::vector<unsigned char> applyErrorBuffer;
354 std::string argsString;
355
356 for (int i = 0; i < argc; i++) {
357 argsString.append(argv[i]);
358 argsString.append(" ");
359 }
360
361 std::string applyPatchCommand =
362 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
363 patchDevicePath.c_str(), argsString.c_str());
364 return send_shell_command(applyPatchCommand);
365}