1 /* 2 * Copyright (C) 2017 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 "chre_host/socket_client.h" 18 19 #include <inttypes.h> 20 21 #include <string.h> 22 23 #include <chrono> 24 25 #include <cutils/sockets.h> 26 #include <utils/RefBase.h> 27 #include <utils/StrongPointer.h> 28 29 #include "chre_host/log.h" 30 31 namespace android { 32 namespace chre { 33 34 SocketClient::SocketClient() { 35 std::atomic_init(&mSockFd, INVALID_SOCKET); 36 } 37 38 SocketClient::~SocketClient() { 39 disconnect(); 40 } 41 42 bool SocketClient::connect(const char *socketName, 43 const sp<ICallbacks>& callbacks) { 44 return doConnect(socketName, callbacks, false /* connectInBackground */); 45 } 46 47 bool SocketClient::connectInBackground(const char *socketName, 48 const sp<ICallbacks>& callbacks) { 49 return doConnect(socketName, callbacks, true /* connectInBackground */); 50 } 51 52 void SocketClient::disconnect() { 53 if (inReceiveThread()) { 54 LOGE("disconnect() can't be called from a receive thread callback"); 55 } else if (receiveThreadRunning()) { 56 // Inform the RX thread that we're requesting a shutdown, breaking it out of 57 // the retry wait if it's currently blocked there 58 { 59 std::lock_guard<std::mutex> lock(mShutdownMutex); 60 mGracefulShutdown = true; 61 } 62 mShutdownCond.notify_all(); 63 64 // Invalidate the socket (will kick the RX thread out of recv if it's 65 // currently blocked there) 66 if (mSockFd != INVALID_SOCKET && shutdown(mSockFd, SHUT_RDWR) != 0) { 67 LOG_ERROR("Couldn't shut down socket", errno); 68 } 69 70 if (mRxThread.joinable()) { 71 LOGD("Waiting for RX thread to exit"); 72 mRxThread.join(); 73 } 74 } 75 } 76 77 bool SocketClient::isConnected() const { 78 return (mSockFd != INVALID_SOCKET); 79 } 80 81 bool SocketClient::sendMessage(const void *data, size_t length) { 82 bool success = false; 83 84 if (mSockFd == INVALID_SOCKET) { 85 LOGW("Tried sending a message, but don't have a valid socket handle"); 86 } else { 87 ssize_t bytesSent = send(mSockFd, data, length, 0); 88 if (bytesSent < 0) { 89 LOGE("Failed to send %zu bytes of data: %s", length, strerror(errno)); 90 } else if (bytesSent == 0) { 91 LOGW("Failed to send data; remote side disconnected"); 92 } else if (static_cast<size_t>(bytesSent) != length) { 93 LOGW("Truncated packet, tried sending %zu bytes, only %zd went through", 94 length, bytesSent); 95 } else { 96 success = true; 97 } 98 } 99 100 return success; 101 } 102 103 bool SocketClient::doConnect(const char *socketName, 104 const sp<ICallbacks>& callbacks, 105 bool connectInBackground) { 106 bool success = false; 107 if (inReceiveThread()) { 108 LOGE("Can't attempt to connect from a receive thread callback"); 109 } else { 110 if (receiveThreadRunning()) { 111 LOGW("Re-connecting socket with implicit disconnect"); 112 disconnect(); 113 } 114 115 size_t socketNameLen = strlcpy(mSocketName, socketName, 116 sizeof(mSocketName)); 117 if (socketNameLen >= sizeof(mSocketName)) { 118 LOGE("Socket name length parameter is too long (%zu, max %zu)", 119 socketNameLen, sizeof(mSocketName)); 120 } else if (callbacks == nullptr) { 121 LOGE("Callbacks parameter must be provided"); 122 } else if (connectInBackground || tryConnect()) { 123 mGracefulShutdown = false; 124 mCallbacks = callbacks; 125 mRxThread = std::thread([this]() { 126 receiveThread(); 127 }); 128 success = true; 129 } 130 } 131 132 return success; 133 } 134 135 bool SocketClient::inReceiveThread() const { 136 return (std::this_thread::get_id() == mRxThread.get_id()); 137 } 138 139 void SocketClient::receiveThread() { 140 constexpr size_t kReceiveBufferSize = 4096; 141 uint8_t buffer[kReceiveBufferSize]; 142 143 LOGV("Receive thread started"); 144 while (!mGracefulShutdown && (mSockFd != INVALID_SOCKET || reconnect())) { 145 while (!mGracefulShutdown) { 146 ssize_t bytesReceived = recv(mSockFd, buffer, sizeof(buffer), 0); 147 if (bytesReceived < 0) { 148 LOG_ERROR("Exiting RX thread", errno); 149 break; 150 } else if (bytesReceived == 0) { 151 if (!mGracefulShutdown) { 152 LOGI("Socket disconnected on remote end"); 153 mCallbacks->onDisconnected(); 154 } 155 break; 156 } 157 158 mCallbacks->onMessageReceived(buffer, bytesReceived); 159 } 160 161 if (close(mSockFd) != 0) { 162 LOG_ERROR("Couldn't close socket", errno); 163 } 164 mSockFd = INVALID_SOCKET; 165 } 166 167 if (!mGracefulShutdown) { 168 mCallbacks->onConnectionAborted(); 169 } 170 171 mCallbacks.clear(); 172 LOGV("Exiting receive thread"); 173 } 174 175 bool SocketClient::receiveThreadRunning() const { 176 return mRxThread.joinable(); 177 } 178 179 bool SocketClient::reconnect() { 180 auto delay = std::chrono::duration<int32_t, std::milli>(500); 181 constexpr auto kMaxDelay = std::chrono::minutes(5); 182 int retryLimit = 40; // ~2.5 hours total 183 184 while (--retryLimit > 0) { 185 { 186 std::unique_lock<std::mutex> lock(mShutdownMutex); 187 mShutdownCond.wait_for(lock, delay, 188 [this]() { return mGracefulShutdown.load(); }); 189 if (mGracefulShutdown) { 190 break; 191 } 192 } 193 194 if (!tryConnect()) { 195 LOGW("Failed to (re)connect, next try in %" PRId32 " ms", delay.count()); 196 delay *= 2; 197 if (delay > kMaxDelay) { 198 delay = kMaxDelay; 199 } 200 } else { 201 LOGD("Successfully (re)connected"); 202 mCallbacks->onConnected(); 203 return true; 204 } 205 } 206 207 return false; 208 } 209 210 bool SocketClient::tryConnect() { 211 errno = 0; 212 mSockFd = socket_local_client(mSocketName, 213 ANDROID_SOCKET_NAMESPACE_RESERVED, 214 SOCK_SEQPACKET); 215 if (mSockFd == INVALID_SOCKET) { 216 LOGE("Couldn't create/connect client socket to '%s': %s", 217 mSocketName, strerror(errno)); 218 } 219 220 return (mSockFd != INVALID_SOCKET); 221 } 222 223 } // namespace chre 224 } // namespace android 225