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 
17 #include <cctype>
18 #include <iomanip>
19 #include <sstream>
20 #include <string>
21 #include <memory>
22 #include <vector>
23 #include <glog/logging.h>
24 
25 #include <unistd.h>
26 
27 #include "common/libs/fs/shared_fd.h"
28 #include "host/frontend/adb_connector/adb_connection_maintainer.h"
29 
30 namespace {
31 
MakeMessage(const std::string & user_message)32 std::string MakeMessage(const std::string& user_message) {
33   std::ostringstream ss;
34   ss << std::setfill('0') << std::setw(4) << std::hex << user_message.size()
35      << user_message;
36   return ss.str();
37 }
38 
MakeShellUptimeMessage()39 std::string MakeShellUptimeMessage() {
40   return MakeMessage("shell,raw:cut -d. -f1 /proc/uptime");
41 }
42 
MakeTransportMessage(const std::string & address)43 std::string MakeTransportMessage(const std::string& address) {
44   return MakeMessage("host:transport:" + address);
45 }
46 
MakeConnectMessage(const std::string & address)47 std::string MakeConnectMessage(const std::string& address) {
48   return MakeMessage("host:connect:" + address);
49 }
50 
MakeDisconnectMessage(const std::string & address)51 std::string MakeDisconnectMessage(const std::string& address) {
52   return MakeMessage("host:connect:" + address);
53 }
54 
55 // returns true if successfully sent the whole message
SendAll(cvd::SharedFD sock,const std::string & msg)56 bool SendAll(cvd::SharedFD sock, const std::string& msg) {
57   ssize_t total_written{};
58   while (total_written < static_cast<ssize_t>(msg.size())) {
59     if (!sock->IsOpen()) {
60       return false;
61     }
62     auto just_written = sock->Send(msg.c_str() + total_written,
63                                    msg.size() - total_written, MSG_NOSIGNAL);
64     if (just_written <= 0) {
65       return false;
66     }
67     total_written += just_written;
68   }
69   return true;
70 }
71 
RecvAll(cvd::SharedFD sock,const size_t count)72 std::string RecvAll(cvd::SharedFD sock, const size_t count) {
73   size_t total_read{};
74   std::unique_ptr<char[]> data(new char[count]);
75   while (total_read < count) {
76     auto just_read = sock->Read(data.get() + total_read, count - total_read);
77     if (just_read <= 0) {
78       LOG(WARNING) << "adb daemon socket closed early";
79       return {};
80     }
81     total_read += just_read;
82   }
83   return {data.get(), count};
84 }
85 
86 // Response will either be OKAY or FAIL
87 constexpr char kAdbOkayStatusResponse[] = "OKAY";
88 constexpr std::size_t kAdbStatusResponseLength =
89     sizeof kAdbOkayStatusResponse - 1;
90 // adb sends the length of what is to follow as a 4 characters string of hex
91 // digits
92 constexpr std::size_t kAdbMessageLengthLength = 4;
93 
94 constexpr int kAdbDaemonPort = 5037;
95 
AdbSendMessage(cvd::SharedFD sock,const std::string & message)96 bool AdbSendMessage(cvd::SharedFD sock, const std::string& message) {
97   if (!sock->IsOpen()) {
98     return false;
99   }
100   if (!SendAll(sock, message)) {
101     LOG(WARNING) << "failed to send all bytes to adb daemon";
102     return false;
103   }
104   return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse;
105 }
106 
AdbSendMessage(const std::string & message)107 bool AdbSendMessage(const std::string& message) {
108   auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
109   return AdbSendMessage(sock, message);
110 }
111 
AdbConnect(const std::string & address)112 bool AdbConnect(const std::string& address) {
113   return AdbSendMessage(MakeConnectMessage(address));
114 }
115 
AdbDisconnect(const std::string & address)116 bool AdbDisconnect(const std::string& address) {
117   return AdbSendMessage(MakeDisconnectMessage(address));
118 }
119 
IsInteger(const std::string & str)120 bool IsInteger(const std::string& str) {
121   return !str.empty() && std::all_of(str.begin(), str.end(),
122                                      [](char c) { return std::isdigit(c); });
123 }
124 
125 // assumes the OKAY/FAIL status has already been read
RecvAdbResponse(cvd::SharedFD sock)126 std::string RecvAdbResponse(cvd::SharedFD sock) {
127   auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength);
128   if (!IsInteger(length_as_hex_str)) {
129     return {};
130   }
131   auto length = std::stoi(length_as_hex_str, nullptr, 16);
132   return RecvAll(sock, length);
133 }
134 
135 // Returns a negative value if uptime result couldn't be read for
136 // any reason.
RecvUptimeResult(cvd::SharedFD sock)137 int RecvUptimeResult(cvd::SharedFD sock) {
138   std::vector<char> uptime_vec{};
139   std::vector<char> just_read(16);
140   do {
141     auto count = sock->Read(just_read.data(), just_read.size());
142     if (count < 0) {
143       LOG(INFO) << "couldn't receive adb shell output";
144       return -1;
145     }
146     just_read.resize(count);
147     uptime_vec.insert(uptime_vec.end(), just_read.begin(), just_read.end());
148   } while (!just_read.empty());
149 
150   if (uptime_vec.empty()) {
151     LOG(INFO) << "empty adb shell result";
152     return -1;
153   }
154 
155   uptime_vec.pop_back();
156 
157   auto uptime_str = std::string{uptime_vec.data(), uptime_vec.size()};
158   if (!IsInteger(uptime_str)) {
159     LOG(INFO) << "non-numeric: uptime result: " << uptime_str;
160     return -1;
161   }
162 
163   return std::stoi(uptime_str);
164 }
165 
166 // There needs to be a gap between the adb commands, the daemon isn't able to
167 // handle the avalanche of requests we would be sending without a sleep. Five
168 // seconds is much larger than seems necessary so we should be more than okay.
169 static constexpr int kAdbCommandGapTime = 5;
170 
EstablishConnection(const std::string & address)171 void EstablishConnection(const std::string& address) {
172   LOG(INFO) << "Attempting to connect to device with address " << address;
173   while (!AdbConnect(address)) {
174     sleep(kAdbCommandGapTime);
175   }
176   LOG(INFO) << "adb connect message for " << address << " successfully sent";
177   sleep(kAdbCommandGapTime);
178 }
179 
WaitForAdbDisconnection(const std::string & address)180 void WaitForAdbDisconnection(const std::string& address) {
181   // adb daemon doesn't seem to handle quick, successive messages well. The
182   // sleeps stabilize the communication.
183   LOG(INFO) << "Watching for disconnect on " << address;
184   while (true) {
185     auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
186     if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
187       LOG(INFO) << "transport message failed, response body: "
188                 << RecvAdbResponse(sock);
189       break;
190     }
191     if (!AdbSendMessage(sock, MakeShellUptimeMessage())) {
192       LOG(INFO) << "adb shell uptime message failed";
193       break;
194     }
195 
196     auto uptime = RecvUptimeResult(sock);
197     if (uptime < 0) {
198       LOG(INFO) << "couldn't read uptime result";
199       break;
200     }
201     LOG(DEBUG) << "device on " << address << " uptime " << uptime;
202     sleep(kAdbCommandGapTime);
203   }
204   LOG(INFO) << "Sending adb disconnect";
205   AdbDisconnect(address);
206   sleep(kAdbCommandGapTime);
207 }
208 
209 }  // namespace
210 
EstablishAndMaintainConnection(std::string address)211 [[noreturn]] void cvd::EstablishAndMaintainConnection(std::string address) {
212   while (true) {
213     EstablishConnection(address);
214     WaitForAdbDisconnection(address);
215   }
216 }
217