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 <iomanip> 17 #include <sstream> 18 #include <string> 19 #include <memory> 20 #include <glog/logging.h> 21 22 #include <unistd.h> 23 24 #include "common/libs/fs/shared_fd.h" 25 #include "host/libs/adb_connection_maintainer/adb_connection_maintainer.h" 26 27 namespace { 28 29 std::string MakeMessage(const std::string& user_message) { 30 static constexpr char kPrefix[] = "host:"; 31 static constexpr std::size_t kPrefixLen = sizeof kPrefix - 1; 32 std::ostringstream ss; 33 ss << std::setfill('0') << std::setw(4) << std::hex 34 << (kPrefixLen + user_message.size()) << kPrefix << user_message; 35 return ss.str(); 36 } 37 38 std::string MakeIPAndPort(int port) { 39 static constexpr char kLocalHostPrefix[] = "127.0.0.1:"; 40 return kLocalHostPrefix + std::to_string(port); 41 } 42 43 std::string MakeConnectMessage(int port) { 44 static constexpr char kConnectPrefix[] = "connect:"; 45 return MakeMessage(kConnectPrefix + MakeIPAndPort(port)); 46 } 47 48 // returns true if successfully sent the whole message 49 bool SendAll(cvd::SharedFD sock, const std::string& msg) { 50 ssize_t total_written{}; 51 while (total_written < static_cast<ssize_t>(msg.size())) { 52 if (!sock->IsOpen()) { 53 return false; 54 } 55 auto just_written = sock->Send(msg.c_str() + total_written, 56 msg.size() - total_written, MSG_NOSIGNAL); 57 if (just_written <= 0) { 58 return false; 59 } 60 total_written += just_written; 61 } 62 return true; 63 } 64 65 std::string RecvAll(cvd::SharedFD sock, const size_t count) { 66 size_t total_read{}; 67 std::unique_ptr<char[]> data(new char[count]); 68 while (total_read < count) { 69 auto just_read = sock->Read(data.get() + total_read, count - total_read); 70 if (just_read <= 0) { 71 return {}; 72 } 73 total_read += just_read; 74 } 75 return {data.get(), count}; 76 } 77 78 // Response will either be OKAY or FAIL 79 constexpr char kAdbOkayStatusResponse[] = "OKAY"; 80 constexpr std::size_t kAdbStatusResponseLength = 81 sizeof kAdbOkayStatusResponse - 1; 82 // adb sends the length of what is to follow as a 4 characters string of hex 83 // digits 84 constexpr std::size_t kAdbMessageLengthLength = 4; 85 86 constexpr int kAdbDaemonPort = 5037; 87 88 bool AdbConnect(cvd::SharedFD sock, int port) { 89 if (!SendAll(sock, MakeConnectMessage(port))) { 90 return false; 91 } 92 return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse; 93 } 94 95 // assumes the OKAY/FAIL status has already been read 96 std::string RecvAdbResponse(cvd::SharedFD sock) { 97 auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength); 98 auto length = std::stoi(length_as_hex_str, nullptr, 16); 99 return RecvAll(sock, length); 100 } 101 102 void EstablishConnection(int port) { 103 while (true) { 104 LOG(INFO) << "Attempting to connect to device on port " << port; 105 auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM); 106 if (sock->IsOpen() && AdbConnect(sock, port)) { 107 LOG(INFO) << "connection attempted to device on port " << port; 108 break; 109 } 110 sleep(2); 111 } 112 } 113 114 void WaitForAdbDisconnection(int port) { 115 LOG(INFO) << "Watching for disconnect on port " << port; 116 while (true) { 117 auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM); 118 if (!SendAll(sock, MakeMessage("devices"))) { 119 break; 120 } 121 if (RecvAll(sock, 4) != kAdbOkayStatusResponse) { 122 break; 123 } 124 auto devices_str = RecvAdbResponse(sock); 125 if (devices_str.find(MakeIPAndPort(port)) == std::string::npos) { 126 break; 127 } 128 sleep(2); 129 } 130 } 131 132 } // namespace 133 134 [[noreturn]] void cvd::EstablishAndMaintainConnection(int port) { 135 while (true) { 136 EstablishConnection(port); 137 WaitForAdbDisconnection(port); 138 } 139 } 140