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 /** 18 * @file 19 * The daemon that hosts CHRE on the SLPI via FastRPC. 20 * 21 * Several threads are required for this functionality: 22 * - Main thread: blocked waiting on SIGINT/SIGTERM, and requests graceful 23 * shutdown of CHRE when caught 24 * - Monitor thread: persistently blocked in a FastRPC call to the SLPI that 25 * only returns when CHRE exits or the SLPI crashes 26 * - TODO: see whether we can merge this with the RX thread 27 * - Reverse monitor thread: after initializing the SLPI-side monitor for this 28 * process, blocks on a condition variable. If this thread exits, CHRE on 29 * the SLPI side will be notified and shut down (this is only possible if 30 * this thread is not blocked in a FastRPC call). 31 * - TODO: confirm this and see whether we can merge this responsibility 32 * into the TX thread 33 * - Message to host (RX) thread: blocks in FastRPC call, waiting on incoming 34 * message from CHRE 35 * - Message to CHRE (TX) thread: blocks waiting on outbound queue, delivers 36 * messages to CHRE over FastRPC 37 * 38 * TODO: This file originated from an implementation for another device, and was 39 * written in C, but then it was converted to C++ when adding socket support. It 40 * should be fully converted to C++. 41 */ 42 43 #define LOG_NDEBUG 0 // TODO: for initial testing only 44 45 #include <ctype.h> 46 #include <pthread.h> 47 #include <stdbool.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #include "chre/platform/slpi/fastrpc.h" 54 #include "chre_host/log.h" 55 #include "chre_host/host_protocol_host.h" 56 #include "chre_host/socket_server.h" 57 #include "generated/chre_slpi.h" 58 59 using android::chre::HostProtocolHost; 60 61 typedef void *(thread_entry_point_f)(void *); 62 63 struct reverse_monitor_thread_data { 64 pthread_t thread; 65 pthread_mutex_t mutex; 66 pthread_cond_t cond; 67 }; 68 69 static void *chre_message_to_host_thread(void *arg); 70 static void *chre_monitor_thread(void *arg); 71 static void *chre_reverse_monitor_thread(void *arg); 72 static bool init_reverse_monitor(struct reverse_monitor_thread_data *data); 73 static bool start_thread(pthread_t *thread_handle, 74 thread_entry_point_f *thread_entry, 75 void *arg); 76 77 //! Set to true when we request a graceful shutdown of CHRE 78 static volatile bool chre_shutdown_requested = false; 79 80 // TODO: debug-only code 81 static void log_buffer(const uint8_t *buffer, size_t size) { 82 char line[32]; 83 int offset = 0; 84 char line_chars[32]; 85 int offset_chars = 0; 86 87 size_t orig_size = size; 88 if (size > 128) { 89 size = 128; 90 LOGV("Dumping first 128 bytes of buffer of size %zu", orig_size); 91 } else { 92 LOGV("Dumping buffer of size %zu bytes", size); 93 } 94 for (size_t i = 1; i <= size; ++i) { 95 offset += snprintf(&line[offset], sizeof(line) - offset, "%02x ", 96 buffer[i - 1]); 97 offset_chars += snprintf( 98 &line_chars[offset_chars], sizeof(line_chars) - offset_chars, 99 "%c", (isprint(buffer[i - 1])) ? buffer[i - 1] : '.'); 100 if ((i % 8) == 0) { 101 LOGV(" %s\t%s", line, line_chars); 102 offset = 0; 103 offset_chars = 0; 104 } else if ((i % 4) == 0) { 105 offset += snprintf(&line[offset], sizeof(line) - offset, " "); 106 } 107 } 108 109 if (offset > 0) { 110 char tabs[8]; 111 char *pos = tabs; 112 while (offset < 28) { 113 *pos++ = '\t'; 114 offset += 8; 115 } 116 *pos = '\0'; 117 LOGV(" %s%s%s", line, tabs, line_chars); 118 } 119 } 120 121 /** 122 * Entry point for the thread that receives messages sent by CHRE. 123 * 124 * @return always returns NULL 125 */ 126 static void *chre_message_to_host_thread(void *arg) { 127 // TODO: size this appropriately to handle encoded messages 128 unsigned char messageBuffer[4096]; 129 unsigned int messageLen; 130 int result = 0; 131 auto *server = static_cast<::android::chre::SocketServer *>(arg); 132 133 while (!chre_shutdown_requested) { 134 messageLen = 0; 135 LOGD("Calling into chre_slpi_get_message_to_host"); 136 result = chre_slpi_get_message_to_host( 137 messageBuffer, sizeof(messageBuffer), &messageLen); 138 LOGV("Got message from CHRE with size %u (result %d)", messageLen, result); 139 140 if (result == CHRE_FASTRPC_ERROR_SHUTTING_DOWN) { 141 LOGD("CHRE shutting down, exiting CHRE->Host message thread"); 142 break; 143 } else if (result == CHRE_FASTRPC_SUCCESS && messageLen > 0) { 144 log_buffer(messageBuffer, messageLen); 145 uint16_t hostClientId; 146 if (!HostProtocolHost::extractHostClientId(messageBuffer, messageLen, 147 &hostClientId)) { 148 LOGW("Failed to extract host client ID from message - sending " 149 "broadcast"); 150 hostClientId = chre::kHostClientIdUnspecified; 151 } 152 153 if (hostClientId == chre::kHostClientIdUnspecified) { 154 server->sendToAllClients(messageBuffer, 155 static_cast<size_t>(messageLen)); 156 } else { 157 server->sendToClientById(messageBuffer, 158 static_cast<size_t>(messageLen), hostClientId); 159 } 160 } 161 } 162 163 LOGV("Message to host thread exited"); 164 return NULL; 165 } 166 167 /** 168 * Entry point for the thread that blocks in a FastRPC call to monitor for 169 * abnormal exit of CHRE or reboot of the SLPI. 170 * 171 * @return always returns NULL 172 */ 173 static void *chre_monitor_thread(void *arg) { 174 (void) arg; 175 int ret = chre_slpi_wait_on_thread_exit(); 176 if (!chre_shutdown_requested) { 177 LOGE("Detected unexpected CHRE thread exit (%d)\n", ret); 178 exit(EXIT_FAILURE); 179 } 180 181 LOGV("Monitor thread exited"); 182 return NULL; 183 } 184 185 /** 186 * Entry point for the "reverse" monitor thread, which invokes a FastRPC method 187 * to register a thread destructor, and blocks waiting on a condition variable. 188 * This allows for the code running in the SLPI to detect abnormal shutdown of 189 * the host-side binary and perform graceful cleanup. 190 * 191 * @return always returns NULL 192 */ 193 static void *chre_reverse_monitor_thread(void *arg) { 194 struct reverse_monitor_thread_data *thread_data = 195 (struct reverse_monitor_thread_data *) arg; 196 197 int ret = chre_slpi_initialize_reverse_monitor(); 198 if (ret != CHRE_FASTRPC_SUCCESS) { 199 LOGE("Failed to initialize reverse monitor on SLPI: %d", ret); 200 } else { 201 // Block here on the condition variable until the main thread notifies 202 // us to exit 203 pthread_mutex_lock(&thread_data->mutex); 204 pthread_cond_wait(&thread_data->cond, &thread_data->mutex); 205 pthread_mutex_unlock(&thread_data->mutex); 206 } 207 208 LOGV("Reverse monitor thread exited"); 209 return NULL; 210 } 211 212 /** 213 * Initializes the data shared with the reverse monitor thread, and starts the 214 * thread. 215 * 216 * @param data Pointer to structure containing the (uninitialized) condition 217 * variable and associated data passed to the reverse monitor thread 218 * 219 * @return true on success 220 */ 221 static bool init_reverse_monitor(struct reverse_monitor_thread_data *data) { 222 bool success = false; 223 int ret; 224 225 if ((ret = pthread_mutex_init(&data->mutex, NULL)) != 0) { 226 LOG_ERROR("Failed to initialize mutex", ret); 227 } else if ((ret = pthread_cond_init(&data->cond, NULL)) != 0) { 228 LOG_ERROR("Failed to initialize condition variable", ret); 229 } else if (!start_thread(&data->thread, chre_reverse_monitor_thread, data)) { 230 LOGE("Couldn't start reverse monitor thread"); 231 } else { 232 success = true; 233 } 234 235 return success; 236 } 237 238 /** 239 * Start a thread with default attributes, or log an error on failure 240 * 241 * @return bool true if the thread was successfully started 242 */ 243 static bool start_thread(pthread_t *thread_handle, 244 thread_entry_point_f *thread_entry, 245 void *arg) { 246 int ret = pthread_create(thread_handle, NULL, thread_entry, arg); 247 if (ret != 0) { 248 LOG_ERROR("pthread_create failed", ret); 249 } 250 return (ret == 0); 251 } 252 253 namespace { 254 255 void onMessageReceivedFromClient(uint16_t clientId, void *data, size_t length) { 256 constexpr size_t kMaxPayloadSize = 1024 * 1024; // 1 MiB 257 258 // This limitation is due to FastRPC, but there's no case where we should come 259 // close to this limit... 260 static_assert(kMaxPayloadSize <= INT32_MAX, 261 "SLPI uses 32-bit signed integers to represent message size"); 262 263 if (length > kMaxPayloadSize) { 264 LOGE("Message too large to pass to SLPI (got %zu, max %zu bytes)", length, 265 kMaxPayloadSize); 266 } else if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) { 267 LOGE("Couldn't set host client ID in message container!"); 268 } else { 269 LOGD("Delivering message from host (size %zu)", length); 270 log_buffer(static_cast<const uint8_t *>(data), length); 271 int ret = chre_slpi_deliver_message_from_host( 272 static_cast<const unsigned char *>(data), static_cast<int>(length)); 273 if (ret != 0) { 274 LOGE("Failed to deliver message from host to CHRE: %d", ret); 275 } 276 } 277 } 278 279 } // anonymous namespace 280 281 int main() { 282 int ret = -1; 283 pthread_t monitor_thread; 284 pthread_t msg_to_host_thread; 285 struct reverse_monitor_thread_data reverse_monitor; 286 ::android::chre::SocketServer server; 287 288 if (!init_reverse_monitor(&reverse_monitor)) { 289 LOGE("Couldn't initialize reverse monitor"); 290 } else if ((ret = chre_slpi_start_thread()) != CHRE_FASTRPC_SUCCESS) { 291 LOGE("Failed to start CHRE on SLPI: %d", ret); 292 } else { 293 if (!start_thread(&monitor_thread, chre_monitor_thread, NULL)) { 294 LOGE("Couldn't start monitor thread"); 295 } else if (!start_thread(&msg_to_host_thread, chre_message_to_host_thread, 296 &server)) { 297 LOGE("Couldn't start CHRE->Host message thread"); 298 } else { 299 LOGI("CHRE on SLPI started"); 300 // TODO: take 2nd argument as command-line parameter 301 server.run("chre", true, onMessageReceivedFromClient); 302 } 303 304 chre_shutdown_requested = true; 305 ret = chre_slpi_stop_thread(); 306 if (ret != CHRE_FASTRPC_SUCCESS) { 307 LOGE("Failed to stop CHRE on SLPI: %d", ret); 308 } else { 309 // TODO: don't call pthread_join if the thread failed to start 310 LOGV("Joining monitor thread"); 311 ret = pthread_join(monitor_thread, NULL); 312 if (ret != 0) { 313 LOG_ERROR("Join on monitor thread failed", ret); 314 } 315 316 LOGV("Joining reverse monitor thread"); 317 pthread_cond_signal(&reverse_monitor.cond); 318 ret = pthread_join(reverse_monitor.thread, NULL); 319 if (ret != 0) { 320 LOG_ERROR("Join on reverse monitor thread failed", ret); 321 } 322 323 LOGV("Joining message to host thread"); 324 ret = pthread_join(msg_to_host_thread, NULL); 325 if (ret != 0) { 326 LOG_ERROR("Join on monitor thread failed", ret); 327 } 328 329 LOGI("Shutdown complete"); 330 } 331 } 332 333 return ret; 334 } 335 336