blob: 070e9d77101f8c3a416ad5cb290145036947530f [file] [log] [blame]
Doug Zongker9270a202012-01-09 15:16:13 -08001/*
2 * Copyright (C) 2012 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
xunchang24788852019-03-22 16:08:52 -070017#include "install/adb_install.h"
Tao Bao0150d012017-05-01 11:31:28 -070018
Doug Zongker9270a202012-01-09 15:16:13 -080019#include <errno.h>
Tao Bao0150d012017-05-01 11:31:28 -070020#include <fcntl.h>
21#include <signal.h>
Doug Zongker9270a202012-01-09 15:16:13 -080022#include <stdlib.h>
23#include <string.h>
xunchang95d67322019-04-05 16:16:07 -070024#include <sys/epoll.h>
25#include <sys/socket.h>
Tao Bao0150d012017-05-01 11:31:28 -070026#include <sys/stat.h>
Doug Zongker9270a202012-01-09 15:16:13 -080027#include <sys/types.h>
28#include <sys/wait.h>
Tao Bao0150d012017-05-01 11:31:28 -070029#include <unistd.h>
Doug Zongker9270a202012-01-09 15:16:13 -080030
xunchang95d67322019-04-05 16:16:07 -070031#include <atomic>
32#include <functional>
33#include <map>
Tao Bao7b9b7db2019-04-19 15:22:15 -070034#include <utility>
Tao Bao378bfbf2019-04-16 14:22:25 -070035#include <vector>
xunchang95d67322019-04-05 16:16:07 -070036
37#include <android-base/file.h>
Tao Bao0167d4c2017-05-11 14:44:15 -070038#include <android-base/logging.h>
xunchang95d67322019-04-05 16:16:07 -070039#include <android-base/memory.h>
Elliott Hughescb220402016-09-23 15:30:55 -070040#include <android-base/properties.h>
xunchang95d67322019-04-05 16:16:07 -070041#include <android-base/strings.h>
42#include <android-base/unique_fd.h>
Elliott Hughescb220402016-09-23 15:30:55 -070043
Tao Bao0150d012017-05-01 11:31:28 -070044#include "fuse_sideload.h"
xunchang24788852019-03-22 16:08:52 -070045#include "install/install.h"
xunchang5a1916b2019-04-22 12:18:14 -070046#include "install/wipe_data.h"
xunchang95d67322019-04-05 16:16:07 -070047#include "minadbd_types.h"
Tao Bao378bfbf2019-04-16 14:22:25 -070048#include "otautil/sysutil.h"
Tao Bao7b9b7db2019-04-19 15:22:15 -070049#include "recovery_ui/device.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070050#include "recovery_ui/ui.h"
Tao Bao0150d012017-05-01 11:31:28 -070051
Tao Bao7b9b7db2019-04-19 15:22:15 -070052// A CommandFunction returns a pair of (result, should_continue), which indicates the command
53// execution result and whether it should proceed to the next iteration. The execution result will
54// always be sent to the minadbd side.
55using CommandFunction = std::function<std::pair<bool, bool>()>;
xunchang95d67322019-04-05 16:16:07 -070056
xunchang24788852019-03-22 16:08:52 -070057static bool SetUsbConfig(const std::string& state) {
58 android::base::SetProperty("sys.usb.config", state);
59 return android::base::WaitForProperty("sys.usb.state", state);
60}
61
Tao Bao7b9b7db2019-04-19 15:22:15 -070062// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors.
63static MinadbdCommand ParseMinadbdCommand(const std::string& message) {
xunchang95d67322019-04-05 16:16:07 -070064 if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
65 LOG(ERROR) << "Failed to parse command in message " << message;
Tao Bao7b9b7db2019-04-19 15:22:15 -070066 return MinadbdCommand::kError;
xunchang95d67322019-04-05 16:16:07 -070067 }
68
69 auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
70 auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
Tao Bao7b9b7db2019-04-19 15:22:15 -070071 if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) {
xunchang95d67322019-04-05 16:16:07 -070072 LOG(ERROR) << "Unsupported command code: " << cmd_code;
Tao Bao7b9b7db2019-04-19 15:22:15 -070073 return MinadbdCommand::kError;
xunchang95d67322019-04-05 16:16:07 -070074 }
75
Tao Bao7b9b7db2019-04-19 15:22:15 -070076 return static_cast<MinadbdCommand>(cmd_code);
xunchang95d67322019-04-05 16:16:07 -070077}
78
79static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
80 char message[kMinadbdMessageSize];
81 memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
82 android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
83
84 if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
85 PLOG(ERROR) << "Failed to write message " << message;
86 return false;
87 }
88 return true;
89}
90
Tao Bao7b9b7db2019-04-19 15:22:15 -070091// Installs the package from FUSE. Returns the installation result and whether it should continue
92// waiting for new commands.
Alessandro Astone4e1e8662020-03-17 22:26:22 +010093static auto AdbInstallPackageHandler(Device* device, int* result) {
Tom Marshall03354052018-06-21 00:57:24 +020094 RecoveryUI* ui = device->GetUI();
95
xunchang95d67322019-04-05 16:16:07 -070096 // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
97 // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
98 // will start to exist once the host connects and starts serving a package. Poll for its
99 // appearance. (Note that inotify doesn't work with FUSE.)
100 constexpr int ADB_INSTALL_TIMEOUT = 15;
Tao Bao7b9b7db2019-04-19 15:22:15 -0700101 bool should_continue = true;
xunchang95d67322019-04-05 16:16:07 -0700102 *result = INSTALL_ERROR;
103 for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
104 struct stat st;
105 if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
106 if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
107 sleep(1);
108 continue;
109 } else {
Tao Bao7b9b7db2019-04-19 15:22:15 -0700110 should_continue = false;
xunchang95d67322019-04-05 16:16:07 -0700111 ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
112 break;
113 }
114 }
Tom Marshalla9ac9552018-12-17 15:57:44 -0800115 ui->CancelWaitKey();
Tom Marshall03354052018-06-21 00:57:24 +0200116
Alessandro Astone8b726282020-03-17 23:06:52 +0100117 *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, true /* verify */,
118 false /* allow_ab_downgrade */, ui);
Alessandro Astone4e1e8662020-03-17 22:26:22 +0100119 if (*result == INSTALL_UNVERIFIED &&
120 ui->IsTextVisible() && ask_to_continue_unverified(device)) {
Alessandro Astone8b726282020-03-17 23:06:52 +0100121 *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, false /* verify */,
122 false /* allow_ab_downgrade */, ui);
123 }
124 if (*result == INSTALL_DOWNGRADE &&
125 ui->IsTextVisible() && ask_to_continue_downgrade(device)) {
126 *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, false /* verify */,
127 true /* allow_ab_downgrade */, ui);
Tom Marshall03354052018-06-21 00:57:24 +0200128 }
xunchang95d67322019-04-05 16:16:07 -0700129 break;
130 }
131
132 // Calling stat() on this magic filename signals the FUSE to exit.
133 struct stat st;
134 stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
Tao Bao7b9b7db2019-04-19 15:22:15 -0700135 return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
xunchang95d67322019-04-05 16:16:07 -0700136}
137
Tao Bao7b9b7db2019-04-19 15:22:15 -0700138static auto AdbRebootHandler(MinadbdCommand command, int* result,
139 Device::BuiltinAction* reboot_action) {
Tao Bao75321ad2019-04-23 11:46:25 -0700140 // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows
141 // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly
142 // installed bootloader/recovery image.
Tao Bao7b9b7db2019-04-19 15:22:15 -0700143 switch (command) {
144 case MinadbdCommand::kRebootBootloader:
145 *reboot_action = Device::REBOOT_BOOTLOADER;
146 break;
147 case MinadbdCommand::kRebootFastboot:
Tao Bao75321ad2019-04-23 11:46:25 -0700148 *reboot_action = Device::REBOOT_FASTBOOT;
Tao Bao7b9b7db2019-04-19 15:22:15 -0700149 break;
150 case MinadbdCommand::kRebootRecovery:
Tao Bao75321ad2019-04-23 11:46:25 -0700151 *reboot_action = Device::REBOOT_RECOVERY;
Tao Bao7b9b7db2019-04-19 15:22:15 -0700152 break;
153 case MinadbdCommand::kRebootRescue:
Tao Bao7b9b7db2019-04-19 15:22:15 -0700154 *reboot_action = Device::REBOOT_RESCUE;
155 break;
156 case MinadbdCommand::kRebootAndroid:
157 default:
158 *reboot_action = Device::REBOOT;
159 break;
160 }
161 *result = INSTALL_REBOOT;
162 return std::make_pair(true, false);
163}
164
165// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for
166// next command.
167static bool HandleMessageFromMinadbd(int socket_fd,
168 const std::map<MinadbdCommand, CommandFunction>& command_map) {
xunchang95d67322019-04-05 16:16:07 -0700169 char buffer[kMinadbdMessageSize];
170 if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
171 PLOG(ERROR) << "Failed to read message from minadbd";
172 return false;
173 }
174
175 std::string message(buffer, buffer + kMinadbdMessageSize);
Tao Bao7b9b7db2019-04-19 15:22:15 -0700176 auto command_type = ParseMinadbdCommand(message);
177 if (command_type == MinadbdCommand::kError) {
xunchang95d67322019-04-05 16:16:07 -0700178 return false;
179 }
180 if (command_map.find(command_type) == command_map.end()) {
181 LOG(ERROR) << "Unsupported command: "
182 << android::base::get_unaligned<unsigned int>(
183 message.substr(strlen(kMinadbdCommandPrefix)).c_str());
184 return false;
185 }
186
187 // We have received a valid command, execute the corresponding function.
188 const auto& command_func = command_map.at(command_type);
Tao Bao7b9b7db2019-04-19 15:22:15 -0700189 const auto [result, should_continue] = command_func();
190 LOG(INFO) << "Command " << static_cast<uint32_t>(command_type) << " finished with " << result;
191 if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure,
192 socket_fd)) {
193 return false;
xunchang95d67322019-04-05 16:16:07 -0700194 }
Tao Bao7b9b7db2019-04-19 15:22:15 -0700195 return should_continue;
xunchang95d67322019-04-05 16:16:07 -0700196}
197
198// TODO(xunchang) add a wrapper function and kill the minadbd service there.
199static void ListenAndExecuteMinadbdCommands(
Tao Bao75321ad2019-04-23 11:46:25 -0700200 RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
Tao Bao7b9b7db2019-04-19 15:22:15 -0700201 const std::map<MinadbdCommand, CommandFunction>& command_map) {
xunchang95d67322019-04-05 16:16:07 -0700202 android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
203 if (epoll_fd == -1) {
204 PLOG(ERROR) << "Failed to create epoll";
205 kill(minadbd_pid, SIGKILL);
206 return;
207 }
208
209 constexpr int EPOLL_MAX_EVENTS = 10;
210 struct epoll_event ev = {};
211 ev.events = EPOLLIN | EPOLLHUP;
212 ev.data.fd = socket_fd.get();
213 struct epoll_event events[EPOLL_MAX_EVENTS];
214 if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
215 PLOG(ERROR) << "Failed to add socket fd to epoll";
216 kill(minadbd_pid, SIGKILL);
217 return;
218 }
219
220 // Set the timeout to be 300s when waiting for minadbd commands.
221 constexpr int TIMEOUT_MILLIS = 300 * 1000;
222 while (true) {
Tao Bao75321ad2019-04-23 11:46:25 -0700223 // Reset the progress bar and the background image before each command.
224 ui->SetProgressType(RecoveryUI::EMPTY);
225 ui->SetBackground(RecoveryUI::NO_COMMAND);
226
xunchang95d67322019-04-05 16:16:07 -0700227 // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
228 // read.
229 int event_count =
230 TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
231 if (event_count == -1) {
232 PLOG(ERROR) << "Failed to wait for epoll events";
233 kill(minadbd_pid, SIGKILL);
234 return;
235 }
236 if (event_count == 0) {
237 LOG(ERROR) << "Timeout waiting for messages from minadbd";
238 kill(minadbd_pid, SIGKILL);
239 return;
240 }
241
242 for (int n = 0; n < event_count; n++) {
243 if (events[n].events & EPOLLHUP) {
244 LOG(INFO) << "Socket has been closed";
245 kill(minadbd_pid, SIGKILL);
246 return;
247 }
248 if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
249 kill(minadbd_pid, SIGKILL);
250 return;
251 }
252 }
253 }
254}
255
256// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
257// message from minadbd through a socket pair. Here is an example to execute one command from adb
258// host.
259// a. recovery b. listener thread c. minadbd service
260//
261// a1. create socket pair
262// a2. fork minadbd service
263// c3. wait for the adb commands
264// from host
265// c4. after receiving host commands:
266// 1) set up pre-condition (i.e.
267// start fuse for adb sideload)
268// 2) issue command through
269// socket.
270// 3) wait for result
271// a5. start listener thread
272// b6. listen for message from
273// minadbd in a loop.
274// b7. After receiving a minadbd
275// command from socket
276// 1) execute the command function
277// 2) send the result back to
278// minadbd
279// ......
280// c8. exit upon receiving the
281// result
282// a9. wait for listener thread
283// to exit.
284//
285// a10. wait for minadbd to
286// exit
287// b11. exit the listening loop
288//
289static void CreateMinadbdServiceAndExecuteCommands(
Tom Marshalla9ac9552018-12-17 15:57:44 -0800290 Device* device, const std::map<MinadbdCommand, CommandFunction>& command_map,
Tao Bao75321ad2019-04-23 11:46:25 -0700291 bool rescue_mode) {
xunchang95d67322019-04-05 16:16:07 -0700292 signal(SIGPIPE, SIG_IGN);
293
294 android::base::unique_fd recovery_socket;
295 android::base::unique_fd minadbd_socket;
296 if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
297 PLOG(ERROR) << "Failed to create socket";
298 return;
299 }
300
301 pid_t child = fork();
302 if (child == -1) {
303 PLOG(ERROR) << "Failed to fork child process";
304 return;
305 }
306 if (child == 0) {
307 recovery_socket.reset();
Tao Bao378bfbf2019-04-16 14:22:25 -0700308 std::vector<std::string> minadbd_commands = {
309 "/system/bin/minadbd",
310 "--socket_fd",
311 std::to_string(minadbd_socket.release()),
312 };
313 if (rescue_mode) {
314 minadbd_commands.push_back("--rescue");
315 }
316 auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands);
317 execv(exec_args[0], exec_args.data());
xunchang95d67322019-04-05 16:16:07 -0700318 _exit(EXIT_FAILURE);
319 }
320
321 minadbd_socket.reset();
322
323 // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
324 // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
325 if (!SetUsbConfig("sideload")) {
326 LOG(ERROR) << "Failed to set usb config to sideload";
327 return;
328 }
329
Tom Marshalla9ac9552018-12-17 15:57:44 -0800330 RecoveryUI* ui = device->GetUI();
Tao Bao75321ad2019-04-23 11:46:25 -0700331 std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child,
332 std::move(recovery_socket), std::ref(command_map));
Tom Marshalla9ac9552018-12-17 15:57:44 -0800333
334 if (ui->IsTextVisible()) {
335 std::vector<std::string> headers{ rescue_mode ? "Rescue mode" : "ADB Sideload" };
336 std::vector<std::string> entries{ "Cancel" };
337 size_t chosen_item = ui->ShowMenu(
338 headers, entries, 0, true,
339 std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
340
Alessandroe4c344a2020-04-11 12:11:29 +0200341 if (chosen_item != Device::kDoSideload) {
Tom Marshalla9ac9552018-12-17 15:57:44 -0800342 // Kill minadbd if 'cancel' was selected, to abort sideload.
343 kill(child, SIGKILL);
344 }
345 }
346
xunchang95d67322019-04-05 16:16:07 -0700347 if (listener_thread.joinable()) {
348 listener_thread.join();
349 }
350
351 int status;
352 waitpid(child, &status, 0);
353 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
354 if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
355 LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
356 } else if (!WIFSIGNALED(status)) {
357 LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
358 }
359 }
360
361 signal(SIGPIPE, SIG_DFL);
362}
363
Alessandro Astone4e1e8662020-03-17 22:26:22 +0100364int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
Hridya Valsarajue4ef4532018-08-31 11:57:51 -0700365 // Save the usb state to restore after the sideload operation.
366 std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
367 // Clean up state and stop adbd.
368 if (usb_state != "none" && !SetUsbConfig("none")) {
369 LOG(ERROR) << "Failed to clear USB config";
370 return INSTALL_ERROR;
371 }
Tao Bao682c34b2015-04-07 17:16:35 -0700372
xunchang95d67322019-04-05 16:16:07 -0700373 int install_result = INSTALL_ERROR;
Tao Bao7b9b7db2019-04-19 15:22:15 -0700374 std::map<MinadbdCommand, CommandFunction> command_map{
Alessandro Astone4e1e8662020-03-17 22:26:22 +0100375 { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, device, &install_result) },
Tao Bao7b9b7db2019-04-19 15:22:15 -0700376 { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
377 &install_result, reboot_action) },
378 { MinadbdCommand::kRebootBootloader,
379 std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result,
380 reboot_action) },
381 { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot,
382 &install_result, reboot_action) },
383 { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery,
384 &install_result, reboot_action) },
385 { MinadbdCommand::kRebootRescue,
386 std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
xunchang95d67322019-04-05 16:16:07 -0700387 };
Doug Zongker9270a202012-01-09 15:16:13 -0800388
Tom Marshall03354052018-06-21 00:57:24 +0200389 RecoveryUI* ui = device->GetUI();
390
xunchang5a1916b2019-04-22 12:18:14 -0700391 if (!rescue_mode) {
392 ui->Print(
393 "\n\nNow send the package you want to apply\n"
394 "to the device with \"adb sideload <filename>\"...\n");
395 } else {
xunchang5a1916b2019-04-22 12:18:14 -0700396 command_map.emplace(MinadbdCommand::kWipeData, [&device]() {
397 bool result = WipeData(device, false);
398 return std::make_pair(result, true);
399 });
Tao Bao0bbb2ed2019-07-08 18:07:22 -0700400 command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); });
401
402 ui->Print("\n\nWaiting for rescue commands...\n");
xunchang5a1916b2019-04-22 12:18:14 -0700403 }
404
Tom Marshalla9ac9552018-12-17 15:57:44 -0800405 CreateMinadbdServiceAndExecuteCommands(device, command_map, rescue_mode);
Doug Zongker075ad802014-06-26 15:35:51 -0700406
Hridya Valsarajue4ef4532018-08-31 11:57:51 -0700407 // Clean up before switching to the older state, for example setting the state
408 // to none sets sys/class/android_usb/android0/enable to 0.
409 if (!SetUsbConfig("none")) {
410 LOG(ERROR) << "Failed to clear USB config";
411 }
412
413 if (usb_state != "none") {
414 if (!SetUsbConfig(usb_state)) {
415 LOG(ERROR) << "Failed to set USB config to " << usb_state;
416 }
417 }
Doug Zongker9270a202012-01-09 15:16:13 -0800418
xunchang95d67322019-04-05 16:16:07 -0700419 return install_result;
Doug Zongker9270a202012-01-09 15:16:13 -0800420}