• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007-2016 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 <errno.h>
18 #include <stdatomic.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/time.h>
22 
23 #ifdef __BIONIC__
24 #include <android/set_abort_message.h>
25 #endif
26 
27 #include <log/event_tag_map.h>
28 #include <log/log_transport.h>
29 #include <private/android_filesystem_config.h>
30 #include <private/android_logger.h>
31 
32 #include "config_read.h" /* __android_log_config_read_close() definition */
33 #include "config_write.h"
34 #include "log_portability.h"
35 #include "logger.h"
36 #include "uio.h"
37 
38 #define LOG_BUF_SIZE 1024
39 
40 static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
41 static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
42 
43 /*
44  * This is used by the C++ code to decide if it should write logs through
45  * the C code.  Basically, if /dev/socket/logd is available, we're running in
46  * the simulator rather than a desktop tool and want to use the device.
47  */
48 static enum { kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized;
49 
check_log_uid_permissions()50 static int check_log_uid_permissions() {
51 #if defined(__ANDROID__)
52   uid_t uid = __android_log_uid();
53 
54   /* Matches clientHasLogCredentials() in logd */
55   if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
56     uid = geteuid();
57     if ((uid != AID_SYSTEM) && (uid != AID_ROOT) && (uid != AID_LOG)) {
58       gid_t gid = getgid();
59       if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
60         gid = getegid();
61         if ((gid != AID_SYSTEM) && (gid != AID_ROOT) && (gid != AID_LOG)) {
62           int num_groups;
63           gid_t* groups;
64 
65           num_groups = getgroups(0, NULL);
66           if (num_groups <= 0) {
67             return -EPERM;
68           }
69           groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
70           if (!groups) {
71             return -ENOMEM;
72           }
73           num_groups = getgroups(num_groups, groups);
74           while (num_groups > 0) {
75             if (groups[num_groups - 1] == AID_LOG) {
76               break;
77             }
78             --num_groups;
79           }
80           free(groups);
81           if (num_groups <= 0) {
82             return -EPERM;
83           }
84         }
85       }
86     }
87   }
88 #endif
89   return 0;
90 }
91 
__android_log_cache_available(struct android_log_transport_write * node)92 static void __android_log_cache_available(struct android_log_transport_write* node) {
93   uint32_t i;
94 
95   if (node->logMask) {
96     return;
97   }
98 
99   for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
100     if (node->write && (i != LOG_ID_KERNEL) &&
101         ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
102         (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
103       node->logMask |= 1 << i;
104     }
105   }
106 }
107 
__android_log_dev_available()108 extern "C" int __android_log_dev_available() {
109   struct android_log_transport_write* node;
110 
111   if (list_empty(&__android_log_transport_write)) {
112     return kLogUninitialized;
113   }
114 
115   write_transport_for_each(node, &__android_log_transport_write) {
116     __android_log_cache_available(node);
117     if (node->logMask) {
118       return kLogAvailable;
119     }
120   }
121   return kLogNotAvailable;
122 }
123 
124 #if defined(__ANDROID__)
125 static atomic_uintptr_t tagMap;
126 #endif
127 
128 /*
129  * Release any logger resources. A new log write will immediately re-acquire.
130  */
__android_log_close()131 void __android_log_close() {
132   struct android_log_transport_write* transport;
133 #if defined(__ANDROID__)
134   EventTagMap* m;
135 #endif
136 
137   __android_log_lock();
138 
139   write_to_log = __write_to_log_init;
140 
141   /*
142    * Threads that are actively writing at this point are not held back
143    * by a lock and are at risk of dropping the messages with a return code
144    * -EBADF. Prefer to return error code than add the overhead of a lock to
145    * each log writing call to guarantee delivery. In addition, anyone
146    * calling this is doing so to release the logging resources and shut down,
147    * for them to do so with outstanding log requests in other threads is a
148    * disengenuous use of this function.
149    */
150 
151   write_transport_for_each(transport, &__android_log_persist_write) {
152     if (transport->close) {
153       (*transport->close)();
154     }
155   }
156 
157   write_transport_for_each(transport, &__android_log_transport_write) {
158     if (transport->close) {
159       (*transport->close)();
160     }
161   }
162 
163   __android_log_config_write_close();
164 
165 #if defined(__ANDROID__)
166   /*
167    * Additional risk here somewhat mitigated by immediately unlock flushing
168    * the processor cache. The multi-threaded race that we choose to accept,
169    * to minimize locking, is an atomic_load in a writer picking up a value
170    * just prior to entering this routine. There will be an use after free.
171    *
172    * Again, anyone calling this is doing so to release the logging resources
173    * is most probably going to quiesce then shut down; or to restart after
174    * a fork so the risk should be non-existent. For this reason we
175    * choose a mitigation stance for efficiency instead of incuring the cost
176    * of a lock for every log write.
177    */
178   m = (EventTagMap*)atomic_exchange(&tagMap, (uintptr_t)0);
179 #endif
180 
181   __android_log_unlock();
182 
183 #if defined(__ANDROID__)
184   if (m != (EventTagMap*)(uintptr_t)-1LL) android_closeEventTagMap(m);
185 #endif
186 }
187 
188 /* log_init_lock assumed */
__write_to_log_initialize()189 static int __write_to_log_initialize() {
190   struct android_log_transport_write* transport;
191   struct listnode* n;
192   int i = 0, ret = 0;
193 
194   __android_log_config_write();
195   write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
196     __android_log_cache_available(transport);
197     if (!transport->logMask) {
198       list_remove(&transport->node);
199       continue;
200     }
201     if (!transport->open || ((*transport->open)() < 0)) {
202       if (transport->close) {
203         (*transport->close)();
204       }
205       list_remove(&transport->node);
206       continue;
207     }
208     ++ret;
209   }
210   write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
211     __android_log_cache_available(transport);
212     if (!transport->logMask) {
213       list_remove(&transport->node);
214       continue;
215     }
216     if (!transport->open || ((*transport->open)() < 0)) {
217       if (transport->close) {
218         (*transport->close)();
219       }
220       list_remove(&transport->node);
221       continue;
222     }
223     ++i;
224   }
225   if (!ret && !i) {
226     return -ENODEV;
227   }
228 
229   return ret;
230 }
231 
232 /*
233  * Extract a 4-byte value from a byte stream. le32toh open coded
234  */
get4LE(const uint8_t * src)235 static inline uint32_t get4LE(const uint8_t* src) {
236   return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
237 }
238 
__write_to_log_daemon(log_id_t log_id,struct iovec * vec,size_t nr)239 static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
240   struct android_log_transport_write* node;
241   int ret, save_errno;
242   struct timespec ts;
243   size_t len, i;
244 
245   for (len = i = 0; i < nr; ++i) {
246     len += vec[i].iov_len;
247   }
248   if (!len) {
249     return -EINVAL;
250   }
251 
252   save_errno = errno;
253 #if defined(__ANDROID__)
254   clock_gettime(android_log_clockid(), &ts);
255 
256   if (log_id == LOG_ID_SECURITY) {
257     if (vec[0].iov_len < 4) {
258       errno = save_errno;
259       return -EINVAL;
260     }
261 
262     ret = check_log_uid_permissions();
263     if (ret < 0) {
264       errno = save_errno;
265       return ret;
266     }
267     if (!__android_log_security()) {
268       /* If only we could reset downstream logd counter */
269       errno = save_errno;
270       return -EPERM;
271     }
272   } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
273     const char* tag;
274     size_t len;
275     EventTagMap *m, *f;
276 
277     if (vec[0].iov_len < 4) {
278       errno = save_errno;
279       return -EINVAL;
280     }
281 
282     tag = NULL;
283     len = 0;
284     f = NULL;
285     m = (EventTagMap*)atomic_load(&tagMap);
286 
287     if (!m) {
288       ret = __android_log_trylock();
289       m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
290       if (!m) {
291         m = android_openEventTagMap(NULL);
292         if (ret) { /* trylock failed, use local copy, mark for close */
293           f = m;
294         } else {
295           if (!m) { /* One chance to open map file */
296             m = (EventTagMap*)(uintptr_t)-1LL;
297           }
298           atomic_store(&tagMap, (uintptr_t)m);
299         }
300       }
301       if (!ret) { /* trylock succeeded, unlock */
302         __android_log_unlock();
303       }
304     }
305     if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
306       tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base)));
307     }
308     ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE);
309     if (f) { /* local copy marked for close */
310       android_closeEventTagMap(f);
311     }
312     if (!ret) {
313       errno = save_errno;
314       return -EPERM;
315     }
316   } else {
317     /* Validate the incoming tag, tag content can not split across iovec */
318     char prio = ANDROID_LOG_VERBOSE;
319     const char* tag = static_cast<const char*>(vec[0].iov_base);
320     size_t len = vec[0].iov_len;
321     if (!tag) {
322       len = 0;
323     }
324     if (len > 0) {
325       prio = *tag;
326       if (len > 1) {
327         --len;
328         ++tag;
329       } else {
330         len = vec[1].iov_len;
331         tag = ((const char*)vec[1].iov_base);
332         if (!tag) {
333           len = 0;
334         }
335       }
336     }
337     /* tag must be nul terminated */
338     if (tag && strnlen(tag, len) >= len) {
339       tag = NULL;
340     }
341 
342     if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
343       errno = save_errno;
344       return -EPERM;
345     }
346   }
347 #else
348   /* simulate clock_gettime(CLOCK_REALTIME, &ts); */
349   {
350     struct timeval tv;
351     gettimeofday(&tv, NULL);
352     ts.tv_sec = tv.tv_sec;
353     ts.tv_nsec = tv.tv_usec * 1000;
354   }
355 #endif
356 
357   ret = 0;
358   i = 1 << log_id;
359   write_transport_for_each(node, &__android_log_transport_write) {
360     if (node->logMask & i) {
361       ssize_t retval;
362       retval = (*node->write)(log_id, &ts, vec, nr);
363       if (ret >= 0) {
364         ret = retval;
365       }
366     }
367   }
368 
369   write_transport_for_each(node, &__android_log_persist_write) {
370     if (node->logMask & i) {
371       (void)(*node->write)(log_id, &ts, vec, nr);
372     }
373   }
374 
375   errno = save_errno;
376   return ret;
377 }
378 
__write_to_log_init(log_id_t log_id,struct iovec * vec,size_t nr)379 static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
380   int ret, save_errno = errno;
381 
382   __android_log_lock();
383 
384   if (write_to_log == __write_to_log_init) {
385     ret = __write_to_log_initialize();
386     if (ret < 0) {
387       __android_log_unlock();
388       if (!list_empty(&__android_log_persist_write)) {
389         __write_to_log_daemon(log_id, vec, nr);
390       }
391       errno = save_errno;
392       return ret;
393     }
394 
395     write_to_log = __write_to_log_daemon;
396   }
397 
398   __android_log_unlock();
399 
400   ret = write_to_log(log_id, vec, nr);
401   errno = save_errno;
402   return ret;
403 }
404 
__android_log_write(int prio,const char * tag,const char * msg)405 int __android_log_write(int prio, const char* tag, const char* msg) {
406   return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
407 }
408 
__android_log_buf_write(int bufID,int prio,const char * tag,const char * msg)409 int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
410   struct iovec vec[3];
411   char tmp_tag[32];
412 
413   if (!tag) tag = "";
414 
415   /* XXX: This needs to go! */
416 #pragma clang diagnostic push
417 #pragma clang diagnostic ignored "-Wstring-plus-int"
418   if (bufID != LOG_ID_RADIO) {
419     switch (tag[0]) {
420       case 'H':
421         if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
422         goto inform;
423       case 'R':
424         /* Any log tag with "RIL" as the prefix */
425         if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
426         goto inform;
427       case 'Q':
428         /* Any log tag with "QC_RIL" as the prefix */
429         if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
430         goto inform;
431       case 'I':
432         /* Any log tag with "IMS" as the prefix */
433         if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
434         goto inform;
435       case 'A':
436         if (strcmp(tag + 1, "AT" + 1)) break;
437         goto inform;
438       case 'G':
439         if (strcmp(tag + 1, "GSM" + 1)) break;
440         goto inform;
441       case 'S':
442         if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
443         goto inform;
444       case 'C':
445         if (strcmp(tag + 1, "CDMA" + 1)) break;
446         goto inform;
447       case 'P':
448         if (strcmp(tag + 1, "PHONE" + 1)) break;
449       /* FALLTHRU */
450       inform:
451         bufID = LOG_ID_RADIO;
452         snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
453         tag = tmp_tag;
454         [[fallthrough]];
455       default:
456         break;
457     }
458   }
459 #pragma clang diagnostic pop
460 
461 #if __BIONIC__
462   if (prio == ANDROID_LOG_FATAL) {
463     android_set_abort_message(msg);
464   }
465 #endif
466 
467   vec[0].iov_base = (unsigned char*)&prio;
468   vec[0].iov_len = 1;
469   vec[1].iov_base = (void*)tag;
470   vec[1].iov_len = strlen(tag) + 1;
471   vec[2].iov_base = (void*)msg;
472   vec[2].iov_len = strlen(msg) + 1;
473 
474   return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
475 }
476 
__android_log_vprint(int prio,const char * tag,const char * fmt,va_list ap)477 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
478   char buf[LOG_BUF_SIZE];
479 
480   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
481 
482   return __android_log_write(prio, tag, buf);
483 }
484 
__android_log_print(int prio,const char * tag,const char * fmt,...)485 int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
486   va_list ap;
487   char buf[LOG_BUF_SIZE];
488 
489   va_start(ap, fmt);
490   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
491   va_end(ap);
492 
493   return __android_log_write(prio, tag, buf);
494 }
495 
__android_log_buf_print(int bufID,int prio,const char * tag,const char * fmt,...)496 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
497   va_list ap;
498   char buf[LOG_BUF_SIZE];
499 
500   va_start(ap, fmt);
501   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
502   va_end(ap);
503 
504   return __android_log_buf_write(bufID, prio, tag, buf);
505 }
506 
__android_log_assert(const char * cond,const char * tag,const char * fmt,...)507 void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
508   char buf[LOG_BUF_SIZE];
509 
510   if (fmt) {
511     va_list ap;
512     va_start(ap, fmt);
513     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
514     va_end(ap);
515   } else {
516     /* Msg not provided, log condition.  N.B. Do not use cond directly as
517      * format string as it could contain spurious '%' syntax (e.g.
518      * "%d" in "blocks%devs == 0").
519      */
520     if (cond)
521       snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
522     else
523       strcpy(buf, "Unspecified assertion failed");
524   }
525 
526   // Log assertion failures to stderr for the benefit of "adb shell" users
527   // and gtests (http://b/23675822).
528   TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
529   TEMP_FAILURE_RETRY(write(2, "\n", 1));
530 
531   __android_log_write(ANDROID_LOG_FATAL, tag, buf);
532   abort(); /* abort so we have a chance to debug the situation */
533            /* NOTREACHED */
534 }
535 
__android_log_bwrite(int32_t tag,const void * payload,size_t len)536 int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
537   struct iovec vec[2];
538 
539   vec[0].iov_base = &tag;
540   vec[0].iov_len = sizeof(tag);
541   vec[1].iov_base = (void*)payload;
542   vec[1].iov_len = len;
543 
544   return write_to_log(LOG_ID_EVENTS, vec, 2);
545 }
546 
__android_log_stats_bwrite(int32_t tag,const void * payload,size_t len)547 int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
548   struct iovec vec[2];
549 
550   vec[0].iov_base = &tag;
551   vec[0].iov_len = sizeof(tag);
552   vec[1].iov_base = (void*)payload;
553   vec[1].iov_len = len;
554 
555   return write_to_log(LOG_ID_STATS, vec, 2);
556 }
557 
__android_log_security_bwrite(int32_t tag,const void * payload,size_t len)558 int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
559   struct iovec vec[2];
560 
561   vec[0].iov_base = &tag;
562   vec[0].iov_len = sizeof(tag);
563   vec[1].iov_base = (void*)payload;
564   vec[1].iov_len = len;
565 
566   return write_to_log(LOG_ID_SECURITY, vec, 2);
567 }
568 
569 /*
570  * Like __android_log_bwrite, but takes the type as well.  Doesn't work
571  * for the general case where we're generating lists of stuff, but very
572  * handy if we just want to dump an integer into the log.
573  */
__android_log_btwrite(int32_t tag,char type,const void * payload,size_t len)574 int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
575   struct iovec vec[3];
576 
577   vec[0].iov_base = &tag;
578   vec[0].iov_len = sizeof(tag);
579   vec[1].iov_base = &type;
580   vec[1].iov_len = sizeof(type);
581   vec[2].iov_base = (void*)payload;
582   vec[2].iov_len = len;
583 
584   return write_to_log(LOG_ID_EVENTS, vec, 3);
585 }
586 
587 /*
588  * Like __android_log_bwrite, but used for writing strings to the
589  * event log.
590  */
__android_log_bswrite(int32_t tag,const char * payload)591 int __android_log_bswrite(int32_t tag, const char* payload) {
592   struct iovec vec[4];
593   char type = EVENT_TYPE_STRING;
594   uint32_t len = strlen(payload);
595 
596   vec[0].iov_base = &tag;
597   vec[0].iov_len = sizeof(tag);
598   vec[1].iov_base = &type;
599   vec[1].iov_len = sizeof(type);
600   vec[2].iov_base = &len;
601   vec[2].iov_len = sizeof(len);
602   vec[3].iov_base = (void*)payload;
603   vec[3].iov_len = len;
604 
605   return write_to_log(LOG_ID_EVENTS, vec, 4);
606 }
607 
608 /*
609  * Like __android_log_security_bwrite, but used for writing strings to the
610  * security log.
611  */
__android_log_security_bswrite(int32_t tag,const char * payload)612 int __android_log_security_bswrite(int32_t tag, const char* payload) {
613   struct iovec vec[4];
614   char type = EVENT_TYPE_STRING;
615   uint32_t len = strlen(payload);
616 
617   vec[0].iov_base = &tag;
618   vec[0].iov_len = sizeof(tag);
619   vec[1].iov_base = &type;
620   vec[1].iov_len = sizeof(type);
621   vec[2].iov_base = &len;
622   vec[2].iov_len = sizeof(len);
623   vec[3].iov_base = (void*)payload;
624   vec[3].iov_len = len;
625 
626   return write_to_log(LOG_ID_SECURITY, vec, 4);
627 }
628 
__write_to_log_null(log_id_t log_id,struct iovec * vec,size_t nr)629 static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
630   size_t len, i;
631 
632   if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
633     return -EINVAL;
634   }
635 
636   for (len = i = 0; i < nr; ++i) {
637     len += vec[i].iov_len;
638   }
639   if (!len) {
640     return -EINVAL;
641   }
642   return len;
643 }
644 
645 /* Following functions need access to our internal write_to_log status */
646 
647 int __android_log_transport;
648 
android_set_log_transport(int transport_flag)649 int android_set_log_transport(int transport_flag) {
650   int retval;
651 
652   if (transport_flag < 0) {
653     return -EINVAL;
654   }
655 
656   retval = LOGGER_NULL;
657 
658   __android_log_lock();
659 
660   if (transport_flag & LOGGER_NULL) {
661     write_to_log = __write_to_log_null;
662 
663     __android_log_unlock();
664 
665     return retval;
666   }
667 
668   __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
669 
670   transport_flag &= LOGGER_LOGD | LOGGER_STDERR;
671 
672   if (__android_log_transport != transport_flag) {
673     __android_log_transport = transport_flag;
674     __android_log_config_write_close();
675     __android_log_config_read_close();
676 
677     write_to_log = __write_to_log_init;
678     /* generically we only expect these two values for write_to_log */
679   } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
680     write_to_log = __write_to_log_init;
681   }
682 
683   retval = __android_log_transport;
684 
685   __android_log_unlock();
686 
687   return retval;
688 }
689 
android_get_log_transport()690 int android_get_log_transport() {
691   int ret = LOGGER_DEFAULT;
692 
693   __android_log_lock();
694   if (write_to_log == __write_to_log_null) {
695     ret = LOGGER_NULL;
696   } else {
697     __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
698     ret = __android_log_transport;
699     if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
700       ret = -EINVAL;
701     }
702   }
703   __android_log_unlock();
704 
705   return ret;
706 }
707