Home | History | Annotate | Download | only in daemon
      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