blob: 175dd77b523520bf06a9b81e473d606df9e28318 [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 "fastdeploy.h"
18
Henry Daitxe4cc4d92018-12-12 10:40:57 +000019#include <string.h>
Idries Hamadi1ecee442018-01-29 16:30:36 +000020#include <algorithm>
Idries Hamadi7919be22018-08-28 12:58:09 +010021#include <array>
Idries Hamadi78330f02018-09-13 18:00:25 +010022#include <memory>
Idries Hamadi1ecee442018-01-29 16:30:36 +000023
Idries Hamadi7919be22018-08-28 12:58:09 +010024#include "android-base/file.h"
Elliott Hughes787eeae2025-03-17 14:05:49 -070025#include "android-base/parseint.h"
Idries Hamadi7919be22018-08-28 12:58:09 +010026#include "android-base/strings.h"
Idries Hamadi78330f02018-09-13 18:00:25 +010027#include "androidfw/ResourceTypes.h"
28#include "androidfw/ZipFileRO.h"
Idries Hamadi1ecee442018-01-29 16:30:36 +000029#include "client/file_sync_client.h"
30#include "commandline.h"
Joshua Gilpatrick0d384112019-07-19 09:42:12 -070031#include "deployagent.inc" // Generated include via build rule.
32#include "deployagentscript.inc" // Generated include via build rule.
33#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070034#include "fastdeploy/deploypatchgenerator/patch_utils.h"
35#include "fastdeploy/proto/ApkEntry.pb.h"
Elliott Hughese64126b2018-10-19 13:59:44 -070036#include "sysdeps.h"
Elliott Hughes0119a912018-10-22 17:02:51 -070037
Elliott Hughese76e5ef2024-06-25 21:43:49 +000038#include "adb_client.h"
Elliott Hughes0119a912018-10-22 17:02:51 -070039#include "adb_utils.h"
Idries Hamadi1ecee442018-01-29 16:30:36 +000040
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070041static constexpr int kPackageMissing = 3;
42static constexpr int kInvalidAgentVersion = 4;
43
Joshua Gilpatrick0d384112019-07-19 09:42:12 -070044static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
45static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
Idries Hamadi1ecee442018-01-29 16:30:36 +000046
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070047static constexpr bool g_verbose_timings = false;
48static FastDeploy_AgentUpdateStrategy g_agent_update_strategy =
49 FastDeploy_AgentUpdateDifferentVersion;
Idries Hamadi7919be22018-08-28 12:58:09 +010050
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070051using APKMetaData = com::android::fastdeploy::APKMetaData;
Idries Hamadi1ecee442018-01-29 16:30:36 +000052
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070053namespace {
Idries Hamadi1ecee442018-01-29 16:30:36 +000054
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070055struct TimeReporter {
56 TimeReporter(const char* label) : label_(label) {}
57 ~TimeReporter() {
58 if (g_verbose_timings) {
59 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
60 std::chrono::steady_clock::now() - start_);
61 fprintf(stderr, "%s finished in %lldms\n", label_,
62 static_cast<long long>(duration.count()));
63 }
Idries Hamadi1ecee442018-01-29 16:30:36 +000064 }
65
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070066 private:
67 const char* label_;
68 std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
69};
70#define REPORT_FUNC_TIME() TimeReporter reporter(__func__)
71
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070072} // namespace
Idries Hamadi1ecee442018-01-29 16:30:36 +000073
74int get_device_api_level() {
Alex Buynytskyy175ce292020-02-13 06:52:04 -080075 static const int api_level = [] {
76 REPORT_FUNC_TIME();
Idries Hamadi1ecee442018-01-29 16:30:36 +000077
Elliott Hughes787eeae2025-03-17 14:05:49 -070078 std::string getprop_stdout, getprop_stderr;
79 DefaultStandardStreamsCallback cb(&getprop_stdout, &getprop_stderr);
80 int status_code = send_shell_command("getprop ro.build.version.sdk", false, &cb);
Idries Hamadi1ecee442018-01-29 16:30:36 +000081
Elliott Hughes787eeae2025-03-17 14:05:49 -070082 int api_level;
83 return android::base::ParseInt(getprop_stdout, &api_level) ? api_level : -1;
Alex Buynytskyy175ce292020-02-13 06:52:04 -080084 }();
Idries Hamadi1ecee442018-01-29 16:30:36 +000085 return api_level;
86}
87
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070088void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy) {
89 g_agent_update_strategy = agent_update_strategy;
Idries Hamadi7919be22018-08-28 12:58:09 +010090}
91
Alex Buynytskyy1af550e2019-09-16 12:10:54 -070092static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
Idries Hamadi1ecee442018-01-29 16:30:36 +000093 std::vector<const char*> srcs;
Elliott Hughes22c15072019-11-07 15:38:00 -080094 TemporaryFile tf;
95 android::base::WriteFully(tf.fd, data, byte_count);
96 srcs.push_back(tf.path);
97 // On Windows, the file needs to be flushed before pushing to device,
98 // but can't be removed until after the push.
99 unix_close(tf.release());
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700100
Peter Collingbournea41eb3d2024-03-28 20:05:07 -0700101 if (!do_sync_push(srcs, dst, sync, CompressionType::Any, false, false)) {
Joshua Gilpatrick0d384112019-07-19 09:42:12 -0700102 error_exit("Failed to push fastdeploy agent to device.");
103 }
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700104}
105
106static bool deploy_agent(bool check_time_stamps) {
107 REPORT_FUNC_TIME();
108
109 push_to_device(kDeployAgent, sizeof(kDeployAgent), kDeviceAgentFile, check_time_stamps);
110 push_to_device(kDeployAgentScript, sizeof(kDeployAgentScript), kDeviceAgentScript,
111 check_time_stamps);
112
Joshua Gilpatrick0d384112019-07-19 09:42:12 -0700113 // on windows the shell script might have lost execute permission
114 // so need to set this explicitly
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700115 std::string chmod_command =
Elliott Hughes787eeae2025-03-17 14:05:49 -0700116 android::base::StringPrintf("chmod 777 %s", kDeviceAgentScript);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700117 int ret = send_shell_command(chmod_command);
Joshua Gilpatrick0d384112019-07-19 09:42:12 -0700118 if (ret != 0) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700119 error_exit("Error executing %s returncode: %d", chmod_command.c_str(), ret);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000120 }
Idries Hamadib1702db2018-09-06 18:42:39 +0100121
122 return true;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000123}
124
Idries Hamadi78330f02018-09-13 18:00:25 +0100125static std::string get_string_from_utf16(const char16_t* input, int input_len) {
126 ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
127 if (utf8_length <= 0) {
128 return {};
Idries Hamadi1ecee442018-01-29 16:30:36 +0000129 }
Idries Hamadi78330f02018-09-13 18:00:25 +0100130 std::string utf8;
131 utf8.resize(utf8_length);
132 utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
133 return utf8;
Idries Hamadi7919be22018-08-28 12:58:09 +0100134}
135
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700136static std::string get_package_name_from_apk(const char* apk_path) {
Elliott Hughese64126b2018-10-19 13:59:44 -0700137#undef open
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700138 std::unique_ptr<android::ZipFileRO> zip_file((android::ZipFileRO::open)(apk_path));
Elliott Hughese64126b2018-10-19 13:59:44 -0700139#define open ___xxx_unix_open
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700140 if (zip_file == nullptr) {
141 perror_exit("Could not open %s", apk_path);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000142 }
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700143 android::ZipEntryRO entry = zip_file->findEntryByName("AndroidManifest.xml");
Idries Hamadi78330f02018-09-13 18:00:25 +0100144 if (entry == nullptr) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700145 error_exit("Could not find AndroidManifest.xml inside %s", apk_path);
Idries Hamadi78330f02018-09-13 18:00:25 +0100146 }
147 uint32_t manifest_len = 0;
Pawan Wagh40a36812024-06-03 21:48:37 +0000148 if (!zip_file->getEntryInfo(entry, nullptr, &manifest_len, nullptr, nullptr, nullptr, nullptr,
149 nullptr)) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700150 error_exit("Could not read AndroidManifest.xml inside %s", apk_path);
Idries Hamadi78330f02018-09-13 18:00:25 +0100151 }
152 std::vector<char> manifest_data(manifest_len);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700153 if (!zip_file->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
154 error_exit("Could not uncompress AndroidManifest.xml inside %s", apk_path);
Idries Hamadi78330f02018-09-13 18:00:25 +0100155 }
156 android::ResXMLTree tree;
157 android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
158 if (setto_status != android::OK) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700159 error_exit("Could not parse AndroidManifest.xml inside %s", apk_path);
Idries Hamadi78330f02018-09-13 18:00:25 +0100160 }
161 android::ResXMLParser::event_code_t code;
162 while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
163 code != android::ResXMLParser::END_DOCUMENT) {
164 switch (code) {
165 case android::ResXMLParser::START_TAG: {
166 size_t element_name_length;
167 const char16_t* element_name = tree.getElementName(&element_name_length);
168 if (element_name == nullptr) {
169 continue;
170 }
171 std::u16string element_name_string(element_name, element_name_length);
172 if (element_name_string == u"manifest") {
173 for (size_t i = 0; i < tree.getAttributeCount(); i++) {
174 size_t attribute_name_length;
175 const char16_t* attribute_name_text =
176 tree.getAttributeName(i, &attribute_name_length);
177 if (attribute_name_text == nullptr) {
178 continue;
179 }
180 std::u16string attribute_name_string(attribute_name_text,
181 attribute_name_length);
182 if (attribute_name_string == u"package") {
183 size_t attribute_value_length;
184 const char16_t* attribute_value_text =
185 tree.getAttributeStringValue(i, &attribute_value_length);
186 if (attribute_value_text == nullptr) {
187 continue;
188 }
189 return get_string_from_utf16(attribute_value_text,
190 attribute_value_length);
191 }
192 }
193 }
194 break;
195 }
196 default:
197 break;
198 }
199 }
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700200 error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apk_path);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000201}
202
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700203static void update_agent_if_necessary() {
204 switch (g_agent_update_strategy) {
205 case FastDeploy_AgentUpdateAlways:
206 deploy_agent(/*check_time_stamps=*/false);
207 break;
208 case FastDeploy_AgentUpdateNewerTimeStamp:
209 deploy_agent(/*check_time_stamps=*/true);
210 break;
211 default:
212 break;
213 }
214}
215
216std::optional<APKMetaData> extract_metadata(const char* apk_path) {
217 // Update agent if there is a command line argument forcing to do so.
218 update_agent_if_necessary();
219
220 REPORT_FUNC_TIME();
221
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700222 // Dump apk command checks the required vs current agent version and if they match then returns
223 // the APK dump for package. Doing this in a single call saves round-trip and agent launch time.
Elliott Hughes787eeae2025-03-17 14:05:49 -0700224 std::string package_name(escape_arg(get_package_name_from_apk(apk_path)));
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700225 std::string dump_command = android::base::StringPrintf(
Elliott Hughes787eeae2025-03-17 14:05:49 -0700226 "/data/local/tmp/deployagent dump 3 %s", package_name.c_str());
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700227
Elliott Hughes787eeae2025-03-17 14:05:49 -0700228 std::string dump_out_buffer;
229 std::string dump_error_buffer;
230 DefaultStandardStreamsCallback cb(&dump_out_buffer, &dump_error_buffer);
Elliott Hughes710a6552025-03-20 06:09:59 -0700231 int returnCode = send_shell_command(dump_command, false, &cb);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700232 if (returnCode >= kInvalidAgentVersion) {
Elliott Hughes787eeae2025-03-17 14:05:49 -0700233 long agent_version;
234 if (!android::base::ParseInt(dump_out_buffer, &agent_version)) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700235 printf("Could not detect agent on device, deploying\n");
236 } else {
Elliott Hughes787eeae2025-03-17 14:05:49 -0700237 printf("Device agent version is old (%ld), re-deploying\n", agent_version);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700238 }
239 deploy_agent(/*check_time_stamps=*/false);
240
241 // Retry with new agent.
242 dump_out_buffer.clear();
243 dump_error_buffer.clear();
Elliott Hughes710a6552025-03-20 06:09:59 -0700244 returnCode = send_shell_command(dump_command, false, &cb);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700245 }
Idries Hamadi78330f02018-09-13 18:00:25 +0100246 if (returnCode != 0) {
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700247 if (returnCode == kInvalidAgentVersion) {
Elliott Hughes787eeae2025-03-17 14:05:49 -0700248 error_exit("After update agent version remains old: %s", dump_out_buffer.c_str());
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700249 }
250 if (returnCode == kPackageMissing) {
251 fprintf(stderr, "Package %s not found, falling back to install\n",
252 package_name.c_str());
253 return {};
254 }
255 fprintf(stderr, "Executing %s returned %d\n", dump_command.c_str(), returnCode);
256 fprintf(stderr, "%*s\n", int(dump_error_buffer.size()), dump_error_buffer.data());
Henry Daitx61bf7482018-11-30 18:02:43 +0000257 error_exit("Aborting");
Idries Hamadi1ecee442018-01-29 16:30:36 +0000258 }
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700259
260 com::android::fastdeploy::APKDump dump;
261 if (!dump.ParseFromArray(dump_out_buffer.data(), dump_out_buffer.size())) {
262 fprintf(stderr, "Can't parse output of %s\n", dump_command.c_str());
263 error_exit("Aborting");
264 }
265
266 return PatchUtils::GetDeviceAPKMetaData(dump);
Idries Hamadi1ecee442018-01-29 16:30:36 +0000267}
268
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700269unique_fd install_patch(int argc, const char** argv) {
270 REPORT_FUNC_TIME();
Idries Hamadi1ecee442018-01-29 16:30:36 +0000271
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700272 std::vector<unsigned char> apply_output_buffer;
273 std::vector<unsigned char> apply_error_buffer;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000274 std::string argsString;
275
Henry Daitxe4cc4d92018-12-12 10:40:57 +0000276 bool rSwitchPresent = false;
Idries Hamadi1ecee442018-01-29 16:30:36 +0000277 for (int i = 0; i < argc; i++) {
278 argsString.append(argv[i]);
279 argsString.append(" ");
Henry Daitxe4cc4d92018-12-12 10:40:57 +0000280 if (!strcmp(argv[i], "-r")) {
281 rSwitchPresent = true;
282 }
283 }
284 if (!rSwitchPresent) {
285 argsString.append("-r");
Idries Hamadi1ecee442018-01-29 16:30:36 +0000286 }
287
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700288 std::string error;
289 std::string apply_patch_service_string =
Elliott Hughes787eeae2025-03-17 14:05:49 -0700290 android::base::StringPrintf("shell:/data/local/tmp/deployagent apply - -pm %s",
291 argsString.c_str());
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700292 unique_fd fd{adb_connect(apply_patch_service_string, &error)};
293 if (fd < 0) {
294 error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
295 }
296 return fd;
297}
298
299unique_fd apply_patch_on_device(const char* output_path) {
300 REPORT_FUNC_TIME();
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700301 std::string error;
302 std::string apply_patch_service_string =
Elliott Hughes787eeae2025-03-17 14:05:49 -0700303 android::base::StringPrintf("shell:/data/local/tmp/deployagent apply - -o %s",
304 output_path);
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700305 unique_fd fd{adb_connect(apply_patch_service_string, &error)};
306 if (fd < 0) {
307 error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
308 }
309 return fd;
310}
311
312static void create_patch(const char* apk_path, APKMetaData metadata, borrowed_fd patch_fd) {
313 REPORT_FUNC_TIME();
314 DeployPatchGenerator generator(/*is_verbose=*/false);
315 bool success = generator.CreatePatch(apk_path, std::move(metadata), patch_fd);
316 if (!success) {
317 error_exit("Failed to create patch for %s", apk_path);
Idries Hamadi78330f02018-09-13 18:00:25 +0100318 }
Idries Hamadi1ecee442018-01-29 16:30:36 +0000319}
Henry Daitxe4cc4d92018-12-12 10:40:57 +0000320
Alex Buynytskyy1af550e2019-09-16 12:10:54 -0700321int stream_patch(const char* apk_path, APKMetaData metadata, unique_fd patch_fd) {
322 create_patch(apk_path, std::move(metadata), patch_fd);
323
324 REPORT_FUNC_TIME();
325 return read_and_dump(patch_fd.get());
Henry Daitxe4cc4d92018-12-12 10:40:57 +0000326}