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