blob: d79f6f4b0efc49a45cb9bc14caa7e5469f84c25f [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>
xunchang34690ce2019-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
xunchang34690ce2019-04-05 16:16:07 -070031#include <atomic>
32#include <functional>
33#include <map>
Tao Bao10f441a2019-04-19 15:22:15 -070034#include <utility>
Tao Baoc6dc3252019-04-16 14:22:25 -070035#include <vector>
xunchang34690ce2019-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>
xunchang34690ce2019-04-05 16:16:07 -070039#include <android-base/memory.h>
Elliott Hughescb220402016-09-23 15:30:55 -070040#include <android-base/properties.h>
xunchang34690ce2019-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"
xunchang34690ce2019-04-05 16:16:07 -070046#include "minadbd_types.h"
Tao Baoc6dc3252019-04-16 14:22:25 -070047#include "otautil/sysutil.h"
Tao Bao10f441a2019-04-19 15:22:15 -070048#include "recovery_ui/device.h"
Tianjie Xu8f397302018-08-20 13:40:47 -070049#include "recovery_ui/ui.h"
Tao Bao0150d012017-05-01 11:31:28 -070050
Tao Bao10f441a2019-04-19 15:22:15 -070051// A CommandFunction returns a pair of (result, should_continue), which indicates the command
52// execution result and whether it should proceed to the next iteration. The execution result will
53// always be sent to the minadbd side.
54using CommandFunction = std::function<std::pair<bool, bool>()>;
xunchang34690ce2019-04-05 16:16:07 -070055
xunchang24788852019-03-22 16:08:52 -070056static bool SetUsbConfig(const std::string& state) {
57 android::base::SetProperty("sys.usb.config", state);
58 return android::base::WaitForProperty("sys.usb.state", state);
59}
60
Tao Bao10f441a2019-04-19 15:22:15 -070061// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors.
62static MinadbdCommand ParseMinadbdCommand(const std::string& message) {
xunchang34690ce2019-04-05 16:16:07 -070063 if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
64 LOG(ERROR) << "Failed to parse command in message " << message;
Tao Bao10f441a2019-04-19 15:22:15 -070065 return MinadbdCommand::kError;
xunchang34690ce2019-04-05 16:16:07 -070066 }
67
68 auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
69 auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
Tao Bao10f441a2019-04-19 15:22:15 -070070 if (cmd_code >= static_cast<uint32_t>(MinadbdCommand::kError)) {
xunchang34690ce2019-04-05 16:16:07 -070071 LOG(ERROR) << "Unsupported command code: " << cmd_code;
Tao Bao10f441a2019-04-19 15:22:15 -070072 return MinadbdCommand::kError;
xunchang34690ce2019-04-05 16:16:07 -070073 }
74
Tao Bao10f441a2019-04-19 15:22:15 -070075 return static_cast<MinadbdCommand>(cmd_code);
xunchang34690ce2019-04-05 16:16:07 -070076}
77
78static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
79 char message[kMinadbdMessageSize];
80 memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
81 android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
82
83 if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
84 PLOG(ERROR) << "Failed to write message " << message;
85 return false;
86 }
87 return true;
88}
89
Tao Bao10f441a2019-04-19 15:22:15 -070090// Installs the package from FUSE. Returns the installation result and whether it should continue
91// waiting for new commands.
92static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
xunchang34690ce2019-04-05 16:16:07 -070093 // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
94 // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
95 // will start to exist once the host connects and starts serving a package. Poll for its
96 // appearance. (Note that inotify doesn't work with FUSE.)
97 constexpr int ADB_INSTALL_TIMEOUT = 15;
Tao Bao10f441a2019-04-19 15:22:15 -070098 bool should_continue = true;
xunchang34690ce2019-04-05 16:16:07 -070099 *result = INSTALL_ERROR;
100 for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
101 struct stat st;
102 if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
103 if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
104 sleep(1);
105 continue;
106 } else {
Tao Bao10f441a2019-04-19 15:22:15 -0700107 should_continue = false;
xunchang34690ce2019-04-05 16:16:07 -0700108 ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
109 break;
110 }
111 }
xunchang316e9712019-04-12 16:22:15 -0700112 *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
xunchang34690ce2019-04-05 16:16:07 -0700113 break;
114 }
115
116 // Calling stat() on this magic filename signals the FUSE to exit.
117 struct stat st;
118 stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
Tao Bao10f441a2019-04-19 15:22:15 -0700119 return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
xunchang34690ce2019-04-05 16:16:07 -0700120}
121
Tao Bao10f441a2019-04-19 15:22:15 -0700122static auto AdbRebootHandler(MinadbdCommand command, int* result,
123 Device::BuiltinAction* reboot_action) {
124 switch (command) {
125 case MinadbdCommand::kRebootBootloader:
126 *reboot_action = Device::REBOOT_BOOTLOADER;
127 break;
128 case MinadbdCommand::kRebootFastboot:
129 *reboot_action = Device::ENTER_FASTBOOT;
130 break;
131 case MinadbdCommand::kRebootRecovery:
132 *reboot_action = Device::ENTER_RECOVERY;
133 break;
134 case MinadbdCommand::kRebootRescue:
135 // Use Device::REBOOT_RESCUE instead of Device::ENTER_RESCUE. This allows rebooting back into
136 // rescue mode (potentially using a newly installed recovery image).
137 *reboot_action = Device::REBOOT_RESCUE;
138 break;
139 case MinadbdCommand::kRebootAndroid:
140 default:
141 *reboot_action = Device::REBOOT;
142 break;
143 }
144 *result = INSTALL_REBOOT;
145 return std::make_pair(true, false);
146}
147
148// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for
149// next command.
150static bool HandleMessageFromMinadbd(int socket_fd,
151 const std::map<MinadbdCommand, CommandFunction>& command_map) {
xunchang34690ce2019-04-05 16:16:07 -0700152 char buffer[kMinadbdMessageSize];
153 if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
154 PLOG(ERROR) << "Failed to read message from minadbd";
155 return false;
156 }
157
158 std::string message(buffer, buffer + kMinadbdMessageSize);
Tao Bao10f441a2019-04-19 15:22:15 -0700159 auto command_type = ParseMinadbdCommand(message);
160 if (command_type == MinadbdCommand::kError) {
xunchang34690ce2019-04-05 16:16:07 -0700161 return false;
162 }
163 if (command_map.find(command_type) == command_map.end()) {
164 LOG(ERROR) << "Unsupported command: "
165 << android::base::get_unaligned<unsigned int>(
166 message.substr(strlen(kMinadbdCommandPrefix)).c_str());
167 return false;
168 }
169
170 // We have received a valid command, execute the corresponding function.
171 const auto& command_func = command_map.at(command_type);
Tao Bao10f441a2019-04-19 15:22:15 -0700172 const auto [result, should_continue] = command_func();
173 LOG(INFO) << "Command " << static_cast<uint32_t>(command_type) << " finished with " << result;
174 if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure,
175 socket_fd)) {
176 return false;
xunchang34690ce2019-04-05 16:16:07 -0700177 }
Tao Bao10f441a2019-04-19 15:22:15 -0700178 return should_continue;
xunchang34690ce2019-04-05 16:16:07 -0700179}
180
181// TODO(xunchang) add a wrapper function and kill the minadbd service there.
182static void ListenAndExecuteMinadbdCommands(
183 pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
Tao Bao10f441a2019-04-19 15:22:15 -0700184 const std::map<MinadbdCommand, CommandFunction>& command_map) {
xunchang34690ce2019-04-05 16:16:07 -0700185 android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
186 if (epoll_fd == -1) {
187 PLOG(ERROR) << "Failed to create epoll";
188 kill(minadbd_pid, SIGKILL);
189 return;
190 }
191
192 constexpr int EPOLL_MAX_EVENTS = 10;
193 struct epoll_event ev = {};
194 ev.events = EPOLLIN | EPOLLHUP;
195 ev.data.fd = socket_fd.get();
196 struct epoll_event events[EPOLL_MAX_EVENTS];
197 if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
198 PLOG(ERROR) << "Failed to add socket fd to epoll";
199 kill(minadbd_pid, SIGKILL);
200 return;
201 }
202
203 // Set the timeout to be 300s when waiting for minadbd commands.
204 constexpr int TIMEOUT_MILLIS = 300 * 1000;
205 while (true) {
206 // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
207 // read.
208 int event_count =
209 TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
210 if (event_count == -1) {
211 PLOG(ERROR) << "Failed to wait for epoll events";
212 kill(minadbd_pid, SIGKILL);
213 return;
214 }
215 if (event_count == 0) {
216 LOG(ERROR) << "Timeout waiting for messages from minadbd";
217 kill(minadbd_pid, SIGKILL);
218 return;
219 }
220
221 for (int n = 0; n < event_count; n++) {
222 if (events[n].events & EPOLLHUP) {
223 LOG(INFO) << "Socket has been closed";
224 kill(minadbd_pid, SIGKILL);
225 return;
226 }
227 if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
228 kill(minadbd_pid, SIGKILL);
229 return;
230 }
231 }
232 }
233}
234
235// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
236// message from minadbd through a socket pair. Here is an example to execute one command from adb
237// host.
238// a. recovery b. listener thread c. minadbd service
239//
240// a1. create socket pair
241// a2. fork minadbd service
242// c3. wait for the adb commands
243// from host
244// c4. after receiving host commands:
245// 1) set up pre-condition (i.e.
246// start fuse for adb sideload)
247// 2) issue command through
248// socket.
249// 3) wait for result
250// a5. start listener thread
251// b6. listen for message from
252// minadbd in a loop.
253// b7. After receiving a minadbd
254// command from socket
255// 1) execute the command function
256// 2) send the result back to
257// minadbd
258// ......
259// c8. exit upon receiving the
260// result
261// a9. wait for listener thread
262// to exit.
263//
264// a10. wait for minadbd to
265// exit
266// b11. exit the listening loop
267//
268static void CreateMinadbdServiceAndExecuteCommands(
Tao Bao10f441a2019-04-19 15:22:15 -0700269 const std::map<MinadbdCommand, CommandFunction>& command_map, bool rescue_mode) {
xunchang34690ce2019-04-05 16:16:07 -0700270 signal(SIGPIPE, SIG_IGN);
271
272 android::base::unique_fd recovery_socket;
273 android::base::unique_fd minadbd_socket;
274 if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
275 PLOG(ERROR) << "Failed to create socket";
276 return;
277 }
278
279 pid_t child = fork();
280 if (child == -1) {
281 PLOG(ERROR) << "Failed to fork child process";
282 return;
283 }
284 if (child == 0) {
285 recovery_socket.reset();
Tao Baoc6dc3252019-04-16 14:22:25 -0700286 std::vector<std::string> minadbd_commands = {
287 "/system/bin/minadbd",
288 "--socket_fd",
289 std::to_string(minadbd_socket.release()),
290 };
291 if (rescue_mode) {
292 minadbd_commands.push_back("--rescue");
293 }
294 auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands);
295 execv(exec_args[0], exec_args.data());
xunchang34690ce2019-04-05 16:16:07 -0700296 _exit(EXIT_FAILURE);
297 }
298
299 minadbd_socket.reset();
300
301 // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
302 // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
303 if (!SetUsbConfig("sideload")) {
304 LOG(ERROR) << "Failed to set usb config to sideload";
305 return;
306 }
307
308 std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket),
309 std::ref(command_map));
xunchang34690ce2019-04-05 16:16:07 -0700310 if (listener_thread.joinable()) {
311 listener_thread.join();
312 }
313
314 int status;
315 waitpid(child, &status, 0);
316 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
317 if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
318 LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
319 } else if (!WIFSIGNALED(status)) {
320 LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
321 }
322 }
323
324 signal(SIGPIPE, SIG_DFL);
325}
326
Tao Bao10f441a2019-04-19 15:22:15 -0700327int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) {
Hridya Valsarajue4ef4532018-08-31 11:57:51 -0700328 // Save the usb state to restore after the sideload operation.
329 std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
330 // Clean up state and stop adbd.
331 if (usb_state != "none" && !SetUsbConfig("none")) {
332 LOG(ERROR) << "Failed to clear USB config";
333 return INSTALL_ERROR;
334 }
Tao Bao682c34b2015-04-07 17:16:35 -0700335
Tao Baoc6dc3252019-04-16 14:22:25 -0700336 if (!rescue_mode) {
337 ui->Print(
338 "\n\nNow send the package you want to apply\n"
339 "to the device with \"adb sideload <filename>\"...\n");
340 } else {
341 ui->Print("\n\nWaiting for rescue commands...\n");
342 }
Doug Zongker9270a202012-01-09 15:16:13 -0800343
xunchang34690ce2019-04-05 16:16:07 -0700344 int install_result = INSTALL_ERROR;
Tao Bao10f441a2019-04-19 15:22:15 -0700345 std::map<MinadbdCommand, CommandFunction> command_map{
346 { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
347 { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
348 &install_result, reboot_action) },
349 { MinadbdCommand::kRebootBootloader,
350 std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result,
351 reboot_action) },
352 { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot,
353 &install_result, reboot_action) },
354 { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery,
355 &install_result, reboot_action) },
356 { MinadbdCommand::kRebootRescue,
357 std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) },
xunchang34690ce2019-04-05 16:16:07 -0700358 };
Doug Zongker9270a202012-01-09 15:16:13 -0800359
Tao Baoc6dc3252019-04-16 14:22:25 -0700360 CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode);
Doug Zongker075ad802014-06-26 15:35:51 -0700361
Hridya Valsarajue4ef4532018-08-31 11:57:51 -0700362 // Clean up before switching to the older state, for example setting the state
363 // to none sets sys/class/android_usb/android0/enable to 0.
364 if (!SetUsbConfig("none")) {
365 LOG(ERROR) << "Failed to clear USB config";
366 }
367
368 if (usb_state != "none") {
369 if (!SetUsbConfig(usb_state)) {
370 LOG(ERROR) << "Failed to set USB config to " << usb_state;
371 }
372 }
Doug Zongker9270a202012-01-09 15:16:13 -0800373
xunchang34690ce2019-04-05 16:16:07 -0700374 return install_result;
Doug Zongker9270a202012-01-09 15:16:13 -0800375}