1 /*
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 #include "host/frontend/adb_connector/adb_connection_maintainer.h"
17
18 #include <android-base/logging.h>
19 #include <android-base/strings.h>
20 #include <cctype>
21 #include <iomanip>
22 #include <memory>
23 #include <sstream>
24 #include <string>
25 #include <vector>
26
27 #include <unistd.h>
28
29 #include "common/libs/fs/shared_buf.h"
30 #include "common/libs/fs/shared_fd.h"
31
32 namespace cuttlefish {
33 namespace {
34
MakeMessage(const std::string & user_message)35 std::string MakeMessage(const std::string& user_message) {
36 std::ostringstream ss;
37 ss << std::setfill('0') << std::setw(4) << std::hex << user_message.size()
38 << user_message;
39 return ss.str();
40 }
41
MakeShellUptimeMessage()42 std::string MakeShellUptimeMessage() {
43 return MakeMessage("shell,raw:cut -d. -f1 /proc/uptime");
44 }
45
MakeShellTradeInModeGetStatusMessage()46 std::string MakeShellTradeInModeGetStatusMessage() {
47 return MakeMessage("shell,raw:tradeinmode getstatus");
48 }
49
MakeTransportMessage(const std::string & address)50 std::string MakeTransportMessage(const std::string& address) {
51 return MakeMessage("host:transport:" + address);
52 }
53
MakeConnectMessage(const std::string & address)54 std::string MakeConnectMessage(const std::string& address) {
55 return MakeMessage("host:connect:" + address);
56 }
57
MakeDisconnectMessage(const std::string & address)58 std::string MakeDisconnectMessage(const std::string& address) {
59 return MakeMessage("host:disconnect:" + address);
60 }
61
MakeGetStateMessage(const std::string & address)62 std::string MakeGetStateMessage(const std::string& address) {
63 return MakeMessage("host-serial:" + address + ":get-state");
64 }
65
66 // Response will either be OKAY or FAIL
67 constexpr char kAdbOkayStatusResponse[] = "OKAY";
68 constexpr std::size_t kAdbStatusResponseLength =
69 sizeof kAdbOkayStatusResponse - 1;
70 constexpr std::string_view kAdbUnauthorizedMsg = "device unauthorized.";
71 // adb sends the length of what is to follow as a 4 characters string of hex
72 // digits
73 constexpr std::size_t kAdbMessageLengthLength = 4;
74
75 constexpr int kAdbDaemonPort = 5037;
76
AdbSendMessage(const SharedFD & sock,const std::string & message)77 bool AdbSendMessage(const SharedFD& sock, const std::string& message) {
78 if (!sock->IsOpen()) {
79 return false;
80 }
81 if (!SendAll(sock, message)) {
82 LOG(WARNING) << "failed to send all bytes to adb daemon";
83 return false;
84 }
85
86 return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse;
87 }
88
AdbSendMessage(const std::string & message)89 bool AdbSendMessage(const std::string& message) {
90 auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
91 return AdbSendMessage(sock, message);
92 }
93
AdbConnect(const std::string & address)94 bool AdbConnect(const std::string& address) {
95 return AdbSendMessage(MakeConnectMessage(address));
96 }
97
AdbDisconnect(const std::string & address)98 bool AdbDisconnect(const std::string& address) {
99 return AdbSendMessage(MakeDisconnectMessage(address));
100 }
101
IsHexInteger(const std::string & str)102 bool IsHexInteger(const std::string& str) {
103 return !str.empty() && std::all_of(str.begin(), str.end(),
104 [](char c) { return std::isxdigit(c); });
105 }
106
IsInteger(const std::string & str)107 bool IsInteger(const std::string& str) {
108 return !str.empty() && std::all_of(str.begin(), str.end(),
109 [](char c) { return std::isdigit(c); });
110 }
111
112 // assumes the OKAY/FAIL status has already been read
RecvAdbResponse(const SharedFD & sock)113 std::string RecvAdbResponse(const SharedFD& sock) {
114 auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength);
115 if (!IsHexInteger(length_as_hex_str)) {
116 LOG(ERROR) << "invalid adb response prefix: " << length_as_hex_str;
117 return {};
118 }
119 auto length = std::stoi(length_as_hex_str, nullptr, 16);
120 return RecvAll(sock, length);
121 }
122
123 // Returns a negative value if uptime result couldn't be read for
124 // any reason.
RecvUptimeResult(const SharedFD & sock)125 int RecvUptimeResult(const SharedFD& sock) {
126 std::vector<char> uptime_vec{};
127 std::vector<char> just_read(16);
128 do {
129 auto count = sock->Read(just_read.data(), just_read.size());
130 if (count < 0) {
131 LOG(WARNING) << "couldn't receive adb shell output";
132 return -1;
133 }
134 just_read.resize(count);
135 uptime_vec.insert(uptime_vec.end(), just_read.begin(), just_read.end());
136 } while (!just_read.empty());
137
138 if (uptime_vec.empty()) {
139 LOG(WARNING) << "empty adb shell result";
140 return -1;
141 }
142
143 uptime_vec.pop_back();
144
145 auto uptime_str = std::string{uptime_vec.data(), uptime_vec.size()};
146 if (!IsInteger(uptime_str)) {
147 LOG(WARNING) << "non-numeric: uptime result: " << uptime_str;
148 return -1;
149 }
150
151 return std::stoi(uptime_str);
152 }
153
154 // Returns a negative value if getstatus result couldn't be read for
155 // any reason.
RecvGetStatusResult(const SharedFD & sock)156 int RecvGetStatusResult(const SharedFD& sock) {
157 std::vector<char> status_vec{};
158 std::vector<char> just_read(16);
159 do {
160 auto count = sock->Read(just_read.data(), just_read.size());
161 if (count < 0) {
162 LOG(WARNING) << "couldn't receive adb shell output";
163 return -1;
164 }
165 just_read.resize(count);
166 status_vec.insert(status_vec.end(), just_read.begin(), just_read.end());
167 } while (!just_read.empty());
168
169 if (status_vec.empty()) {
170 LOG(WARNING) << "empty adb shell result";
171 return -1;
172 }
173
174 auto status_str = std::string{status_vec.data(), status_vec.size()};
175 LOG(DEBUG) << "Status received " << status_str;
176
177 return 0;
178 }
179
180 // Check if the connection state is waiting for authorization. This function
181 // returns true only when explicitly receiving the unauthorized error message,
182 // while returns false for all the other error cases because we need to call
183 // AdbConnect() again rather than waiting for users' authorization.
WaitForAdbAuthorization(const std::string & address)184 bool WaitForAdbAuthorization(const std::string& address) {
185 auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
186 // Socket doesn't open, so we should not block at waiting for authorization.
187 if (!sock->IsOpen()) {
188 LOG(WARNING) << "failed to open adb connection: " << sock->StrError();
189 return false;
190 }
191
192 if (!SendAll(sock, MakeGetStateMessage(address))) {
193 LOG(WARNING) << "failed to send get state message to adb daemon";
194 return false;
195 }
196
197 const std::string status = RecvAll(sock, kAdbStatusResponseLength);
198 // Stop waiting because the authorization check passed.
199 if (status == kAdbOkayStatusResponse) {
200 return false;
201 }
202
203 const auto response = RecvAdbResponse(sock);
204 // Do not wait for authorization due to failure to receive an adb response.
205 if (response.empty()) {
206 return false;
207 }
208
209 return android::base::StartsWith(response, kAdbUnauthorizedMsg);
210 }
211
212 // There needs to be a gap between the adb commands, the daemon isn't able to
213 // handle the avalanche of requests we would be sending without a sleep. Five
214 // seconds is much larger than seems necessary so we should be more than okay.
215 static constexpr int kAdbCommandGapTime = 5;
216
EstablishConnection(const std::string & address)217 void EstablishConnection(const std::string& address) {
218 LOG(DEBUG) << "Attempting to connect to device with address " << address;
219 while (!AdbConnect(address)) {
220 sleep(kAdbCommandGapTime);
221 }
222 LOG(DEBUG) << "adb connect message for " << address << " successfully sent";
223 sleep(kAdbCommandGapTime);
224
225 while (WaitForAdbAuthorization(address)) {
226 LOG(WARNING) << "adb unauthorized, retrying";
227 sleep(kAdbCommandGapTime);
228 }
229 LOG(DEBUG) << "adb connected to " << address;
230 sleep(kAdbCommandGapTime);
231 }
232
WaitForAdbDisconnection(const std::string & address)233 void WaitForAdbDisconnection(const std::string& address) {
234 // adb daemon doesn't seem to handle quick, successive messages well. The
235 // sleeps stabilize the communication.
236 LOG(DEBUG) << "Watching for disconnect on " << address;
237 while (true) {
238 // First try uptime
239 auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
240 if (!sock->IsOpen()) {
241 LOG(ERROR) << "failed to open adb connection: " << sock->StrError();
242 break;
243 }
244 if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
245 LOG(WARNING) << "transport message failed, response body: "
246 << RecvAdbResponse(sock);
247 break;
248 }
249
250 if (AdbSendMessage(sock, MakeShellUptimeMessage())) {
251 auto uptime = RecvUptimeResult(sock);
252 if (uptime < 0) {
253 LOG(WARNING) << "couldn't read uptime result";
254 break;
255 }
256 LOG(VERBOSE) << "device on " << address << " uptime " << uptime;
257 } else {
258 // If uptime fails, maybe we are in trade-in mode
259 // Try adb shell tradeinmode getstatus
260 auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
261 if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
262 LOG(WARNING) << "transport message failed, response body: "
263 << RecvAdbResponse(sock);
264 break;
265 }
266 if (!AdbSendMessage(sock, MakeShellTradeInModeGetStatusMessage())) {
267 LOG(WARNING) << "transport message failed, response body: "
268 << RecvAdbResponse(sock);
269 break;
270 }
271 auto status = RecvGetStatusResult(sock);
272 if (status < 0) {
273 LOG(WARNING) << "transport message failed, response body: "
274 << RecvAdbResponse(sock);
275 break;
276 }
277 }
278 sleep(kAdbCommandGapTime);
279 }
280 LOG(DEBUG) << "Sending adb disconnect";
281 AdbDisconnect(address);
282 sleep(kAdbCommandGapTime);
283 }
284
285 } // namespace
286
EstablishAndMaintainConnection(const std::string & address)287 [[noreturn]] void EstablishAndMaintainConnection(const std::string& address) {
288 while (true) {
289 EstablishConnection(address);
290 WaitForAdbDisconnection(address);
291 }
292 }
293
294 } // namespace cuttlefish
295