blob: fbae2190d716115cb9241fde3d8068cee43606b0 [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 Hamadi269a4b42018-09-13 18:00:25 +010017#include "fastdeploy.h"
18
Henry Daitxee01c802018-12-12 10:40:57 +000019#include <string.h>
Idries Hamadied409ea2018-01-29 16:30:36 +000020#include <algorithm>
Idries Hamadi5b6bf942018-08-28 12:58:09 +010021#include <array>
Idries Hamadi269a4b42018-09-13 18:00:25 +010022#include <memory>
Idries Hamadied409ea2018-01-29 16:30:36 +000023
Idries Hamadi5b6bf942018-08-28 12:58:09 +010024#include "android-base/file.h"
25#include "android-base/strings.h"
Idries Hamadi269a4b42018-09-13 18:00:25 +010026#include "androidfw/ResourceTypes.h"
27#include "androidfw/ZipFileRO.h"
Idries Hamadied409ea2018-01-29 16:30:36 +000028#include "client/file_sync_client.h"
29#include "commandline.h"
Joshua Gilpatrickaf3ce082019-07-19 09:42:12 -070030#include "deployagent.inc" // Generated include via build rule.
31#include "deployagentscript.inc" // Generated include via build rule.
32#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
Idries Hamadied409ea2018-01-29 16:30:36 +000033#include "fastdeploycallbacks.h"
Elliott Hughes4679a392018-10-19 13:59:44 -070034#include "sysdeps.h"
Elliott Hughesd2aaede2018-10-22 17:02:51 -070035
36#include "adb_utils.h"
Idries Hamadied409ea2018-01-29 16:30:36 +000037
Henry Daitxee01c802018-12-12 10:40:57 +000038static constexpr long kRequiredAgentVersion = 0x00000002;
Idries Hamadied409ea2018-01-29 16:30:36 +000039
Elliott Hughes86ab9ff2018-09-05 12:13:11 -070040static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
Joshua Gilpatrickaf3ce082019-07-19 09:42:12 -070041static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
42static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
Idries Hamadied409ea2018-01-29 16:30:36 +000043
Idries Hamadi4af6ee42018-09-06 18:42:39 +010044static bool g_use_localagent = false;
Idries Hamadi5b6bf942018-08-28 12:58:09 +010045
Idries Hamadied409ea2018-01-29 16:30:36 +000046long get_agent_version() {
47 std::vector<char> versionOutputBuffer;
48 std::vector<char> versionErrorBuffer;
49
Idries Hamadi7575cd92018-08-24 11:46:45 +010050 int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
Idries Hamadied409ea2018-01-29 16:30:36 +000051 &versionOutputBuffer, &versionErrorBuffer);
52 long version = -1;
53
54 if (statusCode == 0 && versionOutputBuffer.size() > 0) {
55 version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
56 }
57
58 return version;
59}
60
61int get_device_api_level() {
62 std::vector<char> sdkVersionOutputBuffer;
63 std::vector<char> sdkVersionErrorBuffer;
64 int api_level = -1;
65
66 int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
67 &sdkVersionErrorBuffer);
Idries Hamadi7f51e002018-08-23 17:22:21 +010068 if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
Idries Hamadied409ea2018-01-29 16:30:36 +000069 api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
70 }
71
72 return api_level;
73}
74
Idries Hamadi4af6ee42018-09-06 18:42:39 +010075void fastdeploy_set_local_agent(bool use_localagent) {
76 g_use_localagent = use_localagent;
Idries Hamadi5b6bf942018-08-28 12:58:09 +010077}
78
Idries Hamadi5b6bf942018-08-28 12:58:09 +010079static bool deploy_agent(bool checkTimeStamps) {
Idries Hamadied409ea2018-01-29 16:30:36 +000080 std::vector<const char*> srcs;
Joshua Gilpatrickaf3ce082019-07-19 09:42:12 -070081 // TODO: Deploy agent from bin2c directly instead of writing to disk first.
82 TemporaryFile tempAgent;
83 android::base::WriteFully(tempAgent.fd, kDeployAgent, sizeof(kDeployAgent));
84 srcs.push_back(tempAgent.path);
85 if (!do_sync_push(srcs, kDeviceAgentFile, checkTimeStamps)) {
86 error_exit("Failed to push fastdeploy agent to device.");
87 }
88 srcs.clear();
89 // TODO: Deploy agent from bin2c directly instead of writing to disk first.
90 TemporaryFile tempAgentScript;
91 android::base::WriteFully(tempAgentScript.fd, kDeployAgentScript, sizeof(kDeployAgentScript));
92 srcs.push_back(tempAgentScript.path);
93 if (!do_sync_push(srcs, kDeviceAgentScript, checkTimeStamps)) {
94 error_exit("Failed to push fastdeploy agent script to device.");
95 }
96 srcs.clear();
97 // on windows the shell script might have lost execute permission
98 // so need to set this explicitly
99 const char* kChmodCommandPattern = "chmod 777 %s";
100 std::string chmodCommand =
101 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
102 int ret = send_shell_command(chmodCommand);
103 if (ret != 0) {
104 error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
Idries Hamadied409ea2018-01-29 16:30:36 +0000105 }
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100106
107 return true;
Idries Hamadied409ea2018-01-29 16:30:36 +0000108}
109
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100110void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
Idries Hamadied409ea2018-01-29 16:30:36 +0000111 long agent_version = get_agent_version();
112 switch (agentUpdateStrategy) {
113 case FastDeploy_AgentUpdateAlways:
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100114 deploy_agent(false);
Idries Hamadied409ea2018-01-29 16:30:36 +0000115 break;
116 case FastDeploy_AgentUpdateNewerTimeStamp:
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100117 deploy_agent(true);
Idries Hamadied409ea2018-01-29 16:30:36 +0000118 break;
119 case FastDeploy_AgentUpdateDifferentVersion:
120 if (agent_version != kRequiredAgentVersion) {
121 if (agent_version < 0) {
122 printf("Could not detect agent on device, deploying\n");
123 } else {
124 printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
125 agent_version, kRequiredAgentVersion);
126 }
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100127 deploy_agent(false);
Idries Hamadied409ea2018-01-29 16:30:36 +0000128 }
129 break;
130 }
131
132 agent_version = get_agent_version();
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100133 if (agent_version != kRequiredAgentVersion) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700134 error_exit("After update agent version remains incorrect! Expected %ld but version is %ld",
135 kRequiredAgentVersion, agent_version);
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100136 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000137}
138
Idries Hamadi269a4b42018-09-13 18:00:25 +0100139static std::string get_string_from_utf16(const char16_t* input, int input_len) {
140 ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
141 if (utf8_length <= 0) {
142 return {};
Idries Hamadied409ea2018-01-29 16:30:36 +0000143 }
Idries Hamadi269a4b42018-09-13 18:00:25 +0100144 std::string utf8;
145 utf8.resize(utf8_length);
146 utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
147 return utf8;
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100148}
149
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100150static std::string get_packagename_from_apk(const char* apkPath) {
Elliott Hughes4679a392018-10-19 13:59:44 -0700151#undef open
Idries Hamadi269a4b42018-09-13 18:00:25 +0100152 std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
Elliott Hughes4679a392018-10-19 13:59:44 -0700153#define open ___xxx_unix_open
Idries Hamadi269a4b42018-09-13 18:00:25 +0100154 if (zipFile == nullptr) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700155 perror_exit("Could not open %s", apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000156 }
Idries Hamadi269a4b42018-09-13 18:00:25 +0100157 android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
158 if (entry == nullptr) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700159 error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100160 }
161 uint32_t manifest_len = 0;
162 if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700163 error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100164 }
165 std::vector<char> manifest_data(manifest_len);
166 if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700167 error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100168 }
169 android::ResXMLTree tree;
170 android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
171 if (setto_status != android::OK) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700172 error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100173 }
174 android::ResXMLParser::event_code_t code;
175 while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
176 code != android::ResXMLParser::END_DOCUMENT) {
177 switch (code) {
178 case android::ResXMLParser::START_TAG: {
179 size_t element_name_length;
180 const char16_t* element_name = tree.getElementName(&element_name_length);
181 if (element_name == nullptr) {
182 continue;
183 }
184 std::u16string element_name_string(element_name, element_name_length);
185 if (element_name_string == u"manifest") {
186 for (size_t i = 0; i < tree.getAttributeCount(); i++) {
187 size_t attribute_name_length;
188 const char16_t* attribute_name_text =
189 tree.getAttributeName(i, &attribute_name_length);
190 if (attribute_name_text == nullptr) {
191 continue;
192 }
193 std::u16string attribute_name_string(attribute_name_text,
194 attribute_name_length);
195 if (attribute_name_string == u"package") {
196 size_t attribute_value_length;
197 const char16_t* attribute_value_text =
198 tree.getAttributeStringValue(i, &attribute_value_length);
199 if (attribute_value_text == nullptr) {
200 continue;
201 }
202 return get_string_from_utf16(attribute_value_text,
203 attribute_value_length);
204 }
205 }
206 }
207 break;
208 }
209 default:
210 break;
211 }
212 }
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700213 error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000214}
215
Idries Hamadi269a4b42018-09-13 18:00:25 +0100216void extract_metadata(const char* apkPath, FILE* outputFp) {
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100217 std::string packageName = get_packagename_from_apk(apkPath);
Idries Hamadi7575cd92018-08-24 11:46:45 +0100218 const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
Idries Hamadied409ea2018-01-29 16:30:36 +0000219 std::string extractCommand =
220 android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
221
222 std::vector<char> extractErrorBuffer;
Henry Daitxf21edf32018-11-30 18:02:43 +0000223 DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100224 int returnCode = send_shell_command(extractCommand, false, &cb);
225 if (returnCode != 0) {
Henry Daitxf21edf32018-11-30 18:02:43 +0000226 fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
227 fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
228 error_exit("Aborting");
Idries Hamadied409ea2018-01-29 16:30:36 +0000229 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000230}
231
Idries Hamadi269a4b42018-09-13 18:00:25 +0100232void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
Joshua Gilpatrickaf3ce082019-07-19 09:42:12 -0700233 DeployPatchGenerator generator(false);
234 unique_fd patchFd(adb_open(patchPath, O_WRONLY | O_CREAT | O_CLOEXEC));
235 if (patchFd < 0) {
236 perror_exit("adb: failed to create %s", patchPath);
237 }
238 bool success = generator.CreatePatch(apkPath, metadataPath, patchFd);
239 if (!success) {
240 error_exit("Failed to create patch for %s", apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100241 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000242}
243
244std::string get_patch_path(const char* apkPath) {
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100245 std::string packageName = get_packagename_from_apk(apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000246 std::string patchDevicePath =
247 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
248 return patchDevicePath;
249}
250
Idries Hamadi269a4b42018-09-13 18:00:25 +0100251void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100252 const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100253 std::string packageName = get_packagename_from_apk(apkPath);
Idries Hamadied409ea2018-01-29 16:30:36 +0000254 std::string patchDevicePath = get_patch_path(apkPath);
255
256 std::vector<const char*> srcs = {patchPath};
257 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
Idries Hamadied409ea2018-01-29 16:30:36 +0000258 if (!push_ok) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700259 error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
Idries Hamadied409ea2018-01-29 16:30:36 +0000260 }
261
262 std::string applyPatchCommand =
263 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
264 patchDevicePath.c_str(), outputPath);
Idries Hamadi5b6bf942018-08-28 12:58:09 +0100265
Idries Hamadi269a4b42018-09-13 18:00:25 +0100266 int returnCode = send_shell_command(applyPatchCommand);
267 if (returnCode != 0) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700268 error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100269 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000270}
271
Idries Hamadi269a4b42018-09-13 18:00:25 +0100272void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
Idries Hamadi7575cd92018-08-24 11:46:45 +0100273 const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
Idries Hamadi4af6ee42018-09-06 18:42:39 +0100274 std::string packageName = get_packagename_from_apk(apkPath);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100275
Idries Hamadied409ea2018-01-29 16:30:36 +0000276 std::string patchDevicePath =
277 android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
Idries Hamadied409ea2018-01-29 16:30:36 +0000278
Idries Hamadi269a4b42018-09-13 18:00:25 +0100279 std::vector<const char*> srcs{patchPath};
280 bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
Idries Hamadied409ea2018-01-29 16:30:36 +0000281 if (!push_ok) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700282 error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
Idries Hamadied409ea2018-01-29 16:30:36 +0000283 }
284
285 std::vector<unsigned char> applyOutputBuffer;
286 std::vector<unsigned char> applyErrorBuffer;
287 std::string argsString;
288
Henry Daitxee01c802018-12-12 10:40:57 +0000289 bool rSwitchPresent = false;
Idries Hamadied409ea2018-01-29 16:30:36 +0000290 for (int i = 0; i < argc; i++) {
291 argsString.append(argv[i]);
292 argsString.append(" ");
Henry Daitxee01c802018-12-12 10:40:57 +0000293 if (!strcmp(argv[i], "-r")) {
294 rSwitchPresent = true;
295 }
296 }
297 if (!rSwitchPresent) {
298 argsString.append("-r");
Idries Hamadied409ea2018-01-29 16:30:36 +0000299 }
300
301 std::string applyPatchCommand =
302 android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
303 patchDevicePath.c_str(), argsString.c_str());
Idries Hamadi269a4b42018-09-13 18:00:25 +0100304 int returnCode = send_shell_command(applyPatchCommand);
305 if (returnCode != 0) {
Elliott Hughesd2aaede2018-10-22 17:02:51 -0700306 error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
Idries Hamadi269a4b42018-09-13 18:00:25 +0100307 }
Idries Hamadied409ea2018-01-29 16:30:36 +0000308}
Henry Daitxee01c802018-12-12 10:40:57 +0000309
310bool find_package(const char* apkPath) {
311 const std::string findCommand =
312 "/data/local/tmp/deployagent find " + get_packagename_from_apk(apkPath);
313 return !send_shell_command(findCommand);
314}