Merge "fastbootd: exporting more properties"
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bdf4aac..7caf468 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -126,6 +126,26 @@
     return *ret;
 }
 
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+            {"Unknown", BatteryCapacityLevel::UNKNOWN},
+            {"Critical", BatteryCapacityLevel::CRITICAL},
+            {"Low", BatteryCapacityLevel::LOW},
+            {"Normal", BatteryCapacityLevel::NORMAL},
+            {"High", BatteryCapacityLevel::HIGH},
+            {"Full", BatteryCapacityLevel::FULL},
+            {NULL, BatteryCapacityLevel::UNKNOWN},
+    };
+
+    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unknown battery capacity level '%s'\n", capacityLevel);
+        *ret = BatteryCapacityLevel::UNKNOWN;
+    }
+
+    return *ret;
+}
+
 BatteryHealth getBatteryHealth(const char* status) {
     static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
             {"Unknown", BatteryHealth::UNKNOWN},
@@ -241,9 +261,10 @@
         mHealthInfo->legacy.batteryCurrentAverage =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
-    // TODO(b/142260281): Retrieve these values correctly.
-    mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
-    mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        mHealthInfo->batteryChargeTimeToFullNowSeconds =
+                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
     mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
 
     props.batteryTemperature = mBatteryFixedTemperature ?
@@ -252,6 +273,9 @@
 
     std::string buf;
 
+    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
         props.batteryStatus = getBatteryStatus(buf.c_str());
 
@@ -585,6 +609,19 @@
                         mHealthdConfig->batteryCycleCountPath = path;
                 }
 
+                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                }
+
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
@@ -653,6 +690,10 @@
             KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
         if (mHealthdConfig->batteryCycleCountPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index a900071..8ffb114 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -69,6 +69,8 @@
     android::String8 batteryChargeCounterPath;
     android::String8 batteryFullChargePath;
     android::String8 batteryCycleCountPath;
+    android::String8 batteryCapacityLevelPath;
+    android::String8 batteryChargeTimeToFullNowPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index f1147dc..cd4c11e 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -516,13 +516,12 @@
   memset(&log_state, 0, sizeof(log_state));
 }
 
-int __android_log_is_loggable(int prio, const char*, int def) {
+int __android_log_is_loggable(int prio, const char*, int) {
   int minimum_priority = __android_log_get_minimum_priority();
   if (minimum_priority == ANDROID_LOG_DEFAULT) {
-    return prio >= def;
-  } else {
-    return prio >= minimum_priority;
+    minimum_priority = ANDROID_LOG_INFO;
   }
+  return prio >= minimum_priority;
 }
 
 int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 6530704..00aafaa 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -194,6 +194,10 @@
  * Writes the log message specified with logger_data and msg to the log.  logger_data includes
  * additional file name and line number information that a logger may use.  logger_data is versioned
  * for backwards compatibility.
+ * This assumes that loggability has already been checked through __android_log_is_loggable().
+ * Higher level logging libraries, such as libbase, first check loggability, then format their
+ * buffers, then pass the message to liblog via this function, and therefore we do not want to
+ * duplicate the loggability check here.
  */
 void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg);
 
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 61b6fce..1783854 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -185,15 +185,6 @@
       errno = save_errno;
       return -EINVAL;
     }
-  } else {
-    int prio = *static_cast<int*>(vec[0].iov_base);
-    const char* tag = static_cast<const char*>(vec[1].iov_base);
-    size_t len = vec[1].iov_len;
-
-    if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
-      errno = save_errno;
-      return -EPERM;
-    }
   }
 
   ret = LogdWrite(log_id, &ts, vec, nr);
@@ -292,20 +283,35 @@
 }
 
 int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
   __android_log_write_logger_data(&logger_data, msg);
   return 1;
 }
 
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   char buf[LOG_BUF_SIZE];
 
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -313,10 +319,17 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_write(prio, tag, buf);
+  __android_logger_data logger_data = {
+      sizeof(__android_logger_data), LOG_ID_MAIN, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
+  if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+    return 0;
+  }
+
   va_list ap;
   char buf[LOG_BUF_SIZE];
 
@@ -324,7 +337,9 @@
   vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
   va_end(ap);
 
-  return __android_log_buf_write(bufID, prio, tag, buf);
+  __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0};
+  __android_log_write_logger_data(&logger_data, buf);
+  return 1;
 }
 
 void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index 3b6efbb..9fd9fbc 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -75,3 +75,22 @@
         "libgtest_prod",
     ],
 }
+
+cc_test {
+    name: "libstatssocket_test",
+    srcs: ["tests/stats_event_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libgmock",
+        "libstatssocket",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    test_suites: ["device_tests"],
+}
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
new file mode 100644
index 0000000..cf0592c
--- /dev/null
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2019 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 "stats_event.h"
+#include <gtest/gtest.h>
+#include <utils/SystemClock.h>
+
+using std::string;
+using std::vector;
+
+// Side-effect: this function moves the start of the buffer past the read value
+template <class T>
+T readNext(uint8_t** buffer) {
+    T value = *(T*)(*buffer);
+    *buffer += sizeof(T);
+    return value;
+}
+
+void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
+    uint8_t typeHeader = (numAnnotations << 4) | typeId;
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
+}
+
+template <class T>
+void checkScalar(uint8_t** buffer, T expectedValue) {
+    EXPECT_EQ(readNext<T>(buffer), expectedValue);
+}
+
+void checkString(uint8_t** buffer, const string& expectedString) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    string parsedString((char*)(*buffer), size);
+    EXPECT_EQ(parsedString, expectedString);
+    *buffer += size;  // move buffer past string we just read
+}
+
+void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
+    uint32_t size = readNext<uint32_t>(buffer);
+    vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
+    EXPECT_EQ(parsedByteArray, expectedByteArray);
+    *buffer += size;  // move buffer past byte array we just read
+}
+
+template <class T>
+void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
+    EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
+    EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
+    checkScalar<T>(buffer, annotationValue);
+}
+
+void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
+                   uint32_t atomId) {
+    // All events start with OBJECT_TYPE id.
+    checkTypeHeader(buffer, OBJECT_TYPE);
+
+    // We increment by 2 because the number of elements listed in the
+    // serialization accounts for the timestamp and atom id as well.
+    checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
+
+    // Check timestamp
+    checkTypeHeader(buffer, INT64_TYPE);
+    int64_t timestamp = readNext<int64_t>(buffer);
+    EXPECT_GE(timestamp, startTime);
+    EXPECT_LE(timestamp, endTime);
+
+    // Check atom id
+    checkTypeHeader(buffer, INT32_TYPE);
+    checkScalar(buffer, atomId);
+}
+
+TEST(StatsEventTest, TestScalars) {
+    uint32_t atomId = 100;
+    int32_t int32Value = -5;
+    int64_t int64Value = -2 * android::elapsedRealtimeNano();
+    float floatValue = 2.0;
+    bool boolValue = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_int32(event, int32Value);
+    stats_event_write_int64(event, int64Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_write_bool(event, boolValue);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
+
+    // check int32 element
+    checkTypeHeader(&buffer, INT32_TYPE);
+    checkScalar(&buffer, int32Value);
+
+    // check int64 element
+    checkTypeHeader(&buffer, INT64_TYPE);
+    checkScalar(&buffer, int64Value);
+
+    // check float element
+    checkTypeHeader(&buffer, FLOAT_TYPE);
+    checkScalar(&buffer, floatValue);
+
+    // check bool element
+    checkTypeHeader(&buffer, BOOL_TYPE);
+    checkScalar(&buffer, boolValue);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestStrings) {
+    uint32_t atomId = 100;
+    string str = "test_string";
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_string8(event, str.c_str());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, STRING_TYPE);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestByteArrays) {
+    uint32_t atomId = 100;
+    vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_byte_array(event, message.data(), message.size());
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+    checkByteArray(&buffer, message);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAttributionChains) {
+    uint32_t atomId = 100;
+
+    uint8_t numNodes = 50;
+    uint32_t uids[numNodes];
+    vector<string> tags(numNodes);  // storage that cTag elements point to
+    const char* cTags[numNodes];
+    for (int i = 0; i < (int)numNodes; i++) {
+        uids[i] = i;
+        tags.push_back("test" + std::to_string(i));
+        cTags[i] = tags[i].c_str();
+    }
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_attribution_chain(event, uids, cTags, numNodes);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
+    checkScalar(&buffer, numNodes);
+    for (int i = 0; i < numNodes; i++) {
+        checkScalar(&buffer, uids[i]);
+        checkString(&buffer, tags[i]);
+    }
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestKeyValuePairs) {
+    uint32_t atomId = 100;
+
+    uint8_t numPairs = 4;
+    struct key_value_pair pairs[numPairs];
+    pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = -1};
+    pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
+    pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 5.5};
+    string str = "test_key_value_pair_string";
+    pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, atomId);
+    stats_event_write_key_value_pairs(event, pairs, numPairs);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, KEY_VALUE_PAIRS_TYPE);
+    checkScalar(&buffer, numPairs);
+
+    // first pair
+    checkScalar(&buffer, pairs[0].key);
+    checkTypeHeader(&buffer, pairs[0].valueType);
+    checkScalar(&buffer, pairs[0].int32Value);
+
+    // second pair
+    checkScalar(&buffer, pairs[1].key);
+    checkTypeHeader(&buffer, pairs[1].valueType);
+    checkScalar(&buffer, pairs[1].int64Value);
+
+    // third pair
+    checkScalar(&buffer, pairs[2].key);
+    checkTypeHeader(&buffer, pairs[2].valueType);
+    checkScalar(&buffer, pairs[2].floatValue);
+
+    // fourth pair
+    checkScalar(&buffer, pairs[3].key);
+    checkTypeHeader(&buffer, pairs[3].valueType);
+    checkString(&buffer, str);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestAnnotations) {
+    uint32_t atomId = 100;
+
+    // first element information
+    bool boolValue = false;
+    uint8_t boolAnnotation1Id = 1;
+    uint8_t boolAnnotation2Id = 2;
+    bool boolAnnotation1Value = true;
+    int32_t boolAnnotation2Value = 3;
+
+    // second element information
+    float floatValue = -5.0;
+    uint8_t floatAnnotation1Id = 3;
+    uint8_t floatAnnotation2Id = 4;
+    int32_t floatAnnotation1Value = 8;
+    bool floatAnnotation2Value = false;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    stats_event_write_bool(event, boolValue);
+    stats_event_add_bool_annotation(event, boolAnnotation1Id, boolAnnotation1Value);
+    stats_event_add_int32_annotation(event, boolAnnotation2Id, boolAnnotation2Value);
+    stats_event_write_float(event, floatValue);
+    stats_event_add_int32_annotation(event, floatAnnotation1Id, floatAnnotation1Value);
+    stats_event_add_bool_annotation(event, floatAnnotation2Id, floatAnnotation2Value);
+    stats_event_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = stats_event_get_buffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
+
+    // check first element
+    checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, boolValue);
+    checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
+    checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
+
+    // check second element
+    checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
+    checkScalar(&buffer, floatValue);
+    checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
+    checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(stats_event_get_errors(event), 0);
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestNoAtomIdError) {
+    struct stats_event* event = stats_event_obtain();
+    // Don't set the atom id in order to trigger the error.
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_NO_ATOM_ID, 0);
+
+    stats_event_release(event);
+}
+
+TEST(StatsEventTest, TestOverflowError) {
+    struct stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, 100);
+    // Add 1000 int32s to the event. Each int32 takes 5 bytes so this will
+    // overflow the 4068 byte buffer.
+    for (int i = 0; i < 1000; i++) {
+        stats_event_write_int32(event, 0);
+    }
+    stats_event_build(event);
+
+    uint32_t errors = stats_event_get_errors(event);
+    EXPECT_NE(errors | ERROR_OVERFLOW, 0);
+
+    stats_event_release(event);
+}
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index ec977e1..ee6c8a5 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -70,30 +70,28 @@
   // This also works:
   //   f0000-f2000 0 r-- /system/lib/libc.so
   //   f2000-f3000 2000 rw- /system/lib/libc.so
-  MapInfo* map_start = nullptr;
+  // It is also possible to see empty maps after the read-only like so:
+  //   f0000-f1000 0 r-- /system/lib/libc.so
+  //   f1000-f2000 0 ---
+  //   f2000-f3000 1000 r-x /system/lib/libc.so
+  //   f3000-f4000 2000 rw- /system/lib/libc.so
+  MapInfo* map_zero = nullptr;
   for (const auto& info : *maps) {
-    if (map_start != nullptr && map_start->name == info->name) {
-      if (info->offset != 0 &&
-          (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
-        Elf* elf = map_start->GetElf(memory_, arch());
-        uint64_t ptr;
-        if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
-          uint64_t offset_end = info->offset + info->end - info->start;
-          if (ptr >= info->offset && ptr < offset_end) {
-            ptr = info->start + ptr - info->offset;
-            if (ReadVariableData(ptr)) {
-              break;
-            }
+    if (info->offset != 0 && (info->flags & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+        map_zero != nullptr && Searchable(info->name) && info->name == map_zero->name) {
+      Elf* elf = map_zero->GetElf(memory_, arch());
+      uint64_t ptr;
+      if (elf->GetGlobalVariableOffset(variable, &ptr) && ptr != 0) {
+        uint64_t offset_end = info->offset + info->end - info->start;
+        if (ptr >= info->offset && ptr < offset_end) {
+          ptr = info->start + ptr - info->offset;
+          if (ReadVariableData(ptr)) {
+            break;
           }
         }
-        map_start = nullptr;
       }
-    } else {
-      map_start = nullptr;
-    }
-    if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
-        Searchable(info->name)) {
-      map_start = info.get();
+    } else if (info->offset == 0 && !info->name.empty()) {
+      map_zero = info.get();
     }
   }
 }
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 0dd3af6..477cf8e 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -64,7 +64,11 @@
                        "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
                        "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
                        "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
-                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"));
+                       "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+                       "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+                       "501000-502000 ---p 0000000 00:00 0\n"
+                       "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+                       "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
     ASSERT_TRUE(maps_->Parse());
 
     // Global variable in a section that is not readable.
@@ -81,6 +85,11 @@
     map_info = maps_->Get(kMapGlobal);
     ASSERT_TRUE(map_info != nullptr);
     CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+    // Global variable set in this map, but there is an empty map before rw map.
+    map_info = maps_->Get(kMapGlobalAfterEmpty);
+    ASSERT_TRUE(map_info != nullptr);
+    CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
   }
 
   void SetUp() override {
@@ -102,6 +111,8 @@
   static constexpr size_t kMapGlobalRw = 6;
   static constexpr size_t kMapDexFileEntries = 7;
   static constexpr size_t kMapDexFiles = 8;
+  static constexpr size_t kMapGlobalAfterEmpty = 9;
+  static constexpr size_t kMapDexFilesAfterEmpty = 12;
 
   std::shared_ptr<Memory> process_memory_;
   MemoryFake* memory_;
@@ -328,4 +339,18 @@
   EXPECT_EQ(0x123U, method_offset);
 }
 
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+  std::string method_name = "nothing";
+  uint64_t method_offset = 0x124;
+  MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+  WriteDescriptor32(0x503800, 0x506000);
+  WriteEntry32(0x506000, 0, 0, 0x510000);
+  WriteDex(0x510000);
+
+  dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+  EXPECT_EQ("Main.<init>", method_name);
+  EXPECT_EQ(0U, method_offset);
+}
+
 }  // namespace unwindstack
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 5821379..a9d0ed0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -67,6 +67,11 @@
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
 endif
 
+EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
+ifeq ($(CLANG_COVERAGE),true)
+  EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+endif
+
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -147,6 +152,7 @@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 50005d9..fdaaf1a 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -15,4 +15,5 @@
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
+    %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
     %EXPORT_GLOBAL_HWASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6a6169c..e575808 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -173,7 +173,7 @@
     mkdir /mnt/user/0/emulated/0 0755 root root
 
     # Prepare directories for pass through processes
-    mkdir /mnt/pass_through 0755 root root
+    mkdir /mnt/pass_through 0700 root root
     mkdir /mnt/pass_through/0 0755 root root
     mkdir /mnt/pass_through/0/self 0755 root root
     mkdir /mnt/pass_through/0/emulated 0755 root root