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 
MakeTransportMessage(const std::string & address)46 std::string MakeTransportMessage(const std::string& address) {
47   return MakeMessage("host:transport:" + address);
48 }
49 
MakeConnectMessage(const std::string & address)50 std::string MakeConnectMessage(const std::string& address) {
51   return MakeMessage("host:connect:" + address);
52 }
53 
MakeDisconnectMessage(const std::string & address)54 std::string MakeDisconnectMessage(const std::string& address) {
55   return MakeMessage("host:disconnect:" + address);
56 }
57 
MakeGetStateMessage(const std::string & address)58 std::string MakeGetStateMessage(const std::string& address) {
59   return MakeMessage("host-serial:" + address + ":get-state");
60 }
61 
62 // Response will either be OKAY or FAIL
63 constexpr char kAdbOkayStatusResponse[] = "OKAY";
64 constexpr std::size_t kAdbStatusResponseLength =
65     sizeof kAdbOkayStatusResponse - 1;
66 constexpr std::string_view kAdbUnauthorizedMsg = "device unauthorized.";
67 // adb sends the length of what is to follow as a 4 characters string of hex
68 // digits
69 constexpr std::size_t kAdbMessageLengthLength = 4;
70 
71 constexpr int kAdbDaemonPort = 5037;
72 
AdbSendMessage(const SharedFD & sock,const std::string & message)73 bool AdbSendMessage(const SharedFD& sock, const std::string& message) {
74   if (!sock->IsOpen()) {
75     return false;
76   }
77   if (!SendAll(sock, message)) {
78     LOG(WARNING) << "failed to send all bytes to adb daemon";
79     return false;
80   }
81   return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse;
82 }
83 
AdbSendMessage(const std::string & message)84 bool AdbSendMessage(const std::string& message) {
85   auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
86   return AdbSendMessage(sock, message);
87 }
88 
AdbConnect(const std::string & address)89 bool AdbConnect(const std::string& address) {
90   return AdbSendMessage(MakeConnectMessage(address));
91 }
92 
AdbDisconnect(const std::string & address)93 bool AdbDisconnect(const std::string& address) {
94   return AdbSendMessage(MakeDisconnectMessage(address));
95 }
96 
IsHexInteger(const std::string & str)97 bool IsHexInteger(const std::string& str) {
98   return !str.empty() && std::all_of(str.begin(), str.end(),
99                                      [](char c) { return std::isxdigit(c); });
100 }
101 
IsInteger(const std::string & str)102 bool IsInteger(const std::string& str) {
103   return !str.empty() && std::all_of(str.begin(), str.end(),
104                                      [](char c) { return std::isdigit(c); });
105 }
106 
107 // assumes the OKAY/FAIL status has already been read
RecvAdbResponse(const SharedFD & sock)108 std::string RecvAdbResponse(const SharedFD& sock) {
109   auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength);
110   if (!IsHexInteger(length_as_hex_str)) {
111     LOG(ERROR) << "invalid adb response prefix: " << length_as_hex_str;
112     return {};
113   }
114   auto length = std::stoi(length_as_hex_str, nullptr, 16);
115   return RecvAll(sock, length);
116 }
117 
118 // Returns a negative value if uptime result couldn't be read for
119 // any reason.
RecvUptimeResult(const SharedFD & sock)120 int RecvUptimeResult(const SharedFD& sock) {
121   std::vector<char> uptime_vec{};
122   std::vector<char> just_read(16);
123   do {
124     auto count = sock->Read(just_read.data(), just_read.size());
125     if (count < 0) {
126       LOG(WARNING) << "couldn't receive adb shell output";
127       return -1;
128     }
129     just_read.resize(count);
130     uptime_vec.insert(uptime_vec.end(), just_read.begin(), just_read.end());
131   } while (!just_read.empty());
132 
133   if (uptime_vec.empty()) {
134     LOG(WARNING) << "empty adb shell result";
135     return -1;
136   }
137 
138   uptime_vec.pop_back();
139 
140   auto uptime_str = std::string{uptime_vec.data(), uptime_vec.size()};
141   if (!IsInteger(uptime_str)) {
142     LOG(WARNING) << "non-numeric: uptime result: " << uptime_str;
143     return -1;
144   }
145 
146   return std::stoi(uptime_str);
147 }
148 
149 // Check if the connection state is waiting for authorization. This function
150 // returns true only when explicitly receiving the unauthorized error message,
151 // while returns false for all the other error cases because we need to call
152 // AdbConnect() again rather than waiting for users' authorization.
WaitForAdbAuthorization(const std::string & address)153 bool WaitForAdbAuthorization(const std::string& address) {
154   auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
155   // Socket doesn't open, so we should not block at waiting for authorization.
156   if (!sock->IsOpen()) {
157     LOG(WARNING) << "failed to open adb connection: " << sock->StrError();
158     return false;
159   }
160 
161   if (!SendAll(sock, MakeGetStateMessage(address))) {
162     LOG(WARNING) << "failed to send get state message to adb daemon";
163     return false;
164   }
165 
166   const std::string status = RecvAll(sock, kAdbStatusResponseLength);
167   // Stop waiting because the authorization check passed.
168   if (status == kAdbOkayStatusResponse) {
169     return false;
170   }
171 
172   const auto response = RecvAdbResponse(sock);
173   // Do not wait for authorization due to failure to receive an adb response.
174   if (response.empty()) {
175     return false;
176   }
177 
178   return android::base::StartsWith(response, kAdbUnauthorizedMsg);
179 }
180 
181 // There needs to be a gap between the adb commands, the daemon isn't able to
182 // handle the avalanche of requests we would be sending without a sleep. Five
183 // seconds is much larger than seems necessary so we should be more than okay.
184 static constexpr int kAdbCommandGapTime = 5;
185 
EstablishConnection(const std::string & address)186 void EstablishConnection(const std::string& address) {
187   LOG(DEBUG) << "Attempting to connect to device with address " << address;
188   while (!AdbConnect(address)) {
189     sleep(kAdbCommandGapTime);
190   }
191   LOG(DEBUG) << "adb connect message for " << address << " successfully sent";
192   sleep(kAdbCommandGapTime);
193 
194   while (WaitForAdbAuthorization(address)) {
195     LOG(WARNING) << "adb unauthorized, retrying";
196     sleep(kAdbCommandGapTime);
197   }
198   LOG(DEBUG) << "adb connected to " << address;
199   sleep(kAdbCommandGapTime);
200 }
201 
WaitForAdbDisconnection(const std::string & address)202 void WaitForAdbDisconnection(const std::string& address) {
203   // adb daemon doesn't seem to handle quick, successive messages well. The
204   // sleeps stabilize the communication.
205   LOG(DEBUG) << "Watching for disconnect on " << address;
206   while (true) {
207     auto sock = SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
208     if (!sock->IsOpen()) {
209       LOG(ERROR) << "failed to open adb connection: " << sock->StrError();
210       break;
211     }
212     if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
213       LOG(WARNING) << "transport message failed, response body: "
214                    << RecvAdbResponse(sock);
215       break;
216     }
217     if (!AdbSendMessage(sock, MakeShellUptimeMessage())) {
218       LOG(WARNING) << "adb shell uptime message failed";
219       break;
220     }
221 
222     auto uptime = RecvUptimeResult(sock);
223     if (uptime < 0) {
224       LOG(WARNING) << "couldn't read uptime result";
225       break;
226     }
227     LOG(VERBOSE) << "device on " << address << " uptime " << uptime;
228     sleep(kAdbCommandGapTime);
229   }
230   LOG(DEBUG) << "Sending adb disconnect";
231   AdbDisconnect(address);
232   sleep(kAdbCommandGapTime);
233 }
234 
235 }  // namespace
236 
EstablishAndMaintainConnection(const std::string & address)237 [[noreturn]] void EstablishAndMaintainConnection(const std::string& address) {
238   while (true) {
239     EstablishConnection(address);
240     WaitForAdbDisconnection(address);
241   }
242 }
243 
244 }  // namespace cuttlefish
245