blob: 593180bb32f0da3a5da3f3157a05b1adcc1c4406 [file] [log] [blame]
xunchang23f15fc2019-04-17 14:43:58 -07001/*
2 * Copyright (C) 2019 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 <errno.h>
18#include <fcntl.h>
19#include <signal.h>
20#include <strings.h>
21#include <sys/mount.h>
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/wait.h>
25#include <unistd.h>
26
27#include <string>
28#include <vector>
29
30#include <android-base/file.h>
31#include <android-base/unique_fd.h>
32#include <gtest/gtest.h>
33
34#include "adb.h"
35#include "adb_io.h"
36#include "fuse_adb_provider.h"
37#include "fuse_sideload.h"
38#include "minadbd_services.h"
39#include "minadbd_types.h"
40#include "socket.h"
41
42class MinadbdServicesTest : public ::testing::Test {
43 protected:
44 static constexpr int EXIT_TIME_OUT = 10;
45
46 void SetUp() override {
47 ASSERT_TRUE(
48 android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_));
49 SetMinadbdSocketFd(minadbd_socket_);
50 SetSideloadMountPoint(mount_point_.path);
51
52 package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME;
53 exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG;
54
55 signal(SIGPIPE, SIG_IGN);
56 }
57
58 void TearDown() override {
59 // Umount in case the test fails. Ignore the result.
60 umount(mount_point_.path);
61
62 signal(SIGPIPE, SIG_DFL);
63 }
64
Tao Bao7b9b7db2019-04-19 15:22:15 -070065 void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) {
xunchang23f15fc2019-04-17 14:43:58 -070066 std::vector<uint8_t> received(kMinadbdMessageSize, '\0');
67 ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize));
68
69 std::vector<uint8_t> expected(kMinadbdMessageSize, '\0');
70 memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix));
71 memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command,
72 sizeof(expected_command));
73 ASSERT_EQ(expected, received);
74 }
75
76 void WaitForFusePath() {
77 constexpr int TIME_OUT = 10;
78 for (int i = 0; i < TIME_OUT; ++i) {
79 struct stat sb;
80 if (stat(package_path_.c_str(), &sb) == 0) {
81 return;
82 }
83
84 if (errno == ENOENT) {
85 sleep(1);
86 continue;
87 }
88 FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno);
89 }
90 }
91
92 void StatExitFlagAndExitProcess(int exit_code) {
93 struct stat sb;
94 if (stat(exit_flag_.c_str(), &sb) != 0) {
95 PLOG(ERROR) << "Failed to stat " << exit_flag_;
96 }
97
98 exit(exit_code);
99 }
100
101 void WriteMinadbdCommandStatus(MinadbdCommandStatus status) {
102 std::string status_message(kMinadbdMessageSize, '\0');
103 memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
104 memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status));
105 ASSERT_TRUE(
106 android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize));
107 }
108
109 void ExecuteCommandAndWaitForExit(const std::string& command) {
110 unique_fd fd = daemon_service_to_fd(command, nullptr);
111 ASSERT_NE(-1, fd);
112 sleep(EXIT_TIME_OUT);
113 }
114
115 android::base::unique_fd minadbd_socket_;
116 android::base::unique_fd recovery_socket_;
117
118 TemporaryDir mount_point_;
119 std::string package_path_;
120 std::string exit_flag_;
121};
122
123TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) {
124 ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"),
125 ::testing::ExitedWithCode(kMinadbdPackageSizeError), "");
126}
127
128TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) {
129 ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"),
130 ::testing::ExitedWithCode(kMinadbdFuseStartError), "");
131}
132
133TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) {
134 SetMinadbdSocketFd(-1);
135 ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
136 ::testing::ExitedWithCode(kMinadbdSocketIOError), "");
137}
138
139TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) {
140 recovery_socket_.reset();
141 ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"),
142 ::testing::ExitedWithCode(kMinadbdSocketIOError), "");
143}
144
145TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) {
146 auto test_body = [&](const std::string& command) {
147 unique_fd fd = daemon_service_to_fd(command, nullptr);
148 ASSERT_NE(-1, fd);
149 WaitForFusePath();
Tao Bao7b9b7db2019-04-19 15:22:15 -0700150 ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
xunchang23f15fc2019-04-17 14:43:58 -0700151
152 struct stat sb;
153 ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb));
154 ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_));
155 sleep(EXIT_TIME_OUT);
156 };
157
158 ASSERT_EXIT(test_body("sideload-host:4096:4096"),
159 ::testing::ExitedWithCode(kMinadbdMessageFormatError), "");
160}
161
162TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) {
163 auto test_body = [&]() {
164 std::vector<uint8_t> content(4096, 'a');
165 // Start a new process instead of a thread to read from the package mounted by FUSE. Because
166 // the test may not exit and report failures correctly when the thread blocks by a syscall.
167 pid_t pid = fork();
168 if (pid == 0) {
169 WaitForFusePath();
170 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY)));
171 // Do not use assertion here because we want to stat the exit flag and exit the process.
172 // Otherwise the test will wait for the time out instead of failing immediately.
173 if (fd == -1) {
174 PLOG(ERROR) << "Failed to open " << package_path_;
175 StatExitFlagAndExitProcess(1);
176 }
177 std::vector<uint8_t> content_from_fuse(4096);
178 if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) {
179 PLOG(ERROR) << "Failed to read from " << package_path_;
180 StatExitFlagAndExitProcess(1);
181 }
182 if (content_from_fuse != content) {
183 LOG(ERROR) << "Content read from fuse doesn't match with the expected value";
184 StatExitFlagAndExitProcess(1);
185 }
186 StatExitFlagAndExitProcess(0);
187 }
188
189 unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr);
190 ASSERT_NE(-1, fd);
Tao Bao7b9b7db2019-04-19 15:22:15 -0700191 ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall);
xunchang23f15fc2019-04-17 14:43:58 -0700192
193 // Mimic the response from adb host.
194 std::string adb_message(8, '\0');
195 ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8));
196 ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message);
197 ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096));
198
199 // Check that we read the correct data from fuse.
200 int child_status;
201 waitpid(pid, &child_status, 0);
202 ASSERT_TRUE(WIFEXITED(child_status));
203 ASSERT_EQ(0, WEXITSTATUS(child_status));
204
205 WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess);
206
207 // TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between
208 // receiving the message and exit of test body (by detached thread in minadbd service).
209 exit(kMinadbdSuccess);
210 };
211
212 ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), "");
213}