Introduce a new wrapper for log_msg -- LogEvent

It stores all of the parsed fields in a single vector, and
provides accessor methods to get at fields by index with
their correct type.

Test: statsd_test
Change-Id: I4fa94e4ce52db3ac87f19b62f9c85398de6e8145
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
new file mode 100644
index 0000000..9fa2baf
--- /dev/null
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 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 "logd/LogEvent.h"
+
+#include <sstream>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::ostringstream;
+
+LogEvent::LogEvent(const log_msg& msg) {
+    init(msg);
+}
+
+LogEvent::LogEvent(int64_t timestampNs, android_log_event_list* reader) {
+    init(timestampNs, reader);
+}
+
+LogEvent::~LogEvent() {
+}
+
+/**
+ * The elements of each log event are stored as a vector of android_log_list_elements.
+ * The goal is to do as little preprocessing as possible, because we read a tiny fraction
+ * of the elements that are written to the log.
+ */
+void LogEvent::init(const log_msg& msg) {
+
+    android_log_event_list list(const_cast<log_msg&>(msg));
+    init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &list);
+}
+
+void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
+    mTimestampNs = timestampNs;
+    mTagId = reader->tag();
+
+    mElements.clear();
+    android_log_list_element elem;
+
+    // TODO: The log is actually structured inside one list.  This is convenient
+    // because we'll be able to use it to put the attribution (WorkSource) block first
+    // without doing our own tagging scheme.  Until that change is in, just drop the
+    // list-related log elements and the order we get there is our index-keyed data
+    // structure.
+    do {
+        elem = android_log_read_next(reader->context());
+        switch ((int)elem.type) {
+            case EVENT_TYPE_INT:
+            case EVENT_TYPE_FLOAT:
+            case EVENT_TYPE_STRING:
+            case EVENT_TYPE_LONG:
+                mElements.push_back(elem);
+                break;
+            case EVENT_TYPE_LIST:
+                break;
+            case EVENT_TYPE_LIST_STOP:
+                break;
+            case EVENT_TYPE_UNKNOWN:
+                break;
+            default:
+                break;
+        }
+    } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
+}
+
+int64_t LogEvent::GetLong(size_t key, status_t* err) const {
+    if (key < 1 || (key - 1)  >= mElements.size()) {
+        *err = BAD_INDEX;
+        return 0;
+    }
+    key--;
+    const android_log_list_element& elem = mElements[key];
+    if (elem.type == EVENT_TYPE_INT) {
+        return elem.data.int32;
+    } else if (elem.type == EVENT_TYPE_LONG) {
+        return elem.data.int64;
+    } else if (elem.type == EVENT_TYPE_FLOAT) {
+        return (int64_t)elem.data.float32;
+    } else {
+        *err = BAD_TYPE;
+        return 0;
+    }
+}
+
+const char* LogEvent::GetString(size_t key, status_t* err) const {
+    if (key < 1 || (key - 1)  >= mElements.size()) {
+        *err = BAD_INDEX;
+        return NULL;
+    }
+    key--;
+    const android_log_list_element& elem = mElements[key];
+    if (elem.type != EVENT_TYPE_STRING) {
+        *err = BAD_TYPE;
+        return NULL;
+    }
+    return elem.data.string;
+}
+
+bool LogEvent::GetBool(size_t key, status_t* err) const {
+    if (key < 1 || (key - 1)  >= mElements.size()) {
+        *err = BAD_INDEX;
+        return 0;
+    }
+    key--;
+    const android_log_list_element& elem = mElements[key];
+    if (elem.type == EVENT_TYPE_INT) {
+        return elem.data.int32 != 0;
+    } else if (elem.type == EVENT_TYPE_LONG) {
+        return elem.data.int64 != 0;
+    } else if (elem.type == EVENT_TYPE_FLOAT) {
+        return elem.data.float32 != 0;
+    } else {
+        *err = BAD_TYPE;
+        return 0;
+    }
+}
+
+float LogEvent::GetFloat(size_t key, status_t* err) const {
+    if (key < 1 || (key - 1)  >= mElements.size()) {
+        *err = BAD_INDEX;
+        return 0;
+    }
+    key--;
+    const android_log_list_element& elem = mElements[key];
+    if (elem.type == EVENT_TYPE_INT) {
+        return (float)elem.data.int32;
+    } else if (elem.type == EVENT_TYPE_LONG) {
+        return (float)elem.data.int64;
+    } else if (elem.type == EVENT_TYPE_FLOAT) {
+        return elem.data.float32;
+    } else {
+        *err = BAD_TYPE;
+        return 0;
+    }
+}
+
+string LogEvent::ToString() const {
+    ostringstream result;
+    result << "{ " << mTimestampNs << " (" << mTagId << ")";
+    const size_t N = mElements.size();
+    for (size_t i=0; i<N; i++) {
+        result << " ";
+        result << (i + 1);
+        result << "->";
+        const android_log_list_element& elem = mElements[i];
+        if (elem.type == EVENT_TYPE_INT) {
+            result << elem.data.int32;
+        } else if (elem.type == EVENT_TYPE_LONG) {
+            result << elem.data.int64;
+        } else if (elem.type == EVENT_TYPE_FLOAT) {
+            result << elem.data.float32;
+        } else if (elem.type == EVENT_TYPE_STRING) {
+            result << elem.data.string;
+        }
+    }
+    result << " }";
+    return result.str();
+}
+
+void LogEvent::ToProto(EventMetricData* out) const {
+    // TODO: Implement this when we have the ProtoOutputStream version.
+
+    // set timestamp of the event.
+    out->set_timestamp_nanos(mTimestampNs);
+
+    // uint64_t token = proto->StartObject(EventMetricData.FIELD);
+    const size_t N = mElements.size();
+    for (size_t i=0; i<N; i++) {
+        const int key = i + 1;
+
+        const android_log_list_element& elem = mElements[i];
+        if (elem.type == EVENT_TYPE_INT) {
+            // proto->Write(key, elem.data.int32);
+        } else if (elem.type == EVENT_TYPE_LONG) {
+            // proto->Write(key, elem.data.int64);
+        } else if (elem.type == EVENT_TYPE_FLOAT) {
+            // proto->Write(key, elem.data.float32);
+        } else if (elem.type == EVENT_TYPE_STRING) {
+            // proto->Write(key, elem.data.string);
+        }
+    }
+
+    //proto->EndObject(token);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android