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
log_buffer(const uint8_t * buffer,size_t size)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  */
chre_message_to_host_thread(void * arg)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  */
chre_monitor_thread(void * arg)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  */
chre_reverse_monitor_thread(void * arg)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  */
init_reverse_monitor(struct reverse_monitor_thread_data * data)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  */
start_thread(pthread_t * thread_handle,thread_entry_point_f * thread_entry,void * arg)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 
onMessageReceivedFromClient(uint16_t clientId,void * data,size_t length)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 
main()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