diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h index 9192a709e..db2221160 100644 --- a/liblog/include/log/log.h +++ b/liblog/include/log/log.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include /* helper to define iovec for portability */ @@ -145,253 +146,6 @@ typedef enum { (void) __android_log_bswrite(_tag, _value); #endif -/* --------------------------------------------------------------------- */ - -/* - * Native log reading interface section. See logcat for sample code. - * - * The preferred API is an exec of logcat. Likely uses of this interface - * are if native code suffers from exec or filtration being too costly, - * access to raw information, or parsing is an issue. - */ - -/* - * The userspace structure for version 1 of the logger_entry ABI. - */ -#ifndef __struct_logger_entry_defined -#define __struct_logger_entry_defined -struct logger_entry { - uint16_t len; /* length of the payload */ - uint16_t __pad; /* no matter what, we get 2 bytes of padding */ - int32_t pid; /* generating process's pid */ - int32_t tid; /* generating process's tid */ - int32_t sec; /* seconds since Epoch */ - int32_t nsec; /* nanoseconds */ -#ifndef __cplusplus - char msg[0]; /* the entry's payload */ -#endif -}; -#endif - -/* - * The userspace structure for version 2 of the logger_entry ABI. - */ -#ifndef __struct_logger_entry_v2_defined -#define __struct_logger_entry_v2_defined -struct logger_entry_v2 { - uint16_t len; /* length of the payload */ - uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */ - int32_t pid; /* generating process's pid */ - int32_t tid; /* generating process's tid */ - int32_t sec; /* seconds since Epoch */ - int32_t nsec; /* nanoseconds */ - uint32_t euid; /* effective UID of logger */ -#ifndef __cplusplus - char msg[0]; /* the entry's payload */ -#endif -} __attribute__((__packed__)); -#endif - -/* - * The userspace structure for version 3 of the logger_entry ABI. - */ -#ifndef __struct_logger_entry_v3_defined -#define __struct_logger_entry_v3_defined -struct logger_entry_v3 { - uint16_t len; /* length of the payload */ - uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */ - int32_t pid; /* generating process's pid */ - int32_t tid; /* generating process's tid */ - int32_t sec; /* seconds since Epoch */ - int32_t nsec; /* nanoseconds */ - uint32_t lid; /* log id of the payload */ -#ifndef __cplusplus - char msg[0]; /* the entry's payload */ -#endif -} __attribute__((__packed__)); -#endif - -/* - * The userspace structure for version 4 of the logger_entry ABI. - */ -#ifndef __struct_logger_entry_v4_defined -#define __struct_logger_entry_v4_defined -struct logger_entry_v4 { - uint16_t len; /* length of the payload */ - uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */ - int32_t pid; /* generating process's pid */ - uint32_t tid; /* generating process's tid */ - uint32_t sec; /* seconds since Epoch */ - uint32_t nsec; /* nanoseconds */ - uint32_t lid; /* log id of the payload, bottom 4 bits currently */ - uint32_t uid; /* generating process's uid */ -#ifndef __cplusplus - char msg[0]; /* the entry's payload */ -#endif -}; -#endif - -/* - * The maximum size of the log entry payload that can be - * written to the logger. An attempt to write more than - * this amount will result in a truncated log entry. - */ -#define LOGGER_ENTRY_MAX_PAYLOAD 4068 - -/* - * The maximum size of a log entry which can be read from the - * kernel logger driver. An attempt to read less than this amount - * may result in read() returning EINVAL. - */ -#define LOGGER_ENTRY_MAX_LEN (5*1024) - -#ifndef __struct_log_msg_defined -#define __struct_log_msg_defined -struct log_msg { - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry_v4 entry; - struct logger_entry_v4 entry_v4; - struct logger_entry_v3 entry_v3; - struct logger_entry_v2 entry_v2; - struct logger_entry entry_v1; - } __attribute__((aligned(4))); -#ifdef __cplusplus - /* Matching log_time operators */ - bool operator== (const log_msg& T) const - { - return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec); - } - bool operator!= (const log_msg& T) const - { - return !(*this == T); - } - bool operator< (const log_msg& T) const - { - return (entry.sec < T.entry.sec) - || ((entry.sec == T.entry.sec) - && (entry.nsec < T.entry.nsec)); - } - bool operator>= (const log_msg& T) const - { - return !(*this < T); - } - bool operator> (const log_msg& T) const - { - return (entry.sec > T.entry.sec) - || ((entry.sec == T.entry.sec) - && (entry.nsec > T.entry.nsec)); - } - bool operator<= (const log_msg& T) const - { - return !(*this > T); - } - uint64_t nsec() const - { - return static_cast(entry.sec) * NS_PER_SEC + entry.nsec; - } - - /* packet methods */ - log_id_t id() - { - return static_cast(entry.lid); - } - char* msg() - { - unsigned short hdr_size = entry.hdr_size; - if (!hdr_size) { - hdr_size = sizeof(entry_v1); - } - if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) { - return NULL; - } - return reinterpret_cast(buf) + hdr_size; - } - unsigned int len() - { - return (entry.hdr_size ? - entry.hdr_size : - static_cast(sizeof(entry_v1))) + - entry.len; - } -#endif -}; -#endif - -#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE -#ifndef __ANDROID_API__ -#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3 -#elif __ANDROID_API__ > 23 /* > Marshmallow */ -#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3 -#elif __ANDROID_API__ > 22 /* > Lollipop */ -#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2 -#elif __ANDROID_API__ > 19 /* > KitKat */ -#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1 -#else -#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0 -#endif -#endif - -#if __ANDROID_USE_LIBLOG_READER_INTERFACE - -struct logger; - -log_id_t android_logger_get_id(struct logger* logger); - -int android_logger_clear(struct logger* logger); -long android_logger_get_log_size(struct logger* logger); -int android_logger_set_log_size(struct logger* logger, unsigned long size); -long android_logger_get_log_readable_size(struct logger* logger); -int android_logger_get_log_version(struct logger* logger); - -struct logger_list; - -#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1 -ssize_t android_logger_get_statistics(struct logger_list* logger_list, - char* buf, size_t len); -ssize_t android_logger_get_prune_list(struct logger_list* logger_list, - char* buf, size_t len); -int android_logger_set_prune_list(struct logger_list* logger_list, - char* buf, size_t len); -#endif - -#define ANDROID_LOG_RDONLY O_RDONLY -#define ANDROID_LOG_WRONLY O_WRONLY -#define ANDROID_LOG_RDWR O_RDWR -#define ANDROID_LOG_ACCMODE O_ACCMODE -#define ANDROID_LOG_NONBLOCK O_NONBLOCK -#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2 -#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */ -#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */ -#endif -#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1 -#define ANDROID_LOG_PSTORE 0x80000000 -#endif - -struct logger_list* android_logger_list_alloc(int mode, - unsigned int tail, - pid_t pid); -struct logger_list* android_logger_list_alloc_time(int mode, - log_time start, - pid_t pid); -void android_logger_list_free(struct logger_list* logger_list); -/* In the purest sense, the following two are orthogonal interfaces */ -int android_logger_list_read(struct logger_list* logger_list, - struct log_msg* log_msg); - -/* Multiple log_id_t opens */ -struct logger* android_logger_open(struct logger_list* logger_list, - log_id_t id); -#define android_logger_close android_logger_free -/* Single log_id_t open */ -struct logger_list* android_logger_list_open(log_id_t id, - int mode, - unsigned int tail, - pid_t pid); -#define android_logger_list_close android_logger_list_free - -#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */ - #ifdef __linux__ #ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h new file mode 100644 index 000000000..5b5eebca6 --- /dev/null +++ b/liblog/include/log/log_read.h @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2005-2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBS_LOG_LOG_READ_H +#define _LIBS_LOG_LOG_READ_H + +/* deal with possible sys/cdefs.h conflict with fcntl.h */ +#ifdef __unused +#define __unused_defined __unused +#undef __unused +#endif + +#include /* Pick up O_* macros */ + +/* restore definitions from above */ +#ifdef __unused_defined +#define __unused __attribute__((__unused__)) +#endif + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Native log reading interface section. See logcat for sample code. + * + * The preferred API is an exec of logcat. Likely uses of this interface + * are if native code suffers from exec or filtration being too costly, + * access to raw information, or parsing is an issue. + */ + +/* + * The userspace structure for version 1 of the logger_entry ABI. + */ +#ifndef __struct_logger_entry_defined +#define __struct_logger_entry_defined +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t __pad; /* no matter what, we get 2 bytes of padding */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ +#ifndef __cplusplus + char msg[0]; /* the entry's payload */ +#endif +}; +#endif + +/* + * The userspace structure for version 2 of the logger_entry ABI. + */ +#ifndef __struct_logger_entry_v2_defined +#define __struct_logger_entry_v2_defined +struct logger_entry_v2 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + uint32_t euid; /* effective UID of logger */ +#ifndef __cplusplus + char msg[0]; /* the entry's payload */ +#endif +} __attribute__((__packed__)); +#endif + +/* + * The userspace structure for version 3 of the logger_entry ABI. + */ +#ifndef __struct_logger_entry_v3_defined +#define __struct_logger_entry_v3_defined +struct logger_entry_v3 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + uint32_t lid; /* log id of the payload */ +#ifndef __cplusplus + char msg[0]; /* the entry's payload */ +#endif +} __attribute__((__packed__)); +#endif + +/* + * The userspace structure for version 4 of the logger_entry ABI. + */ +#ifndef __struct_logger_entry_v4_defined +#define __struct_logger_entry_v4_defined +struct logger_entry_v4 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */ + int32_t pid; /* generating process's pid */ + uint32_t tid; /* generating process's tid */ + uint32_t sec; /* seconds since Epoch */ + uint32_t nsec; /* nanoseconds */ + uint32_t lid; /* log id of the payload, bottom 4 bits currently */ + uint32_t uid; /* generating process's uid */ +#ifndef __cplusplus + char msg[0]; /* the entry's payload */ +#endif +}; +#endif + +/* + * The maximum size of the log entry payload that can be + * written to the logger. An attempt to write more than + * this amount will result in a truncated log entry. + */ +#define LOGGER_ENTRY_MAX_PAYLOAD 4068 + +/* + * The maximum size of a log entry which can be read. + * An attempt to read less than this amount may result + * in read() returning EINVAL. + */ +#define LOGGER_ENTRY_MAX_LEN (5*1024) + +#ifndef __struct_log_msg_defined +#define __struct_log_msg_defined +struct log_msg { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry_v4 entry; + struct logger_entry_v4 entry_v4; + struct logger_entry_v3 entry_v3; + struct logger_entry_v2 entry_v2; + struct logger_entry entry_v1; + } __attribute__((aligned(4))); +#ifdef __cplusplus + /* Matching log_time operators */ + bool operator== (const log_msg& T) const + { + return (entry.sec == T.entry.sec) && (entry.nsec == T.entry.nsec); + } + bool operator!= (const log_msg& T) const + { + return !(*this == T); + } + bool operator< (const log_msg& T) const + { + return (entry.sec < T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec < T.entry.nsec)); + } + bool operator>= (const log_msg& T) const + { + return !(*this < T); + } + bool operator> (const log_msg& T) const + { + return (entry.sec > T.entry.sec) + || ((entry.sec == T.entry.sec) + && (entry.nsec > T.entry.nsec)); + } + bool operator<= (const log_msg& T) const + { + return !(*this > T); + } + uint64_t nsec() const + { + return static_cast(entry.sec) * NS_PER_SEC + entry.nsec; + } + + /* packet methods */ + log_id_t id() + { + return static_cast(entry.lid); + } + char* msg() + { + unsigned short hdr_size = entry.hdr_size; + if (!hdr_size) { + hdr_size = sizeof(entry_v1); + } + if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) { + return NULL; + } + return reinterpret_cast(buf) + hdr_size; + } + unsigned int len() + { + return (entry.hdr_size ? + entry.hdr_size : + static_cast(sizeof(entry_v1))) + + entry.len; + } +#endif +}; +#endif + +#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE +#ifndef __ANDROID_API__ +#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3 +#elif __ANDROID_API__ > 23 /* > Marshmallow */ +#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3 +#elif __ANDROID_API__ > 22 /* > Lollipop */ +#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2 +#elif __ANDROID_API__ > 19 /* > KitKat */ +#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1 +#else +#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0 +#endif +#endif + +#if __ANDROID_USE_LIBLOG_READER_INTERFACE + +struct logger; + +log_id_t android_logger_get_id(struct logger* logger); + +int android_logger_clear(struct logger* logger); +long android_logger_get_log_size(struct logger* logger); +int android_logger_set_log_size(struct logger* logger, unsigned long size); +long android_logger_get_log_readable_size(struct logger* logger); +int android_logger_get_log_version(struct logger* logger); + +struct logger_list; + +#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1 +ssize_t android_logger_get_statistics(struct logger_list* logger_list, + char* buf, size_t len); +ssize_t android_logger_get_prune_list(struct logger_list* logger_list, + char* buf, size_t len); +int android_logger_set_prune_list(struct logger_list* logger_list, + char* buf, size_t len); +#endif + +#define ANDROID_LOG_RDONLY O_RDONLY +#define ANDROID_LOG_WRONLY O_WRONLY +#define ANDROID_LOG_RDWR O_RDWR +#define ANDROID_LOG_ACCMODE O_ACCMODE +#define ANDROID_LOG_NONBLOCK O_NONBLOCK +#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2 +#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */ +#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */ +#endif +#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1 +#define ANDROID_LOG_PSTORE 0x80000000 +#endif + +struct logger_list* android_logger_list_alloc(int mode, + unsigned int tail, + pid_t pid); +struct logger_list* android_logger_list_alloc_time(int mode, + log_time start, + pid_t pid); +void android_logger_list_free(struct logger_list* logger_list); +/* In the purest sense, the following two are orthogonal interfaces */ +int android_logger_list_read(struct logger_list* logger_list, + struct log_msg* log_msg); + +/* Multiple log_id_t opens */ +struct logger* android_logger_open(struct logger_list* logger_list, + log_id_t id); +#define android_logger_close android_logger_free +/* Single log_id_t open */ +struct logger_list* android_logger_list_open(log_id_t id, + int mode, + unsigned int tail, + pid_t pid); +#define android_logger_list_close android_logger_list_free + +#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBS_LOG_LOG_H */ diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk index 845067503..097befc6d 100644 --- a/liblog/tests/Android.mk +++ b/liblog/tests/Android.mk @@ -58,6 +58,7 @@ test_src_files := \ liblog_test.cpp \ log_id_test.cpp \ log_radio_test.cpp \ + log_read_test.cpp \ log_system_test.cpp \ log_time_test.cpp diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index d03c6d150..8b9f0f0eb 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -1115,47 +1115,6 @@ TEST(liblog, dual_reader) { #endif } -TEST(liblog, android_logger_get_) { -#ifdef __ANDROID__ - // This test assumes the log buffers are filled with noise from - // normal operations. It will fail if done immediately after a - // logcat -c. - struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0); - - for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { - log_id_t id = static_cast(i); - const char *name = android_log_id_to_name(id); - if (id != android_name_to_log_id(name)) { - continue; - } - fprintf(stderr, "log buffer %s\r", name); - struct logger * logger; - EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id))); - EXPECT_EQ(id, android_logger_get_id(logger)); - ssize_t get_log_size = android_logger_get_log_size(logger); - /* security buffer is allowed to be denied */ - if (strcmp("security", name)) { - EXPECT_LT(0, get_log_size); - /* crash buffer is allowed to be empty, that is actually healthy! */ - EXPECT_LE((strcmp("crash", name)) != 0, - android_logger_get_log_readable_size(logger)); - } else { - EXPECT_NE(0, get_log_size); - if (get_log_size < 0) { - EXPECT_GT(0, android_logger_get_log_readable_size(logger)); - } else { - EXPECT_LE(0, android_logger_get_log_readable_size(logger)); - } - } - EXPECT_LT(0, android_logger_get_log_version(logger)); - } - - android_logger_list_close(logger_list); -#else - GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif -} - #ifdef __ANDROID__ static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) { return android_log_shouldPrintLine(p_format, tag, pri) diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp new file mode 100644 index 000000000..2e02407b8 --- /dev/null +++ b/liblog/tests/log_read_test.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013-2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +#include // minimal logging API +#include +#include +// Test the APIs in this standalone include file +#include +// Do not use anything in log/log_time.h despite side effects of the above. + +TEST(liblog, __android_log_write__android_logger_list_read) { +#ifdef __ANDROID__ + pid_t pid = getpid(); + + struct logger_list *logger_list; + ASSERT_TRUE(NULL != (logger_list = android_logger_list_open( + LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid))); + + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", + pid, ts.tv_sec, ts.tv_nsec); + static const char tag[] = "liblog.__android_log_write__android_logger_list_read"; + static const char prio = ANDROID_LOG_DEBUG; + ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str())); + usleep(1000000); + + buf = std::string(&prio, sizeof(prio)) + + tag + + std::string("", 1) + + buf + + std::string("", 1); + + int count = 0; + + for (;;) { + log_msg log_msg; + if (android_logger_list_read(logger_list, &log_msg) <= 0) break; + + EXPECT_EQ(log_msg.entry.pid, pid); + // There may be a future where we leak "liblog" tagged LOG_ID_EVENT + // binary messages through so that logger losses can be correlated? + EXPECT_EQ(log_msg.id(), LOG_ID_MAIN); + + if (log_msg.entry.len != buf.length()) continue; + + if (buf != std::string(log_msg.msg(), log_msg.entry.len)) continue; + + ++count; + } + android_logger_list_close(logger_list); + + EXPECT_EQ(1, count); +#else + GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif +} + +TEST(liblog, android_logger_get_) { +#ifdef __ANDROID__ + // This test assumes the log buffers are filled with noise from + // normal operations. It will fail if done immediately after a + // logcat -c. + struct logger_list * logger_list = android_logger_list_alloc(ANDROID_LOG_WRONLY, 0, 0); + + for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { + log_id_t id = static_cast(i); + const char *name = android_log_id_to_name(id); + if (id != android_name_to_log_id(name)) { + continue; + } + fprintf(stderr, "log buffer %s\r", name); + struct logger * logger; + EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id))); + EXPECT_EQ(id, android_logger_get_id(logger)); + ssize_t get_log_size = android_logger_get_log_size(logger); + /* security buffer is allowed to be denied */ + if (strcmp("security", name)) { + EXPECT_LT(0, get_log_size); + /* crash buffer is allowed to be empty, that is actually healthy! */ + EXPECT_LE((strcmp("crash", name)) != 0, + android_logger_get_log_readable_size(logger)); + } else { + EXPECT_NE(0, get_log_size); + if (get_log_size < 0) { + EXPECT_GT(0, android_logger_get_log_readable_size(logger)); + } else { + EXPECT_LE(0, android_logger_get_log_readable_size(logger)); + } + } + EXPECT_LT(0, android_logger_get_log_version(logger)); + } + + android_logger_list_close(logger_list); +#else + GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif +} +