Handle null values in AStatsEvent

- Treat NULL strings and byte arrays as zero-length strings/byte arrays.

This maintains backwards compatibility with legacy protocol which
handled nulls.

Bug: 155363739
Test: atest libstatssocket_test
Change-Id: I484b7c968270ae7228ea53bb97c7e6a2dbebe983
Merged-In: I484b7c968270ae7228ea53bb97c7e6a2dbebe983
(cherry picked from commit 7a19241f21ab47b7be70d47dd5297effed900280)
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
index f3e8087..dcd34aa 100644
--- a/libstats/socket/stats_event.c
+++ b/libstats/socket/stats_event.c
@@ -232,14 +232,19 @@
 
 void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
     start_field(event, BYTE_ARRAY_TYPE);
+    if (buf == NULL) {
+        numBytes = 0;
+    }
     append_int32(event, numBytes);
-    append_byte_array(event, buf, numBytes);
+    if (numBytes > 0) {
+        append_byte_array(event, buf, numBytes);
+    }
 }
 
 // Value is assumed to be encoded using UTF8
 void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
     start_field(event, STRING_TYPE);
-    append_string(event, value);
+    append_string(event, value == NULL ? "" : value);
 }
 
 // Tags are assumed to be encoded using UTF8
@@ -255,7 +260,7 @@
 
     for (uint8_t i = 0; i < numNodes; i++) {
         append_int32(event, uids[i]);
-        append_string(event, tags[i]);
+        append_string(event, tags[i] == NULL ? "" : tags[i]);
     }
 }
 
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
index 9a1fac8..2f9ccdc 100644
--- a/libstats/socket/tests/stats_event_test.cpp
+++ b/libstats/socket/tests/stats_event_test.cpp
@@ -183,6 +183,31 @@
     AStatsEvent_release(event);
 }
 
+TEST(StatsEventTest, TestNullString) {
+    uint32_t atomId = 100;
+    char* str = nullptr;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, atomId);
+    AStatsEvent_writeString(event, str);
+    AStatsEvent_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, STRING_TYPE);
+    checkString(&buffer, "");
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+    AStatsEvent_release(event);
+}
+
 TEST(StatsEventTest, TestByteArrays) {
     uint32_t atomId = 100;
     vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
@@ -208,6 +233,32 @@
     AStatsEvent_release(event);
 }
 
+TEST(StatsEventTest, TestNullByteArrays) {
+    uint32_t atomId = 100;
+    uint8_t* buf = nullptr;
+    vector<uint8_t> message;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, atomId);
+    AStatsEvent_writeByteArray(event, buf, 2);
+    AStatsEvent_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = AStatsEvent_getBuffer(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(AStatsEvent_getErrors(event), 0);
+    AStatsEvent_release(event);
+}
+
 TEST(StatsEventTest, TestAttributionChains) {
     uint32_t atomId = 100;
 
@@ -217,8 +268,13 @@
     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();
+        if (0 == i) {
+            tags.push_back("");
+            cTags[i] = nullptr;
+        } else {
+            tags.push_back("test" + std::to_string(i));
+            cTags[i] = tags[i].c_str();
+        }
     }
 
     int64_t startTime = android::elapsedRealtimeNano();