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