/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "common/libs/fs/shared_fd.h" #include "host/frontend/adb_connector/adb_connection_maintainer.h" namespace { std::string MakeMessage(const std::string& user_message) { std::ostringstream ss; ss << std::setfill('0') << std::setw(4) << std::hex << user_message.size() << user_message; return ss.str(); } std::string MakeShellUptimeMessage() { return MakeMessage("shell,raw:cut -d. -f1 /proc/uptime"); } std::string MakeTransportMessage(const std::string& address) { return MakeMessage("host:transport:" + address); } std::string MakeConnectMessage(const std::string& address) { return MakeMessage("host:connect:" + address); } std::string MakeDisconnectMessage(const std::string& address) { return MakeMessage("host:connect:" + address); } // returns true if successfully sent the whole message bool SendAll(cvd::SharedFD sock, const std::string& msg) { ssize_t total_written{}; while (total_written < static_cast(msg.size())) { if (!sock->IsOpen()) { return false; } auto just_written = sock->Send(msg.c_str() + total_written, msg.size() - total_written, MSG_NOSIGNAL); if (just_written <= 0) { return false; } total_written += just_written; } return true; } std::string RecvAll(cvd::SharedFD sock, const size_t count) { size_t total_read{}; std::unique_ptr data(new char[count]); while (total_read < count) { auto just_read = sock->Read(data.get() + total_read, count - total_read); if (just_read <= 0) { LOG(WARNING) << "adb daemon socket closed early"; return {}; } total_read += just_read; } return {data.get(), count}; } // Response will either be OKAY or FAIL constexpr char kAdbOkayStatusResponse[] = "OKAY"; constexpr std::size_t kAdbStatusResponseLength = sizeof kAdbOkayStatusResponse - 1; // adb sends the length of what is to follow as a 4 characters string of hex // digits constexpr std::size_t kAdbMessageLengthLength = 4; constexpr int kAdbDaemonPort = 5037; bool AdbSendMessage(cvd::SharedFD sock, const std::string& message) { if (!sock->IsOpen()) { return false; } if (!SendAll(sock, message)) { LOG(WARNING) << "failed to send all bytes to adb daemon"; return false; } return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse; } bool AdbSendMessage(const std::string& message) { auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM); return AdbSendMessage(sock, message); } bool AdbConnect(const std::string& address) { return AdbSendMessage(MakeConnectMessage(address)); } bool AdbDisconnect(const std::string& address) { return AdbSendMessage(MakeDisconnectMessage(address)); } bool IsInteger(const std::string& str) { return !str.empty() && std::all_of(str.begin(), str.end(), [](char c) { return std::isdigit(c); }); } // assumes the OKAY/FAIL status has already been read std::string RecvAdbResponse(cvd::SharedFD sock) { auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength); if (!IsInteger(length_as_hex_str)) { return {}; } auto length = std::stoi(length_as_hex_str, nullptr, 16); return RecvAll(sock, length); } // Returns a negative value if uptime result couldn't be read for // any reason. int RecvUptimeResult(cvd::SharedFD sock) { std::vector uptime_vec{}; std::vector just_read(16); do { auto count = sock->Read(just_read.data(), just_read.size()); if (count < 0) { LOG(INFO) << "couldn't receive adb shell output"; return -1; } just_read.resize(count); uptime_vec.insert(uptime_vec.end(), just_read.begin(), just_read.end()); } while (!just_read.empty()); if (uptime_vec.empty()) { LOG(INFO) << "empty adb shell result"; return -1; } uptime_vec.pop_back(); auto uptime_str = std::string{uptime_vec.data(), uptime_vec.size()}; if (!IsInteger(uptime_str)) { LOG(INFO) << "non-numeric: uptime result: " << uptime_str; return -1; } return std::stoi(uptime_str); } // There needs to be a gap between the adb commands, the daemon isn't able to // handle the avalanche of requests we would be sending without a sleep. Five // seconds is much larger than seems necessary so we should be more than okay. static constexpr int kAdbCommandGapTime = 5; void EstablishConnection(const std::string& address) { LOG(INFO) << "Attempting to connect to device with address " << address; while (!AdbConnect(address)) { sleep(kAdbCommandGapTime); } LOG(INFO) << "adb connect message for " << address << " successfully sent"; sleep(kAdbCommandGapTime); } void WaitForAdbDisconnection(const std::string& address) { // adb daemon doesn't seem to handle quick, successive messages well. The // sleeps stabilize the communication. LOG(INFO) << "Watching for disconnect on " << address; while (true) { auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM); if (!AdbSendMessage(sock, MakeTransportMessage(address))) { LOG(INFO) << "transport message failed, response body: " << RecvAdbResponse(sock); break; } if (!AdbSendMessage(sock, MakeShellUptimeMessage())) { LOG(INFO) << "adb shell uptime message failed"; break; } auto uptime = RecvUptimeResult(sock); if (uptime < 0) { LOG(INFO) << "couldn't read uptime result"; break; } LOG(DEBUG) << "device on " << address << " uptime " << uptime; sleep(kAdbCommandGapTime); } LOG(INFO) << "Sending adb disconnect"; AdbDisconnect(address); sleep(kAdbCommandGapTime); } } // namespace [[noreturn]] void cvd::EstablishAndMaintainConnection(std::string address) { while (true) { EstablishConnection(address); WaitForAdbDisconnection(address); } }