Merge "Use activity-alias to show usb debugging dialog"
diff --git a/api/current.txt b/api/current.txt
index eed8464..3a82ed2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -73,7 +73,6 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
@@ -6874,6 +6873,7 @@
     method public java.lang.String getIdEntry();
     method public java.lang.String getIdPackage();
     method public java.lang.String getIdType();
+    method public int getImportantForAutofill();
     method public int getInputType();
     method public int getLeft();
     method public android.os.LocaleList getLocaleList();
@@ -23371,6 +23371,7 @@
     method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
     method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException;
     method public int getMaxHdcpLevel();
+    method public static int getMaxSecurityLevel();
     method public int getMaxSessionCount();
     method public android.os.PersistableBundle getMetrics();
     method public int getOpenSessionCount();
@@ -23384,6 +23385,7 @@
     method public static boolean isCryptoSchemeSupported(java.util.UUID);
     method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
     method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
+    method public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
     method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
     method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
@@ -23399,7 +23401,6 @@
     method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
     method public void setPropertyByteArray(java.lang.String, byte[]);
     method public void setPropertyString(java.lang.String, java.lang.String);
-    method public void setSecurityLevel(byte[], int);
     field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
     field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
     field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
@@ -48420,6 +48421,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setHtmlInfo(android.view.ViewStructure.HtmlInfo);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public void setImportantForAutofill(int);
     method public abstract void setInputType(int);
     method public abstract void setLocaleList(android.os.LocaleList);
     method public abstract void setLongClickable(boolean);
@@ -50372,7 +50374,7 @@
     method public final void logSelectionModifiedEvent(int, int);
     method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification);
     method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection);
-    method public final void logSelectionStartedEvent(int);
+    method public final void logSelectionStartedEvent(int, int);
     method public abstract void writeEvent(android.view.textclassifier.logging.SelectionEvent);
     field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff
     field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000
@@ -50402,6 +50404,7 @@
     method public int getEventIndex();
     method public long getEventTime();
     method public int getEventType();
+    method public int getInvocationMethod();
     method public java.lang.String getPackageName();
     method public java.lang.String getSessionId();
     method public java.lang.String getSignature();
@@ -50426,6 +50429,8 @@
     field public static final int EVENT_SELECTION_STARTED = 1; // 0x1
     field public static final int EVENT_SMART_SELECTION_MULTI = 4; // 0x4
     field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3
+    field public static final int INVOCATION_LINK = 2; // 0x2
+    field public static final int INVOCATION_MANUAL = 1; // 0x1
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 3dbb333..71df272 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3806,6 +3806,7 @@
     method public void resume();
     method public void suspend();
     method public boolean unbind();
+    method public boolean verifyPayloadMetadata(java.lang.String);
   }
 
   public static final class UpdateEngine.ErrorCodeConstants {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8eea944..67b9089 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -19,11 +19,9 @@
     ../../core/java/android/os/IStatsManager.aidl \
     src/stats_log.proto \
     src/statsd_config.proto \
-    src/statsd_internal.proto \
     src/atoms.proto \
-    src/field_util.cpp \
+    src/FieldValue.cpp \
     src/stats_log_util.cpp \
-    src/dimension.cpp \
     src/anomaly/AnomalyMonitor.cpp \
     src/anomaly/AnomalyTracker.cpp \
     src/anomaly/DurationAnomalyTracker.cpp \
@@ -172,7 +170,6 @@
 
 LOCAL_SRC_FILES := \
     $(statsd_common_src) \
-    tests/dimension_test.cpp \
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
@@ -184,6 +181,7 @@
     tests/MetricsManager_test.cpp \
     tests/StatsLogProcessor_test.cpp \
     tests/UidMap_test.cpp \
+    tests/FieldValue_test.cpp \
     tests/condition/CombinationConditionTracker_test.cpp \
     tests/condition/SimpleConditionTracker_test.cpp \
     tests/metrics/OringDurationTracker_test.cpp \
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
new file mode 100644
index 0000000..7b0b69a
--- /dev/null
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define DEBUG false
+#include "Log.h"
+#include "FieldValue.h"
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool Field::matches(const Matcher& matcher) const {
+    if (mTag != matcher.mMatcher.getTag()) {
+        return false;
+    }
+    if ((mField & matcher.mMask) == matcher.mMatcher.getField()) {
+        return true;
+    }
+
+    return false;
+};
+
+void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask,
+                           std::vector<Matcher>* output) {
+    if (depth > kMaxLogDepth) {
+        ALOGE("depth > 2");
+        return;
+    }
+
+    pos[depth] = matcher.field();
+    mask[depth] = 0x7f;
+
+    if (matcher.has_position()) {
+        depth++;
+        if (depth > 2) {
+            return;
+        }
+        switch (matcher.position()) {
+            case Position::ANY:
+                pos[depth] = 0;
+                mask[depth] = 0;
+                break;
+            case Position::FIRST:
+                pos[depth] = 1;
+                mask[depth] = 0x7f;
+                break;
+            case Position::LAST:
+                pos[depth] = 0x80;
+                mask[depth] = 0x80;
+                break;
+            case Position::POSITION_UNKNOWN:
+                pos[depth] = 0;
+                mask[depth] = 0;
+                break;
+        }
+    }
+
+    if (matcher.child_size() == 0) {
+        output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth)));
+        Matcher matcher = Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth));
+    } else {
+        for (const auto& child : matcher.child()) {
+            translateFieldMatcher(tag, child, depth + 1, pos, mask, output);
+        }
+    }
+}
+
+void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output) {
+    int pos[] = {1, 1, 1};
+    int mask[] = {0x7f, 0x7f, 0x7f};
+    int tag = matcher.field();
+    for (const auto& child : matcher.child()) {
+        translateFieldMatcher(tag, child, 0, pos, mask, output);
+    }
+}
+
+bool isAttributionUidField(const FieldValue& value) {
+    int field = value.mField.getField() & 0xff007f;
+    if (field == 0x10001 && value.mValue.getType() == INT) {
+        return true;
+    }
+    return false;
+}
+
+bool isAttributionUidField(const Field& field, const Value& value) {
+    int f = field.getField() & 0xff007f;
+    if (f == 0x10001 && value.getType() == INT) {
+        return true;
+    }
+    return false;
+}
+
+Value::Value(const Value& from) {
+    type = from.getType();
+    switch (type) {
+        case INT:
+            int_value = from.int_value;
+            break;
+        case LONG:
+            long_value = from.long_value;
+            break;
+        case FLOAT:
+            float_value = from.float_value;
+            break;
+        case STRING:
+            str_value = from.str_value;
+            break;
+    }
+}
+
+std::string Value::toString() const {
+    switch (type) {
+        case INT:
+            return std::to_string(int_value) + "[I]";
+        case LONG:
+            return std::to_string(long_value) + "[L]";
+        case FLOAT:
+            return std::to_string(float_value) + "[F]";
+        case STRING:
+            return str_value + "[S]";
+    }
+}
+
+bool Value::operator==(const Value& that) const {
+    if (type != that.getType()) return false;
+
+    switch (type) {
+        case INT:
+            return int_value == that.int_value;
+        case LONG:
+            return long_value == that.long_value;
+        case FLOAT:
+            return float_value == that.float_value;
+        case STRING:
+            return str_value == that.str_value;
+    }
+}
+
+bool Value::operator!=(const Value& that) const {
+    if (type != that.getType()) return true;
+    switch (type) {
+        case INT:
+            return int_value != that.int_value;
+        case LONG:
+            return long_value != that.long_value;
+        case FLOAT:
+            return float_value != that.float_value;
+        case STRING:
+            return str_value != that.str_value;
+    }
+}
+
+bool Value::operator<(const Value& that) const {
+    if (type != that.getType()) return type < that.getType();
+
+    switch (type) {
+        case INT:
+            return int_value < that.int_value;
+        case LONG:
+            return long_value < that.long_value;
+        case FLOAT:
+            return float_value < that.float_value;
+        case STRING:
+            return str_value < that.str_value;
+        default:
+            return false;
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
new file mode 100644
index 0000000..7484108
--- /dev/null
+++ b/cmds/statsd/src/FieldValue.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#pragma once
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class HashableDimensionKey;
+struct Matcher;
+struct Field;
+struct FieldValue;
+
+const int32_t kAttributionField = 1;
+const int32_t kMaxLogDepth = 2;
+const int32_t kLastBitMask = 0x80;
+const int32_t kClearLastBitDeco = 0x7f;
+
+enum Type { INT, LONG, FLOAT, STRING };
+
+
+static int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) {
+    int32_t field = 0;
+    for (int32_t i = 0; i <= depth; i++) {
+        int32_t shiftBits = 8 * (kMaxLogDepth - i);
+        field |= (pos[i] << shiftBits);
+    }
+
+    if (includeDepth) {
+        field |= (depth << 24);
+    }
+    return field;
+}
+
+static int32_t encodeMatcherMask(int32_t mask[], int32_t depth) {
+    return getEncodedField(mask, depth, false) | 0xff000000;
+}
+
+// Get the encoded field for a leaf with a [field] number at depth 0;
+static int32_t getSimpleField(size_t field) {
+    return ((int32_t)field << 8 * 2);
+}
+
+/**
+ * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
+ * proto.
+ * [mTag]: the atom id.
+ * [mField]: encoded path from the root (atom) to leaf.
+ *
+ * For example:
+ * WakeLockStateChanged {
+ *    repeated AttributionNode = 1;
+ *    int state = 2;
+ *    string tag = 3;
+ * }
+ * Read from logd, the items are structured as below:
+ * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"]
+ *
+ * When we read through the list, we will encode each field in a 32bit integer.
+ * 8bit segments   |--------|--------|--------|--------|
+ *                    Depth   field0 [L]field1 [L]field1
+ *
+ *  The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2.
+ *  The following 3 8-bit are for the item's position at each level.
+ *  The first bit of each 8bits field is reserved to mark if the item is the last item at that level
+ *  this is to make matching easier later.
+ *
+ *  The above wakelock event is translated into FieldValue pairs.
+ *  0x02010101->1000
+ *  0x02010182->tag
+ *  0x02018201->2000
+ *  0x02018282->tag2
+ *  0x00020000->2
+ *  0x00030000->"hello"
+ *
+ *  This encoding is the building block for the later operations.
+ *  Please see the definition for Matcher below to see how the matching is done.
+ */
+struct Field {
+private:
+    int32_t mTag;
+    int32_t mField;
+
+public:
+    Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
+        mField = getEncodedField(pos, depth, true);
+    }
+
+    Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) {
+    }
+
+    Field(int32_t tag, int32_t field) : mTag(tag), mField(field){};
+
+    inline void setField(int32_t field) {
+        mField = field;
+    }
+
+    inline void setTag(int32_t tag) {
+        mTag = tag;
+    }
+
+    inline void decorateLastPos(int32_t depth) {
+        int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
+        mField |= mask;
+    }
+
+    inline int32_t getTag() const {
+        return mTag;
+    }
+
+    inline int32_t getDepth() const {
+        return (mField >> 24);
+    }
+
+    inline int32_t getPath(int32_t depth) const {
+        if (depth > 2 || depth < 0) return 0;
+
+        int32_t field = (mField & 0x00ffffff);
+        int32_t mask = 0xffffffff;
+        return (field & (mask << 8 * (kMaxLogDepth - depth)));
+    }
+
+    inline int32_t getPrefix(int32_t depth) const {
+        if (depth == 0) return 0;
+        return getPath(depth - 1);
+    }
+
+    inline int32_t getField() const {
+        return mField;
+    }
+
+    inline int32_t getRawPosAtDepth(int32_t depth) const {
+        int32_t field = (mField & 0x00ffffff);
+        int32_t shift = 8 * (kMaxLogDepth - depth);
+        int32_t mask = 0xff << shift;
+
+        return (field & mask) >> shift;
+    }
+
+    inline int32_t getPosAtDepth(int32_t depth) const {
+        return getRawPosAtDepth(depth) & kClearLastBitDeco;
+    }
+
+    // Check if the first bit of the 8-bit segment for depth is 1
+    inline bool isLastPos(int32_t depth) const {
+        int32_t field = (mField & 0x00ffffff);
+        int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
+        return (field & mask) != 0;
+    }
+
+    // if the 8-bit segment is all 0's
+    inline bool isAnyPosMatcher(int32_t depth) const {
+        return getDepth() >= depth && getRawPosAtDepth(depth) == 0;
+    }
+    // if the 8bit is 0x80 (1000 0000)
+    inline bool isLastPosMatcher(int32_t depth) const {
+        return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask;
+    }
+
+    inline bool operator==(const Field& that) const {
+        return mTag == that.getTag() && mField == that.getField();
+    };
+
+    inline bool operator!=(const Field& that) const {
+        return mTag != that.getTag() || mField != that.getField();
+    };
+
+    bool operator<(const Field& that) const {
+        if (mTag != that.getTag()) {
+            return mTag < that.getTag();
+        }
+
+        if (mField != that.getField()) {
+            return mField < that.getField();
+        }
+
+        return false;
+    }
+    bool matches(const Matcher& that) const;
+};
+
+/**
+ * Matcher represents a leaf matcher in the FieldMatcher in statsd_config.
+ *
+ * It contains all information needed to match one or more leaf node.
+ * All information is encoded in a Field(2 ints) and a bit mask(1 int).
+ *
+ * For example, to match the first/any/last uid field in attribution chain in Atom 10,
+ * we have the following FieldMatcher in statsd_config
+ *    FieldMatcher {
+ *        field:10
+ *         FieldMatcher {
+ *              field:1
+ *              position: any/last/first
+ *              FieldMatcher {
+ *                  field:1
+ *              }
+ *          }
+ *     }
+ *
+ * We translate the FieldMatcher into a Field, and mask
+ * First: [Matcher Field] 0x02010101  [Mask]0xffff7fff
+ * Last:  [Matcher Field] 0x02018001  [Mask]0xffff80ff
+ * Any:   [Matcher Field] 0x02010001  [Mask]0xffff00ff
+ *
+ * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
+ * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
+ * equal. Nothing can beat the performance of this matching algorithm.
+ *
+ * TODO: ADD EXAMPLE HERE.
+ */
+struct Matcher {
+    Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
+
+    const Field mMatcher;
+    const int32_t mMask;
+
+    bool hasAnyPositionMatcher(int* prefix) const {
+        if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(2) == 0) {
+            (*prefix) = mMatcher.getPrefix(2);
+            return true;
+        }
+        return false;
+    }
+};
+
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct Value {
+    Value(int32_t v) {
+        int_value = v;
+        type = INT;
+    }
+
+    Value(int64_t v) {
+        long_value = v;
+        type = LONG;
+    }
+
+    Value(float v) {
+        float_value = v;
+        type = FLOAT;
+    }
+
+    Value(const std::string& v) {
+        str_value = v;
+        type = STRING;
+    }
+
+    void setInt(int32_t v) {
+        int_value = v;
+        type = INT;
+    }
+
+    void setLong(int64_t v) {
+        long_value = v;
+        type = LONG;
+    }
+
+    union {
+        int32_t int_value;
+        int64_t long_value;
+        float float_value;
+    };
+    std::string str_value;
+
+    Type type;
+
+    std::string toString() const;
+
+    Type getType() const {
+        return type;
+    }
+
+    Value(const Value& from);
+
+    bool operator==(const Value& that) const;
+    bool operator!=(const Value& that) const;
+
+    bool operator<(const Value& that) const;
+
+private:
+    Value(){};
+};
+
+/**
+ * Represents a log item, or a dimension item (They are essentially the same).
+ */
+struct FieldValue {
+    FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
+    }
+    bool operator==(const FieldValue& that) const {
+        return mField == that.mField && mValue == that.mValue;
+    }
+    bool operator!=(const FieldValue& that) const {
+        return mField != that.mField || mValue != that.mValue;
+    }
+    bool operator<(const FieldValue& that) const {
+        if (mField != that.mField) {
+            return mField < that.mField;
+        }
+
+        if (mValue != that.mValue) {
+            return mValue < that.mValue;
+        }
+
+        return false;
+    }
+
+    Field mField;
+    Value mValue;
+};
+
+bool isAttributionUidField(const FieldValue& value);
+
+void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
+
+bool isAttributionUidField(const Field& field, const Value& value);
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 8483b02..68e2176 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -13,177 +13,253 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
 
 #include "HashableDimensionKey.h"
-#include "dimension.h"
+#include "FieldValue.h"
 
 namespace android {
 namespace os {
 namespace statsd {
+using std::vector;
 
-android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value) {
-    android::hash_t hash = seed;
-    hash = android::JenkinsHashMix(hash, android::hash_type(value.field()));
-
-    hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case()));
-    switch (value.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            hash = android::JenkinsHashMix(
-                    hash,
-                    static_cast<uint32_t>(std::hash<std::string>()(value.value_str())));
-            break;
-        case DimensionsValue::ValueCase::kValueInt:
-            hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int()));
-            break;
-        case DimensionsValue::ValueCase::kValueLong:
-            hash = android::JenkinsHashMix(
-                    hash, android::hash_type(static_cast<int64_t>(value.value_long())));
-            break;
-        case DimensionsValue::ValueCase::kValueBool:
-            hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool()));
-            break;
-        case DimensionsValue::ValueCase::kValueFloat: {
-            float floatVal = value.value_float();
-            hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
-            break;
-        }
-        case DimensionsValue::ValueCase::kValueTuple: {
-            hash = android::JenkinsHashMix(hash, android::hash_type(
-                value.value_tuple().dimensions_value_size()));
-            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
-                hash = android::JenkinsHashMix(
-                    hash,
-                    hashDimensionsValue(value.value_tuple().dimensions_value(i)));
+android::hash_t hashDimension(const HashableDimensionKey& value) {
+    android::hash_t hash = 0;
+    for (const auto& fieldValue : value.getValues()) {
+        hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField()));
+        hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag()));
+        hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType()));
+        switch (fieldValue.mValue.getType()) {
+            case INT:
+                hash = android::JenkinsHashMix(hash,
+                                               android::hash_type(fieldValue.mValue.int_value));
+                break;
+            case LONG:
+                hash = android::JenkinsHashMix(hash,
+                                               android::hash_type(fieldValue.mValue.long_value));
+                break;
+            case STRING:
+                hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()(
+                                                             fieldValue.mValue.str_value)));
+                break;
+            case FLOAT: {
+                float floatVal = fieldValue.mValue.float_value;
+                hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+                break;
             }
-            break;
         }
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            break;
     }
     return JenkinsHashWhiten(hash);
 }
 
-android::hash_t hashDimensionsValue(const DimensionsValue& value) {
-    return hashDimensionsValue(0, value);
-}
-
-android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
-    android::hash_t hash = seed;
-    hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
-    return JenkinsHashWhiten(hash);
-}
-
-using std::string;
-
-string HashableDimensionKey::toString() const {
-    return DimensionsValueToString(getDimensionsValue());
-}
-
-bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
-    if (s1.field() != s2.field()) {
-        return false;
-    }
-    if (s1.value_case() != s2.value_case()) {
-        return false;
-    }
-    switch (s1.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            return (s1.value_str() == s2.value_str());
-        case DimensionsValue::ValueCase::kValueInt:
-            return s1.value_int() == s2.value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return s1.value_long() == s2.value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return s1.value_bool() == s2.value_bool();
-        case DimensionsValue::ValueCase::kValueFloat:
-            return s1.value_float() == s2.value_float();
-        case DimensionsValue::ValueCase::kValueTuple:
-            {
-                if (s1.value_tuple().dimensions_value_size() !=
-                        s2.value_tuple().dimensions_value_size()) {
-                    return false;
-                }
-                bool allMatched = true;
-                for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
-                    allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i),
-                                           s2.value_tuple().dimensions_value(i));
-                }
-                return allMatched;
+// Filter fields using the matchers and output the results as a HashableDimensionKey.
+// Note: HashableDimensionKey is just a wrapper for vector<FieldValue>
+bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
+                  vector<HashableDimensionKey>* output) {
+    output->push_back(HashableDimensionKey());
+    // Top level is only tag id. Now take the real child matchers
+    int prevAnyMatcherPrefix = 0;
+    size_t prevPrevFanout = 0;
+    size_t prevFanout = 0;
+    // For each matcher get matched results.
+    for (const auto& matcher : matcherFields) {
+        vector<FieldValue> matchedResults;
+        for (const auto& value : values) {
+            // TODO: potential optimization here to break early because all fields are naturally
+            // sorted.
+            int32_t filteredField;
+            if (value.mField.matches(matcher)) {
+                matchedResults.push_back(FieldValue(
+                        Field(value.mField.getTag(), (value.mField.getField() & matcher.mMask)),
+                        value.mValue));
             }
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-        default:
-            return true;
-    }
-}
+        }
 
-bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
-    if (s1.field() != s2.field()) {
-        return s1.field() < s2.field();
-    }
-    if (s1.value_case() != s2.value_case()) {
-        return s1.value_case() < s2.value_case();
-    }
-    switch (s1.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            return s1.value_str() < s2.value_str();
-        case DimensionsValue::ValueCase::kValueInt:
-            return s1.value_int() < s2.value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return s1.value_long() < s2.value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return (int)s1.value_bool() < (int)s2.value_bool();
-        case DimensionsValue::ValueCase::kValueFloat:
-            return s1.value_float() < s2.value_float();
-        case DimensionsValue::ValueCase::kValueTuple:
-            {
-                if (s1.value_tuple().dimensions_value_size() !=
-                        s2.value_tuple().dimensions_value_size()) {
-                    return s1.value_tuple().dimensions_value_size() <
-                        s2.value_tuple().dimensions_value_size();
-                }
-                for (int i = 0;  i < s1.value_tuple().dimensions_value_size(); ++i) {
-                    if (EqualsTo(s1.value_tuple().dimensions_value(i),
-                                 s2.value_tuple().dimensions_value(i))) {
-                        continue;
-                    } else {
-                        return LessThan(s1.value_tuple().dimensions_value(i),
-                                        s2.value_tuple().dimensions_value(i));
-                    }
-                }
+        if (matchedResults.size() == 0) {
+            VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
+                   matcher.mMatcher.getField());
+            continue;
+        }
+
+        if (matchedResults.size() == 1) {
+            for (auto& dimension : *output) {
+                dimension.addValue(matchedResults[0]);
+            }
+            prevAnyMatcherPrefix = 0;
+            prevFanout = 0;
+            continue;
+        }
+
+        // All the complexity below is because we support ANY in dimension.
+        bool createFanout = true;
+        // createFanout is true when the matcher doesn't need to follow the prev matcher's
+        // order.
+        // e.g., get (uid, tag) from any position in attribution. because we have translated
+        // it as 2 matchers, they need to follow the same ordering, we can't create a cross
+        // product of all uid and tags.
+        // However, if the 2 matchers have different prefix, they will create a cross product
+        // e.g., [any uid] [any some other repeated field], we will create a cross product for them
+        if (prevAnyMatcherPrefix != 0) {
+            int anyMatcherPrefix = 0;
+            bool isAnyMatcher = matcher.hasAnyPositionMatcher(&anyMatcherPrefix);
+            if (isAnyMatcher && anyMatcherPrefix == prevAnyMatcherPrefix) {
+                createFanout = false;
+            } else {
+                prevAnyMatcherPrefix = anyMatcherPrefix;
+            }
+        }
+
+        // Each matcher should match exact one field, unless position is ANY
+        // When x number of fields matches a matcher, the returned dimension
+        // size is multiplied by x.
+        int oldSize;
+        if (createFanout) {
+            // First create fanout (fanout size is matchedResults.Size which could be one,
+            // which means we do nothing here)
+            oldSize = output->size();
+            for (size_t i = 1; i < matchedResults.size(); i++) {
+                output->insert(output->end(), output->begin(), output->begin() + oldSize);
+            }
+            prevPrevFanout = oldSize;
+            prevFanout = matchedResults.size();
+        } else {
+            // If we should not create fanout, e.g., uid tag from same position should be remain
+            // together.
+            oldSize = prevPrevFanout;
+            if (prevFanout != matchedResults.size()) {
+                // sanity check.
+                ALOGE("2 Any matcher result in different output");
                 return false;
             }
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-        default:
-            return false;
+        }
+        // now add the matched field value to output
+        for (size_t i = 0; i < matchedResults.size(); i++) {
+            for (int j = 0; j < oldSize; j++) {
+                (*output)[i * oldSize + j].addValue(matchedResults[i]);
+            }
+        }
     }
+
+    return output->size() > 0 && (*output)[0].getValues().size() > 0;
+}
+
+void filterGaugeValues(const std::vector<Matcher>& matcherFields,
+                       const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
+    for (const auto& field : matcherFields) {
+        for (const auto& value : values) {
+            int filteredField;
+            if (value.mField.matches(field)) {
+                output->push_back(value);
+            }
+        }
+    }
+}
+
+void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+                              vector<HashableDimensionKey>* conditionDimension) {
+    // Get the dimension first by using dimension from what.
+    filterValues(links.metricFields, event.getValues(), conditionDimension);
+
+    // Then replace the field with the dimension from condition.
+    for (auto& dim : *conditionDimension) {
+        size_t count = dim.getValues().size();
+        if (count != links.conditionFields.size()) {
+            // ALOGE("WTF condition link is bad");
+            return;
+        }
+
+        for (size_t i = 0; i < count; i++) {
+            dim.mutableValue(i)->mField.setField(links.conditionFields[i].mMatcher.getField());
+            dim.mutableValue(i)->mField.setTag(links.conditionFields[i].mMatcher.getTag());
+        }
+    }
+}
+
+bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
+    if (s1.size() != s2.size()) {
+        return s1.size() < s2.size();
+    }
+
+    size_t count = s1.size();
+    for (size_t i = 0; i < count; i++) {
+        if (s1[i] != s2[i]) {
+            return s1[i] < s2[i];
+        }
+    }
+    return false;
 }
 
 bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
-    return EqualsTo(getDimensionsValue(), that.getDimensionsValue());
+    if (mValues.size() != that.getValues().size()) {
+        return false;
+    }
+    size_t count = mValues.size();
+    for (size_t i = 0; i < count; i++) {
+        if (mValues[i] != (that.getValues())[i]) {
+            return false;
+        }
+    }
+    return true;
 };
 
 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
-    return LessThan(getDimensionsValue(), that.getDimensionsValue());
+    return LessThan(getValues(), that.getValues());
 };
 
-string MetricDimensionKey::toString() const {
-    string flattened = mDimensionKeyInWhat.toString();
-    flattened += mDimensionKeyInCondition.toString();
-    return flattened;
+bool HashableDimensionKey::contains(const HashableDimensionKey& that) const {
+    if (mValues.size() < that.getValues().size()) {
+        return false;
+    }
+
+    if (mValues.size() == that.getValues().size()) {
+        return (*this) == that;
+    }
+
+    for (const auto& value : that.getValues()) {
+        bool found = false;
+        for (const auto& myValue : mValues) {
+            if (value.mField == myValue.mField && value.mValue == myValue.mValue) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+string HashableDimensionKey::toString() const {
+    std::string output;
+    for (const auto& value : mValues) {
+        output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(),
+                               value.mValue.toString().c_str());
+    }
+    return output;
 }
 
 bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
     return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
-        mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+           mDimensionKeyInCondition == that.getDimensionKeyInCondition();
 };
 
+string MetricDimensionKey::toString() const {
+    return mDimensionKeyInWhat.toString() + mDimensionKeyInCondition.toString();
+}
+
 bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
-    return toString().compare(that.toString()) < 0;
-};
+    if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) {
+        return true;
+    } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) {
+        return false;
+    }
 
-bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
-    return EqualsTo(s1, s2);
+    return mDimensionKeyInCondition < that.getDimensionKeyInCondition();
 }
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index a31d7a6..89fe317 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -17,44 +17,66 @@
 #pragma once
 
 #include <utils/JenkinsHash.h>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include <vector>
+#include "FieldValue.h"
+#include "android-base/stringprintf.h"
+#include "logd/LogEvent.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
+using android::base::StringPrintf;
+
+struct Metric2Condition {
+    int64_t conditionId;
+    std::vector<Matcher> metricFields;
+    std::vector<Matcher> conditionFields;
+};
+
 class HashableDimensionKey {
 public:
-    explicit HashableDimensionKey(const DimensionsValue& dimensionsValue)
-        : mDimensionsValue(dimensionsValue){};
+    explicit HashableDimensionKey(const std::vector<FieldValue>& values) {
+        mValues = values;
+    }
 
     HashableDimensionKey(){};
 
-    HashableDimensionKey(const HashableDimensionKey& that)
-        : mDimensionsValue(that.getDimensionsValue()){};
+    HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){};
 
-    HashableDimensionKey& operator=(const HashableDimensionKey& from) = default;
+    inline void addValue(const FieldValue& value) {
+        mValues.push_back(value);
+    }
+
+    inline const std::vector<FieldValue>& getValues() const {
+        return mValues;
+    }
+
+    inline std::vector<FieldValue>* mutableValues() {
+        return &mValues;
+    }
+
+    inline FieldValue* mutableValue(size_t i) {
+        if (i >= 0 && i < mValues.size()) {
+            return &(mValues[i]);
+        }
+        return nullptr;
+    }
 
     std::string toString() const;
 
-    inline const DimensionsValue& getDimensionsValue() const {
-        return mDimensionsValue;
-    }
-
-    inline DimensionsValue* getMutableDimensionsValue() {
-        return &mDimensionsValue;
+    inline const char* c_str() const {
+        return toString().c_str();
     }
 
     bool operator==(const HashableDimensionKey& that) const;
 
     bool operator<(const HashableDimensionKey& that) const;
 
-    inline const char* c_str() const {
-        return toString().c_str();
-    }
+    bool contains(const HashableDimensionKey& that) const;
 
 private:
-    DimensionsValue mDimensionsValue;
+    std::vector<FieldValue> mValues;
 };
 
 class MetricDimensionKey {
@@ -83,7 +105,7 @@
     }
 
     bool hasDimensionKeyInCondition() const {
-        return mDimensionKeyInCondition.getDimensionsValue().has_field();
+        return mDimensionKeyInCondition.getValues().size() > 0;
     }
 
     bool operator==(const MetricDimensionKey& that) const;
@@ -98,11 +120,32 @@
       HashableDimensionKey mDimensionKeyInCondition;
 };
 
-bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+android::hash_t hashDimension(const HashableDimensionKey& key);
 
-android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
-android::hash_t hashDimensionsValue(const DimensionsValue& value);
-android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
+/**
+ * Creating HashableDimensionKeys from FieldValues using matcher.
+ *
+ * This function may make modifications to the Field if the matcher has Position=LAST or ANY in
+ * it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * In one event, uid 1000 is at position 5 and it's the last
+ * In another event, uid 1000 is at position 6, and it's the last
+ * these 2 events should be mapped to the same dimension.  So we will remove the original position
+ * from the dimension key for the uid field (by applying 0x80 bit mask).
+ */
+bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
+                  std::vector<HashableDimensionKey>* output);
+
+/**
+ * Filter the values from FieldValues using the matchers.
+ *
+ * In contrast to the above function, this function will not do any modification to the original
+ * data. Considering it as taking a snapshot on the atom event.
+ */
+void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values,
+                       std::vector<FieldValue>* output);
+
+void getDimensionForCondition(const LogEvent& event, Metric2Condition links,
+                              std::vector<HashableDimensionKey>* conditionDimension);
 
 }  // namespace statsd
 }  // namespace os
@@ -116,17 +159,15 @@
 template <>
 struct hash<HashableDimensionKey> {
     std::size_t operator()(const HashableDimensionKey& key) const {
-        return hashDimensionsValue(key.getDimensionsValue());
+        return hashDimension(key);
     }
 };
 
 template <>
 struct hash<MetricDimensionKey> {
     std::size_t operator()(const MetricDimensionKey& key) const {
-        android::hash_t hash = hashDimensionsValue(
-            key.getDimensionKeyInWhat().getDimensionsValue());
-        hash = android::JenkinsHashMix(hash,
-                    hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+        android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
+        hash = android::JenkinsHashMix(hash, hashDimension(key.getDimensionKeyInCondition()));
         return android::JenkinsHashWhiten(hash);
     }
 };
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 7662c40..4fac5aa 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define DEBUG false  // STOPSHIP if true
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 #include "statslog.h"
 
@@ -25,8 +25,6 @@
 #include "guardrail/StatsdStats.h"
 #include "metrics/CountMetricProducer.h"
 #include "external/StatsPullerManager.h"
-#include "dimension.h"
-#include "field_util.h"
 #include "stats_util.h"
 #include "storage/StorageManager.h"
 
@@ -93,27 +91,31 @@
     }
 }
 
+void updateUid(Value* value, int hostUid) {
+    int uid = value->int_value;
+    if (uid != hostUid) {
+        value->setInt(hostUid);
+    }
+}
+
 void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
-    std::set<Field, FieldCmp> uidFields;
     if (android::util::kAtomsWithAttributionChain.find(event->GetTagId()) !=
         android::util::kAtomsWithAttributionChain.end()) {
-        FieldMatcher matcher;
-        buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY, &matcher);
-        findFields(event->getFieldValueMap(), matcher, &uidFields);
-    } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
-               android::util::kAtomsWithUidField.end()) {
-        FieldMatcher matcher;
-        buildSimpleAtomFieldMatcher(
-            event->GetTagId(), 1 /* uid is always the 1st field. */, &matcher);
-        findFields(event->getFieldValueMap(), matcher, &uidFields);
-    }
-
-    for (const auto& uidField : uidFields) {
-        DimensionsValue* value = event->findFieldValueOrNull(uidField);
-        if (value != nullptr && value->value_case() == DimensionsValue::ValueCase::kValueInt) {
-            const int uid = mUidMap->getHostUidOrSelf(value->value_int());
-            value->set_value_int(uid);
+        for (auto& value : *(event->getMutableValues())) {
+            if (value.mField.getPosAtDepth(0) > kAttributionField) {
+                break;
+            }
+            if (isAttributionUidField(value)) {
+                const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
+                updateUid(&value.mValue, hostUid);
+            }
         }
+    } else if (android::util::kAtomsWithUidField.find(event->GetTagId()) !=
+                       android::util::kAtomsWithUidField.end() &&
+               event->getValues().size() > 0 && (event->getValues())[0].mValue.getType() == INT) {
+        Value& value = (*event->getMutableValues())[0].mValue;
+        const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+        updateUid(&value, hostUid);
     }
 }
 
@@ -212,27 +214,14 @@
     }
 }
 
-void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
-                                     ConfigMetricsReportList* report) {
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t dumpTimeStampNs,
+                                     vector<uint8_t>* outData) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-    auto it = mMetricsManagers.find(key);
-    if (it == mMetricsManagers.end()) {
-        ALOGW("Config source %s does not exist", key.ToString().c_str());
-        return;
-    }
-    report->mutable_config_key()->set_uid(key.GetUid());
-    report->mutable_config_key()->set_id(key.GetId());
-    ConfigMetricsReport* configMetricsReport = report->add_reports();
-    it->second->onDumpReport(dumpTimeStampNs, configMetricsReport);
-    // TODO: dump uid mapping.
+    onDumpReportLocked(key, dumpTimeStampNs, outData);
 }
 
-void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
-    std::lock_guard<std::mutex> lock(mMetricsMutex);
-    onDumpReportLocked(key, outData);
-}
-
-void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, vector<uint8_t>* outData) {
+void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeStampNs,
+                                           vector<uint8_t>* outData) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
@@ -258,7 +247,7 @@
 
     // First, fill in ConfigMetricsReport using current data on memory, which
     // starts from filling in StatsLogReport's.
-    it->second->onDumpReport(&proto);
+    it->second->onDumpReport(dumpTimeStampNs, &proto);
 
     // Fill in UidMap.
     auto uidMap = mUidMap->getOutput(key);
@@ -292,6 +281,7 @@
             iter.rp()->move(toRead);
         }
     }
+
     StatsdStats::getInstance().noteMetricsReportSent(key);
 }
 
@@ -327,7 +317,7 @@
         StatsdStats::kMaxMetricsBytesPerConfig) {  // Too late. We need to start clearing data.
         // TODO(b/70571383): By 12/15/2017 add API to drop data directly
         ProtoOutputStream proto;
-        metricsManager.onDumpReport(&proto);
+        metricsManager.onDumpReport(time(nullptr) * NS_PER_SEC, &proto);
         StatsdStats::getInstance().noteDataDropped(key);
         VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
     } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
@@ -351,7 +341,7 @@
     for (auto& pair : mMetricsManagers) {
         const ConfigKey& key = pair.first;
         vector<uint8_t> data;
-        onDumpReportLocked(key, &data);
+        onDumpReportLocked(key, time(nullptr) * NS_PER_SEC, &data);
         // TODO: Add a guardrail to prevent accumulation of file on disk.
         string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, time(nullptr),
                                         key.GetUid(), (long long)key.GetId());
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 8bbcd75..1444306 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -46,9 +46,7 @@
 
     size_t GetMetricsSize(const ConfigKey& key) const;
 
-    void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
-    void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
-                      ConfigMetricsReportList* report);
+    void onDumpReport(const ConfigKey& key, const uint64_t dumpTimeNs, vector<uint8_t>* outData);
 
     /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
     void onAnomalyAlarmFired(
@@ -80,7 +78,8 @@
 
     sp<AnomalyMonitor> mAnomalyMonitor;
 
-    void onDumpReportLocked(const ConfigKey& key, vector<uint8_t>* outData);
+    void onDumpReportLocked(const ConfigKey& key, const uint64_t dumpTimeNs,
+                            vector<uint8_t>* outData);
 
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
      * actually delete the data. */
@@ -105,9 +104,14 @@
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
     FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
     FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
-    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration);
-    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration);
-    FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3);
+    FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1);
+    FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
     FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c24efec..e111f58 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -492,7 +492,8 @@
         }
         if (good) {
             vector<uint8_t> data;
-            mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), &data);
+            mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), time(nullptr) * NS_PER_SEC,
+                                     &data);
             // TODO: print the returned StatsLogReport to file instead of printing to logcat.
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
@@ -780,7 +781,7 @@
     VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
         ConfigKey configKey(ipc->getCallingUid(), key);
-        mProcessor->onDumpReport(configKey, output);
+        mProcessor->onDumpReport(configKey, time(nullptr) * NS_PER_SEC, output);
         return Status::ok();
     } else {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 4c20ccb..13a2b7b 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -106,11 +106,9 @@
 }
 
 void CombinationConditionTracker::isConditionMet(
-        const ConditionKey& conditionParameters,
-        const vector<sp<ConditionTracker>>& allConditions,
-        const FieldMatcher& dimensionFields,
-        vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+        const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
+        const std::vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
     // So far, this is fine as there is at most one child having sliced output.
     for (const int childIndex : mChildren) {
         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
@@ -169,8 +167,8 @@
 
 ConditionState CombinationConditionTracker::getMetConditionDimension(
         const std::vector<sp<ConditionTracker>>& allConditions,
-        const FieldMatcher& dimensionFields,
-        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+        const std::vector<Matcher>& dimensionFields,
+        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
     vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
     // So far, this is fine as there is at most one child having sliced output.
     for (const int childIndex : mChildren) {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 0b7f949..ba185f6 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,17 +41,17 @@
                            std::vector<ConditionState>& conditionCache,
                            std::vector<bool>& changedCache) override;
 
-    void isConditionMet(
-        const ConditionKey& conditionParameters,
-        const std::vector<sp<ConditionTracker>>& allConditions,
-        const FieldMatcher& dimensionFields,
-        std::vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+    void isConditionMet(const ConditionKey& conditionParameters,
+                        const std::vector<sp<ConditionTracker>>& allConditions,
+                        const vector<Matcher>& dimensionFields,
+                        std::vector<ConditionState>& conditionCache,
+                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
 
     ConditionState getMetConditionDimension(
             const std::vector<sp<ConditionTracker>>& allConditions,
-            const FieldMatcher& dimensionFields,
-            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+            const vector<Matcher>& dimensionFields,
+            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+
 private:
     LogicalOperation mLogicalOperation;
 
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 81abbdb..2612a9a 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -90,14 +90,13 @@
     virtual void isConditionMet(
             const ConditionKey& conditionParameters,
             const std::vector<sp<ConditionTracker>>& allConditions,
-            const FieldMatcher& dimensionFields,
-            std::vector<ConditionState>& conditionCache,
-            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+            const vector<Matcher>& dimensionFields, std::vector<ConditionState>& conditionCache,
+            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
 
     virtual ConditionState getMetConditionDimension(
             const std::vector<sp<ConditionTracker>>& allConditions,
-            const FieldMatcher& dimensionFields,
-            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+            const vector<Matcher>& dimensionFields,
+            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
 
     // return the list of LogMatchingTracker index that this ConditionTracker uses.
     virtual const std::set<int>& getLogTrackerIndex() const {
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index 0427700..c8722c3 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -24,11 +24,9 @@
 using std::string;
 using std::vector;
 
-ConditionState ConditionWizard::query(
-    const int index, const ConditionKey& parameters,
-    const FieldMatcher& dimensionFields,
-    std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
-
+ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
+                                      const vector<Matcher>& dimensionFields,
+                                      std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
     vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
 
     mAllConditions[index]->isConditionMet(
@@ -37,9 +35,8 @@
 }
 
 ConditionState ConditionWizard::getMetConditionDimension(
-    const int index, const FieldMatcher& dimensionFields,
-    std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
-
+        const int index, const vector<Matcher>& dimensionFields,
+        std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const {
     return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
                                  *dimensionsKeySet);
 }
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index b38b59f..4831d56 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -39,16 +39,13 @@
     //                       condition.
     // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
     // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
-    virtual ConditionState query(
-            const int conditionIndex,
-            const ConditionKey& conditionParameters,
-            const FieldMatcher& dimensionFields,
-            std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+    virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
+                                 const vector<Matcher>& dimensionFields,
+                                 std::unordered_set<HashableDimensionKey>* dimensionKeySet);
 
     virtual ConditionState getMetConditionDimension(
-            const int index,
-            const FieldMatcher& dimensionFields,
-            std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
+            const int index, const vector<Matcher>& dimensionFields,
+            std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
 
 private:
     std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 25265d5..624119f3 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -19,7 +19,6 @@
 
 #include "SimpleConditionTracker.h"
 #include "guardrail/StatsdStats.h"
-#include "dimension.h"
 
 #include <log/logprint.h>
 
@@ -77,10 +76,12 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mOutputDimensions = simplePredicate.dimensions();
-
-    if (mOutputDimensions.child_size() > 0) {
-        mSliced = true;
+    if (simplePredicate.has_dimensions()) {
+        translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
+        if (mOutputDimensions.size() > 0) {
+            mSliced = true;
+            mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
+        }
     }
 
     if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
@@ -104,13 +105,10 @@
                                   vector<bool>& stack) {
     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
     // if the initialization was successful.
-    if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
-        setSliced(true);
-    }
     return mInitialized;
 }
 
-void print(map<HashableDimensionKey, int>& conditions, const int64_t& id) {
+void print(const map<HashableDimensionKey, int>& conditions, const int64_t& id) {
     VLOG("%lld DUMP:", (long long)id);
     for (const auto& pair : conditions) {
         VLOG("\t%s : %d", pair.first.c_str(), pair.second);
@@ -151,24 +149,15 @@
 }
 
 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
-                                                  bool matchStart,
-                                                  std::vector<ConditionState>& conditionCache,
-                                                  std::vector<bool>& conditionChangedCache) {
-    if ((int)conditionChangedCache.size() <= mIndex) {
-        ALOGE("handleConditionEvent: param conditionChangedCache not initialized.");
-        return;
-    }
-    if ((int)conditionCache.size() <= mIndex) {
-        ALOGE("handleConditionEvent: param conditionCache not initialized.");
-        return;
-    }
+                                                  bool matchStart, ConditionState* conditionCache,
+                                                  bool* conditionChangedCache) {
     bool changed = false;
     auto outputIt = mSlicedConditionState.find(outputKey);
     ConditionState newCondition;
     if (hitGuardRail(outputKey)) {
-        conditionChangedCache[mIndex] = false;
+        (*conditionChangedCache) = false;
         // Tells the caller it's evaluated.
-        conditionCache[mIndex] = ConditionState::kUnknown;
+        (*conditionCache) = ConditionState::kUnknown;
         return;
     }
     if (outputIt == mSlicedConditionState.end()) {
@@ -230,9 +219,8 @@
         print(mSlicedConditionState, mConditionId);
     }
 
-    conditionChangedCache[mIndex] = changed;
-    conditionCache[mIndex] = newCondition;
-
+    (*conditionChangedCache) = changed;
+    (*conditionCache) = newCondition;
     VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
          conditionChangedCache[mIndex] == true);
 }
@@ -292,42 +280,42 @@
         return;
     }
 
-    // outputKey is the output values. e.g, uid:1234
-    std::vector<DimensionsValue> outputValues;
-    getDimensionKeys(event, mOutputDimensions, &outputValues);
-    if (outputValues.size() == 0) {
-        // The original implementation would generate an empty string dimension hash when condition
-        // is not sliced.
-        handleConditionEvent(
-            DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache);
-    } else if (outputValues.size() == 1) {
-        handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1,
-            conditionCache, conditionChangedCache);
+    ConditionState overallState = mInitialValue;
+    bool overallChanged = false;
+
+    if (mOutputDimensions.size() == 0) {
+        handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
+                             &overallChanged);
     } else {
+        std::vector<HashableDimensionKey> outputValues;
+        filterValues(mOutputDimensions, event.getValues(), &outputValues);
+
         // If this event has multiple nodes in the attribution chain,  this log event probably will
         // generate multiple dimensions. If so, we will find if the condition changes for any
         // dimension and ask the corresponding metric producer to verify whether the actual sliced
         // condition has changed or not.
         // A high level assumption is that a predicate is either sliced or unsliced. We will never
         // have both sliced and unsliced version of a predicate.
-        for (const DimensionsValue& outputValue : outputValues) {
-            vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
-                                                             ConditionState::kNotEvaluated);
-            vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
-            handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
-                dimensionalConditionCache, dimensionalConditionChangedCache);
-            OrConditionState(dimensionalConditionCache, &conditionCache);
-            OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
+        for (const HashableDimensionKey& outputValue : outputValues) {
+            // For sliced conditions, the value in the cache is not used. We don't need to update
+            // the overall condition state.
+            ConditionState tempState = ConditionState::kUnknown;
+            bool tempChanged = false;
+            handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged);
+            if (tempChanged) {
+                overallChanged = true;
+            }
         }
     }
+    conditionCache[mIndex] = overallState;
+    conditionChangedCache[mIndex] = overallChanged;
 }
 
 void SimpleConditionTracker::isConditionMet(
-        const ConditionKey& conditionParameters,
-        const vector<sp<ConditionTracker>>& allConditions,
-        const FieldMatcher& dimensionFields,
-        vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+        const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
+        const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %lld %d",
@@ -338,8 +326,7 @@
 
     if (pair == conditionParameters.end()) {
         ConditionState conditionState = ConditionState::kNotEvaluated;
-        if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
-            dimensionFields.field() == mOutputDimensions.field()) {
+        if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
             conditionState = conditionState | getMetConditionDimension(
                 allConditions, dimensionFields, dimensionsKeySet);
         } else {
@@ -368,12 +355,10 @@
             ConditionState sliceState =
                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
             conditionState = conditionState | sliceState;
-            if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
-                HashableDimensionKey dimensionKey;
-                if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
-                                    dimensionKey.getMutableDimensionsValue())) {
-                    dimensionsKeySet.insert(dimensionKey);
-                }
+            if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
+                vector<HashableDimensionKey> dimensionKeys;
+                filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKeys);
+                dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
             }
         } else {
             // For unseen key, check whether the require dimensions are subset of sliced condition
@@ -382,31 +367,29 @@
             for (const auto& slice : mSlicedConditionState) {
                 ConditionState sliceState =
                     slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
-                if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+                if (slice.first.contains(key)) {
                     conditionState = conditionState | sliceState;
-                    if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
-                        HashableDimensionKey dimensionKey;
-                        if (getSubDimension(slice.first.getDimensionsValue(),
-                                            dimensionFields, dimensionKey.getMutableDimensionsValue())) {
-                            dimensionsKeySet.insert(dimensionKey);
-                        }
+                    if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
+                        vector<HashableDimensionKey> dimensionKeys;
+                        filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys);
+
+                        dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
+                    }
                     }
                 }
             }
         }
-    }
     conditionCache[mIndex] = conditionState;
     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
 }
 
 ConditionState SimpleConditionTracker::getMetConditionDimension(
         const std::vector<sp<ConditionTracker>>& allConditions,
-        const FieldMatcher& dimensionFields,
-        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+        const vector<Matcher>& dimensionFields,
+        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
     ConditionState conditionState = mInitialValue;
-    if (!dimensionFields.has_field() ||
-        !mOutputDimensions.has_field() ||
-        dimensionFields.field() != mOutputDimensions.field()) {
+    if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
+        dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
         const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
         if (itr != mSlicedConditionState.end()) {
             ConditionState sliceState =
@@ -419,13 +402,13 @@
     for (const auto& slice : mSlicedConditionState) {
         ConditionState sliceState =
             slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
-        DimensionsValue dimensionsValue;
         conditionState = conditionState | sliceState;
-        HashableDimensionKey dimensionKey;
-        if (sliceState == ConditionState::kTrue &&
-            getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
-                            dimensionKey.getMutableDimensionsValue())) {
-            dimensionsKeySet.insert(dimensionKey);
+
+        if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
+            vector<HashableDimensionKey> dimensionKeys;
+            filterValues(dimensionFields, slice.first.getValues(), &dimensionKeys);
+
+            dimensionsKeySet.insert(dimensionKeys.begin(), dimensionKeys.end());
         }
     }
     return conditionState;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index ce9a02d..c565129 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,14 +48,14 @@
 
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        const FieldMatcher& dimensionFields,
+                        const vector<Matcher>& dimensionFields,
                         std::vector<ConditionState>& conditionCache,
-                        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
 
     ConditionState getMetConditionDimension(
             const std::vector<sp<ConditionTracker>>& allConditions,
-            const FieldMatcher& dimensionFields,
-            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+            const vector<Matcher>& dimensionFields,
+            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
 
 private:
     const ConfigKey mConfigKey;
@@ -73,17 +73,17 @@
 
     ConditionState mInitialValue;
 
-    FieldMatcher mOutputDimensions;
+    std::vector<Matcher> mOutputDimensions;
+
+    int mDimensionTag;
 
     std::map<HashableDimensionKey, int> mSlicedConditionState;
 
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
-    void handleConditionEvent(const HashableDimensionKey& outputKey,
-                              bool matchStart,
-                              std::vector<ConditionState>& conditionCache,
-                              std::vector<bool>& changedCache);
+    void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+                              ConditionState* conditionCache, bool* changedCache);
 
     bool hitGuardRail(const HashableDimensionKey& newKey);
 
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 0ab33cf..691356b 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -27,7 +27,6 @@
 #include "ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
-#include "dimension.h"
 
 namespace android {
 namespace os {
@@ -97,109 +96,6 @@
 ConditionState operator|(ConditionState l, ConditionState r) {
     return l >= r ? l : r;
 }
-
-void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored) {
-    if (ref.size() != ored->size()) {
-        return;
-    }
-    for (size_t i = 0; i < ored->size(); ++i) {
-        ored->at(i) = ored->at(i) | ref.at(i);
-    }
-}
-
-void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) {
-    if (ref.size() != ored->size()) {
-        return;
-    }
-    for (size_t i = 0; i < ored->size(); ++i) {
-        ored->at(i) = ored->at(i) | ref.at(i);
-    }
-}
-
-void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
-                               std::vector<Field> *allFields) {
-    if (matcher.has_position()) {
-        leafField->set_position_index(0);
-    }
-    if (matcher.child_size() == 0) {
-        allFields->push_back(*rootField);
-        return;
-    }
-    for (int i = 0; i < matcher.child_size(); ++i) {
-        Field* newLeafField = leafField->add_child();
-        newLeafField->set_field(matcher.child(i).field());
-        getFieldsFromFieldMatcher(matcher.child(i), rootField, newLeafField, allFields);
-    }
-}
-
-void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) {
-    if (!matcher.has_field()) {
-        return;
-    }
-    Field rootField;
-    rootField.set_field(matcher.field());
-    getFieldsFromFieldMatcher(matcher, &rootField, &rootField, allFields);
-}
-
-void flattenValueLeaves(const DimensionsValue& value,
-                        std::vector<const DimensionsValue*> *allLaves) {
-    switch (value.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::kValueInt:
-        case DimensionsValue::ValueCase::kValueLong:
-        case DimensionsValue::ValueCase::kValueBool:
-        case DimensionsValue::ValueCase::kValueFloat:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            allLaves->push_back(&value);
-            break;
-        case DimensionsValue::ValueCase::kValueTuple:
-            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
-                flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves);
-            }
-            break;
-    }
-}
-
-void getDimensionKeysForCondition(
-    const LogEvent& event, const MetricConditionLink& link,
-    std::vector<HashableDimensionKey> *hashableDimensionKeys) {
-    std::vector<Field> whatFields;
-    getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields);
-    std::vector<Field> conditionFields;
-    getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields);
-
-    // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and
-    // directly construct the full condition value tree.
-    std::vector<DimensionsValue> whatValues;
-    getDimensionKeys(event, link.fields_in_what(), &whatValues);
-
-    for (size_t i = 0; i < whatValues.size(); ++i) {
-        std::vector<const DimensionsValue*> whatLeaves;
-        flattenValueLeaves(whatValues[i], &whatLeaves);
-        if (whatLeaves.size() != whatFields.size() ||
-            whatLeaves.size() != conditionFields.size()) {
-            ALOGE("Dimensions between what and condition not equal.");
-            return;
-        }
-        FieldValueMap conditionValueMap;
-        for (size_t j = 0; j < whatLeaves.size(); ++j) {
-            DimensionsValue* conditionValue = &conditionValueMap[conditionFields[j]];
-            *conditionValue = *whatLeaves[i];
-            if (!setFieldInLeafValueProto(conditionFields[j], conditionValue)) {
-                ALOGE("Not able to reset the field for condition leaf value.");
-                return;
-            }
-        }
-        std::vector<DimensionsValue> conditionValueTrees;
-        findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValueTrees);
-        if (conditionValueTrees.size() != 1) {
-            ALOGE("Not able to find unambiguous field value in condition atom.");
-            continue;
-        }
-        hashableDimensionKeys->push_back(HashableDimensionKey(conditionValueTrees[0]));
-    }
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index a7288be..fed90ec 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -33,16 +33,10 @@
 };
 
 ConditionState operator|(ConditionState l, ConditionState r);
-void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored);
-void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored);
 
 ConditionState evaluateCombinationCondition(const std::vector<int>& children,
                                             const LogicalOperation& operation,
                                             const std::vector<ConditionState>& conditionCache);
-
-void getDimensionKeysForCondition(
-        const LogEvent& event, const MetricConditionLink& link,
-        std::vector<HashableDimensionKey> *dimensionKeys);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
deleted file mode 100644
index 8a2e871..0000000
--- a/cmds/statsd/src/dimension.cpp
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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 "Log.h"
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
-#include "dimension.h"
-
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) {
-    if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) {
-        return getSingleLeafValue(&value->value_tuple().dimensions_value(0));
-    } else {
-        return value;
-    }
-}
-
-DimensionsValue getSingleLeafValue(const DimensionsValue& value) {
-    const DimensionsValue* leafValue = getSingleLeafValue(&value);
-    return *leafValue;
-}
-
-void appendLeafNodeToTree(const Field& field,
-                            const DimensionsValue& value,
-                            DimensionsValue* parentValue) {
-    if (field.child_size() <= 0) {
-        *parentValue = value;
-        parentValue->set_field(field.field());
-        return;
-    }
-    parentValue->set_field(field.field());
-    int idx = -1;
-    for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) {
-        if (parentValue->mutable_value_tuple()->dimensions_value(i).field() ==
-                field.child(0).field()) {
-            idx = i;
-        }
-    }
-    if (idx < 0) {
-        parentValue->mutable_value_tuple()->add_dimensions_value();
-        idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1;
-    }
-    appendLeafNodeToTree(
-        field.child(0), value,
-        parentValue->mutable_value_tuple()->mutable_dimensions_value(idx));
-}
-
-void appendLeafNodeToTrees(const Field& field,
-                           const DimensionsValue& node,
-                           std::vector<DimensionsValue>* rootTrees) {
-    if (rootTrees == nullptr) {
-        return;
-    }
-    if (rootTrees->empty()) {
-        DimensionsValue tree;
-        appendLeafNodeToTree(field, node, &tree);
-        rootTrees->push_back(tree);
-    } else {
-        for (size_t i = 0; i < rootTrees->size(); ++i) {
-            appendLeafNodeToTree(field, node, &rootTrees->at(i));
-        }
-    }
-}
-
-namespace {
-
-void findDimensionsValues(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::vector<DimensionsValue>* rootDimensionsValues);
-
-void findNonRepeatedDimensionsValues(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::vector<DimensionsValue>* rootValues) {
-    if (matcher.child_size() > 0) {
-        Field* newLeafField = leafField->add_child();
-        for (const auto& childMatcher : matcher.child()) {
-          newLeafField->set_field(childMatcher.field());
-          findDimensionsValues(fieldValueMap, childMatcher, rootField, newLeafField, rootValues);
-        }
-        leafField->clear_child();
-    } else {
-        auto ret = fieldValueMap.equal_range(*rootField);
-        int found = 0;
-        for (auto it = ret.first; it != ret.second; ++it) {
-            found++;
-        }
-        // Not found.
-        if (found <= 0) {
-            return;
-        }
-        if (found > 1) {
-            ALOGE("Found multiple values for optional field.");
-            return;
-        }
-        appendLeafNodeToTrees(*rootField, ret.first->second, rootValues);
-    }
-}
-
-void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap,
-                                  const FieldMatcher& matcher,
-                                  Field* rootField,
-                                  Field* leafField,
-                                  std::vector<DimensionsValue>* rootValues) {
-    if (matcher.position() == Position::FIRST) {
-        leafField->set_position_index(0);
-        findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField, rootValues);
-        leafField->clear_position_index();
-    } else {
-        auto itLower = fieldValueMap.lower_bound(*rootField);
-        if (itLower == fieldValueMap.end()) {
-            return;
-        }
-        const int leafFieldNum = leafField->field();
-        leafField->set_field(leafFieldNum + 1);
-        auto itUpper = fieldValueMap.lower_bound(*rootField);
-        // Resets the field number.
-        leafField->set_field(leafFieldNum);
-
-        switch (matcher.position()) {
-             case Position::LAST:
-                 {
-                     itUpper--;
-                     if (itUpper != fieldValueMap.end()) {
-                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
-                         if (last_index < 0) {
-                            return;
-                         }
-                         leafField->set_position_index(last_index);
-                         findNonRepeatedDimensionsValues(
-                            fieldValueMap, matcher, rootField, leafField, rootValues);
-                         leafField->clear_position_index();
-                     }
-                 }
-                 break;
-             case Position::ANY:
-                 {
-                    std::set<int> indexes;
-                    for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(*rootField, it->first);
-                        if (index >= 0) {
-                            indexes.insert(index);
-                        }
-                    }
-                    if (!indexes.empty()) {
-                        std::vector<DimensionsValue> allValues;
-                        for (const int index : indexes) {
-                             leafField->set_position_index(index);
-                             std::vector<DimensionsValue> newValues = *rootValues;
-                             findNonRepeatedDimensionsValues(
-                                fieldValueMap, matcher, rootField, leafField, &newValues);
-                             allValues.insert(allValues.end(), newValues.begin(), newValues.end());
-                             leafField->clear_position_index();
-                        }
-                        rootValues->clear();
-                        rootValues->insert(rootValues->end(), allValues.begin(), allValues.end());
-                    }
-                 }
-                 break;
-             default:
-                break;
-         }
-    }
-}
-
-void findDimensionsValues(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::vector<DimensionsValue>* rootDimensionsValues) {
-    if (!matcher.has_position()) {
-        findNonRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField,
-                                        rootDimensionsValues);
-    } else {
-        findRepeatedDimensionsValues(fieldValueMap, matcher, rootField, leafField,
-                                     rootDimensionsValues);
-    }
-}
-
-} // namespace
-
-void findDimensionsValues(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       std::vector<DimensionsValue>* rootDimensionsValues) {
-    Field rootField;
-    buildSimpleAtomField(matcher.field(), &rootField);
-    findDimensionsValues(fieldValueMap, matcher, &rootField, &rootField, rootDimensionsValues);
-}
-
-void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) {
-    matcher->set_field(tagId);
-}
-
-void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) {
-    matcher->set_field(tagId);
-    matcher->add_child()->set_field(fieldNum);
-}
-
-constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1;
-constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1;
-constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2;
-
-void buildAttributionUidFieldMatcher(const int tagId, const Position position,
-                                     FieldMatcher* matcher) {
-    matcher->set_field(tagId);
-    auto child = matcher->add_child();
-    child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
-    child->set_position(position);
-    child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-}
-
-void buildAttributionTagFieldMatcher(const int tagId, const Position position,
-                                     FieldMatcher* matcher) {
-    matcher->set_field(tagId);
-    FieldMatcher* child = matcher->add_child();
-    child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
-    child->set_position(position);
-    child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-}
-
-void buildAttributionFieldMatcher(const int tagId, const Position position, FieldMatcher* matcher) {
-    matcher->set_field(tagId);
-    FieldMatcher* child = matcher->add_child();
-    child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
-    child->set_position(position);
-    child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-    child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
-}
-
-void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
-    if (!value.has_field()) {
-        return;
-    }
-    *flattened += std::to_string(value.field());
-    *flattened += ":";
-    switch (value.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            *flattened += value.value_str();
-            break;
-        case DimensionsValue::ValueCase::kValueInt:
-            *flattened += std::to_string(value.value_int());
-            break;
-        case DimensionsValue::ValueCase::kValueLong:
-            *flattened += std::to_string(value.value_long());
-            break;
-        case DimensionsValue::ValueCase::kValueBool:
-            *flattened += std::to_string(value.value_bool());
-            break;
-        case DimensionsValue::ValueCase::kValueFloat:
-            *flattened += std::to_string(value.value_float());
-            break;
-        case DimensionsValue::ValueCase::kValueTuple:
-            {
-                *flattened += "{";
-                for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
-                    DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened);
-                    *flattened += "|";
-                }
-                *flattened += "}";
-            }
-            break;
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            break;
-    }
-}
-
-std::string DimensionsValueToString(const DimensionsValue& value) {
-    std::string flatten;
-    DimensionsValueToString(value, &flatten);
-    return flatten;
-}
-
-bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) {
-    if (dimension.field() != sub.field()) {
-        return false;
-    }
-    if (dimension.value_case() != sub.value_case()) {
-        return false;
-    }
-    switch (dimension.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            return dimension.value_str() == sub.value_str();
-        case DimensionsValue::ValueCase::kValueInt:
-            return dimension.value_int() == sub.value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return dimension.value_long() == sub.value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return dimension.value_bool() == sub.value_bool();
-        case DimensionsValue::ValueCase::kValueFloat:
-            return dimension.value_float() == sub.value_float();
-        case DimensionsValue::ValueCase::kValueTuple: {
-            if (dimension.value_tuple().dimensions_value_size() <
-                sub.value_tuple().dimensions_value_size()) {
-                return false;
-            }
-            bool allSub = true;
-            for (int i = 0; allSub && i < sub.value_tuple().dimensions_value_size(); ++i) {
-                bool isSub = false;
-                for (int j = 0; !isSub &&
-                        j < dimension.value_tuple().dimensions_value_size(); ++j) {
-                    isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j),
-                                            sub.value_tuple().dimensions_value(i));
-                }
-                allSub &= isSub;
-            }
-            return allSub;
-        }
-        break;
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            return false;
-        default:
-            return false;
-    }
-}
-
-long getLongFromDimenValue(const DimensionsValue& dimensionValue) {
-    switch (dimensionValue.value_case()) {
-        case DimensionsValue::ValueCase::kValueInt:
-            return dimensionValue.value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return dimensionValue.value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return dimensionValue.value_bool() ? 1 : 0;
-        case DimensionsValue::ValueCase::kValueFloat:
-            return (int64_t)dimensionValue.value_float();
-        case DimensionsValue::ValueCase::kValueTuple:
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET:
-            return 0;
-    }
-}
-
-bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
-                     DimensionsValue* subDimension) {
-    if (!matcher.has_field()) {
-        return false;
-    }
-    if (matcher.field() != dimension.field()) {
-        return false;
-    }
-    if (matcher.child_size() <= 0) {
-        if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
-            dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
-            return false;
-        }
-        *subDimension = dimension;
-        return true;
-    } else {
-        if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
-            return false;
-        }
-        bool found_value = true;
-        auto value_tuple = dimension.value_tuple();
-        subDimension->set_field(dimension.field());
-        for (int i = 0; found_value && i < matcher.child_size(); ++i) {
-            int j = 0;
-            for (; j < value_tuple.dimensions_value_size(); ++j) {
-                if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
-                    break;
-                }
-            }
-            if (j < value_tuple.dimensions_value_size()) {
-                found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
-                    subDimension->mutable_value_tuple()->add_dimensions_value());
-            } else {
-                found_value = false;
-            }
-        }
-        return found_value;
-    }
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
deleted file mode 100644
index 138c6e9..0000000
--- a/cmds/statsd/src/dimension.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <log/logprint.h>
-#include <set>
-#include <vector>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "field_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one
-// leaf node at most.
-const DimensionsValue* getSingleLeafValue(const DimensionsValue* value);
-DimensionsValue getSingleLeafValue(const DimensionsValue& value);
-
-// Appends the leaf node to the parent tree.
-void appendLeafNodeToTree(const Field& field, const DimensionsValue& value, DimensionsValue* tree);
-
-// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
-// represents a tree. When the input proto has repeated fields and the input "dimensions" wants
-// "ANY" locations, it will return multiple trees.
-void findDimensionsValues(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       std::vector<DimensionsValue>* rootDimensionsValues);
-
-// Utils to build FieldMatcher proto for simple one-depth atoms.
-void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
-void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
-
-// Utils to build FieldMatcher proto for attribution nodes.
-void buildAttributionUidFieldMatcher(const int tagId, const Position position,
-                                     FieldMatcher* matcher);
-void buildAttributionTagFieldMatcher(const int tagId, const Position position,
-                                     FieldMatcher* matcher);
-void buildAttributionFieldMatcher(const int tagId, const Position position,
-                                  FieldMatcher* matcher);
-
-// Utils to print pretty string for DimensionsValue proto.
-std::string DimensionsValueToString(const DimensionsValue& value);
-void DimensionsValueToString(const DimensionsValue& value, std::string *flattened);
-
-bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub);
-
-// Helper function to get long value from the DimensionsValue proto.
-long getLongFromDimenValue(const DimensionsValue& dimensionValue);
-
-bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
-                    DimensionsValue* subDimension);
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index 7cfc1d48..0b0c5c4 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -18,7 +18,6 @@
 #include "Log.h"
 
 #include "StatsPullerManagerImpl.h"
-#include "field_util.h"
 #include "puller_util.h"
 #include "statslog.h"
 
@@ -30,125 +29,136 @@
 using std::shared_ptr;
 using std::vector;
 
-DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) {
-  Field field;
-  buildSimpleAtomField(tagId, fieldNum, &field);
-  return event->findFieldValueOrNull(field);
-}
-
+namespace {
 bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                 const vector<int>& nonAdditiveFields, int tagId) {
-  for (int f : nonAdditiveFields) {
-    DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
-    DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
-    if (!compareDimensionsValue(*lValue, *rValue)) {
-      return false;
+                 const vector<int>& nonAdditiveFields) {
+    const auto& l_values = lhs->getValues();
+    const auto& r_values = rhs->getValues();
+
+    for (size_t i : nonAdditiveFields) {
+        // We store everything starting from index 0, so we need to use i-1
+        if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
+              l_values[i - 1].mValue == r_values[i - 1].mValue)) {
+            return false;
+        }
     }
-  }
-  return true;
+    return true;
 }
 
 // merge rhs to lhs
-void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                const vector<int>& additiveFields, int tagId) {
-  for (int f : additiveFields) {
-    DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
-    DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
-    if (lValue->has_value_int()) {
-      lValue->set_value_int(lValue->value_int() + rValue->value_int());
-    } else if (lValue->has_value_long()) {
-      lValue->set_value_long(lValue->value_long() + rValue->value_long());
+// when calling this function, all sanity check should be done already.
+// e.g., index boundary, nonAdditiveFields matching etc.
+bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+                const vector<int>& additiveFields) {
+    vector<FieldValue>* host_values = lhs->getMutableValues();
+    const auto& child_values = rhs->getValues();
+    for (int i : additiveFields) {
+        Value& host = (*host_values)[i - 1].mValue;
+        const Value& child = (child_values[i - 1]).mValue;
+        if (child.getType() != host.getType()) {
+            return false;
+        }
+        switch (child.getType()) {
+            case INT:
+                host.setInt(host.int_value + child.int_value);
+                break;
+            case LONG:
+                host.setLong(host.long_value + child.long_value);
+                break;
+            default:
+                ALOGE("Tried to merge 2 fields with unsupported type");
+                return false;
+        }
     }
-  }
+    return true;
 }
 
-// process all data and merge isolated with host if necessary
-void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data,
-                                const sp<UidMap>& uidMap, int tagId) {
-  if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
-      StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
-    VLOG("Unknown pull atom id %d", tagId);
-    return;
-  }
-  if (android::util::kAtomsWithUidField.find(tagId) ==
-      android::util::kAtomsWithUidField.end()) {
-    VLOG("No uid to merge for atom %d", tagId);
-    return;
-  }
-  const vector<int>& additiveFields =
-      StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
-          ->second.additiveFields;
-  const vector<int>& nonAdditiveFields =
-      StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
-          ->second.nonAdditiveFields;
-
-  // map of host uid to isolated uid data index in the original vector.
-  // because of non additive fields, there could be multiple of them that can't
-  // be merged into one
-  map<int, vector<int>> hostToIsolated;
-  // map of host uid to their position in the original vector
-  map<int, vector<int>> hostPosition;
-  vector<int> isolatedUidPos;
-  // all uids in the original vector
-  vector<int> allUids;
-  for (size_t i = 0; i < data.size(); i++) {
-    // uid field is always first primitive filed, if present
-    DimensionsValue* uidField = getFieldValue(data[i], tagId, 1);
-    if (!uidField) {
-      VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str());
-      return;
-    }
-    int uid = uidField->value_int();
-    allUids.push_back(uid);
-    const int hostUid = uidMap->getHostUidOrSelf(uid);
-    if (hostUid != uid) {
-      uidField->set_value_int(hostUid);
-      hostToIsolated[hostUid].push_back(i);
-      isolatedUidPos.push_back(i);
-    }
-  }
-  vector<shared_ptr<LogEvent>> mergedData;
-  for (size_t i = 0; i < allUids.size(); i++) {
-    if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) {
-      hostPosition[allUids[i]].push_back(i);
-    } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) {
-      continue;
-    } else {
-      mergedData.push_back(data[i]);
-    }
-  }
-  for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end();
-       iter++) {
-    int uid = iter->first;
-    vector<int>& isolated = hostToIsolated[uid];
-    vector<int> toBeMerged;
-    toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end());
-    if (hostPosition.find(uid) != hostPosition.end()) {
-      vector<int>& host = hostPosition[uid];
-      toBeMerged.insert(toBeMerged.end(), host.begin(), host.end());
-    }
-    vector<bool> used(toBeMerged.size());
-    for (size_t i = 0; i < toBeMerged.size(); i++) {
-      if (used[i] == true) {
-        continue;
-      }
-      for (size_t j = i + 1; j < toBeMerged.size(); j++) {
-        shared_ptr<LogEvent>& lhs = data[toBeMerged[i]];
-        shared_ptr<LogEvent>& rhs = data[toBeMerged[j]];
-        if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) {
-          mergeEvent(lhs, rhs, additiveFields, tagId);
-          used[j] = true;
+bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
+              const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
+    for (const auto& pos : host_pos) {
+        if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
+            mergeEvent(data[pos], data[child_pos], additiveFields)) {
+            return true;
         }
-      }
     }
-    for (size_t i = 0; i < toBeMerged.size(); i++) {
-      if (used[i] == false) {
-      mergedData.push_back(data[i]);
+    return false;
+}
+
+}  // namespace
+
+/**
+ * Process all data and merge isolated with host if necessary.
+ * For example:
+ *   NetworkBytesAtom {
+ *       int uid = 1;
+ *       State process_state = 2;
+ *       int byte_send = 3;
+ *       int byte_recv = 4;
+ *   }
+ *   additive fields are {3, 4}, non-additive field is {2}
+ * If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
+ * [uid1, fg, 100, 200]
+ * [uid1_child, fg, 100, 200]
+ * [uid1, bg, 100, 200]
+ *
+ * We want to merge them and results should be:
+ * [uid1, fg, 200, 400]
+ * [uid1, bg, 100, 200]
+ */
+void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
+                                int tagId) {
+    if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
+        StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+        VLOG("Unknown pull atom id %d", tagId);
+        return;
     }
+    if (android::util::kAtomsWithUidField.find(tagId) == android::util::kAtomsWithUidField.end()) {
+        VLOG("No uid to merge for atom %d", tagId);
+        return;
     }
-  }
-  data.clear();
-  data = mergedData;
+    const vector<int>& additiveFields =
+            StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+    const vector<int>& nonAdditiveFields =
+            StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
+
+    // map of host uid to their position in the original vector
+    map<int, vector<int>> hostPosition;
+    vector<bool> toRemove = vector<bool>(data.size(), false);
+
+    for (size_t i = 0; i < data.size(); i++) {
+        vector<FieldValue>* valueList = data[i]->getMutableValues();
+
+        int err = 0;
+        int uid = data[i]->GetInt(1, &err);
+        if (err != 0) {
+            VLOG("Bad uid field for %s", data[i]->ToString().c_str());
+            return;
+        }
+
+        const int hostUid = uidMap->getHostUidOrSelf(uid);
+
+        if (hostUid != uid) {
+            (*valueList)[0].mValue.setInt(hostUid);
+        }
+        if (hostPosition.find(hostUid) == hostPosition.end()) {
+            hostPosition[hostUid].push_back(i);
+        } else {
+            if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
+                toRemove[i] = true;
+            } else {
+                hostPosition[hostUid].push_back(i);
+            }
+        }
+    }
+
+    vector<shared_ptr<LogEvent>> mergedData;
+    for (size_t i = 0; i < toRemove.size(); i++) {
+        if (!toRemove[i]) {
+            mergedData.push_back(data[i]);
+        }
+    }
+    data.clear();
+    data = mergedData;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index 70d5321..fd4a4a2 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <vector>
-#include "HashableDimensionKey.h"
 #include "StatsPuller.h"
 #include "logd/LogEvent.h"
 #include "packages/UidMap.h"
diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp
deleted file mode 100644
index acf64fe..0000000
--- a/cmds/statsd/src/field_util.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * 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 "Log.h"
-#include "field_util.h"
-
-#include <set>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// This function is to compare two Field trees where each node has at most one child.
-bool CompareField(const Field& a, const Field& b) {
-    if (a.field() < b.field()) {
-        return true;
-    }
-    if (a.field() > b.field()) {
-        return false;
-    }
-    if (a.position_index() < b.position_index()) {
-        return true;
-    }
-    if (a.position_index() > b.position_index()) {
-        return false;
-    }
-    if (a.child_size() < b.child_size()) {
-        return true;
-    }
-    if (a.child_size() > b.child_size()) {
-        return false;
-    }
-    if (a.child_size() == 0 && b.child_size() == 0) {
-       return false;
-    }
-    return CompareField(a.child(0), b.child(0));
-}
-
-const Field* getSingleLeaf(const Field* field) {
-    if (field->child_size() <= 0) {
-        return field;
-    } else {
-        return getSingleLeaf(&field->child(0));
-    }
-}
-
-Field* getSingleLeaf(Field* field) {
-    if (field->child_size() <= 0) {
-        return field;
-    } else {
-        return getSingleLeaf(field->mutable_child(0));
-    }
-}
-
-void FieldToString(const Field& field, std::string *flattened) {
-    *flattened += std::to_string(field.field());
-    if (field.has_position_index()) {
-        *flattened += "[";
-        *flattened += std::to_string(field.position_index());
-        *flattened += "]";
-    }
-    if (field.child_size() <= 0) {
-        return;
-    }
-    *flattened += ".";
-    *flattened += "{";
-    for (int i = 0 ; i < field.child_size(); ++i) {
-        *flattened += FieldToString(field.child(i));
-    }
-    *flattened += "},";
-}
-
-std::string FieldToString(const Field& field) {
-    std::string flatten;
-    FieldToString(field, &flatten);
-    return flatten;
-}
-
-bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) {
-    if (field.child_size() <= 0) {
-        leafValue->set_field(field.field());
-        return true;
-    } else if (field.child_size() == 1)  {
-        return setFieldInLeafValueProto(field.child(0), leafValue);
-    } else {
-        ALOGE("Not able to set the 'field' in leaf value for multiple children.");
-        return false;
-    }
-}
-
-void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field *field) {
-    field->set_field(tagId);
-    field->add_child()->set_field(atomFieldNum);
-}
-
-void buildSimpleAtomField(const int tagId, Field *field) {
-    field->set_field(tagId);
-}
-
-void appendLeaf(Field *parent, int node_field_num) {
-    if (!parent->has_field()) {
-        parent->set_field(node_field_num);
-    } else if (parent->child_size() <= 0) {
-        parent->add_child()->set_field(node_field_num);
-    } else {
-        appendLeaf(parent->mutable_child(0), node_field_num);
-    }
-}
-
-void appendLeaf(Field *parent, int node_field_num, int position) {
-    if (!parent->has_field()) {
-        parent->set_field(node_field_num);
-        parent->set_position_index(position);
-    } else if (parent->child_size() <= 0) {
-        auto child = parent->add_child();
-        child->set_field(node_field_num);
-        child->set_position_index(position);
-    } else {
-        appendLeaf(parent->mutable_child(0), node_field_num, position);
-    }
-}
-
-void increasePosition(Field *field) {
-    if (!field->has_position_index()) {
-        field->set_position_index(0);
-    } else {
-        field->set_position_index(field->position_index() + 1);
-    }
-}
-
-int getPositionByReferenceField(const Field& ref, const Field& field_with_index) {
-    if (ref.child_size() <= 0) {
-        return field_with_index.position_index();
-    }
-    if (ref.child_size() != 1 ||
-        field_with_index.child_size() != 1) {
-        return -1;
-    }
-    return getPositionByReferenceField(ref.child(0), field_with_index.child(0));
-}
-
-namespace {
-
-void findFields(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::set<Field, FieldCmp>* rootFields);
-
-void findNonRepeatedFields(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::set<Field, FieldCmp>* rootFields) {
-    if (matcher.child_size() > 0) {
-        Field* newLeafField = leafField->add_child();
-        for (const auto& childMatcher : matcher.child()) {
-            newLeafField->set_field(childMatcher.field());
-            findFields(fieldValueMap, childMatcher, rootField, newLeafField, rootFields);
-        }
-        leafField->clear_child();
-    } else {
-        auto ret = fieldValueMap.equal_range(*rootField);
-        int found = 0;
-        for (auto it = ret.first; it != ret.second; ++it) {
-            found++;
-        }
-        // Not found.
-        if (found <= 0) {
-            return;
-        }
-        if (found > 1) {
-            ALOGE("Found multiple values for optional field.");
-            return;
-        }
-        rootFields->insert(ret.first->first);
-    }
-}
-
-void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
-                        Field* rootField, Field* leafField,
-                        std::set<Field, FieldCmp>* rootFields) {
-    if (matcher.position() == Position::FIRST) {
-        leafField->set_position_index(0);
-        findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
-        leafField->clear_position_index();
-    } else {
-        auto itLower = fieldValueMap.lower_bound(*rootField);
-        if (itLower == fieldValueMap.end()) {
-            return;
-        }
-
-        const int leafFieldNum = leafField->field();
-        leafField->set_field(leafFieldNum + 1);
-        auto itUpper = fieldValueMap.lower_bound(*rootField);
-        // Resets the field number.
-        leafField->set_field(leafFieldNum);
-
-        switch (matcher.position()) {
-             case Position::LAST:
-                 {
-                     itUpper--;
-                     if (itUpper != fieldValueMap.end()) {
-                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
-                         if (last_index < 0) {
-                            return;
-                         }
-                         leafField->set_position_index(last_index);
-                         findNonRepeatedFields(
-                            fieldValueMap, matcher, rootField, leafField, rootFields);
-                         leafField->clear_position_index();
-                     }
-                 }
-                 break;
-             case Position::ANY:
-                 {
-                    std::set<int> indexes;
-                    for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(*rootField, it->first);
-                        if (index >= 0) {
-                            indexes.insert(index);
-                        }
-                    }
-                    if (!indexes.empty()) {
-                        for (const int index : indexes) {
-                             leafField->set_position_index(index);
-                             findNonRepeatedFields(
-                                fieldValueMap, matcher, rootField, leafField, rootFields);
-                             leafField->clear_position_index();
-                        }
-                    }
-                 }
-                 break;
-             default:
-                break;
-         }
-    }
-}
-
-void findFields(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       Field* rootField,
-       Field* leafField,
-       std::set<Field, FieldCmp>* rootFields) {
-    if (!matcher.has_position()) {
-        findNonRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
-    } else {
-        findRepeatedFields(fieldValueMap, matcher, rootField, leafField, rootFields);
-    }
-}
-
-}  // namespace
-
-void findFields(
-       const FieldValueMap& fieldValueMap,
-       const FieldMatcher& matcher,
-       std::set<Field, FieldCmp>* rootFields) {
-    if (!matcher.has_field() || fieldValueMap.empty()) {
-        return;
-    }
-    Field rootField;
-    buildSimpleAtomField(matcher.field(), &rootField);
-    return findFields(fieldValueMap, matcher, &rootField, &rootField, rootFields);
-}
-
-void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) {
-    if (!matcher.has_field()) {
-        return;
-    }
-    std::set<Field, FieldCmp> rootFields;
-    findFields(*fieldValueMap, matcher, &rootFields);
-    auto it = fieldValueMap->begin();
-    while (it != fieldValueMap->end()) {
-        if (rootFields.find(it->first) == rootFields.end()) {
-            it = fieldValueMap->erase(it);
-        } else {
-            it++;
-        }
-    }
-}
-
-bool hasLeafNode(const FieldMatcher& matcher) {
-    if (!matcher.has_field()) {
-        return false;
-    }
-    for (int i = 0; i < matcher.child_size(); ++i) {
-        if (hasLeafNode(matcher.child(i))) {
-            return true;
-        }
-    }
-    return true;
-}
-
-bool IsAttributionUidField(const Field& field) {
-    return field.child_size() == 1 && field.child(0).field() == 1
-        && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h
deleted file mode 100644
index b04465d..0000000
--- a/cmds/statsd/src/field_util.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
-#include <map>
-#include <set>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Function to sort the Field protos.
-bool CompareField(const Field& a, const Field& b);
-struct FieldCmp {
-    bool operator()(const Field& a, const Field& b) const {
-        return CompareField(a, b);
-    }
-};
-
-// Flattened dimensions value map. To save space, usually the key contains the tree structure info
-// and value field is only leaf node.
-typedef std::map<Field, DimensionsValue, FieldCmp> FieldValueMap;
-
-// Util function to print the Field proto.
-std::string FieldToString(const Field& field);
-
-// Util function to find the leaf node from the input Field proto and set it in the corresponding
-// value proto.
-bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue);
-
-// Returns the leaf node from the Field proto. It assume that the input has only one
-// leaf node at most.
-const Field* getSingleLeaf(const Field* field);
-Field* getSingleLeaf(Field* field);
-
-// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most.
-void appendLeaf(Field *parent, int node_field_num);
-void appendLeaf(Field *parent, int node_field_num, int position);
-
-// Increase the position index for the node. If the "position_index" is not set, set it as 0.
-void increasePosition(Field *field);
-
-// Returns true if the matcher has specified at least one leaf node.
-bool hasLeafNode(const FieldMatcher& matcher);
-
-// The two input Field proto are describing the same tree structure. Both contain one leaf node at
-// most. This is find the position index info for the leaf node at "reference" stored in the
-// "field_with_index" tree.
-int getPositionByReferenceField(const Field& reference, const Field& field_with_index);
-
-// Utils to build the Field proto for simple atom fields.
-void buildSimpleAtomField(const int tagId, const int atomFieldNum, Field* field);
-void buildSimpleAtomField(const int tagId, Field* field);
-
-// Find out all the fields specified by the matcher.
-void findFields(
-       const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
-       std::set<Field, FieldCmp>* rootFields);
-
-// Filter out the fields not in the field matcher.
-void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap);
-
-// Returns if the field is attribution node uid field.
-bool IsAttributionUidField(const Field& field);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index e1ab5d5..909b74f 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,13 +17,8 @@
 #define DEBUG false  // STOPSHIP if true
 #include "logd/LogEvent.h"
 
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-#include <set>
 #include <sstream>
 
-#include "field_util.h"
-#include "dimension.h"
 #include "stats_log_util.h"
 
 namespace android {
@@ -152,9 +147,6 @@
          if (android_log_write_string8(mContext, node.tag().c_str()) < 0) {
             return false;
          }
-         if (android_log_write_int32(mContext, node.uid()) < 0) {
-            return false;
-         }
          if (android_log_write_list_end(mContext) < 0) {
             return false;
          }
@@ -163,47 +155,23 @@
     return false;
 }
 
-namespace {
-
-void increaseField(Field *field, bool is_child) {
-    if (is_child) {
-        if (field->child_size() <= 0) {
-            field->add_child();
-        }
-    } else {
-        field->clear_child();
-    }
-    Field* curr = is_child ? field->mutable_child(0) : field;
-    if (!curr->has_field()) {
-        curr->set_field(1);
-    } else {
-        curr->set_field(curr->field() + 1);
-    }
-}
-
-}  // namespace
-
 /**
  * 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.
+ *
+ * The idea here is to read through the log items once, we get as much information we need for
+ * matching as possible. Because this log will be matched against lots of matchers.
  */
 void LogEvent::init(android_log_context context) {
-    if (!context) {
-        return;
-    }
     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.
     int i = 0;
 
     int seenListStart = 0;
 
-    Field fieldTree;
-    Field* atomField = fieldTree.add_child();
+    int32_t field = 0;
+    int depth = -1;
+    int pos[] = {1, 1, 1};
     do {
         elem = android_log_read_next(context);
         switch ((int)elem.type) {
@@ -211,55 +179,81 @@
                 // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id.
                 if (i == 1) {
                     mTagId = elem.data.int32;
-                    fieldTree.set_field(mTagId);
                 } else {
-                    increaseField(atomField, seenListStart > 0/* is_child */);
-                    mFieldValueMap[fieldTree].set_value_int(elem.data.int32);
-                }
-                break;
-            case EVENT_TYPE_FLOAT:
-                {
-                    increaseField(atomField, seenListStart > 0/* is_child */);
-                    mFieldValueMap[fieldTree].set_value_float(elem.data.float32);
-                }
-                break;
-            case EVENT_TYPE_STRING:
-                {
-                    increaseField(atomField, seenListStart > 0/* is_child */);
-                    mFieldValueMap[fieldTree].set_value_str(
-                        string(elem.data.string, elem.len).c_str());
-                }
-                break;
-            case EVENT_TYPE_LONG:
-                {
-                    increaseField(atomField, seenListStart > 0 /* is_child */);
-                    mFieldValueMap[fieldTree].set_value_long(elem.data.int64);
-                }
-                break;
-            case EVENT_TYPE_LIST:
-                if (i >= 1) {
-                    if (seenListStart > 0) {
-                       increasePosition(atomField);
-                    } else {
-                        increaseField(atomField, false /* is_child */);
-                    }
-                    seenListStart++;
-                    if (seenListStart >= 3) {
-                        ALOGE("Depth > 2. Not supported!");
+                    if (depth < 0 || depth > 2) {
                         return;
                     }
+
+                    mValues.push_back(
+                            FieldValue(Field(mTagId, pos, depth), Value((int32_t)elem.data.int32)));
+
+                    pos[depth]++;
                 }
                 break;
-            case EVENT_TYPE_LIST_STOP:
-                seenListStart--;
-                if (seenListStart == 0) {
-                    atomField->clear_position_index();
-                } else {
-                    if (atomField->child_size() > 0) {
-                       atomField->mutable_child(0)->clear_field();
+            case EVENT_TYPE_FLOAT: {
+                if (depth < 0 || depth > 2) {
+                    ALOGE("Depth > 2. Not supported!");
+                    return;
+                }
+
+                mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
+
+                pos[depth]++;
+
+            } break;
+            case EVENT_TYPE_STRING: {
+                if (depth < 0 || depth > 2) {
+                    ALOGE("Depth > 2. Not supported!");
+                    return;
+                }
+
+                mValues.push_back(FieldValue(Field(mTagId, pos, depth),
+                                             Value(string(elem.data.string, elem.len))));
+
+                pos[depth]++;
+
+            } break;
+            case EVENT_TYPE_LONG: {
+                if (depth < 0 || depth > 2) {
+                    ALOGE("Depth > 2. Not supported!");
+                    return;
+                }
+                mValues.push_back(
+                        FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
+
+                pos[depth]++;
+
+            } break;
+            case EVENT_TYPE_LIST:
+                depth++;
+                if (depth > 2) {
+                    ALOGE("Depth > 2. Not supported!");
+                    return;
+                }
+                pos[depth] = 1;
+
+                break;
+            case EVENT_TYPE_LIST_STOP: {
+                int prevDepth = depth;
+                depth--;
+                if (depth >= 0 && depth < 2) {
+                    // Now go back to decorate the previous items that are last at prevDepth.
+                    // So that we can later easily match them with Position=Last matchers.
+                    pos[prevDepth]--;
+                    int path = getEncodedField(pos, prevDepth, false);
+                    for (size_t j = mValues.size() - 1; j >= 0; j--) {
+                        if (mValues[j].mField.getDepth() >= prevDepth &&
+                            mValues[j].mField.getPath(prevDepth) == path) {
+                            mValues[j].mField.decorateLastPos(prevDepth);
+                        } else {
+                            // Safe to break, because the items are in DFS order.
+                            break;
+                        }
                     }
+                    pos[depth]++;
                 }
                 break;
+            }
             case EVENT_TYPE_UNKNOWN:
                 break;
             default:
@@ -270,162 +264,115 @@
 }
 
 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
-    DimensionsValue value;
-    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
-        *err = BAD_INDEX;
-        return 0;
-    }
-    const DimensionsValue* leafValue = getSingleLeafValue(&value);
-    switch (leafValue->value_case()) {
-        case DimensionsValue::ValueCase::kValueInt:
-            return (int64_t)leafValue->value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return leafValue->value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return leafValue->value_bool() ? 1 : 0;
-        case DimensionsValue::ValueCase::kValueFloat:
-            return (int64_t)leafValue->value_float();
-        case DimensionsValue::ValueCase::kValueTuple:
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
-            *err = BAD_TYPE;
-            return 0;
+    // TODO: encapsulate the magical operations all in Field struct as a static function.
+    int field = getSimpleField(key);
+    for (const auto& value : mValues) {
+        if (value.mField.getField() == field) {
+            if (value.mValue.getType() == INT) {
+                return value.mValue.int_value;
+            } else {
+                *err = BAD_TYPE;
+                return 0;
+            }
+        }
+        if ((size_t)value.mField.getPosAtDepth(0) > key) {
+            break;
         }
     }
+
+    *err = BAD_INDEX;
+    return 0;
 }
 
 int LogEvent::GetInt(size_t key, status_t* err) const {
-  DimensionsValue value;
-  if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
+    int field = getSimpleField(key);
+    for (const auto& value : mValues) {
+        if (value.mField.getField() == field) {
+            if (value.mValue.getType() == INT) {
+                return value.mValue.int_value;
+            } else {
+                *err = BAD_TYPE;
+                return 0;
+            }
+        }
+        if ((size_t)value.mField.getPosAtDepth(0) > key) {
+            break;
+        }
+    }
+
     *err = BAD_INDEX;
     return 0;
-  }
-  const DimensionsValue* leafValue = getSingleLeafValue(&value);
-  switch (leafValue->value_case()) {
-    case DimensionsValue::ValueCase::kValueInt:
-      return leafValue->value_int();
-    case DimensionsValue::ValueCase::kValueLong:
-    case DimensionsValue::ValueCase::kValueBool:
-    case DimensionsValue::ValueCase::kValueFloat:
-    case DimensionsValue::ValueCase::kValueTuple:
-    case DimensionsValue::ValueCase::kValueStr:
-    case DimensionsValue::ValueCase::VALUE_NOT_SET: {
-      *err = BAD_TYPE;
-      return 0;
-    }
-  }
 }
 
 const char* LogEvent::GetString(size_t key, status_t* err) const {
-    DimensionsValue value;
-    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
-        *err = BAD_INDEX;
-        return 0;
-    }
-    const DimensionsValue* leafValue = getSingleLeafValue(&value);
-    switch (leafValue->value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            return leafValue->value_str().c_str();
-        case DimensionsValue::ValueCase::kValueInt:
-        case DimensionsValue::ValueCase::kValueLong:
-        case DimensionsValue::ValueCase::kValueBool:
-        case DimensionsValue::ValueCase::kValueFloat:
-        case DimensionsValue::ValueCase::kValueTuple:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
-            *err = BAD_TYPE;
-            return 0;
+    int field = getSimpleField(key);
+    for (const auto& value : mValues) {
+        if (value.mField.getField() == field) {
+            if (value.mValue.getType() == STRING) {
+                return value.mValue.str_value.c_str();
+            } else {
+                *err = BAD_TYPE;
+                return 0;
+            }
+        }
+        if ((size_t)value.mField.getPosAtDepth(0) > key) {
+            break;
         }
     }
+
+    *err = BAD_INDEX;
+    return NULL;
 }
 
 bool LogEvent::GetBool(size_t key, status_t* err) const {
-    DimensionsValue value;
-    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
-        *err = BAD_INDEX;
-        return 0;
-    }
-    const DimensionsValue* leafValue = getSingleLeafValue(&value);
-    switch (leafValue->value_case()) {
-        case DimensionsValue::ValueCase::kValueInt:
-            return leafValue->value_int() != 0;
-        case DimensionsValue::ValueCase::kValueLong:
-            return leafValue->value_long() != 0;
-        case DimensionsValue::ValueCase::kValueBool:
-            return leafValue->value_bool();
-        case DimensionsValue::ValueCase::kValueFloat:
-            return leafValue->value_float() != 0;
-        case DimensionsValue::ValueCase::kValueTuple:
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
-            *err = BAD_TYPE;
-            return 0;
+    int field = getSimpleField(key);
+    for (const auto& value : mValues) {
+        if (value.mField.getField() == field) {
+            if (value.mValue.getType() == INT) {
+                return value.mValue.int_value != 0;
+            } else if (value.mValue.getType() == LONG) {
+                return value.mValue.long_value != 0;
+            } else {
+                *err = BAD_TYPE;
+                return false;
+            }
+        }
+        if ((size_t)value.mField.getPosAtDepth(0) > key) {
+            break;
         }
     }
+
+    *err = BAD_INDEX;
+    return false;
 }
 
 float LogEvent::GetFloat(size_t key, status_t* err) const {
-    DimensionsValue value;
-    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
-        *err = BAD_INDEX;
-        return 0;
-    }
-    const DimensionsValue* leafValue = getSingleLeafValue(&value);
-    switch (leafValue->value_case()) {
-        case DimensionsValue::ValueCase::kValueInt:
-            return (float)leafValue->value_int();
-        case DimensionsValue::ValueCase::kValueLong:
-            return (float)leafValue->value_long();
-        case DimensionsValue::ValueCase::kValueBool:
-            return leafValue->value_bool() ? 1.0f : 0.0f;
-        case DimensionsValue::ValueCase::kValueFloat:
-            return leafValue->value_float();
-        case DimensionsValue::ValueCase::kValueTuple:
-        case DimensionsValue::ValueCase::kValueStr:
-        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
-            *err = BAD_TYPE;
-            return 0;
+    int field = getSimpleField(key);
+    for (const auto& value : mValues) {
+        if (value.mField.getField() == field) {
+            if (value.mValue.getType() == FLOAT) {
+                return value.mValue.float_value;
+            } else {
+                *err = BAD_TYPE;
+                return 0.0;
+            }
+        }
+        if ((size_t)value.mField.getPosAtDepth(0) > key) {
+            break;
         }
     }
-}
 
-void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher,
-                                            std::vector<DimensionsValue> *dimensionsValues) const {
-    findDimensionsValues(mFieldValueMap, matcher, dimensionsValues);
-}
-
-bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher,
-                                           DimensionsValue* dimensionsValue) const {
-    std::vector<DimensionsValue> rootDimensionsValues;
-    findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues);
-    if (rootDimensionsValues.size() != 1) {
-        return false;
-    }
-    *dimensionsValue = rootDimensionsValues.front();
-    return true;
-}
-
-bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField,
-                                                 DimensionsValue* dimensionsValue) const {
-    FieldMatcher matcher;
-    buildSimpleAtomFieldMatcher(mTagId, atomField, &matcher);
-    return GetAtomDimensionsValueProto(matcher, dimensionsValue);
-}
-
-DimensionsValue* LogEvent::findFieldValueOrNull(const Field& field) {
-    auto it = mFieldValueMap.find(field);
-    if (it == mFieldValueMap.end()) {
-        return nullptr;
-    }
-    return &it->second;
+    *err = BAD_INDEX;
+    return 0.0;
 }
 
 string LogEvent::ToString() const {
     ostringstream result;
     result << "{ " << mTimestampNs << " (" << mTagId << ")";
-    for (const auto& itr : mFieldValueMap) {
-        result << FieldToString(itr.first);
+    for (const auto& value : mValues) {
+        result << StringPrintf("%#x", value.mField.getField());
         result << "->";
-        result << DimensionsValueToString(itr.second);
+        result << value.mValue.toString();
         result << " ";
     }
     result << " }";
@@ -433,7 +380,7 @@
 }
 
 void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
-    writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput);
+    writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index d521e09..0895daa 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "field_util.h"
+#include "FieldValue.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 #include <android/util/ProtoOutputStream.h>
@@ -24,11 +24,8 @@
 #include <log/log_read.h>
 #include <private/android_logger.h>
 #include <utils/Errors.h>
-#include <utils/JenkinsHash.h>
 
-#include <memory>
 #include <string>
-#include <map>
 #include <vector>
 
 namespace android {
@@ -37,7 +34,6 @@
 
 using std::string;
 using std::vector;
-
 /**
  * Wrapper for the log_msg structure.
  */
@@ -81,19 +77,6 @@
     bool GetBool(size_t key, status_t* err) const;
     float GetFloat(size_t key, status_t* err) const;
 
-    /*
-     * Get DimensionsValue proto objects from FieldMatcher.
-     */
-    void GetAtomDimensionsValueProtos(
-        const FieldMatcher& matcher, std::vector<DimensionsValue> *dimensionsValues) const;
-    bool GetAtomDimensionsValueProto(
-        const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const;
-
-    /*
-     * Get a DimensionsValue proto objects from Field.
-     */
-    bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const;
-
     /**
      * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
      * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
@@ -129,15 +112,16 @@
     void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;}
 
     inline int size() const {
-        return mFieldValueMap.size();
+        return mValues.size();
     }
 
-    /**
-     * Returns the mutable DimensionsValue proto for the specific the field.
-     */
-    DimensionsValue* findFieldValueOrNull(const Field& field);
+    const std::vector<FieldValue>& getValues() const {
+        return mValues;
+    }
 
-    inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; }
+    std::vector<FieldValue>* getMutableValues() {
+        return &mValues;
+    }
 
 private:
     /**
@@ -151,7 +135,9 @@
      */
     void init(android_log_context context);
 
-    FieldValueMap mFieldValueMap;
+    // The items are naturally sorted in DFS order as we read them. this allows us to do fast
+    // matching.
+    std::vector<FieldValue> mValues;
 
     // This field is used when statsD wants to create log event object and write fields to it. After
     // calling init() function, this object would be destroyed to save memory usage.
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index fae9172..944764b 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -13,23 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/LogMatchingTracker.h"
 #include "matchers/matcher_util.h"
-#include "dimension.h"
 #include "stats_util.h"
-#include "field_util.h"
-
-#include <log/event_tag_map.h>
-#include <log/log_event_list.h>
-#include <log/logprint.h>
-#include <utils/Errors.h>
-
-#include <sstream>
-#include <unordered_map>
 
 using std::ostringstream;
 using std::set;
@@ -93,198 +83,224 @@
     return matched;
 }
 
-namespace {
-
-bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                      const FieldValueMatcher&matcher, Field* rootField, Field* leafField);
-
-bool matchesNonRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                             const FieldValueMatcher&matcher, Field* rootField, Field* leafField) {
-    if (matcher.value_matcher_case() ==
-            FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) {
-        return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field();
-    } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) {
-        bool allMatched = true;
-        Field* newLeafField = leafField->add_child();
-        for (int i = 0; allMatched && i <  matcher.matches_tuple().field_value_matcher_size(); ++i) {
-            const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i);
-            newLeafField->set_field(childMatcher.field());
-            allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, rootField, newLeafField);
-        }
-        leafField->clear_child();
-        return allMatched;
-    } else {
-        auto ret = fieldMap.equal_range(*rootField);
-        int found = 0;
-        for (auto it = ret.first; it != ret.second; ++it) {
-            found++;
-        }
-        // Not found.
-        if (found <= 0) {
-            return false;
-        }
-        if (found > 1) {
-            ALOGE("Found multiple values for optional field.");
-            return false;
-        }
-        bool matched = false;
-        switch (matcher.value_matcher_case()) {
-            case FieldValueMatcher::ValueMatcherCase::kEqBool: {
-                // Logd does not support bool, it is int instead.
-                matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kEqString: {
-                if (IsAttributionUidField(*rootField)) {
-                    const int uid = ret.first->second.value_int();
-                    std::set<string> packageNames =
-                            uidMap.getAppNamesFromUid(uid, true /* normalize*/);
-                    matched = packageNames.find(matcher.eq_string()) != packageNames.end();
-                } else {
-                    matched = (ret.first->second.value_str() == matcher.eq_string());
-                }
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kEqInt: {
-                    int64_t val = ret.first->second.has_value_int() ?
-                                  ret.first->second.value_int() : ret.first->second.value_long();
-                    matched = (val == matcher.eq_int());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kLtInt: {
-                int64_t val = ret.first->second.has_value_int() ?
-                              ret.first->second.value_int() : ret.first->second.value_long();
-                matched = (val < matcher.lt_int());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kGtInt: {
-                int64_t val = ret.first->second.has_value_int() ?
-                              ret.first->second.value_int() : ret.first->second.value_long();
-                matched = (val > matcher.gt_int());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
-                matched = (ret.first->second.value_float() < matcher.lt_float());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
-                matched = (ret.first->second.value_float() > matcher.gt_float());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kLteInt: {
-                int64_t val = ret.first->second.has_value_int() ?
-                              ret.first->second.value_int() : ret.first->second.value_long();
-                matched = (val <= matcher.lte_int());
-                break;
-            }
-            case FieldValueMatcher::ValueMatcherCase::kGteInt: {
-                int64_t val = ret.first->second.has_value_int() ?
-                              ret.first->second.value_int() : ret.first->second.value_long();
-                matched = (val >= matcher.gte_int());
-                break;
-            }
-            default:
-                break;
-        }
-        return matched;
+bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
+                    const string& str_match) {
+    if (isAttributionUidField(field, value)) {
+        int uid = value.int_value;
+        std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+        return packageNames.find(str_match) != packageNames.end();
+    } else if (value.getType() == STRING) {
+        return value.str_value == str_match;
     }
+    return false;
 }
 
-bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                          const FieldValueMatcher&matcher,
-                          Field* rootField, Field* leafField) {
-    if (matcher.position() == Position::FIRST) {
-        leafField->set_position_index(0);
-        bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
-        leafField->clear_position_index();
-        return res;
-    } else {
-        auto itLower = fieldMap.lower_bound(*rootField);
-        if (itLower == fieldMap.end()) {
+bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
+                   const vector<FieldValue>& values, int start, int end, int depth) {
+    if (depth > 2) {
+        ALOGE("Depth > 3 not supported");
+        return false;
+    }
+
+    if (start >= end) {
+        return false;
+    }
+
+    // Filter by entry field first
+    int newStart = -1;
+    int newEnd = end;
+    // because the fields are naturally sorted in the DFS order. we can safely
+    // break when pos is larger than the one we are searching for.
+    for (int i = start; i < end; i++) {
+        int pos = values[i].mField.getPosAtDepth(depth);
+        if (pos == matcher.field()) {
+            if (newStart == -1) {
+                newStart = i;
+            }
+            newEnd = i + 1;
+        } else if (pos > matcher.field()) {
+            break;
+        }
+    }
+
+    // Now we have zoomed in to a new range
+    start = newStart;
+    end = newEnd;
+
+    if (start == -1) {
+        // No such field found.
+        return false;
+    }
+
+    vector<pair<int, int>> ranges; // the ranges are for matching ANY position
+    if (matcher.has_position()) {
+        // Repeated fields position is stored as a node in the path.
+        depth++;
+        if (depth > 2) {
             return false;
         }
-
-        const int leafFieldNum = leafField->field();
-        leafField->set_field(leafFieldNum + 1);
-        auto itUpper = fieldMap.lower_bound(*rootField);
-        // Resets the field number.
-        leafField->set_field(leafFieldNum);
-
         switch (matcher.position()) {
-             case Position::LAST:
-                 {
-                     itUpper--;
-                     if (itUpper == fieldMap.end()) {
-                        return false;
-                     } else {
-                         int last_index = getPositionByReferenceField(*rootField, itUpper->first);
-                         if (last_index < 0) {
-                            return false;
-                         }
-                         leafField->set_position_index(last_index);
-                         bool res = matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
-                         leafField->clear_position_index();
-                         return res;
-                     }
-                 }
-                 break;
-             case Position::ANY:
-                 {
-                    bool matched = false;
-                    for (auto it = itLower; it != itUpper; ++it) {
-                        int index = getPositionByReferenceField(*rootField, it->first);
-                        if (index >= 0) {
-                             leafField->set_position_index(index);
-                             matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
-                             leafField->clear_position_index();
-                             if (matched) {
-                                break;
-                             }
-                        }
+            case Position::FIRST: {
+                for (int i = start; i < end; i++) {
+                    int pos = values[i].mField.getPosAtDepth(depth);
+                    if (pos != 1) {
+                        // Again, the log elements are stored in sorted order. so
+                        // once the position is > 1, we break;
+                        end = i;
+                        break;
                     }
-                    return matched;
-                 }
-             default:
-                return false;
-         }
-    }
-
-}
-
-bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
-                      const FieldValueMatcher&matcher, Field* rootField, Field* leafField) {
-    if (!matcher.has_position()) {
-        return matchesNonRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
+                }
+                ranges.push_back(std::make_pair(start, end));
+                break;
+            }
+            case Position::LAST: {
+                // move the starting index to the first LAST field at the depth.
+                for (int i = start; i < end; i++) {
+                    if (values[i].mField.isLastPos(depth)) {
+                        start = i;
+                        break;
+                    }
+                }
+                ranges.push_back(std::make_pair(start, end));
+                break;
+            }
+            case Position::ANY: {
+                // ANY means all the children matchers match in any of the sub trees, it's a match
+                newStart = start;
+                newEnd = end;
+                // Here start is guaranteed to be a valid index.
+                int currentPos = values[start].mField.getPosAtDepth(depth);
+                // Now find all sub trees ranges.
+                for (int i = start; i < end; i++) {
+                    int newPos = values[i].mField.getPosAtDepth(depth);
+                    if (newPos != currentPos) {
+                        ranges.push_back(std::make_pair(newStart, i));
+                        newStart = i;
+                        currentPos = newPos;
+                    }
+                }
+                ranges.push_back(std::make_pair(newStart, end));
+                break;
+            }
+            case Position::POSITION_UNKNOWN:
+                break;
+        }
     } else {
-        return matchesRepeatedField(uidMap, fieldMap, matcher, rootField, leafField);
+        // No position
+        ranges.push_back(std::make_pair(start, end));
+    }
+    // start and end are still pointing to the matched range.
+    switch (matcher.value_matcher_case()) {
+        case FieldValueMatcher::kMatchesTuple: {
+            ++depth;
+            // If any range matches all matchers, good.
+            for (const auto& range : ranges) {
+                bool matched = true;
+                for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
+                    if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
+                                       depth)) {
+                        matched = false;
+                        break;
+                    }
+                }
+                if (matched) return true;
+            }
+            return false;
+        }
+        case FieldValueMatcher::ValueMatcherCase::kEqBool: {
+            for (int i = start; i < end; i++) {
+                if ((values[i].mValue.getType() == INT &&
+                     (values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
+                    (values[i].mValue.getType() == LONG &&
+                     (values[i].mValue.long_value != 0) == matcher.eq_bool())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        case FieldValueMatcher::ValueMatcherCase::kEqString: {
+            for (int i = start; i < end; i++) {
+                if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
+                                   matcher.eq_string())) {
+                    return true;
+                }
+            }
+        }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kEqInt:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == INT &&
+                    (matcher.eq_int() == values[i].mValue.int_value)) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kLtInt:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == INT &&
+                    (values[i].mValue.int_value < matcher.lt_int())) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kGtInt:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == INT &&
+                    (values[i].mValue.int_value > matcher.gt_int())) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kLtFloat:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == FLOAT &&
+                    (values[i].mValue.float_value < matcher.lt_float())) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kGtFloat:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == FLOAT &&
+                    (values[i].mValue.float_value > matcher.gt_float())) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kLteInt:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == INT &&
+                    (values[i].mValue.int_value <= matcher.lte_int())) {
+                    return true;
+                }
+            }
+            return false;
+        case FieldValueMatcher::ValueMatcherCase::kGteInt:
+            for (int i = start; i < end; i++) {
+                if (values[i].mValue.getType() == INT &&
+                    (values[i].mValue.int_value >= matcher.gte_int())) {
+                    return true;
+                }
+            }
+            return false;
+        default:
+            return false;
     }
 }
 
-}  // namespace
-
 bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
                    const LogEvent& event) {
     if (simpleMatcher.field_value_matcher_size() <= 0) {
         return event.GetTagId() == simpleMatcher.atom_id();
     }
-    Field root_field;
-    root_field.set_field(simpleMatcher.atom_id());
-    FieldValueMatcher root_field_matcher;
-    root_field_matcher.set_field(simpleMatcher.atom_id());
-    for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) {
-        *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() =
-            simpleMatcher.field_value_matcher(i);
+    for (const auto& matcher : simpleMatcher.field_value_matcher()) {
+        if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
+            return false;
+        }
     }
-    return matchFieldSimple(
-        uidMap, event.getFieldValueMap(), root_field_matcher, &root_field, &root_field);
+    return true;
 }
 
-void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher,
-                      std::vector<DimensionsValue> *dimensionKeys) {
-    if (matcher.has_field()) {
-        findDimensionsValues(event.getFieldValueMap(), matcher, dimensionKeys);
-    }
-}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index a45a9fb..872cd8e 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -45,9 +45,6 @@
 bool matchesSimple(const UidMap& uidMap,
     const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
 
-void getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher,
-                      std::vector<DimensionsValue> *dimensionKeys);
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5a042b6..bd2674b 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,7 +21,6 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
-#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -69,16 +68,26 @@
         mBucketSizeNs = LLONG_MAX;
     }
 
-    // TODO: use UidMap if uid->pkg_name is required
-    mDimensionsInWhat = metric.dimensions_in_what();
-    mDimensionsInCondition = metric.dimensions_in_condition();
+    if (metric.has_dimensions_in_what()) {
+        translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
+    }
+
+    if (metric.has_dimensions_in_condition()) {
+        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+    }
 
     if (metric.links().size() > 0) {
-        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                               metric.links().end());
+        for (const auto& link : metric.links()) {
+            Metric2Condition mc;
+            mc.conditionId = link.condition();
+            translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
+            translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            mMetric2ConditionLinks.push_back(mc);
+        }
+        mConditionSliced = true;
     }
-    mConditionSliced = (metric.links().size() > 0)||
-        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
+
+    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -92,26 +101,6 @@
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
-void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
-    flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_id(mMetricId);
-
-    auto count_metrics = report->mutable_count_metrics();
-    for (const auto& counter : mPastBuckets) {
-        CountMetricData* metricData = count_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() =
-            counter.first.getDimensionKeyInWhat().getDimensionsValue();
-        *metricData->mutable_dimensions_in_condition() =
-            counter.first.getDimensionKeyInCondition().getDimensionsValue();
-        for (const auto& bucket : counter.second) {
-            CountBucketInfo* bucketInfo = metricData->add_bucket_info();
-            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
-            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
-            bucketInfo->set_count(bucket.mCount);
-        }
-    }
-}
-
 void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
@@ -122,8 +111,6 @@
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
 
-    VLOG("metric %lld dump report now...",(long long)mMetricId);
-
     for (const auto& counter : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = counter.first;
         VLOG("  dimension key %s", dimensionKey.c_str());
@@ -134,15 +121,13 @@
         // First fill dimension.
         long long dimensionInWhatToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(
-            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
         protoOutput->end(dimensionInWhatToken);
 
         if (dimensionKey.hasDimensionKeyInCondition()) {
             long long dimensionInConditionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
-            writeDimensionsValueProtoToStream(
-                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
             protoOutput->end(dimensionInConditionToken);
         }
 
@@ -200,7 +185,6 @@
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
-
     flushIfNeededLocked(eventTimeNs);
 
     if (condition == false) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b06c77b..0c4291d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -57,7 +57,6 @@
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
-    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 65cbc4a..6b321e1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -21,7 +21,6 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
-#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -68,8 +67,7 @@
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
-      mNested(nesting),
-      mInternalDimensions(internalDimensions) {
+      mNested(nesting) {
     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
     // not related. Maybe we should add a template in the future??
@@ -79,16 +77,28 @@
         mBucketSizeNs = LLONG_MAX;
     }
 
-    // TODO: use UidMap if uid->pkg_name is required
-    mDimensionsInWhat = metric.dimensions_in_what();
-    mDimensionsInCondition = metric.dimensions_in_condition();
+    if (metric.has_dimensions_in_what()) {
+        translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
+    }
+
+    if (internalDimensions.has_field()) {
+        translateFieldMatcher(internalDimensions, &mInternalDimensions);
+    }
+
+    if (metric.has_dimensions_in_condition()) {
+        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+    }
 
     if (metric.links().size() > 0) {
-        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                               metric.links().end());
+        for (const auto& link : metric.links()) {
+            Metric2Condition mc;
+            mc.conditionId = link.condition();
+            translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
+            translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            mMetric2ConditionLinks.push_back(mc);
+        }
     }
-    mConditionSliced = (metric.links().size() > 0)||
-        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
+    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -174,37 +184,18 @@
     }
 }
 
-void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
-    flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_id(mMetricId);
-
-    auto duration_metrics = report->mutable_duration_metrics();
-    for (const auto& pair : mPastBuckets) {
-        DurationMetricData* metricData = duration_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() =
-            pair.first.getDimensionKeyInWhat().getDimensionsValue();
-        *metricData->mutable_dimensions_in_condition() =
-            pair.first.getDimensionKeyInCondition().getDimensionsValue();
-        for (const auto& bucket : pair.second) {
-            auto bucketInfo = metricData->add_bucket_info();
-            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
-            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
-            bucketInfo->set_duration_nanos(bucket.mDuration);
-        }
-    }
-}
-
 void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                                 ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
     if (mPastBuckets.empty()) {
+        VLOG(" Duration metric, empty return");
         return;
     }
 
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
 
-    VLOG("metric %lld dump report now...", (long long)mMetricId);
+    VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
 
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
@@ -216,15 +207,13 @@
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(
-            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
         protoOutput->end(dimensionToken);
 
         if (dimensionKey.hasDimensionKeyInCondition()) {
             long long dimensionInConditionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
-            writeDimensionsValueProtoToStream(
-                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
             protoOutput->end(dimensionInConditionToken);
         }
 
@@ -339,8 +328,8 @@
 
     auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
 
-    std::vector<DimensionsValue> values;
-    getDimensionKeys(event, mInternalDimensions, &values);
+    std::vector<HashableDimensionKey> values;
+    filterValues(mInternalDimensions, event.getValues(), &values);
     if (values.empty()) {
         if (matcherIndex == mStartIndex) {
             it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
@@ -349,13 +338,11 @@
             it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false);
         }
     } else {
-        for (const DimensionsValue& value : values) {
+        for (const auto& value : values) {
             if (matcherIndex == mStartIndex) {
-                it->second->noteStart(
-                    HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
+                it->second->noteStart(value, condition, event.GetTimestampNs(), conditionKeys);
             } else if (matcherIndex == mStopIndex) {
-                it->second->noteStop(
-                   HashableDimensionKey(value), event.GetTimestampNs(), false);
+                it->second->noteStop(value, event.GetTimestampNs(), false);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index a496016..5f29281 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -57,7 +57,6 @@
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
-    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -90,7 +89,7 @@
     const bool mNested;
 
     // The dimension from the atom predicate. e.g., uid, wakelock name.
-    const FieldMatcher mInternalDimensions;
+    vector<Matcher> mInternalDimensions;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 936a2ef1..ed7e44d 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -55,8 +55,13 @@
                                          const uint64_t startTimeNs)
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
     if (metric.links().size() > 0) {
-        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                               metric.links().end());
+        for (const auto& link : metric.links()) {
+            Metric2Condition mc;
+            mc.conditionId = link.condition();
+            translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
+            translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            mMetric2ConditionLinks.push_back(mc);
+        }
         mConditionSliced = true;
     }
     mProto = std::make_unique<ProtoOutputStream>();
@@ -88,10 +93,6 @@
     return buffer;
 }
 
-void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
-
-}
-
 void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     if (mProto->size() <= 0) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 394ed23..3f2c5a5 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -48,7 +48,6 @@
 
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
-    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 62ee6ef..da0cafe 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -19,7 +19,6 @@
 
 #include "GaugeMetricProducer.h"
 #include "guardrail/StatsdStats.h"
-#include "dimension.h"
 #include "stats_log_util.h"
 
 #include <cutils/log.h>
@@ -77,18 +76,29 @@
     mBucketSizeNs = bucketSizeMills * 1000000;
 
     mSamplingType = metric.sampling_type();
-    mFieldFilter = metric.gauge_fields_filter();
+    if (!metric.gauge_fields_filter().include_all()) {
+        translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers);
+    }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensionsInWhat = metric.dimensions_in_what();
-    mDimensionsInCondition = metric.dimensions_in_condition();
+    if (metric.has_dimensions_in_what()) {
+        translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
+    }
+
+    if (metric.has_dimensions_in_condition()) {
+        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+    }
 
     if (metric.links().size() > 0) {
-        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                               metric.links().end());
+        for (const auto& link : metric.links()) {
+            Metric2Condition mc;
+            mc.conditionId = link.condition();
+            translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
+            translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            mMetric2ConditionLinks.push_back(mc);
+        }
     }
-    mConditionSliced = (metric.links().size() > 0)||
-        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
+    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     // Kicks off the puller immediately.
     if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -115,13 +125,6 @@
     }
 }
 
-void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
-    flushIfNeededLocked(dumpTimeNs);
-    ProtoOutputStream pbOutput;
-    onDumpReportLocked(dumpTimeNs, &pbOutput);
-    parseProtoOutputStream(pbOutput, report);
-}
-
 void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     VLOG("gauge metric %lld report now...", (long long)mMetricId);
@@ -144,15 +147,13 @@
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(
-            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
         protoOutput->end(dimensionToken);
 
         if (dimensionKey.hasDimensionKeyInCondition()) {
             long long dimensionInConditionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
-            writeDimensionsValueProtoToStream(
-                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
             protoOutput->end(dimensionInConditionToken);
         }
 
@@ -169,7 +170,7 @@
                 long long atomsToken =
                     protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
                 for (const auto& atom : bucket.mGaugeAtoms) {
-                    writeFieldValueTreeToStream(*atom.mFields, protoOutput);
+                    writeFieldValueTreeToStream(mTagId, *(atom.mFields), protoOutput);
                 }
                 protoOutput->end(atomsToken);
 
@@ -246,13 +247,14 @@
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
-std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
-    std::shared_ptr<FieldValueMap> gaugeFields =
-        std::make_shared<FieldValueMap>(event.getFieldValueMap());
-    if (!mFieldFilter.include_all()) {
-        filterFields(mFieldFilter.fields(), gaugeFields.get());
+std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
+    if (mFieldMatchers.size() > 0) {
+        std::shared_ptr<vector<FieldValue>> gaugeFields = std::make_shared<vector<FieldValue>>();
+        filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get());
+        return gaugeFields;
+    } else {
+        return std::make_shared<vector<FieldValue>>(event.getValues());
     }
-    return gaugeFields;
 }
 
 void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
@@ -292,6 +294,7 @@
         return;
     }
     uint64_t eventTimeNs = event.GetTimestampNs();
+    mTagId = event.GetTagId();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
@@ -308,20 +311,18 @@
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
-    GaugeAtom gaugeAtom;
-    gaugeAtom.mFields = getGaugeFields(event);
-    gaugeAtom.mTimestamps = eventTimeNs;
+    GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
     (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
     // Anomaly detection on gauge metric only works when there is one numeric
     // field specified.
     if (mAnomalyTrackers.size() > 0) {
         if (gaugeAtom.mFields->size() == 1) {
-            const DimensionsValue& dimensionsValue = gaugeAtom.mFields->begin()->second;
+            const Value& value = gaugeAtom.mFields->begin()->mValue;
             long gaugeVal = 0;
-            if (dimensionsValue.has_value_int()) {
-                gaugeVal = (long)dimensionsValue.value_int();
-            } else if (dimensionsValue.has_value_long()) {
-                gaugeVal = dimensionsValue.value_long();
+            if (value.getType() == INT) {
+                gaugeVal = (long)value.int_value;
+            } else if (value.getType() == LONG) {
+                gaugeVal = value.long_value;
             }
             for (auto& tracker : mAnomalyTrackers) {
                 tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
@@ -334,15 +335,15 @@
 void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() {
     status_t err = NO_ERROR;
     for (const auto& slice : *mCurrentSlicedBucket) {
-        if (slice.second.empty() || slice.second.front().mFields->empty()) {
+        if (slice.second.empty()) {
             continue;
         }
-        const DimensionsValue& dimensionsValue = slice.second.front().mFields->begin()->second;
+        const Value& value = slice.second.front().mFields->front().mValue;
         long gaugeVal = 0;
-        if (dimensionsValue.has_value_int()) {
-            gaugeVal = (long)dimensionsValue.value_int();
-        } else if (dimensionsValue.has_value_long()) {
-            gaugeVal = dimensionsValue.value_long();
+        if (value.getType() == INT) {
+            gaugeVal = (long)value.int_value;
+        } else if (value.getType() == LONG) {
+            gaugeVal = value.long_value;
         }
         (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index d5d34be..c3ae6ce 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,7 +33,10 @@
 namespace statsd {
 
 struct GaugeAtom {
-    std::shared_ptr<FieldValueMap> mFields;
+    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t timeNs)
+        : mFields(fields), mTimestamps(timeNs) {
+    }
+    std::shared_ptr<vector<FieldValue>> mFields;
     int64_t mTimestamps;
 };
 
@@ -87,7 +90,6 @@
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
-    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
@@ -113,6 +115,8 @@
 
     void pullLocked();
 
+    int mTagId;
+
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
@@ -133,12 +137,12 @@
     void updateCurrentSlicedBucketForAnomaly();
 
     // Whitelist of fields to report. Empty means all are reported.
-    FieldFilter mFieldFilter;
+    std::vector<Matcher> mFieldMatchers;
 
     GaugeMetric::SamplingType mSamplingType;
 
     // apply a whitelist on the original input
-    std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
+    std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 85e655b..beb9015 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "MetricProducer.h"
 
-#include "dimension.h"
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+#include "MetricProducer.h"
 
 namespace android {
 namespace os {
@@ -35,9 +36,10 @@
 
     std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
-        for (const auto& link : mConditionLinks) {
-            getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
+        for (const auto& link : mMetric2ConditionLinks) {
+            getDimensionForCondition(event, link, &conditionKey[link.conditionId]);
         }
+
         auto conditionState =
             mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
                            &dimensionKeysInCondition);
@@ -46,20 +48,19 @@
         condition = mCondition;
     }
 
-    vector<DimensionsValue> dimensionInWhatValues;
-    if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
-        getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+    vector<HashableDimensionKey> dimensionInWhatValues;
+    if (mDimensionsInWhat.size() > 0) {
+        filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhatValues);
     }
 
     if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
         onMatchedLogEventInternalLocked(
             matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
     } else if (dimensionKeysInCondition.empty()) {
-        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
-            onMatchedLogEventInternalLocked(
-                matcherIndex,
-                MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY),
-                conditionKey, condition, event);
+        for (const HashableDimensionKey& whatValue : dimensionInWhatValues) {
+            onMatchedLogEventInternalLocked(matcherIndex,
+                                            MetricDimensionKey(whatValue, DEFAULT_DIMENSION_KEY),
+                                            conditionKey, condition, event);
         }
     } else if (dimensionInWhatValues.empty()) {
         for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
@@ -69,12 +70,11 @@
                 conditionKey, condition, event);
         }
     } else {
-        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+        for (const auto& whatValue : dimensionInWhatValues) {
             for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
                 onMatchedLogEventInternalLocked(
-                    matcherIndex,
-                    MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
-                    conditionKey, condition, event);
+                        matcherIndex, MetricDimensionKey(whatValue, conditionDimensionKey),
+                        conditionKey, condition, event);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 542dd8a..e8f8299 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -19,6 +19,7 @@
 
 #include <shared_mutex>
 
+#include "HashableDimensionKey.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "config/ConfigKey.h"
@@ -109,11 +110,6 @@
         std::lock_guard<std::mutex> lock(mMutex);
         return onDumpReportLocked(dumpTimeNs, protoOutput);
     }
-    // This method does not clear the past buckets.
-    void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
-        std::lock_guard<std::mutex> lock(mMutex);
-        return onDumpReportLocked(dumpTimeNs, report);
-    }
 
     void dumpStates(FILE* out, bool verbose) const {
         std::lock_guard<std::mutex> lock(mMutex);
@@ -150,7 +146,6 @@
     virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0;
     virtual void onDumpReportLocked(const uint64_t dumpTimeNs,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
-    virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0;
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
@@ -203,10 +198,10 @@
 
     int mConditionTrackerIndex;
 
-    FieldMatcher mDimensionsInWhat;  // The dimensions_in_what defined in statsd_config
-    FieldMatcher mDimensionsInCondition;  // The dimensions_in_condition defined in statsd_config
+    vector<Matcher> mDimensionsInWhat;       // The dimensions_in_what defined in statsd_config
+    vector<Matcher> mDimensionsInCondition;  // The dimensions_in_condition defined in statsd_config
 
-    std::vector<MetricConditionLink> mConditionLinks;
+    std::vector<Metric2Condition> mMetric2ConditionLinks;
 
     std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6c21b05..dd6735b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define DEBUG false  // STOPSHIP if true
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 #include "MetricsManager.h"
 #include "statslog.h"
@@ -151,14 +151,6 @@
     initLogSourceWhiteList();
 }
 
-void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) {
-    for (const auto& producer : mAllMetricProducers) {
-        if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
-            producer->onDumpReport(dumpTimeStampNs, report->add_metrics());
-        }
-    }
-}
-
 void MetricsManager::dumpStates(FILE* out, bool verbose) {
     fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
     {
@@ -173,9 +165,8 @@
     }
 }
 
-void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) {
+void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
-    uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
     // one StatsLogReport per MetricProduer
     for (const auto& producer : mAllMetricProducers) {
         if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 2b30f44..d4f844f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -70,8 +70,8 @@
     };
 
     // Config source owner can call onDumpReport() to get all the metrics collected.
-    virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
-    virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
+    virtual void onDumpReport(const uint64_t dumpTimeNs,
+                              android::util::ProtoOutputStream* protoOutput);
 
     // Computes the total byte size of all metrics managed by a single config source.
     // Does not change the state.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7b1944c..45b4ac0 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -17,7 +17,6 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "dimension.h"
 #include "ValueMetricProducer.h"
 #include "guardrail/StatsdStats.h"
 #include "stats_log_util.h"
@@ -79,15 +78,28 @@
     }
 
     mBucketSizeNs = bucketSizeMills * 1000000;
-    mDimensionsInWhat = metric.dimensions_in_what();
-    mDimensionsInCondition = metric.dimensions_in_condition();
+    if (metric.has_dimensions_in_what()) {
+        translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
+    }
+
+    if (metric.has_dimensions_in_condition()) {
+        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+    }
 
     if (metric.links().size() > 0) {
-        mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
-                               metric.links().end());
+        for (const auto& link : metric.links()) {
+            Metric2Condition mc;
+            mc.conditionId = link.condition();
+            translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
+            translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            mMetric2ConditionLinks.push_back(mc);
+        }
     }
-    mConditionSliced = (metric.links().size() > 0)||
-        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
+
+    if (mValueField.child_size()) {
+        mField = mValueField.child(0).field();
+    }
+    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     if (!metric.has_condition() && mPullTagId != -1) {
         VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -117,25 +129,6 @@
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
-void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
-    flushIfNeededLocked(dumpTimeNs);
-    report->set_metric_id(mMetricId);
-    auto value_metrics = report->mutable_value_metrics();
-    for (const auto& pair : mPastBuckets) {
-        ValueMetricData* metricData = value_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() =
-            pair.first.getDimensionKeyInWhat().getDimensionsValue();
-        *metricData->mutable_dimensions_in_condition() =
-            pair.first.getDimensionKeyInCondition().getDimensionsValue();
-        for (const auto& bucket : pair.second) {
-            ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
-            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
-            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
-            bucketInfo->set_value(bucket.mValue);
-        }
-    }
-}
-
 void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %lld dump report now...", (long long)mMetricId);
@@ -155,14 +148,12 @@
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
             FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(
-            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), protoOutput);
         protoOutput->end(dimensionToken);
         if (dimensionKey.hasDimensionKeyInCondition()) {
             long long dimensionInConditionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
-            writeDimensionsValueProtoToStream(
-                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), protoOutput);
             protoOutput->end(dimensionInConditionToken);
         }
 
@@ -284,11 +275,11 @@
     }
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
-    std::shared_ptr<FieldValueMap> valueFieldMap = getValueFields(event);
-    if (valueFieldMap->empty() || valueFieldMap->size() > 1) {
+    int error = 0;
+    const long value = event.GetLong(mField, &error);
+    if (error < 0) {
         return;
     }
-    const long value = getLongFromDimenValue(valueFieldMap->begin()->second);
 
     if (mPullTagId != -1) { // for pulled events
         if (mCondition == true) {
@@ -324,13 +315,6 @@
     }
 }
 
-std::shared_ptr<FieldValueMap> ValueMetricProducer::getValueFields(const LogEvent& event) {
-    std::shared_ptr<FieldValueMap> valueFields =
-        std::make_shared<FieldValueMap>(event.getFieldValueMap());
-    filterFields(mValueField, valueFields.get());
-    return valueFields;
-}
-
 void ValueMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
     uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index bf5b7df..6701a46 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -89,7 +89,6 @@
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
-    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -120,6 +119,8 @@
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
 
+    int mField;
+
     // internal state of a bucket.
     typedef struct {
         // Pulled data always come in pair of <start, end>. This holds the value
@@ -142,8 +143,6 @@
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
-    std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
-
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 356a81c..8f236fa 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -62,7 +62,7 @@
 public:
     DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex,
-                    const FieldMatcher& dimensionInCondition, bool nesting,
+                    const std::vector<Matcher>& dimensionInCondition, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t currentBucketNum, uint64_t startTimeNs,
                     uint64_t bucketSizeNs, bool conditionSliced,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
@@ -182,7 +182,7 @@
 
     const int64_t mBucketSizeNs;
 
-    const FieldMatcher mDimensionInCondition;
+    const std::vector<Matcher>& mDimensionInCondition;
 
     const bool mNested;
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index c3bafc6..c29876b 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -27,7 +27,7 @@
 MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
                                        const MetricDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex,
-                                       const FieldMatcher& dimensionInCondition, bool nesting,
+                                       const vector<Matcher>& dimensionInCondition, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t currentBucketNum,
                                        uint64_t startTimeNs, uint64_t bucketSizeNs,
                                        bool conditionSliced,
@@ -55,9 +55,7 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mInfos.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
-            newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
             ALOGE("MaxDurTracker %lld dropping data for dimension key %s",
@@ -229,10 +227,11 @@
         ConditionState conditionState = mWizard->query(
             mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
             &conditionDimensionKeySet);
-        bool conditionMet = (conditionState == ConditionState::kTrue) &&
-            (!mDimensionInCondition.has_field() ||
-             conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
-                conditionDimensionKeySet.end());
+        bool conditionMet =
+                (conditionState == ConditionState::kTrue) &&
+                (mDimensionInCondition.size() == 0 ||
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+                         conditionDimensionKeySet.end());
         VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
         noteConditionChanged(pair.first, conditionMet, timestamp);
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index fba4119..95863b6 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -30,7 +30,7 @@
 public:
     MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
                        sp<ConditionWizard> wizard, int conditionIndex,
-                       const FieldMatcher& dimensionInCondition, bool nesting,
+                       const std::vector<Matcher>& dimensionInCondition, bool nesting,
                        uint64_t currentBucketStartNs, uint64_t currentBucketNum,
                        uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
                        const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 85f7b7c..f583f91 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -26,7 +26,7 @@
 
 OringDurationTracker::OringDurationTracker(
         const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
-        sp<ConditionWizard> wizard, int conditionIndex, const FieldMatcher& dimensionInCondition,
+        sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition,
         bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum,
         uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
         const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
@@ -53,9 +53,7 @@
     }
     if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mConditionKeyMap.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
-            newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
             ALOGE("OringDurTracker %lld dropping data for dimension key %s",
@@ -229,9 +227,9 @@
                 mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
                                mDimensionInCondition, &conditionDimensionKeySet);
             if (conditionState != ConditionState::kTrue ||
-                (mDimensionInCondition.has_field() &&
+                (mDimensionInCondition.size() != 0 &&
                  conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
-                    conditionDimensionKeySet.end())) {
+                         conditionDimensionKeySet.end())) {
                 startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
                 VLOG("Key %s started -> paused", key.c_str());
@@ -261,9 +259,9 @@
                 mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
                                mDimensionInCondition, &conditionDimensionKeySet);
             if (conditionState == ConditionState::kTrue &&
-                (!mDimensionInCondition.has_field() ||
-                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
-                    != conditionDimensionKeySet.end())) {
+                (mDimensionInCondition.size() == 0 ||
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+                         conditionDimensionKeySet.end())) {
                 pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
                 VLOG("Key %s paused -> started", key.c_str());
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 73e50e0..07c1329 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -29,8 +29,8 @@
 public:
     OringDurationTracker(const ConfigKey& key, const int64_t& id,
                          const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                         int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
-                         uint64_t currentBucketStartNs, uint64_t currentBucketNum,
+                         int conditionIndex, const std::vector<Matcher>& dimensionInCondition,
+                         bool nesting, uint64_t currentBucketStartNs, uint64_t currentBucketNum,
                          uint64_t startTimeNs, uint64_t bucketSizeNs, bool conditionSliced,
                          const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 205c8e4..769f46d 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -38,6 +38,22 @@
 namespace os {
 namespace statsd {
 
+namespace {
+
+bool hasLeafNode(const FieldMatcher& matcher) {
+    if (!matcher.has_field()) {
+        return false;
+    }
+    for (int i = 0; i < matcher.child_size(); ++i) {
+        if (hasLeafNode(matcher.child(i))) {
+            return true;
+        }
+    }
+    return true;
+}
+
+}  // namespace
+
 bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
                                  const bool usedForDimension,
                                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 0d7b722..691423e 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define DEBUG false  // STOPSHIP if true
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 
 #include "guardrail/StatsdStats.h"
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 6c61400..86c258b 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -16,9 +16,10 @@
 
 #include "stats_log_util.h"
 
+#include <logd/LogEvent.h>
+#include <utils/Log.h>
 #include <set>
 #include <stack>
-#include <utils/Log.h>
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
@@ -42,6 +43,8 @@
 const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
 const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
 
+const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
+
 // for MessageValue Proto
 const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1;
 
@@ -51,52 +54,79 @@
 const int FIELD_ID_TOTAL_PULL = 2;
 const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
 const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
+namespace {
 
-void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
-                                       ProtoOutputStream* protoOutput) {
-    if (!dimensionsValue.has_field()) {
+void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
+                                 int prefix, ProtoOutputStream* protoOutput) {
+    size_t count = dims.size();
+    while (*index < count) {
+        const auto& dim = dims[*index];
+        const int valueDepth = dim.mField.getDepth();
+        const int valuePrefix = dim.mField.getPrefix(depth);
+        const int fieldNum = dim.mField.getPosAtDepth(depth);
+        if (valueDepth > 2) {
+            ALOGE("Depth > 2 not supported");
+            return;
+        }
+
+        if (depth == valueDepth && valuePrefix == prefix) {
+            long long token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                                 DIMENSIONS_VALUE_TUPLE_VALUE);
+            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
+            switch (dim.mValue.getType()) {
+                case INT:
+                    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
+                                       dim.mValue.int_value);
+                    break;
+                case LONG:
+                    protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
+                                       (long long)dim.mValue.long_value);
+                    break;
+                case FLOAT:
+                    protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
+                                       dim.mValue.float_value);
+                    break;
+                case STRING:
+                    protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
+                                       dim.mValue.str_value);
+                    break;
+                default:
+                    break;
+            }
+            if (token != 0) {
+                protoOutput->end(token);
+            }
+            (*index)++;
+        } else if (valueDepth > depth && valuePrefix == prefix) {
+            // Writing the sub tree
+            long long dimensionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
+            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
+            long long tupleToken =
+                    protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
+            writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
+                                        protoOutput);
+            protoOutput->end(tupleToken);
+            protoOutput->end(dimensionToken);
+        } else {
+            // Done with the prev sub tree
+            return;
+        }
+    }
+}
+
+}  // namespace
+
+void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStream* protoOutput) {
+    if (dimension.getValues().size() == 0) {
         return;
     }
-    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
-    switch (dimensionsValue.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
-                dimensionsValue.value_str());
-            break;
-        case DimensionsValue::ValueCase::kValueInt:
-            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
-                dimensionsValue.value_int());
-            break;
-        case DimensionsValue::ValueCase::kValueLong:
-            protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
-                dimensionsValue.value_long());
-            break;
-        case DimensionsValue::ValueCase::kValueBool:
-            protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL,
-                dimensionsValue.value_bool());
-            break;
-        case DimensionsValue::ValueCase::kValueFloat:
-            protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
-                dimensionsValue.value_float());
-            break;
-        case DimensionsValue::ValueCase::kValueTuple:
-            {
-                long long tupleToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
-                for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) {
-                    long long token = protoOutput->start(
-                        FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED
-                        | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO);
-                    writeDimensionsValueProtoToStream(
-                        dimensionsValue.value_tuple().dimensions_value(i), protoOutput);
-                    protoOutput->end(token);
-                }
-                protoOutput->end(tupleToken);
-            }
-            break;
-        default:
-            break;
-    }
+    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
+                       dimension.getValues()[0].mField.getTag());
+    long long topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
+    size_t index = 0;
+    writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, protoOutput);
+    protoOutput->end(topToken);
 }
 
 // for Field Proto
@@ -104,133 +134,92 @@
 const int FIELD_POSITION_INDEX = 2;
 const int FIELD_CHILD = 3;
 
-void writeFieldProtoToStream(
-    const Field& field, util::ProtoOutputStream* protoOutput) {
-    if (!field.has_field()) {
-        return;
-    }
-    protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
-    if (field.has_position_index()) {
-      protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
-    }
-    for (int i = 0; i < field.child_size(); ++i) {
-        long long childToken = protoOutput->start(
-            FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD);
-        writeFieldProtoToStream(field.child(i), protoOutput);
-        protoOutput->end(childToken);
-    }
-}
-
-namespace {
-
-void addOrUpdateChildrenMap(
-    const Field& root,
-    const Field& node,
-    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
-    Field parentNode = root;
-    if (node.has_position_index()) {
-        appendLeaf(&parentNode, node.field(), node.position_index());
-    } else {
-        appendLeaf(&parentNode, node.field());
-    }
-    if (childrenMap->find(parentNode) == childrenMap->end()) {
-        childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{}));
-    }
-    auto it = childrenMap->find(parentNode);
-    for (int i = 0; i < node.child_size(); ++i) {
-        auto child = node.child(i);
-        Field childNode = parentNode;
-        if (child.has_position_index()) {
-            appendLeaf(&childNode, child.field(), child.position_index());
-        } else {
-            appendLeaf(&childNode, child.field());
+// Supported Atoms format
+// XYZ_Atom {
+//     repeated SubMsg field_1 = 1;
+//     SubMsg2 field_2 = 2;
+//     int32/float/string/int63 field_3 = 3;
+// }
+// logd's msg format, doesn't allow us to distinguish between the 2 cases below
+// Case (1):
+// Atom {
+//   SubMsg {
+//     int i = 1;
+//     int j = 2;
+//   }
+//   repeated SubMsg
+// }
+//
+// and case (2):
+// Atom {
+//   SubMsg {
+//     repeated int i = 1;
+//     repeated int j = 2;
+//   }
+//   optional SubMsg = 1;
+// }
+//
+//
+void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index,
+                                       int depth, int prefix, ProtoOutputStream* protoOutput) {
+    size_t count = dims.size();
+    while (*index < count) {
+        const auto& dim = dims[*index];
+        const int valueDepth = dim.mField.getDepth();
+        const int valuePrefix = dim.mField.getPrefix(depth);
+        const int fieldNum = dim.mField.getPosAtDepth(depth);
+        if (valueDepth > 2) {
+            ALOGE("Depth > 2 not supported");
+            return;
         }
-        it->second.insert(childNode);
-        addOrUpdateChildrenMap(parentNode, child, childrenMap);
+
+        if (depth == valueDepth && valuePrefix == prefix) {
+            switch (dim.mValue.getType()) {
+                case INT:
+                    protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value);
+                    break;
+                case LONG:
+                    protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
+                                       (long long)dim.mValue.long_value);
+                    break;
+                case FLOAT:
+                    protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
+                    break;
+                case STRING:
+                    protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
+                    break;
+            }
+            (*index)++;
+        } else if (valueDepth > depth && valuePrefix == prefix) {
+            // Writing the sub tree
+            long long msg_token = 0;
+            if (valueDepth == depth + 2) {
+                msg_token =
+                        protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
+            } else if (valueDepth == depth + 1) {
+                msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
+            }
+            // Directly jump to the leaf value because the repeated position field is implied
+            // by the position of the sub msg in the parent field.
+            writeFieldValueTreeToStreamHelper(dims, index, valueDepth,
+                                              dim.mField.getPrefix(valueDepth), protoOutput);
+            if (msg_token != 0) {
+                protoOutput->end(msg_token);
+            }
+        } else {
+            // Done with the prev sub tree
+            return;
+        }
     }
 }
 
-void addOrUpdateChildrenMap(
-    const Field& field,
-    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
-    Field root;
-    addOrUpdateChildrenMap(root, field, childrenMap);
-}
-
-} // namespace
-
-void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
+void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
                                  util::ProtoOutputStream* protoOutput) {
-    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap;
-    // Rebuild the field tree.
-    for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) {
-        addOrUpdateChildrenMap(it->first, &childrenMap);
-    }
-    std::stack<std::pair<long long, Field>> tokenStack;
-    // Iterate over the node tree to fill the Atom proto.
-    for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) {
-        const Field* nodeLeaf = getSingleLeaf(&it->first);
-        const int fieldNum = nodeLeaf->field();
-        while (!tokenStack.empty()) {
-            auto currentMsgNode = tokenStack.top().second;
-            auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode);
-            if (currentMsgNodeChildrenIt->second.find(it->first) ==
-                currentMsgNodeChildrenIt->second.end()) {
-                protoOutput->end(tokenStack.top().first);
-                tokenStack.pop();
-            } else {
-                break;
-            }
-        }
-        if (it->second.size() == 0) {
-            auto itValue = fieldValueMap.find(it->first);
-            if (itValue != fieldValueMap.end()) {
-                const DimensionsValue& value = itValue->second;
-                switch (value.value_case()) {
-                        case DimensionsValue::ValueCase::kValueStr:
-                            protoOutput->write(FIELD_TYPE_STRING | fieldNum,
-                                value.value_str());
-                            break;
-                        case DimensionsValue::ValueCase::kValueInt:
-                            protoOutput->write(FIELD_TYPE_INT32 | fieldNum,
-                                value.value_int());
-                            break;
-                        case DimensionsValue::ValueCase::kValueLong:
-                            protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
-                                value.value_long());
-                            break;
-                        case DimensionsValue::ValueCase::kValueBool:
-                            protoOutput->write(FIELD_TYPE_BOOL | fieldNum,
-                                value.value_bool());
-                            break;
-                        case DimensionsValue::ValueCase::kValueFloat:
-                            protoOutput->write(FIELD_TYPE_FLOAT | fieldNum,
-                                value.value_float());
-                            break;
-                        // This would not happen as the node has no child.
-                        case DimensionsValue::ValueCase::kValueTuple:
-                            break;
-                        default:
-                            break;
-                }
-            } else {
-                ALOGE("Leaf node value not found. This should never happen.");
-            }
-        } else {
-            long long token;
-            if (nodeLeaf->has_position_index()) {
-                token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
-            } else {
-                token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
-            }
-            tokenStack.push(std::make_pair(token, it->first));
-        }
-    }
+    long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
 
-    while (!tokenStack.empty()) {
-        protoOutput->end(tokenStack.top().first);
-        tokenStack.pop();
-    }
+    size_t index = 0;
+    writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput);
+    protoOutput->end(atomToken);
 }
 
 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index cee9200..6583f57 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include <android/util/ProtoOutputStream.h>
-#include "field_util.h"
+#include "FieldValue.h"
+#include "HashableDimensionKey.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "guardrail/StatsdStats.h"
@@ -26,16 +27,10 @@
 namespace os {
 namespace statsd {
 
-// Helper function to write DimensionsValue proto to ProtoOutputStream.
-void writeDimensionsValueProtoToStream(const DimensionsValue& fieldValue,
-                                       util::ProtoOutputStream* protoOutput);
-
-// Helper function to write Field proto to ProtoOutputStream.
-void writeFieldProtoToStream(const Field& field, util::ProtoOutputStream* protoOutput);
-
-// Helper function to construct the field value tree and write to ProtoOutputStream
-void writeFieldValueTreeToStream(const FieldValueMap& fieldValueMap,
+void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
                                  util::ProtoOutputStream* protoOutput);
+void writeDimensionToProto(const HashableDimensionKey& dimension,
+                           util::ProtoOutputStream* protoOutput);
 
 // Convert the TimeUnit enum to the bucket size in millis.
 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
diff --git a/cmds/statsd/src/statsd_internal.proto b/cmds/statsd/src/statsd_internal.proto
deleted file mode 100644
index 25aacee..0000000
--- a/cmds/statsd/src/statsd_internal.proto
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-package android.os.statsd;
-
-option java_package = "com.android.os";
-option java_outer_classname = "StatsdInternalProto";
-
-message Field {
-  optional int32 field = 1;
-  optional int32 position_index = 2 [default = -1];
-  repeated Field child = 3;
-}
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 3af684f..9f68fc4 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -98,50 +98,66 @@
         ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
         return;
     }
-    mStatsCompanionService->sendSubscriberBroadcast(intentSender,
-                                                    configKey.GetUid(),
-                                                    configKey.GetId(),
-                                                    subscription.id(),
-                                                    subscription.rule_id(),
-                                                    protoToStatsDimensionsValue(dimKey));
+    mStatsCompanionService->sendSubscriberBroadcast(
+            intentSender, configKey.GetUid(), configKey.GetId(), subscription.id(),
+            subscription.rule_id(), getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
 }
 
-StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
-        const MetricDimensionKey& dimKey) {
-    return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
-}
-
-StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
-        const DimensionsValue& protoDimsVal) {
-    int32_t field = protoDimsVal.field();
-
-    switch (protoDimsVal.value_case()) {
-        case DimensionsValue::ValueCase::kValueStr:
-            return StatsDimensionsValue(field, String16(protoDimsVal.value_str().c_str()));
-        case DimensionsValue::ValueCase::kValueInt:
-            return StatsDimensionsValue(field, static_cast<int32_t>(protoDimsVal.value_int()));
-        case DimensionsValue::ValueCase::kValueLong:
-            return StatsDimensionsValue(field, static_cast<int64_t>(protoDimsVal.value_long()));
-        case DimensionsValue::ValueCase::kValueBool:
-            return StatsDimensionsValue(field, static_cast<bool>(protoDimsVal.value_bool()));
-        case DimensionsValue::ValueCase::kValueFloat:
-            return StatsDimensionsValue(field, static_cast<float>(protoDimsVal.value_float()));
-        case DimensionsValue::ValueCase::kValueTuple:
-            {
-                int sz = protoDimsVal.value_tuple().dimensions_value_size();
-                std::vector<StatsDimensionsValue> sdvVec(sz);
-                for (int i = 0; i < sz; i++) {
-                    sdvVec[i] = protoToStatsDimensionsValue(
-                            protoDimsVal.value_tuple().dimensions_value(i));
-                }
-                return StatsDimensionsValue(field, sdvVec);
+void getStatsDimensionsValueHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
+                                   int prefix, vector<StatsDimensionsValue>* output) {
+    size_t count = dims.size();
+    while (*index < count) {
+        const auto& dim = dims[*index];
+        const int valueDepth = dim.mField.getDepth();
+        const int valuePrefix = dim.mField.getPrefix(depth);
+        if (valueDepth > 2) {
+            ALOGE("Depth > 2 not supported");
+            return;
+        }
+        if (depth == valueDepth && valuePrefix == prefix) {
+            switch (dim.mValue.getType()) {
+                case INT:
+                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
+                                                           dim.mValue.int_value));
+                    break;
+                case LONG:
+                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
+                                                           dim.mValue.long_value));
+                    break;
+                case FLOAT:
+                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
+                                                           dim.mValue.float_value));
+                    break;
+                case STRING:
+                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
+                                                           String16(dim.mValue.str_value.c_str())));
+                    break;
+                default:
+                    break;
             }
-        default:
-            ALOGW("protoToStatsDimensionsValue failed: illegal type.");
-            return StatsDimensionsValue();
+            (*index)++;
+        } else if (valueDepth > depth && valuePrefix == prefix) {
+            vector<StatsDimensionsValue> childOutput;
+            getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1),
+                                          &childOutput);
+            output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput));
+        } else {
+            return;
+        }
     }
 }
 
+StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) {
+    if (dim.getValues().size() == 0) {
+        return StatsDimensionsValue();
+    }
+
+    vector<StatsDimensionsValue> fields;
+    size_t index = 0;
+    getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields);
+    return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 13fc7fd..c7d1a5b 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -82,6 +82,8 @@
                                   const Subscription& subscription,
                                   const MetricDimensionKey& dimKey) const;
 
+    static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
+
 private:
     SubscriberReporter() {};
 
@@ -102,14 +104,6 @@
                              const ConfigKey& configKey,
                              const Subscription& subscription,
                              const MetricDimensionKey& dimKey) const;
-
-    /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
-    static StatsDimensionsValue protoToStatsDimensionsValue(
-            const DimensionsValue& protoDimsVal);
-
-    /** Converts a HashableDimensionKey to a StatsDimensionsValue. */
-    static StatsDimensionsValue protoToStatsDimensionsValue(
-            const MetricDimensionKey& dimKey);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
new file mode 100644
index 0000000..f1ad0c8
--- /dev/null
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -0,0 +1,350 @@
+/*
+ * 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 <gtest/gtest.h>
+#include "src/logd/LogEvent.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+#include "subscriber/SubscriberReporter.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(AtomMatcherTest, TestFieldTranslation) {
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::ANY);
+
+    child = child->add_child();
+    child->set_field(1);
+
+    vector<Matcher> output;
+    translateFieldMatcher(matcher1, &output);
+
+    EXPECT_EQ((size_t)1, output.size());
+
+    const auto& matcher12 = output[0];
+    EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
+    EXPECT_EQ((int32_t)0x2010001, matcher12.mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask);
+}
+
+TEST(AtomMatcherTest, TestFilter) {
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::ANY);
+
+    child = child->add_child();
+    child->set_field(1);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers;
+    translateFieldMatcher(matcher1, &matchers);
+
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("location1");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("location2");
+
+    AttributionNode attribution_node3;
+    attribution_node3.set_uid(3333);
+    attribution_node3.set_tag("location3");
+    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2,
+                                                      attribution_node3};
+
+    // Set up the event
+    LogEvent event(10, 12345);
+    event.write(attribution_nodes);
+    event.write("some value");
+    // Convert to a LogEvent
+    event.init();
+    vector<HashableDimensionKey> output;
+
+    filterValues(matchers, event.getValues(), &output);
+
+    EXPECT_EQ((size_t)(3), output.size());
+
+    const auto& key1 = output[0];
+    EXPECT_EQ((size_t)2, key1.getValues().size());
+    EXPECT_EQ((int32_t)0x02010001, key1.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)1111, key1.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x00020000, key1.getValues()[1].mField.getField());
+    EXPECT_EQ("some value", key1.getValues()[1].mValue.str_value);
+
+    const auto& key2 = output[1];
+    EXPECT_EQ((size_t)2, key2.getValues().size());
+    EXPECT_EQ((int32_t)0x02010001, key2.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)2222, key2.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x00020000, key2.getValues()[1].mField.getField());
+    EXPECT_EQ("some value", key2.getValues()[1].mValue.str_value);
+
+    const auto& key3 = output[2];
+    EXPECT_EQ((size_t)2, key3.getValues().size());
+    EXPECT_EQ((int32_t)0x02010001, key3.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)3333, key3.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x00020000, key3.getValues()[1].mField.getField());
+    EXPECT_EQ("some value", key3.getValues()[1].mValue.str_value);
+}
+
+TEST(AtomMatcherTest, TestSubDimension) {
+    HashableDimensionKey dim;
+
+    int pos1[] = {1, 1, 1};
+    int pos2[] = {1, 1, 2};
+    int pos3[] = {1, 1, 3};
+    int pos4[] = {2, 0, 0};
+    Field field1(10, pos1, 2);
+    Field field2(10, pos2, 2);
+
+    Field field3(10, pos3, 2);
+    Field field4(10, pos4, 0);
+
+    Value value1((int32_t)10025);
+    Value value2("tag");
+
+    Value value11((int32_t)10026);
+    Value value22("tag2");
+
+    dim.addValue(FieldValue(field1, value1));
+    dim.addValue(FieldValue(field2, value2));
+
+    HashableDimensionKey subDim1;
+    subDim1.addValue(FieldValue(field1, value1));
+
+    HashableDimensionKey subDim2;
+    subDim1.addValue(FieldValue(field2, value2));
+
+    EXPECT_TRUE(dim.contains(dim));
+    EXPECT_TRUE(dim.contains(subDim1));
+    EXPECT_TRUE(dim.contains(subDim2));
+
+    HashableDimensionKey subDim3;
+    subDim3.addValue(FieldValue(field1, value11));
+    EXPECT_FALSE(dim.contains(subDim3));
+
+    HashableDimensionKey subDim4;
+    // Empty dimension is always a sub dimension of other dimensions
+    EXPECT_TRUE(dim.contains(subDim4));
+}
+
+TEST(AtomMatcherTest, TestMetric2ConditionLink) {
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("location1");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("location2");
+
+    AttributionNode attribution_node3;
+    attribution_node3.set_uid(3333);
+    attribution_node3.set_tag("location3");
+    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2,
+                                                      attribution_node3};
+
+    // Set up the event
+    LogEvent event(10, 12345);
+    event.write(attribution_nodes);
+    event.write("some value");
+    // Convert to a LogEvent
+    event.init();
+
+    FieldMatcher whatMatcher;
+    whatMatcher.set_field(10);
+    FieldMatcher* child11 = whatMatcher.add_child();
+    child11->set_field(1);
+    child11->set_position(Position::ANY);
+    child11 = child11->add_child();
+    child11->set_field(1);
+
+    FieldMatcher conditionMatcher;
+    conditionMatcher.set_field(27);
+    FieldMatcher* child2 = conditionMatcher.add_child();
+    child2->set_field(2);
+    child2->set_position(Position::LAST);
+
+    child2 = child2->add_child();
+    child2->set_field(2);
+
+    Metric2Condition link;
+
+    translateFieldMatcher(whatMatcher, &link.metricFields);
+    translateFieldMatcher(conditionMatcher, &link.conditionFields);
+
+    EXPECT_EQ((size_t)1, link.metricFields.size());
+    EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
+    EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
+
+    EXPECT_EQ((size_t)1, link.conditionFields.size());
+    EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
+    EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
+}
+
+TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
+    HashableDimensionKey dim;
+
+    int pos1[] = {1, 1, 1};
+    int pos2[] = {1, 1, 2};
+    int pos3[] = {1, 1, 3};
+    int pos4[] = {2, 0, 0};
+
+    Field field1(10, pos1, 2);
+    Field field2(10, pos2, 2);
+    Field field3(10, pos3, 2);
+    Field field4(10, pos4, 0);
+
+    Value value1((int32_t)10025);
+    Value value2("tag");
+    Value value3((int32_t)987654);
+    Value value4((int32_t)99999);
+
+    dim.addValue(FieldValue(field1, value1));
+    dim.addValue(FieldValue(field2, value2));
+    dim.addValue(FieldValue(field3, value3));
+    dim.addValue(FieldValue(field4, value4));
+
+    SubscriberReporter::getStatsDimensionsValue(dim);
+    // TODO: can't test anything here because SubscriberReport class doesn't have any read api.
+}
+
+TEST(AtomMatcherTest, TestWriteDimensionToProto) {
+    HashableDimensionKey dim;
+    int pos1[] = {1, 1, 1};
+    int pos2[] = {1, 1, 2};
+    int pos3[] = {1, 1, 3};
+    int pos4[] = {2, 0, 0};
+    Field field1(10, pos1, 2);
+    Field field2(10, pos2, 2);
+    Field field3(10, pos3, 2);
+    Field field4(10, pos4, 0);
+
+    Value value1((int32_t)10025);
+    Value value2("tag");
+    Value value3((int32_t)987654);
+    Value value4((int32_t)99999);
+
+    dim.addValue(FieldValue(field1, value1));
+    dim.addValue(FieldValue(field2, value2));
+    dim.addValue(FieldValue(field3, value3));
+    dim.addValue(FieldValue(field4, value4));
+
+    android::util::ProtoOutputStream protoOut;
+    writeDimensionToProto(dim, &protoOut);
+
+    vector<uint8_t> outData;
+    outData.resize(protoOut.size());
+    size_t pos = 0;
+    auto iter = protoOut.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+
+    DimensionsValue result;
+    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+    EXPECT_EQ(10, result.field());
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
+    EXPECT_EQ(2, result.value_tuple().dimensions_value_size());
+
+    const auto& dim1 = result.value_tuple().dimensions_value(0);
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case());
+    EXPECT_EQ(3, dim1.value_tuple().dimensions_value_size());
+
+    const auto& dim11 = dim1.value_tuple().dimensions_value(0);
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case());
+    EXPECT_EQ(10025, dim11.value_int());
+
+    const auto& dim12 = dim1.value_tuple().dimensions_value(1);
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case());
+    EXPECT_EQ("tag", dim12.value_str());
+
+    const auto& dim13 = dim1.value_tuple().dimensions_value(2);
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case());
+    EXPECT_EQ(987654, dim13.value_int());
+
+    const auto& dim2 = result.value_tuple().dimensions_value(1);
+    EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case());
+    EXPECT_EQ(99999, dim2.value_int());
+}
+
+TEST(AtomMatcherTest, TestWriteAtomToProto) {
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("location1");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("location2");
+
+    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2};
+
+    // Set up the event
+    LogEvent event(4, 12345);
+    event.write(attribution_nodes);
+    event.write((int32_t)999);
+    // Convert to a LogEvent
+    event.init();
+
+    android::util::ProtoOutputStream protoOutput;
+    writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
+
+    vector<uint8_t> outData;
+    outData.resize(protoOutput.size());
+    size_t pos = 0;
+    auto iter = protoOutput.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+
+    Atom result;
+    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+    EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
+    const auto& atom = result.ble_scan_result_received();
+    EXPECT_EQ(2, atom.attribution_node_size());
+    EXPECT_EQ(1111, atom.attribution_node(0).uid());
+    EXPECT_EQ("location1", atom.attribution_node(0).tag());
+    EXPECT_EQ(2222, atom.attribution_node(1).uid());
+    EXPECT_EQ("location2", atom.attribution_node(1).tag());
+    EXPECT_EQ(999, atom.num_of_results());
+}
+
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
\ No newline at end of file
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 111b4ba..1023ea4 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -14,6 +14,7 @@
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/matcher_util.h"
+#include "stats_log_util.h"
 #include "stats_util.h"
 
 #include <gtest/gtest.h>
@@ -35,8 +36,6 @@
 const int ATTRIBUTION_UID_FIELD_ID = 1;
 const int ATTRIBUTION_TAG_FIELD_ID = 2;
 
-// Private API from liblog.
-extern "C" void android_log_rewind(android_log_context ctx);
 
 #ifdef __ANDROID__
 TEST(AtomMatcherTest, TestSimpleMatcher) {
@@ -597,7 +596,6 @@
     matcherResults.push_back(MatchingState::kMatched);
     EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
 }
-
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index fd28460..b649215 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -22,548 +22,142 @@
 namespace os {
 namespace statsd {
 
-TEST(LogEventTest, testEmptyEvent) {
-    const int32_t TAG_ID = 123;
-    LogEvent event(TAG_ID, 0);
-    event.init();
+TEST(LogEventTest, TestLogParsing) {
+    LogEvent event1(1, 2000);
 
-    DimensionsValue dimensionsValue;
-    EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue));
-    FieldMatcher dimensions;
-    dimensions.set_field(event.GetTagId());
-    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+    std::vector<AttributionNode> nodes;
 
-    dimensions.add_child()->set_field(3);
-    dimensions.mutable_child(0)->set_position(Position::FIRST);
-    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+    AttributionNode node1;
+    node1.set_uid(1000);
+    node1.set_tag("tag1");
+    nodes.push_back(node1);
 
-    dimensions.mutable_child(0)->set_position(Position::ANY);
-    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+    AttributionNode node2;
+    node2.set_uid(2000);
+    node2.set_tag("tag2");
+    nodes.push_back(node2);
 
-    dimensions.mutable_child(0)->set_position(Position::LAST);
-    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+    event1.write(nodes);
+    event1.write("hello");
+    event1.write((int32_t)10);
+    event1.write((int64_t)20);
+    event1.write((float)1.1);
+    event1.init();
+
+    const auto& items = event1.getValues();
+    EXPECT_EQ((size_t)8, items.size());
+    EXPECT_EQ(1, event1.GetTagId());
+
+    const FieldValue& item0 = event1.getValues()[0];
+    EXPECT_EQ(0x2010101, item0.mField.getField());
+    EXPECT_EQ(Type::INT, item0.mValue.getType());
+    EXPECT_EQ(1000, item0.mValue.int_value);
+
+    const FieldValue& item1 = event1.getValues()[1];
+    EXPECT_EQ(0x2010182, item1.mField.getField());
+    EXPECT_EQ(Type::STRING, item1.mValue.getType());
+    EXPECT_EQ("tag1", item1.mValue.str_value);
+
+    const FieldValue& item2 = event1.getValues()[2];
+    EXPECT_EQ(0x2018201, item2.mField.getField());
+    EXPECT_EQ(Type::INT, item2.mValue.getType());
+    EXPECT_EQ(2000, item2.mValue.int_value);
+
+    const FieldValue& item3 = event1.getValues()[3];
+    EXPECT_EQ(0x2018282, item3.mField.getField());
+    EXPECT_EQ(Type::STRING, item3.mValue.getType());
+    EXPECT_EQ("tag2", item3.mValue.str_value);
+
+    const FieldValue& item4 = event1.getValues()[4];
+    EXPECT_EQ(0x20000, item4.mField.getField());
+    EXPECT_EQ(Type::STRING, item4.mValue.getType());
+    EXPECT_EQ("hello", item4.mValue.str_value);
+
+    const FieldValue& item5 = event1.getValues()[5];
+    EXPECT_EQ(0x30000, item5.mField.getField());
+    EXPECT_EQ(Type::INT, item5.mValue.getType());
+    EXPECT_EQ(10, item5.mValue.int_value);
+
+    const FieldValue& item6 = event1.getValues()[6];
+    EXPECT_EQ(0x40000, item6.mField.getField());
+    EXPECT_EQ(Type::LONG, item6.mValue.getType());
+    EXPECT_EQ((int64_t)20, item6.mValue.long_value);
+
+    const FieldValue& item7 = event1.getValues()[7];
+    EXPECT_EQ(0x50000, item7.mField.getField());
+    EXPECT_EQ(Type::FLOAT, item7.mValue.getType());
+    EXPECT_EQ((float)1.1, item7.mValue.float_value);
 }
 
-TEST(LogEventTest, testRepeatedAttributionNode) {
-    const int32_t TAG_ID = 123;
-    LogEvent event(TAG_ID, 0);
-    AttributionNode attribution_node1;
-    attribution_node1.set_uid(1111);
-    attribution_node1.set_tag("locationService");
+TEST(LogEventTest, TestLogParsing2) {
+    LogEvent event1(1, 2000);
 
-    AttributionNode attribution_node2;
-    attribution_node2.set_uid(2222);
-    attribution_node2.set_tag("locationService2");
+    std::vector<AttributionNode> nodes;
 
-    AttributionNode attribution_node3;
-    attribution_node3.set_uid(3333);
-    attribution_node3.set_tag("locationService3");
-    std::vector<AttributionNode> attribution_nodes =
-        {attribution_node1, attribution_node2, attribution_node3};
+    event1.write("hello");
 
-    // 1nd field: int32.
-    EXPECT_TRUE(event.write(int32_t(11)));
-    // 2rd field: float.
-    EXPECT_TRUE(event.write(3.45f));
-    // Here it assume that the atom proto contains a repeated AttributionNode field.
-    // 3rd field: attribution node. This is repeated field.
-    EXPECT_TRUE(event.write(attribution_nodes));
-    // 4th field: bool.
-    EXPECT_TRUE(event.write(true));
-    // 5th field: long.
-    EXPECT_TRUE(event.write(uint64_t(1234)));
+    // repeated msg can be in the middle
+    AttributionNode node1;
+    node1.set_uid(1000);
+    node1.set_tag("tag1");
+    nodes.push_back(node1);
 
-    event.init();
+    AttributionNode node2;
+    node2.set_uid(2000);
+    node2.set_tag("tag2");
+    nodes.push_back(node2);
+    event1.write(nodes);
 
-    DimensionsValue dimensionsValue;
-    // Query single primitive fields.
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+    event1.write((int32_t)10);
+    event1.write((int64_t)20);
+    event1.write((float)1.1);
+    event1.init();
 
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+    const auto& items = event1.getValues();
+    EXPECT_EQ((size_t)8, items.size());
+    EXPECT_EQ(1, event1.GetTagId());
 
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
-    // The bool value is stored in value_int field as logD does not support bool.
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+    const FieldValue& item = event1.getValues()[0];
+    EXPECT_EQ(0x00010000, item.mField.getField());
+    EXPECT_EQ(Type::STRING, item.mValue.getType());
+    EXPECT_EQ("hello", item.mValue.str_value);
 
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+    const FieldValue& item0 = event1.getValues()[1];
+    EXPECT_EQ(0x2020101, item0.mField.getField());
+    EXPECT_EQ(Type::INT, item0.mValue.getType());
+    EXPECT_EQ(1000, item0.mValue.int_value);
 
-    // First attribution.
-    FieldMatcher first_uid_dimensions;
-    first_uid_dimensions.set_field(event.GetTagId());
-    first_uid_dimensions.add_child()->set_field(3);
-    first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST);
-    first_uid_dimensions.mutable_child(0)->add_child()->set_field(1);
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
+    const FieldValue& item1 = event1.getValues()[2];
+    EXPECT_EQ(0x2020182, item1.mField.getField());
+    EXPECT_EQ(Type::STRING, item1.mValue.getType());
+    EXPECT_EQ("tag1", item1.mValue.str_value);
 
-    FieldMatcher first_tag_dimensions = first_uid_dimensions;
-    first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2);
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_str(), "locationService");
+    const FieldValue& item2 = event1.getValues()[3];
+    EXPECT_EQ(0x2028201, item2.mField.getField());
+    EXPECT_EQ(Type::INT, item2.mValue.getType());
+    EXPECT_EQ(2000, item2.mValue.int_value);
 
-    FieldMatcher first_attribution_dimensions = first_uid_dimensions;
-    first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2);
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService");
+    const FieldValue& item3 = event1.getValues()[4];
+    EXPECT_EQ(0x2028282, item3.mField.getField());
+    EXPECT_EQ(Type::STRING, item3.mValue.getType());
+    EXPECT_EQ("tag2", item3.mValue.str_value);
 
-    FieldMatcher last_attribution_dimensions = first_attribution_dimensions;
-    last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST);
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 3333);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService3");
+    const FieldValue& item5 = event1.getValues()[5];
+    EXPECT_EQ(0x30000, item5.mField.getField());
+    EXPECT_EQ(Type::INT, item5.mValue.getType());
+    EXPECT_EQ(10, item5.mValue.int_value);
 
-    FieldMatcher any_attribution_dimensions = first_attribution_dimensions;
-    any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY);
-    std::vector<DimensionsValue> dimensionsValues;
-    event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues);
-    EXPECT_EQ(dimensionsValues.size(), 3u);
-    EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService");
-    EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 2222);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService2");
-    EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 3333);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService3");
+    const FieldValue& item6 = event1.getValues()[6];
+    EXPECT_EQ(0x40000, item6.mField.getField());
+    EXPECT_EQ(Type::LONG, item6.mValue.getType());
+    EXPECT_EQ((int64_t)20, item6.mValue.long_value);
 
-    FieldMatcher mixed_dimensions = any_attribution_dimensions;
-    mixed_dimensions.add_child()->set_field(1000);
-    mixed_dimensions.add_child()->set_field(6); // missing field.
-    mixed_dimensions.add_child()->set_field(3); // position not set.
-    mixed_dimensions.add_child()->set_field(5);
-    mixed_dimensions.add_child()->set_field(1);
-    dimensionsValues.clear();
-    event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues);
-    EXPECT_EQ(dimensionsValues.size(), 3u);
-    EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
-              1111);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
-              "locationService");
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234));
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1);
-    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11);
-
-    EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
-              2222);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
-              "locationService2");
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234));
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1);
-    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11);
-
-    EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
-              3333);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
-              "locationService3");
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234));
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1);
-    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11);
-
-    FieldMatcher wrong_dimensions = mixed_dimensions;
-    // Wrong tagId.
-    wrong_dimensions.set_field(event.GetTagId() + 100);
-    dimensionsValues.clear();
-    event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues);
-    EXPECT_TRUE(dimensionsValues.empty());
+    const FieldValue& item7 = event1.getValues()[7];
+    EXPECT_EQ(0x50000, item7.mField.getField());
+    EXPECT_EQ(Type::FLOAT, item7.mValue.getType());
+    EXPECT_EQ((float)1.1, item7.mValue.float_value);
 }
 
-TEST(LogEventTest, testMessageField) {
-    const int32_t TAG_ID = 123;
-    LogEvent event(TAG_ID, 0);
-    AttributionNode attribution_node1;
-    attribution_node1.set_uid(1111);
-    attribution_node1.set_tag("locationService");
-
-    AttributionNode attribution_node2;
-    attribution_node2.set_uid(2222);
-    attribution_node2.set_tag("locationService2");
-
-    // 1nd field: int32.
-    EXPECT_TRUE(event.write(int32_t(11)));
-    // 2rd field: float.
-    EXPECT_TRUE(event.write(3.45f));
-    // Here it assume that the atom proto contains two optional AttributionNode fields.
-    // 3rd field: attribution node. This is not repeated field.
-    EXPECT_TRUE(event.write(attribution_node1));
-    // 4th field: another attribution field. This is not repeated field.
-    EXPECT_TRUE(event.write(attribution_node2));
-    // 5th field: bool.
-    EXPECT_TRUE(event.write(true));
-    // 6th field: long.
-    EXPECT_TRUE(event.write(uint64_t(1234)));
-
-    event.init();
-
-    FieldMatcher uid_dimensions1;
-    uid_dimensions1.set_field(event.GetTagId());
-    uid_dimensions1.add_child()->set_field(3);
-    uid_dimensions1.mutable_child(0)->add_child()->set_field(1);
-
-    FieldMatcher tag_dimensions1;
-    tag_dimensions1.set_field(event.GetTagId());
-    tag_dimensions1.add_child()->set_field(3);
-    tag_dimensions1.mutable_child(0)->add_child()->set_field(2);
-
-    FieldMatcher attribution_dimensions1;
-    attribution_dimensions1.set_field(event.GetTagId());
-    attribution_dimensions1.add_child()->set_field(3);
-    attribution_dimensions1.mutable_child(0)->add_child()->set_field(1);
-    attribution_dimensions1.mutable_child(0)->add_child()->set_field(2);
-
-    FieldMatcher uid_dimensions2 = uid_dimensions1;
-    uid_dimensions2.mutable_child(0)->set_field(4);
-
-    FieldMatcher tag_dimensions2 = tag_dimensions1;
-    tag_dimensions2.mutable_child(0)->set_field(4);
-
-    FieldMatcher attribution_dimensions2 = attribution_dimensions1;
-    attribution_dimensions2.mutable_child(0)->set_field(4);
-
-    FieldMatcher mixed_dimensions = attribution_dimensions1;
-    mixed_dimensions.add_child()->set_field(4);
-    mixed_dimensions.mutable_child(1)->add_child()->set_field(1);
-    mixed_dimensions.add_child()->set_field(1000);
-    mixed_dimensions.add_child()->set_field(5);
-    mixed_dimensions.add_child()->set_field(1);
-
-    DimensionsValue dimensionsValue;
-
-    // Query single primitive fields.
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
-    // The bool value is stored in value_int field as logD does not support bool.
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
-
-    // Query atom field 3: attribution node uid field only.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
-
-    // Query atom field 3: attribution node tag field only.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_str(), "locationService");
-
-    // Query atom field 3: attribution node uid + tag fields.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService");
-
-    // Query atom field 4: attribution node uid field only.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 2222);
-
-    // Query atom field 4: attribution node tag field only.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_str(), "locationService2");
-
-    // Query atom field 4: attribution node uid + tag fields.
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 2222);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService2");
-
-    // Query multiple fields:
-    // 1/ Field 3: attribution uid + tag.
-    // 2/ Field 4: attribution uid only.
-    // 3/ Field not exist.
-    // 4/ Primitive fields #5
-    // 5/ Primitive fields #1
-    EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value_size(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(0).value_int(), 1111);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
-        .dimensions_value(1).value_str(), "locationService");
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
-        .dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
-        .dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
-        .dimensions_value(0).value_int(), 2222);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5);
-    // The bool value is stored in value_int field as logD does not support bool.
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11);
-}
-
-TEST(LogEventTest, testAllPrimitiveFields) {
-    const int32_t TAG_ID = 123;
-    LogEvent event(TAG_ID, 0);
-
-    // 1nd field: int32.
-    EXPECT_TRUE(event.write(int32_t(11)));
-    // 2rd field: float.
-    EXPECT_TRUE(event.write(3.45f));
-    // 3th field: string.
-    EXPECT_TRUE(event.write("test"));
-    // 4th field: bool.
-    EXPECT_TRUE(event.write(true));
-    // 5th field: bool.
-    EXPECT_TRUE(event.write(false));
-    // 6th field: long.
-    EXPECT_TRUE(event.write(uint64_t(1234)));
-
-    event.init();
-
-    DimensionsValue dimensionsValue;
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test");
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
-    // The bool value is stored in value_int field as logD does not support bool.
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
-    // The bool value is stored in value_int field as logD does not support bool.
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false);
-
-    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
-    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
-    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
-
-    // Field not exist.
-    EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue));
-}
-
-TEST(LogEventTest, testWriteAtomProtoToStream) {
-    AttributionNode attribution_node1;
-    attribution_node1.set_uid(1111);
-    attribution_node1.set_tag("locationService");
-
-    AttributionNode attribution_node2;
-    attribution_node2.set_uid(2222);
-    attribution_node2.set_tag("locationService2");
-
-    AttributionNode attribution_node3;
-    attribution_node3.set_uid(3333);
-    attribution_node3.set_tag("locationService3");
-    std::vector<AttributionNode> attribution_nodes =
-        {attribution_node1, attribution_node2, attribution_node3};
-
-    LogEvent event(1, 0);
-    EXPECT_TRUE(event.write("222"));
-    EXPECT_TRUE(event.write(attribution_nodes));
-    EXPECT_TRUE(event.write(345));
-    EXPECT_TRUE(event.write(attribution_node3));
-    EXPECT_TRUE(event.write("hello"));
-    event.init();
-
-    util::ProtoOutputStream protoOutput;
-    // For now only see whether it will crash.
-    // TODO(yanglu): test parsing from stream.
-    event.ToProto(protoOutput);
-}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index aab5bed..cb72697 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -45,7 +45,7 @@
     }
 
     MOCK_METHOD0(byteSize, size_t());
-    MOCK_METHOD1(onDumpReport, void(ProtoOutputStream* output));
+    MOCK_METHOD2(onDumpReport, void(const uint64_t timeNs, ProtoOutputStream* output));
 };
 
 TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
@@ -69,24 +69,26 @@
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyMonitor, 0,
-                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+    StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) {
+        broadcastCount++;
+    });
 
     MockMetricsManager mockMetricsManager;
 
     ConfigKey key(100, 12345);
     EXPECT_CALL(mockMetricsManager, byteSize())
-            .Times(2)
+            .Times(1)
             .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
 
     // Expect only one broadcast despite always returning a size that should trigger broadcast.
     p.flushIfNecessaryLocked(1, key, mockMetricsManager);
     EXPECT_EQ(1, broadcastCount);
 
+    // b/73089712
     // This next call to flush should not trigger a broadcast.
-    p.mLastByteSizeTimes.clear();  // Force another check for byte size.
-    p.flushIfNecessaryLocked(2, key, mockMetricsManager);
-    EXPECT_EQ(1, broadcastCount);
+    // p.mLastByteSizeTimes.clear();  // Force another check for byte size.
+    // p.flushIfNecessaryLocked(2, key, mockMetricsManager);
+    // EXPECT_EQ(1, broadcastCount);
 }
 
 TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
@@ -103,7 +105,7 @@
             .Times(1)
             .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
 
-    EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1);
+    EXPECT_CALL(mockMetricsManager, onDumpReport(_, _)).Times(1);
 
     // Expect to call the onDumpReport and skip the broadcast.
     p.flushIfNecessaryLocked(1, key, mockMetricsManager);
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index a415ea1..b4a7bb7 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -34,10 +34,10 @@
 const ConfigKey kConfigKey(0, 12345);
 
 MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
-    DimensionsValue dimensionsValue;
-    dimensionsValue.set_field(key);
-    dimensionsValue.set_value_str(value);
-    return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+    int pos[] = {key, 0, 0};
+    HashableDimensionKey dim;
+    dim.addValue(FieldValue(Field(1, pos, 0), Value(value)));
+    return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY);
 }
 
 void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index d1b7b28..038d449 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -80,31 +80,31 @@
     const std::vector<int> &uids, const string& conditionName) {
     std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
     std::vector<int> uid_indexes;
+    int pos[] = {1, 1, 1};
+    int depth = 2;
+    Field field(1, pos, depth);
     switch(position) {
         case Position::FIRST:
             uid_indexes.push_back(0);
             break;
         case Position::LAST:
             uid_indexes.push_back(uids.size() - 1);
+            field.setField(0x02018001);
             break;
         case Position::ANY:
             uid_indexes.resize(uids.size());
             std::iota(uid_indexes.begin(), uid_indexes.end(), 0);
+            field.setField(0x02010001);
             break;
         default:
             break;
     }
 
     for (const int idx : uid_indexes) {
-        DimensionsValue dimensionsValue;
-        dimensionsValue.set_field(TAG_ID);
-        dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(
-            ATTRIBUTION_NODE_FIELD_ID);
-        dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
-            ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
-        dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
-            ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]);
-        outputKeyMap[StringToId(conditionName)].push_back(HashableDimensionKey(dimensionsValue));
+        Value value((int32_t)uids[idx]);
+        HashableDimensionKey dim;
+        dim.addValue(FieldValue(field, value));
+        outputKeyMap[StringToId(conditionName)].push_back(dim);
     }
     return outputKeyMap;
 }
@@ -265,7 +265,7 @@
 TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
     for (Position position :
             { Position::ANY, Position::FIRST, Position::LAST}) {
-        FieldMatcher dimensionInCondition;
+        vector<Matcher> dimensionInCondition;
         std::unordered_set<HashableDimensionKey> dimensionKeys;
 
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
@@ -374,7 +374,7 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     std::unordered_set<HashableDimensionKey> dimensionKeys;
 
     SimplePredicate simplePredicate = getWakeLockHeldCondition(
@@ -470,7 +470,7 @@
 TEST(SimpleConditionTrackerTest, TestStopAll) {
     for (Position position :
             {Position::ANY, Position::FIRST, Position::LAST}) {
-        FieldMatcher dimensionInCondition;
+        vector<Matcher> dimensionInCondition;
         std::unordered_set<HashableDimensionKey> dimensionKeys;
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
@@ -576,7 +576,6 @@
                                         conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
     }
-
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp
deleted file mode 100644
index 678abae..0000000
--- a/cmds/statsd/tests/dimension_test.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-// 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 "dimension.h"
-
-#include <gtest/gtest.h>
-
-using namespace android::os::statsd;
-
-#ifdef __ANDROID__
-
-TEST(DimensionTest, subLeafNodes) {
-    DimensionsValue dimension;
-    int tagId = 100;
-    dimension.set_field(tagId);
-    auto child = dimension.mutable_value_tuple()->add_dimensions_value();
-    child->set_field(1);
-    child->set_value_int(2000);
-
-    child = dimension.mutable_value_tuple()->add_dimensions_value();
-    child->set_field(3);
-    child->set_value_str("test");
-
-    child = dimension.mutable_value_tuple()->add_dimensions_value();
-    child->set_field(4);
-    auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
-    grandChild->set_field(1);
-    grandChild->set_value_float(1.3f);
-    grandChild = child->mutable_value_tuple()->add_dimensions_value();
-    grandChild->set_field(3);
-    grandChild->set_value_str("tag");
-
-    child = dimension.mutable_value_tuple()->add_dimensions_value();
-    child->set_field(6);
-    child->set_value_bool(false);
-
-    DimensionsValue sub_dimension;
-    FieldMatcher matcher;
-
-    // Tag id not matched.
-    matcher.set_field(tagId + 1);
-    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Field not exist.
-    matcher.Clear();
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(5);
-    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Field exists.
-    matcher.Clear();
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(3);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Field exists.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(6);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Field exists.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(1);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Not leaf field.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    matcher.add_child()->set_field(4);
-    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Grand-child leaf field not exist.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    auto childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(2);
-    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Grand-child leaf field.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(1);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(3);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Multiple grand-child fields.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(3);
-    childMatcher->add_child()->set_field(1);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Multiple fields.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(3);
-    childMatcher->add_child()->set_field(1);
-    matcher.add_child()->set_field(3);
-    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
-
-    // Subset of the fields not exist.
-    matcher.Clear();
-    sub_dimension.Clear();
-    matcher.set_field(tagId);
-    childMatcher = matcher.add_child();
-    childMatcher->set_field(4);
-    childMatcher->add_child()->set_field(3);
-    childMatcher->add_child()->set_field(1);
-    matcher.add_child()->set_field(2);
-    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index a56db28..01743ef 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -146,9 +146,13 @@
         processor->OnLogEvent(event.get());
     }
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+
     StatsLogReport::CountMetricDataWrapper countMetrics;
     sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
     EXPECT_EQ(countMetrics.data_size(), 4);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
index b5d48ef..275b5824 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -43,8 +43,8 @@
     auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
     // The predicate is dimensioning by any attribution node and both by uid and tag.
     *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
-        CreateAttributionUidAndTagDimensions(
-            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+            CreateAttributionUidAndTagDimensions(android::util::WAKELOCK_STATE_CHANGED,
+                                                 {Position::FIRST});
     *config.add_predicate() = holdingWakelockPredicate;
 
     auto combinationPredicate = config.add_predicate();
@@ -57,8 +57,8 @@
     metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
     metric->set_what(screenBrightnessChangeAtomMatcher.id());
     metric->set_condition(combinationPredicate->id());
-    *metric->mutable_dimensions_in_what() = CreateDimensions(
-            android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+    *metric->mutable_dimensions_in_what() =
+            CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
     *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
             android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
     metric->set_bucket(ONE_MINUTE);
@@ -72,62 +72,54 @@
     auto config = CreateCountMetricWithNoLinkConfig();
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs =
-        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
     auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-    std::vector<AttributionNode> attributions1 =
-        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-         CreateAttribution(222, "GMSCoreModule2")};
+    std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
+                                                  CreateAttribution(222, "GMSCoreModule1"),
+                                                  CreateAttribution(222, "GMSCoreModule2")};
 
-    std::vector<AttributionNode> attributions2 =
-        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-         CreateAttribution(555, "GMSCoreModule2")};
+    std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
+                                                  CreateAttribution(222, "GMSCoreModule1"),
+                                                  CreateAttribution(555, "GMSCoreModule2")};
 
     std::vector<std::unique_ptr<LogEvent>> events;
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+    events.push_back(
+            CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                   bucketStartTimeNs + bucketSizeNs + 1));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   bucketStartTimeNs + 2 * bucketSizeNs - 10));
 
-    events.push_back(CreateAcquireWakelockEvent(
-        attributions1, "wl1", bucketStartTimeNs + 200));
-    events.push_back(CreateReleaseWakelockEvent(
-        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+    events.push_back(CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 200));
+    events.push_back(
+            CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
 
-    events.push_back(CreateAcquireWakelockEvent(
-        attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
-    events.push_back(CreateReleaseWakelockEvent(
-        attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+    events.push_back(CreateAcquireWakelockEvent(attributions2, "wl2",
+                                                bucketStartTimeNs + bucketSizeNs - 100));
+    events.push_back(CreateReleaseWakelockEvent(attributions2, "wl2",
+                                                bucketStartTimeNs + 2 * bucketSizeNs - 50));
 
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        123, bucketStartTimeNs + 11));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        123, bucketStartTimeNs + 101));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        123, bucketStartTimeNs + 201));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        456, bucketStartTimeNs + 203));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        456, bucketStartTimeNs + bucketSizeNs - 99));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        456, bucketStartTimeNs + bucketSizeNs - 2));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        789, bucketStartTimeNs + bucketSizeNs - 1));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        456, bucketStartTimeNs + bucketSizeNs + 2));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
-    events.push_back(CreateScreenBrightnessChangedEvent(
-        789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 11));
+    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 101));
+    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 201));
+    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + 203));
+    events.push_back(
+            CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 99));
+    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 2));
+    events.push_back(CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs + 2));
+    events.push_back(
+            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+    events.push_back(
+            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+    events.push_back(
+            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
 
     sortLogEventsByTimestamp(&events);
 
@@ -136,7 +128,10 @@
     }
 
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -147,7 +142,7 @@
     auto data = countMetrics.data(0);
     EXPECT_EQ(data.bucket_info_size(), 1);
     EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
     EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
     EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
@@ -164,7 +159,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
 
     data = countMetrics.data(2);
     EXPECT_EQ(data.bucket_info_size(), 1);
@@ -175,7 +171,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
 
     data = countMetrics.data(3);
     EXPECT_EQ(data.bucket_info_size(), 2);
@@ -189,7 +186,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 333);
 
     data = countMetrics.data(4);
     EXPECT_EQ(data.bucket_info_size(), 1);
@@ -211,7 +209,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 111);
 
     data = countMetrics.data(6);
     EXPECT_EQ(data.bucket_info_size(), 1);
@@ -222,7 +221,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(),
+                                    android::util::WAKELOCK_STATE_CHANGED, 333);
 }
 
 namespace {
@@ -239,8 +239,8 @@
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
     auto isSyncingPredicate = CreateIsSyncingPredicate();
     auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(
-        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
+                                                          {Position::FIRST});
     syncDimension->add_child()->set_field(2 /* name field*/);
 
     *config.add_predicate() = screenIsOffPredicate;
@@ -256,8 +256,8 @@
     metric->set_id(StringToId("AppCrashMetric"));
     metric->set_what(appCrashMatcher.id());
     metric->set_condition(combinationPredicate->id());
-    *metric->mutable_dimensions_in_what() = CreateDimensions(
-            android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+    *metric->mutable_dimensions_in_what() =
+            CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
     *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
             android::util::SYNC_STATE_CHANGED, {Position::FIRST});
 
@@ -267,8 +267,8 @@
     auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *links->mutable_fields_in_condition() =
+            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
     return config;
 }
 
@@ -279,18 +279,18 @@
     auto config = CreateCountMetricWithLinkConfig();
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs =
-        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
     auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    std::vector<AttributionNode> attributions1 =
-        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-         CreateAttribution(222, "GMSCoreModule2")};
+    std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
+                                                  CreateAttribution(222, "GMSCoreModule1"),
+                                                  CreateAttribution(222, "GMSCoreModule2")};
 
-    std::vector<AttributionNode> attributions2 =
-        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-         CreateAttribution(555, "GMSCoreModule2")};
+    std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
+                                                  CreateAttribution(222, "GMSCoreModule1"),
+                                                  CreateAttribution(555, "GMSCoreModule2")};
 
     std::vector<std::unique_ptr<LogEvent>> events;
 
@@ -311,26 +311,26 @@
 
     events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
 
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
-    events.push_back(CreateScreenStateChangedEvent(
-        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+    events.push_back(
+            CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                   bucketStartTimeNs + 202));
+    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   bucketStartTimeNs + bucketSizeNs + 700));
 
     events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-        bucketStartTimeNs + bucketSizeNs + 300));
+    events.push_back(
+            CreateSyncEndEvent(attributions1, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300));
 
     events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-        bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(
+            CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
 
     events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-        bucketStartTimeNs + bucketSizeNs + 600));
+    events.push_back(
+            CreateSyncEndEvent(attributions2, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 600));
 
     sortLogEventsByTimestamp(&events);
 
@@ -339,7 +339,10 @@
     }
 
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -363,8 +366,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-    ValidateAttributionUidAndTagDimension(
-        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                          android::util::SYNC_STATE_CHANGED, 111, "App1");
     EXPECT_EQ(data.bucket_info_size(), 1);
     EXPECT_EQ(data.bucket_info(0).count(), 2);
     EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -386,8 +389,8 @@
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
-    ValidateAttributionUidAndTagDimension(
-        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                          android::util::SYNC_STATE_CHANGED, 333, "App2");
     EXPECT_EQ(data.bucket_info_size(), 2);
     EXPECT_EQ(data.bucket_info(0).count(), 1);
     EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -424,8 +427,8 @@
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
     auto isSyncingPredicate = CreateIsSyncingPredicate();
     auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(
-        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
+                                                          {Position::FIRST});
     syncDimension->add_child()->set_field(2 /* name field */);
 
     *config.add_predicate() = inBatterySaverModePredicate;
@@ -449,26 +452,25 @@
 
 }  // namespace
 
-
 TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
-    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+    for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
         ConfigKey cfgKey;
         auto config = CreateDurationMetricConfigNoLink(aggregationType);
         int64_t bucketStartTimeNs = 10000000000;
         int64_t bucketSizeNs =
-            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
         auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-        std::vector<AttributionNode> attributions1 =
-            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-             CreateAttribution(222, "GMSCoreModule2")};
+        std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(222, "GMSCoreModule2")};
 
-        std::vector<AttributionNode> attributions2 =
-            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-             CreateAttribution(555, "GMSCoreModule2")};
+        std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(555, "GMSCoreModule2")};
 
         std::vector<std::unique_ptr<LogEvent>> events;
 
@@ -485,26 +487,26 @@
         events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
         events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
 
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                       bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                       bucketStartTimeNs + bucketSizeNs + 800));
 
         events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
         events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-            bucketStartTimeNs + bucketSizeNs + 300));
+                                            bucketStartTimeNs + bucketSizeNs + 300));
 
         events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-            bucketStartTimeNs + bucketSizeNs - 1));
+        events.push_back(
+                CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
 
         events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
         events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-            bucketStartTimeNs + bucketSizeNs + 700));
+                                            bucketStartTimeNs + bucketSizeNs + 700));
 
         sortLogEventsByTimestamp(&events);
 
@@ -513,7 +515,10 @@
         }
 
         ConfigMetricsReportList reports;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+        vector<uint8_t> buffer;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        EXPECT_TRUE(buffer.size() > 0);
+        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
         EXPECT_EQ(reports.reports_size(), 1);
         EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -534,8 +539,8 @@
 
         data = metrics.data(1);
         EXPECT_FALSE(data.dimensions_in_what().has_field());
-        ValidateAttributionUidAndTagDimension(
-            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                              android::util::SYNC_STATE_CHANGED, 111, "App1");
         EXPECT_EQ(data.bucket_info_size(), 2);
         EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
         EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -546,8 +551,8 @@
 
         data = metrics.data(2);
         EXPECT_FALSE(data.dimensions_in_what().has_field());
-        ValidateAttributionUidAndTagDimension(
-            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                              android::util::SYNC_STATE_CHANGED, 333, "App2");
         EXPECT_EQ(data.bucket_info_size(), 2);
         EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
         EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -572,13 +577,13 @@
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
     auto isSyncingPredicate = CreateIsSyncingPredicate();
     auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(
-        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
+                                                          {Position::FIRST});
     syncDimension->add_child()->set_field(2 /* name field */);
 
     auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
     *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
-        CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
 
     *config.add_predicate() = screenIsOffPredicate;
     *config.add_predicate() = isSyncingPredicate;
@@ -594,8 +599,8 @@
     metric->set_id(StringToId("AppInBackgroundMetric"));
     metric->set_what(isInBackgroundPredicate.id());
     metric->set_condition(combinationPredicate->id());
-    *metric->mutable_dimensions_in_what() = CreateDimensions(
-        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+    *metric->mutable_dimensions_in_what() =
+            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
     *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
             android::util::SYNC_STATE_CHANGED, {Position::FIRST});
 
@@ -605,32 +610,32 @@
     auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *links->mutable_fields_in_condition() =
+            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
     return config;
 }
 
 }  // namespace
 
 TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
-    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+    for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
         ConfigKey cfgKey;
         auto config = CreateDurationMetricConfigWithLink(aggregationType);
         int64_t bucketStartTimeNs = 10000000000;
         int64_t bucketSizeNs =
-            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
         auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-        std::vector<AttributionNode> attributions1 =
-            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-             CreateAttribution(222, "GMSCoreModule2")};
+        std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(222, "GMSCoreModule2")};
 
-        std::vector<AttributionNode> attributions2 =
-            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-             CreateAttribution(555, "GMSCoreModule2")};
+        std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(555, "GMSCoreModule2")};
 
         std::vector<std::unique_ptr<LogEvent>> events;
 
@@ -643,26 +648,26 @@
         events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
         events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
 
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
-        events.push_back(CreateScreenStateChangedEvent(
-            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                       bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                       bucketStartTimeNs + bucketSizeNs + 801));
 
         events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
         events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-            bucketStartTimeNs + bucketSizeNs + 300));
+                                            bucketStartTimeNs + bucketSizeNs + 300));
 
         events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-            bucketStartTimeNs + bucketSizeNs - 1));
+        events.push_back(
+                CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
 
         events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
         events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-            bucketStartTimeNs + bucketSizeNs + 700));
+                                            bucketStartTimeNs + bucketSizeNs + 700));
 
         sortLogEventsByTimestamp(&events);
 
@@ -671,7 +676,10 @@
         }
 
         ConfigMetricsReportList reports;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+        vector<uint8_t> buffer;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        EXPECT_TRUE(buffer.size() > 0);
+        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
         EXPECT_EQ(reports.reports_size(), 1);
         EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -691,8 +699,8 @@
         data = metrics.data(1);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-        ValidateAttributionUidAndTagDimension(
-            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                              android::util::SYNC_STATE_CHANGED, 111, "App1");
         EXPECT_EQ(data.bucket_info_size(), 2);
         EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
         EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
@@ -704,8 +712,8 @@
         data = metrics.data(2);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
-        ValidateAttributionUidAndTagDimension(
-            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
+                                              android::util::SYNC_STATE_CHANGED, 333, "App2");
         EXPECT_EQ(data.bucket_info_size(), 2);
         EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
         EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
index a80fdc5..674d810 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -140,7 +140,10 @@
         processor->OnLogEvent(event.get());
     }
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 233031c..d005181 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -95,8 +95,10 @@
 }
 }  // namespace
 
-
-TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
+// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
+// we should use the real API which will clear the data after dump data is called.
+// TODO: better refactor the code so that the tests are not so verbose.
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
     auto config = CreateStatsdConfig();
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
@@ -195,7 +197,10 @@
         processor->OnLogEvent(event.get());
     }
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
@@ -208,16 +213,115 @@
     // Uid field.
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
     EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
+}
 
-    reports.Clear();
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) {
+    auto config = CreateStatsdConfig();
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int appUid = 123;
+    auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
+    auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
+    auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
+
+    auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
+    auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
+    auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
+
+    auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
+    auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
+
+    auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
+    auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
+
+    auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2);
+    auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
+    auto screenTurnedOnEvent2 =
+            CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+                                          bucketStartTimeNs + 2 * bucketSizeNs - 100);
+
+    std::vector<AttributionNode> attributions = {CreateAttribution(appUid, "App1"),
+                                                 CreateAttribution(appUid + 1, "GMSCoreModule1")};
+    auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
+    auto syncOffEvent1 =
+            CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+    auto syncOnEvent2 =
+            CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+
+    auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
+    auto moveToForegroundEvent1 =
+            CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
+
+    auto moveToBackgroundEvent2 =
+            CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
+    auto moveToForegroundEvent2 =
+            CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
+
+    /*
+                    bucket #1                               bucket #2
+
+
+       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
+    |-------------------------------------|-----------------------------------|---------
+
+             |                                           |                        (MoveToBkground)
+
+                                             |                               |    (MoveToForeground)
+
+                |                                                 |                (SyncIsOn)
+                                                  |                                (SyncIsOff)
+          |                                                               |        (ScreenIsOn)
+                   |                                                               (ScreenIsOff)
+    */
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(std::move(crashEvent1));
+    events.push_back(std::move(crashEvent2));
+    events.push_back(std::move(crashEvent3));
+    events.push_back(std::move(crashEvent4));
+    events.push_back(std::move(crashEvent5));
+    events.push_back(std::move(crashEvent6));
+    events.push_back(std::move(crashEvent7));
+    events.push_back(std::move(crashEvent8));
+    events.push_back(std::move(crashEvent9));
+    events.push_back(std::move(crashEvent10));
+    events.push_back(std::move(screenTurnedOnEvent));
+    events.push_back(std::move(screenTurnedOffEvent));
+    events.push_back(std::move(screenTurnedOnEvent2));
+    events.push_back(std::move(syncOnEvent1));
+    events.push_back(std::move(syncOffEvent1));
+    events.push_back(std::move(syncOnEvent2));
+    events.push_back(std::move(moveToBackgroundEvent1));
+    events.push_back(std::move(moveToForegroundEvent1));
+    events.push_back(std::move(moveToBackgroundEvent2));
+    events.push_back(std::move(moveToForegroundEvent2));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
     EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
-    data = reports.reports(0).metrics(0).count_metrics().data(0);
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
     // Validate dimension value.
     EXPECT_EQ(data.dimensions_in_what().field(),
               android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index a99dbe8..3b25694b 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -114,7 +114,7 @@
 
 }  // namespace
 
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration) {
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) {
     ConfigKey cfgKey;
     auto config = CreateStatsdConfig(DurationMetric::SUM);
     uint64_t bucketStartTimeNs = 10000000000;
@@ -124,8 +124,11 @@
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
+    vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
@@ -142,15 +145,30 @@
     // The wakelock holding interval starts from the screen off event and to the end of the 1st
     // bucket.
     EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
+}
 
-    reports.Clear();
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::SUM);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
     // Dump the report after the end of 2nd bucket.
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
-    data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
     // Validate dimension value.
     ValidateAttributionUidDimension(data.dimensions_in_what(),
                                     android::util::WAKELOCK_STATE_CHANGED, 111);
@@ -162,6 +180,19 @@
     // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
     // ends at the second screen on event.
     EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+}
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::SUM);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
 
     std::vector<std::unique_ptr<LogEvent>> events;
     events.push_back(
@@ -175,13 +206,15 @@
     for (const auto& event : events) {
         processor->OnLogEvent(event.get());
     }
-    reports.Clear();
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
-    data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
     ValidateAttributionUidDimension(data.dimensions_in_what(),
                                     android::util::WAKELOCK_STATE_CHANGED, 111);
     // The last wakelock holding spans 4 buckets.
@@ -191,7 +224,7 @@
     EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
 }
 
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration) {
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) {
     ConfigKey cfgKey;
     auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
     uint64_t bucketStartTimeNs = 10000000000;
@@ -202,15 +235,35 @@
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
     EXPECT_EQ(reports.reports_size(), 1);
-    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-    // Nothing has ended in the first bucket.
-    EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 0);
 
-    reports.Clear();
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+    // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
+    // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
+    // itself.
+    EXPECT_EQ(0, reports.reports(0).metrics_size());
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
@@ -222,6 +275,20 @@
                                     android::util::WAKELOCK_STATE_CHANGED, 111);
     // The max is acquire event for wl1 to screen off start.
     EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) {
+    ConfigKey cfgKey;
+    auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    FeedEvents(config, processor);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
 
     std::vector<std::unique_ptr<LogEvent>> events;
     events.push_back(
@@ -235,13 +302,15 @@
     for (const auto& event : events) {
         processor->OnLogEvent(event.get());
     }
-    reports.Clear();
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &reports);
+
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
     EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
-    data = reports.reports(0).metrics(0).duration_metrics().data(0);
+    auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
     ValidateAttributionUidDimension(data.dimensions_in_what(),
                                     android::util::WAKELOCK_STATE_CHANGED, 111);
     // The last wakelock holding spans 4 buckets.
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 87a1079..1e71b73 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/metrics/CountMetricProducer.h"
-#include "src/dimension.h"
 #include "src/stats_log_util.h"
 #include "metrics_test_helper.h"
 #include "tests/statsd_test_util.h"
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 3deab37..8246268 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 #include "src/metrics/EventMetricProducer.h"
-#include "src/dimension.h"
 #include "metrics_test_helper.h"
 #include "tests/statsd_test_util.h"
 
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 470d4d0..26f7c26 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -80,9 +80,10 @@
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
-    EXPECT_EQ(10, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(10, it->mValue.int_value);
     it++;
-    EXPECT_EQ(11, it->second.value_int());
+    EXPECT_EQ(11, it->mValue.int_value);
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     allData.clear();
@@ -96,16 +97,20 @@
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
     it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
-    EXPECT_EQ(24, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(24, it->mValue.int_value);
     it++;
-    EXPECT_EQ(25, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(25, it->mValue.int_value);
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
-    EXPECT_EQ(10L, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(10L, it->mValue.int_value);
     it++;
-    EXPECT_EQ(11L, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(11L, it->mValue.int_value);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 
     gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
@@ -114,9 +119,11 @@
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
-    EXPECT_EQ(24L, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(24L, it->mValue.int_value);
     it++;
-    EXPECT_EQ(25L, it->second.value_int());
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(25L, it->mValue.int_value);
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 }
 
@@ -230,7 +237,7 @@
     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
                          ->second.front()
                          .mFields->begin()
-                         ->second.value_int());
+                         ->mValue.int_value);
 
     gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
@@ -240,7 +247,7 @@
     EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
                          ->second.front()
                          .mFields->begin()
-                         ->second.value_int());
+                         ->mValue.int_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1);
@@ -254,7 +261,7 @@
     EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
                          ->second.front()
                          .mFields->begin()
-                         ->second.value_int());
+                         ->mValue.int_value);
 }
 
 TEST(GaugeMetricProducerTest, TestWithCondition) {
@@ -288,9 +295,10 @@
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(100,
-        gaugeProducer.mCurrentSlicedBucket->begin()->
-            second.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
@@ -303,19 +311,26 @@
     gaugeProducer.onDataPulled(allData);
 
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(110,
-        gaugeProducer.mCurrentSlicedBucket->begin()->
-            second.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back()
-        .mGaugeAtoms.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
+                           ->second.back()
+                           .mGaugeAtoms.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
 
     gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
     gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back()
-        .mGaugeAtoms.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
+                            ->second.back()
+                            .mGaugeAtoms.front()
+                            .mFields->begin()
+                            ->mValue.int_value);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 }
 
@@ -353,9 +368,10 @@
 
     gaugeProducer.onDataPulled({event1});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(13L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->
-            second.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     std::shared_ptr<LogEvent> event2 =
@@ -366,9 +382,10 @@
 
     gaugeProducer.onDataPulled({event2});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(15L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->
-            second.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
@@ -380,9 +397,10 @@
 
     gaugeProducer.onDataPulled({event3});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(26L,
-        gaugeProducer.mCurrentSlicedBucket->begin()->
-            second.front().mFields->begin()->second.value_int());
+    EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 2658e4e..3397f14 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -53,7 +53,8 @@
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
+
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -89,7 +90,7 @@
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -126,7 +127,7 @@
     const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -168,7 +169,7 @@
     const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -206,7 +207,7 @@
     const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
 
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 4b579b1..293b1a8 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -52,7 +52,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -88,7 +88,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -122,7 +122,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -155,7 +155,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -197,7 +197,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
@@ -238,7 +238,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
@@ -283,7 +283,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
@@ -326,7 +326,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -395,7 +395,7 @@
     const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -446,7 +446,7 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    FieldMatcher dimensionInCondition;
+    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index ab9345a..7b9c0d6 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -19,20 +19,26 @@
 namespace statsd {
 
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) {
-    DimensionsValue dimensionsValue;
-    dimensionsValue.set_field(tagId);
-    dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
-    dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
-    return HashableDimensionKey(dimensionsValue);
+    HashableDimensionKey dimension;
+    int pos[] = {key, 0, 0};
+    dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value)));
+
+    return dimension;
 }
 
 MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
-    DimensionsValue dimensionsValue;
-    dimensionsValue.set_field(tagId);
-    dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
-    dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
-    return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+    return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY);
 }
+
+void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+}
+
+void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) {
+    matcher->set_field(tagId);
+    matcher->add_child()->set_field(fieldNum);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index b48de54..a01de63 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -26,12 +26,10 @@
 
 class MockConditionWizard : public ConditionWizard {
 public:
-    MOCK_METHOD4(
-            query,
-            ConditionState(const int conditionIndex,
-                           const ConditionKey& conditionParameters,
-                           const FieldMatcher& dimensionFields,
-                           std::unordered_set<HashableDimensionKey> *dimensionKeySet));
+    MOCK_METHOD4(query,
+                 ConditionState(const int conditionIndex, const ConditionKey& conditionParameters,
+                                const vector<Matcher>& dimensionFields,
+                                std::unordered_set<HashableDimensionKey>* dimensionKeySet));
 };
 
 class MockStatsPullerManager : public StatsPullerManager {
@@ -49,6 +47,10 @@
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
 MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
 
+// Utils to build FieldMatcher proto for simple one-depth atoms.
+void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
+void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 13055cb..d3a89617 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -186,7 +186,7 @@
 
 Predicate CreateScreenIsOffPredicate() {
     Predicate predicate;
-    predicate.set_id(StringToId("ScreenIsOff"));
+    predicate.set_id(1111123);
     predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
     predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
     return predicate;
@@ -202,7 +202,7 @@
 
 Predicate CreateIsSyncingPredicate() {
     Predicate predicate;
-    predicate.set_id(StringToId("IsSyncing"));
+    predicate.set_id(33333333333333);
     predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
     predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
     return predicate;
@@ -461,6 +461,93 @@
         .value_tuple().dimensions_value(1).value_str(), tag);
 }
 
+bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
+    if (s1.field() != s2.field()) {
+        return false;
+    }
+    if (s1.value_case() != s2.value_case()) {
+        return false;
+    }
+    switch (s1.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return (s1.value_str() == s2.value_str());
+        case DimensionsValue::ValueCase::kValueInt:
+            return s1.value_int() == s2.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return s1.value_long() == s2.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return s1.value_bool() == s2.value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return s1.value_float() == s2.value_float();
+        case DimensionsValue::ValueCase::kValueTuple: {
+            if (s1.value_tuple().dimensions_value_size() !=
+                s2.value_tuple().dimensions_value_size()) {
+                return false;
+            }
+            bool allMatched = true;
+            for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
+                allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i),
+                                       s2.value_tuple().dimensions_value(i));
+            }
+            return allMatched;
+        }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+        default:
+            return true;
+    }
+}
+
+bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
+    if (s1.field() != s2.field()) {
+        return s1.field() < s2.field();
+    }
+    if (s1.value_case() != s2.value_case()) {
+        return s1.value_case() < s2.value_case();
+    }
+    switch (s1.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return s1.value_str() < s2.value_str();
+        case DimensionsValue::ValueCase::kValueInt:
+            return s1.value_int() < s2.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return s1.value_long() < s2.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return (int)s1.value_bool() < (int)s2.value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return s1.value_float() < s2.value_float();
+        case DimensionsValue::ValueCase::kValueTuple: {
+            if (s1.value_tuple().dimensions_value_size() !=
+                s2.value_tuple().dimensions_value_size()) {
+                return s1.value_tuple().dimensions_value_size() <
+                       s2.value_tuple().dimensions_value_size();
+            }
+            for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) {
+                if (EqualsTo(s1.value_tuple().dimensions_value(i),
+                             s2.value_tuple().dimensions_value(i))) {
+                    continue;
+                } else {
+                    return LessThan(s1.value_tuple().dimensions_value(i),
+                                    s2.value_tuple().dimensions_value(i));
+                }
+            }
+            return false;
+        }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+        default:
+            return false;
+    }
+}
+
+bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) {
+    if (LessThan(s1.dimInWhat, s2.dimInWhat)) {
+        return true;
+    } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) {
+        return false;
+    }
+
+    return LessThan(s1.dimInCondition, s2.dimInCondition);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 6638893..5d83ed7 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -159,14 +159,30 @@
 void ValidateAttributionUidAndTagDimension(
     const DimensionsValue& value, int atomId, int uid, const std::string& tag);
 
+struct DimensionsPair {
+    DimensionsPair(DimensionsValue m1, DimensionsValue m2) : dimInWhat(m1), dimInCondition(m2){};
+
+    DimensionsValue dimInWhat;
+    DimensionsValue dimInCondition;
+};
+
+bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2);
+bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2);
+
+struct DimensionCompare {
+    bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const {
+        return LessThan(s1, s2);
+    }
+};
+
 template <typename T>
 void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
-    std::map<MetricDimensionKey, int> dimensionIndexMap;
+    std::map<DimensionsPair, int, DimensionCompare> dimensionIndexMap;
     for (int i = 0; i < metricData.data_size(); ++i) {
-        dimensionIndexMap.insert(std::make_pair(
-            MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
-            HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
-            i));
+        dimensionIndexMap.insert(
+                std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(),
+                                              metricData.data(i).dimensions_in_condition()),
+                               i));
     }
     for (const auto& itr : dimensionIndexMap) {
         *sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3eeaf65..7b70f59 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -37,9 +37,11 @@
 Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application;
 Landroid/app/ActivityThread;->currentPackageName()Ljava/lang/String;
 Landroid/app/ActivityThread;->currentProcessName()Ljava/lang/String;
+Landroid/app/ActivityThread;->getActivity(Landroid/os/IBinder;)Landroid/app/Activity;
 Landroid/app/ActivityThread;->getApplication()Landroid/app/Application;
 Landroid/app/ActivityThread;->getApplicationThread()Landroid/app/ActivityThread$ApplicationThread;
 Landroid/app/ActivityThread;->getHandler()Landroid/os/Handler;
+Landroid/app/ActivityThread;->getInstrumentation()Landroid/app/Instrumentation;
 Landroid/app/ActivityThread;->getPackageManager()Landroid/content/pm/IPackageManager;
 Landroid/app/ActivityThread;->getProcessName()Ljava/lang/String;
 Landroid/app/ActivityThread$H;->BIND_SERVICE:I
@@ -573,6 +575,7 @@
 Landroid/net/TrafficStats;->setThreadStatsTagBackup()V
 Landroid/net/TrafficStats;->setThreadStatsTagRestore()V
 Landroid/net/TrafficStats;->setThreadStatsUid(I)V
+Landroid/net/WebAddress;-><init>(Ljava/lang/String;)V
 Landroid/net/WifiKey;-><init>(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
 Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
@@ -616,6 +619,7 @@
 Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;Landroid/os/WorkSource;)V
 Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V
 Landroid/net/wifi/WifiScanner;->stopBackgroundScan(Landroid/net/wifi/WifiScanner$ScanListener;)V
+Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
 Landroid/nfc/NfcAdapter;->addNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;[Ljava/lang/String;)Z
 Landroid/nfc/NfcAdapter;->disable()Z
 Landroid/nfc/NfcAdapter;->enable()Z
@@ -875,6 +879,7 @@
 Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
 Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
+Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
 Landroid/telecom/AudioState;->getRoute()I
 Landroid/telecom/AudioState;->getSupportedRouteMask()I
 Landroid/telecom/AudioState;->isMuted()Z
@@ -969,6 +974,7 @@
 Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;III)V
 Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;IIIZ)V
 Landroid/text/SpannableStringInternal;->START:I
+Landroid/text/StaticLayout;-><init>(Ljava/lang/CharSequence;IILandroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZLandroid/text/TextUtils$TruncateAt;II)V
 Landroid/text/StaticLayout;->mColumns:I
 Landroid/text/StaticLayout;->mLineCount:I
 Landroid/text/StaticLayout;->mLines:[I
@@ -1017,6 +1023,7 @@
 Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory;
 Landroid/view/LayoutInflater;->mFactorySet:Z
 Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
+Landroid/view/LayoutInflater;->setPrivateFactory(Landroid/view/LayoutInflater$Factory2;)V
 Landroid/view/MotionEvent;->HISTORY_CURRENT:I
 Landroid/view/MotionEvent;->mNativePtr:J
 Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
@@ -1089,6 +1096,7 @@
 Landroid/view/View;->mScrollX:I
 Landroid/view/View;->mScrollY:I
 Landroid/view/View;->mStartActivityRequestWho:Ljava/lang/String;
+Landroid/view/View;->mTag:Ljava/lang/Object;
 Landroid/view/View;->mTop:I
 Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
 Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
@@ -1127,20 +1135,72 @@
 Landroid/view/Window;->mAppName:Ljava/lang/String;
 Landroid/view/Window;->mAppToken:Landroid/os/IBinder;
 Landroid/view/Window;->mHardwareAccelerated:Z
+Landroid/webkit/FindActionModeCallback;->findAll()V
+Landroid/webkit/FindActionModeCallback;-><init>(Landroid/content/Context;)V
+Landroid/webkit/FindActionModeCallback;->setText(Ljava/lang/String;)V
+Landroid/webkit/FindActionModeCallback;->setWebView(Landroid/webkit/WebView;)V
+Landroid/webkit/FindActionModeCallback;->showSoftInput()V
+Landroid/webkit/GeolocationPermissions;-><init>()V
+Landroid/webkit/HttpAuthHandler;-><init>()V
+Landroid/webkit/JsDialogHelper;-><init>(Landroid/webkit/JsPromptResult;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/webkit/JsDialogHelper;->showDialog(Landroid/content/Context;)V
+Landroid/webkit/JsPromptResult;->getStringResult()Ljava/lang/String;
+Landroid/webkit/JsPromptResult;-><init>(Landroid/webkit/JsResult$ResultReceiver;)V
+Landroid/webkit/SslErrorHandler;-><init>()V
+Landroid/webkit/TokenBindingService;-><init>()V
+Landroid/webkit/TokenBindingService$TokenBindingKey;-><init>()V
+Landroid/webkit/WebChromeClient;->openFileChooser(Landroid/webkit/ValueCallback;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/webkit/WebMessagePort;-><init>()V
+Landroid/webkit/WebResourceError;-><init>()V
+Landroid/webkit/WebResourceResponse;-><init>(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/util/Map;Ljava/io/InputStream;)V
+Landroid/webkit/WebSettings;->getAcceptThirdPartyCookies()Z
+Landroid/webkit/WebSettings;->setAcceptThirdPartyCookies(Z)V
 Landroid/webkit/WebSettings;->setNavDump(Z)V
 Landroid/webkit/WebSettings;->setPluginsEnabled(Z)V
+Landroid/webkit/WebStorage;-><init>()V
+Landroid/webkit/WebStorage$Origin;-><init>(Ljava/lang/String;JJ)V
 Landroid/webkit/WebView;->debugDump()V
+Landroid/webkit/WebViewDelegate;->addWebViewAssetPath(Landroid/content/Context;)V
+Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;JLjava/lang/Runnable;)V
+Landroid/webkit/WebViewDelegate;->callDrawGlFunction(Landroid/graphics/Canvas;J)V
+Landroid/webkit/WebViewDelegate;->canInvokeDrawGlFunctor(Landroid/view/View;)Z
+Landroid/webkit/WebViewDelegate;->detachDrawGlFunctor(Landroid/view/View;J)V
+Landroid/webkit/WebViewDelegate;->getApplication()Landroid/app/Application;
+Landroid/webkit/WebViewDelegate;->getDataDirectorySuffix()Ljava/lang/String;
+Landroid/webkit/WebViewDelegate;->getErrorString(Landroid/content/Context;I)Ljava/lang/String;
+Landroid/webkit/WebViewDelegate;->getPackageId(Landroid/content/res/Resources;Ljava/lang/String;)I
+Landroid/webkit/WebViewDelegate;->invokeDrawGlFunctor(Landroid/view/View;JZ)V
+Landroid/webkit/WebViewDelegate;->isMultiProcessEnabled()Z
+Landroid/webkit/WebViewDelegate;->isTraceTagEnabled()Z
+Landroid/webkit/WebViewDelegate;->setOnTraceEnabledChangeListener(Landroid/webkit/WebViewDelegate$OnTraceEnabledChangeListener;)V
 Landroid/webkit/WebView;->disablePlatformNotifications()V
 Landroid/webkit/WebView;->emulateShiftHeld()V
 Landroid/webkit/WebView;->enablePlatformNotifications()V
 Landroid/webkit/WebViewFactory;->getLoadedPackageInfo()Landroid/content/pm/PackageInfo;
 Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebViewFactory;->loadWebViewNativeLibraryFromPackage(Ljava/lang/String;Ljava/lang/ClassLoader;)I
 Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo;
 Landroid/webkit/WebView;->getVisibleTitleHeight()I
 Landroid/webkit/WebView;->getWebViewProvider()Landroid/webkit/WebViewProvider;
+Landroid/webkit/WebView$HitTestResult;-><init>()V
+Landroid/webkit/WebView$HitTestResult;->setExtra(Ljava/lang/String;)V
+Landroid/webkit/WebView$HitTestResult;->setType(I)V
 Landroid/webkit/WebView;->isPaused()Z
 Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
 Landroid/webkit/WebView;->notifyFindDialogDismissed()V
+Landroid/webkit/WebView$PrivateAccess;->overScrollBy(IIIIIIIIZ)V
+Landroid/webkit/WebView$PrivateAccess;->setMeasuredDimension(II)V
+Landroid/webkit/WebView$PrivateAccess;->super_dispatchKeyEvent(Landroid/view/KeyEvent;)Z
+Landroid/webkit/WebView$PrivateAccess;->super_getScrollBarStyle()I
+Landroid/webkit/WebView$PrivateAccess;->super_onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
+Landroid/webkit/WebView$PrivateAccess;->super_onGenericMotionEvent(Landroid/view/MotionEvent;)Z
+Landroid/webkit/WebView$PrivateAccess;->super_performAccessibilityAction(ILandroid/os/Bundle;)Z
+Landroid/webkit/WebView$PrivateAccess;->super_performLongClick()Z
+Landroid/webkit/WebView$PrivateAccess;->super_requestFocus(ILandroid/graphics/Rect;)Z
+Landroid/webkit/WebView$PrivateAccess;->super_scrollTo(II)V
+Landroid/webkit/WebView$PrivateAccess;->super_setFrame(IIII)Z
+Landroid/webkit/WebView$PrivateAccess;->super_setLayoutParams(Landroid/view/ViewGroup$LayoutParams;)V
+Landroid/webkit/WebView$PrivateAccess;->super_startActivityForResult(Landroid/content/Intent;I)V
 Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
 Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z
 Landroid/webkit/WebView;->sEnforceThreadChecking:Z
@@ -1395,6 +1455,7 @@
 Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
 Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
 Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
 Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
 Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
 Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
@@ -1414,6 +1475,7 @@
 Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
 Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
 Ldalvik/system/VMRuntime;->registerNativeFree(I)V
+Ldalvik/system/VMRuntime;->runFinalization(J)V
 Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
 Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
 Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
@@ -1434,13 +1496,39 @@
 Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
 Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
 Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
+Ljava/lang/Daemons;->requestHeapTrim()V
+Ljava/lang/Daemons;->start()V
+Ljava/lang/Daemons;->stop()V
+Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
+Ljava/lang/reflect/Executable;->artMethod:J
+Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
+Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
 Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
+Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/Thread;->daemon:Z
+Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
+Ljava/lang/ThreadGroup;->add(Ljava/lang/Thread;)V
+Ljava/lang/ThreadGroup;->groups:[Ljava/lang/ThreadGroup;
+Ljava/lang/Thread;->group:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->mainThreadGroup:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->name:Ljava/lang/String;
+Ljava/lang/ThreadGroup;->ngroups:I
 Ljava/lang/ThreadGroup;->parent:Ljava/lang/ThreadGroup;
 Ljava/lang/ThreadGroup;->systemThreadGroup:Ljava/lang/ThreadGroup;
+Ljava/lang/ThreadGroup;->threadTerminated(Ljava/lang/Thread;)V
 Ljava/lang/Thread;->inheritableThreadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
+Ljava/lang/Thread;-><init>(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V
+Ljava/lang/Thread;->lock:Ljava/lang/Object;
+Ljava/lang/Thread;->name:Ljava/lang/String;
+Ljava/lang/Thread;->nativePeer:J
+Ljava/lang/Thread;->priority:I
+Ljava/lang/Throwable;->backtrace:Ljava/lang/Object;
+Ljava/lang/Throwable;->cause:Ljava/lang/Throwable;
 Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
+Ljava/lang/Throwable;->stackTrace:[Ljava/lang/StackTraceElement;
+Ljava/lang/Throwable;->suppressedExceptions:Ljava/util/List;
 Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
 Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
 Ljava/net/InetAddress;->clearDnsCache()V
@@ -1449,9 +1537,17 @@
 Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
 Ljava/net/URI;->host:Ljava/lang/String;
 Ljava/nio/Buffer;->address:J
+Ljava/nio/Buffer;->capacity:I
+Ljava/nio/Buffer;->limit:I
+Ljava/nio/ByteBuffer;->hb:[B
+Ljava/nio/ByteBuffer;->isReadOnly:Z
+Ljava/nio/ByteBuffer;->offset:I
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
+Ljava/nio/DirectByteBuffer;-><init>(JI)V
 Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
 Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
+Ljava/util/ArrayList;->elementData:[Ljava/lang/Object;
+Ljava/util/ArrayList;->size:I
 Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
 Ljava/util/ArrayList$SubList;->parentOffset:I
 Ljava/util/ArrayList$SubList;->size:I
@@ -1461,6 +1557,7 @@
 Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Llibcore/util/ZoneInfo;->mTransitions:[J
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
 Lorg/json/JSONArray;->values:Ljava/util/List;
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index ea0fd75..256c479 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -471,6 +471,14 @@
      * {@link #onStart} and returns either {@link #START_STICKY}
      * or {@link #START_STICKY_COMPATIBILITY}.
      * 
+     * <p>If you need your application to run on platform versions prior to API
+     * level 5, you can use the following model to handle the older {@link #onStart}
+     * callback in that case.  The <code>handleCommand</code> method is implemented by
+     * you as appropriate:
+     * 
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
+     *   start_compatibility}
+     *
      * <p class="caution">Note that the system calls this on your
      * service's main thread.  A service's main thread is the same
      * thread where UI operations take place for Activities running in the
@@ -679,10 +687,6 @@
      * {@link #startService(Intent)} first to tell the system it should keep the service running,
      * and then use this method to tell it to keep it running harder.</p>
      *
-     * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request
-     * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
-     * this API.</p>
-     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 8f49bc1..0f1c249 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.view.View;
+import android.view.View.AutofillImportance;
 import android.view.ViewRootImpl;
 import android.view.ViewStructure;
 import android.view.ViewStructure.HtmlInfo;
@@ -632,6 +633,7 @@
         int mMaxEms = -1;
         int mMaxLength = -1;
         @Nullable String mTextIdEntry;
+        @AutofillImportance int mImportantForAutofill;
 
         // POJO used to override some autofill-related values when the node is parcelized.
         // Not written to parcel.
@@ -733,6 +735,7 @@
                 mMaxEms = in.readInt();
                 mMaxLength = in.readInt();
                 mTextIdEntry = preader.readString();
+                mImportantForAutofill = in.readInt();
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 mX = in.readInt();
@@ -900,6 +903,7 @@
                 out.writeInt(mMaxEms);
                 out.writeInt(mMaxLength);
                 pwriter.writeString(mTextIdEntry);
+                out.writeInt(mImportantForAutofill);
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 out.writeInt(mX);
@@ -1512,6 +1516,16 @@
         public int getMaxTextLength() {
             return mMaxLength;
         }
+
+        /**
+         * Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of
+         * the view associated with this node.
+         *
+         * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
+         */
+        public @AutofillImportance int getImportantForAutofill() {
+            return mImportantForAutofill;
+        }
     }
 
     /**
@@ -1844,6 +1858,11 @@
         }
 
         @Override
+        public void setImportantForAutofill(@AutofillImportance int mode) {
+            mNode.mImportantForAutofill = mode;
+        }
+
+        @Override
         public void setInputType(int inputType) {
             mNode.mInputType = inputType;
         }
@@ -2144,7 +2163,8 @@
                     + ", options=" + Arrays.toString(node.getAutofillOptions())
                     + ", hints=" + Arrays.toString(node.getAutofillHints())
                     + ", value=" + node.getAutofillValue()
-                    + ", sanitized=" + node.isSanitized());
+                    + ", sanitized=" + node.isSanitized()
+                    + ", importantFor=" + node.getImportantForAutofill());
         }
 
         final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 0a38eb9..dc79256 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -68,6 +68,23 @@
     private static final String TAG = "timezone.RulesManager";
     private static final boolean DEBUG = false;
 
+    /**
+     * The action of the intent that the Android system will broadcast when a time zone rules update
+     * operation has been successfully staged  (i.e. to be applied next reboot) or unstaged.
+     *
+     * <p>See {@link #EXTRA_OPERATION_STAGED}
+     *
+     * <p>This is a protected intent that can only be sent by the system.
+     */
+    public static final String ACTION_RULES_UPDATE_OPERATION =
+            "com.android.intent.action.timezone.RULES_UPDATE_OPERATION";
+
+    /**
+     * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to
+     * indicate whether the operation was a "stage" or an "unstage".
+     */
+    public static final String EXTRA_OPERATION_STAGED = "staged";
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
             SUCCESS,
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 91dd7ee..cf01451 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -912,7 +912,7 @@
      * first try to load CSL from the cache. If not found, try to get from the constant state.
      * Last, parse the XML and generate the CSL.
      */
-    @NonNull
+    @Nullable
     private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
             TypedValue value, int id) {
         final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -932,15 +932,17 @@
             complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
         }
 
-        complexColor.setBaseChangingConfigurations(value.changingConfigurations);
+        if (complexColor != null) {
+            complexColor.setBaseChangingConfigurations(value.changingConfigurations);
 
-        if (mPreloading) {
-            if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
-                    0, value.resourceId, "color")) {
-                sPreloadedComplexColors.put(key, complexColor.getConstantState());
+            if (mPreloading) {
+                if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
+                        0, value.resourceId, "color")) {
+                    sPreloadedComplexColors.put(key, complexColor.getConstantState());
+                }
+            } else {
+                cache.put(key, theme, complexColor.getConstantState());
             }
-        } else {
-            cache.put(key, theme, complexColor.getConstantState());
         }
         return complexColor;
     }
@@ -1044,7 +1046,8 @@
      * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
      * and selector tag.
      *
-     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or
+     *     {@code null} if the XML file is neither.
      */
     @NonNull
     private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index fc78861..48f5684 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -894,14 +894,6 @@
 
         /**
          * P.
-         *
-         * <p>Applications targeting this or a later release will get these
-         * new changes in behavior:</p>
-         * <ul>
-         * <li>{@link android.app.Service#startForeground Service.startForeground} requires
-         * that apps hold the permission
-         * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
-         * </ul>
          */
         public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
     }
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index c6149be..24c9c91 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -271,4 +271,22 @@
             }
         }
     }
+
+    /**
+     * Verifies that a payload associated with the given payload metadata
+     * {@code payloadMetadataFilename} can be safely applied to ths device.
+     * Returns {@code true} if the update can successfully be applied and
+     * returns {@code false} otherwise.
+     *
+     * @param payloadMetadataFilename the location of the metadata without the
+     * {@code file://} prefix.
+     */
+    @SystemApi
+    public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
+        try {
+            return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5de25ba..cf26b2b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7871,6 +7871,7 @@
                 structure.setAutofillHints(getAutofillHints());
                 structure.setAutofillValue(getAutofillValue());
             }
+            structure.setImportantForAutofill(getImportantForAutofill());
         }
 
         int ignoredParentLeft = 0;
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 1d94abe..3f7ab2a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -23,6 +23,8 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.util.Pair;
+import android.view.View.AutofillImportance;
+import android.view.ViewStructure.HtmlInfo;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
@@ -347,6 +349,12 @@
     public abstract void setAutofillOptions(CharSequence[] options);
 
     /**
+     * Sets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of the
+     * view associated with this node.
+     */
+    public void setImportantForAutofill(@AutofillImportance int mode) {}
+
+    /**
      * Sets the {@link android.text.InputType} bits of this node.
      *
      * @param inputType inputType bits as defined by {@link android.text.InputType}.
diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java
index 03a6d3a..f510879 100644
--- a/core/java/android/view/textclassifier/logging/DefaultLogger.java
+++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java
@@ -80,7 +80,7 @@
         Preconditions.checkNotNull(event);
         final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
                 .setType(getLogType(event))
-                .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
+                .setSubtype(getLogSubType(event))
                 .setPackageName(event.getPackageName())
                 .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
                 .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
@@ -137,6 +137,17 @@
         }
     }
 
+    private static int getLogSubType(SelectionEvent event) {
+        switch (event.getInvocationMethod()) {
+            case SelectionEvent.INVOCATION_MANUAL:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
+            case SelectionEvent.INVOCATION_LINK:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
+            default:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
+        }
+    }
+
     private static String getLogTypeString(int logType) {
         switch (logType) {
             case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
@@ -176,6 +187,17 @@
         }
     }
 
+    private static String getLogSubTypeString(int logSubType) {
+        switch (logSubType) {
+            case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
+                return "MANUAL";
+            case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
+                return "LINK";
+            default:
+                return UNKNOWN;
+        }
+    }
+
     private static void debugLog(LogMaker log) {
         if (!DEBUG_LOG_ENABLED) return;
 
@@ -193,6 +215,7 @@
         final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
         final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
         final String type = getLogTypeString(log.getType());
+        final String subType = getLogSubTypeString(log.getSubtype());
         final int smartStart = Integer.parseInt(
                 Objects.toString(log.getTaggedData(SMART_START), ZERO));
         final int smartEnd = Integer.parseInt(
@@ -202,8 +225,9 @@
         final int eventEnd = Integer.parseInt(
                 Objects.toString(log.getTaggedData(EVENT_END), ZERO));
 
-        Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
-                index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
+        Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget,
+                model));
     }
 
     /**
diff --git a/core/java/android/view/textclassifier/logging/Logger.java b/core/java/android/view/textclassifier/logging/Logger.java
index 40e4d8c..4448b2b 100644
--- a/core/java/android/view/textclassifier/logging/Logger.java
+++ b/core/java/android/view/textclassifier/logging/Logger.java
@@ -71,6 +71,7 @@
     public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
     public static final String WIDGET_UNKNOWN = "unknown";
 
+    private @SelectionEvent.InvocationMethod int mInvocationMethod;
     private SelectionEvent mPrevEvent;
     private SelectionEvent mSmartEvent;
     private SelectionEvent mStartEvent;
@@ -124,16 +125,19 @@
     /**
      * Logs a "selection started" event.
      *
+     * @param invocationMethod  the way the selection was triggered
      * @param start  the token index of the selected token
      */
-    public final void logSelectionStartedEvent(int start) {
+    public final void logSelectionStartedEvent(
+            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
         if (mConfig == null) {
             return;
         }
 
+        mInvocationMethod = invocationMethod;
         logEvent(new SelectionEvent(
                 start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
-                TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig));
     }
 
     /**
@@ -152,7 +156,7 @@
 
         logEvent(new SelectionEvent(
                 start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig));
     }
 
     /**
@@ -179,7 +183,7 @@
         final String signature = classification.getSignature();
         logEvent(new SelectionEvent(
                 start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                entityType, signature, mConfig));
+                entityType, mInvocationMethod, signature, mConfig));
     }
 
     /**
@@ -213,7 +217,8 @@
                 ? selection.getEntity(0)
                 : TextClassifier.TYPE_UNKNOWN;
         final String signature = selection.getSignature();
-        logEvent(new SelectionEvent(start, end, eventType, entityType, signature, mConfig));
+        logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod, signature,
+                mConfig));
     }
 
     /**
@@ -234,7 +239,8 @@
         }
 
         logEvent(new SelectionEvent(
-                start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+                start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod,
+                NO_SIGNATURE, mConfig));
     }
 
     /**
@@ -265,7 +271,8 @@
                 ? classification.getEntity(0)
                 : TextClassifier.TYPE_UNKNOWN;
         final String signature = classification.getSignature();
-        logEvent(new SelectionEvent(start, end, actionType, entityType, signature, mConfig));
+        logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod,
+                signature, mConfig));
     }
 
     private void logEvent(@NonNull SelectionEvent event) {
diff --git a/core/java/android/view/textclassifier/logging/SelectionEvent.java b/core/java/android/view/textclassifier/logging/SelectionEvent.java
index f40b655..a8de308 100644
--- a/core/java/android/view/textclassifier/logging/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/logging/SelectionEvent.java
@@ -98,6 +98,16 @@
     /** Something else other than User or the default TextClassifier triggered a selection. */
     public static final int EVENT_AUTO_SELECTION = 5;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({INVOCATION_MANUAL, INVOCATION_LINK})
+    public @interface InvocationMethod {}
+
+    /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
+    public static final int INVOCATION_MANUAL = 1;
+    /** Selection was invoked by the user tapping on a link. */
+    public static final int INVOCATION_LINK = 2;
+
     private final int mAbsoluteStart;
     private final int mAbsoluteEnd;
     private final @EventType int mEventType;
@@ -105,6 +115,7 @@
     @Nullable private final String mWidgetVersion;
     private final String mPackageName;
     private final String mWidgetType;
+    private final @InvocationMethod int mInvocationMethod;
 
     // These fields should only be set by creator of a SelectionEvent.
     private String mSignature;
@@ -121,7 +132,7 @@
     SelectionEvent(
             int start, int end,
             @EventType int eventType, @EntityType String entityType,
-            String signature, Logger.Config config) {
+            @InvocationMethod int invocationMethod, String signature, Logger.Config config) {
         Preconditions.checkArgument(end >= start, "end cannot be less than start");
         mAbsoluteStart = start;
         mAbsoluteEnd = end;
@@ -132,6 +143,7 @@
         mWidgetVersion = config.getWidgetVersion();
         mPackageName = Preconditions.checkNotNull(config.getPackageName());
         mWidgetType = Preconditions.checkNotNull(config.getWidgetType());
+        mInvocationMethod = invocationMethod;
     }
 
     int getAbsoluteStart() {
@@ -180,6 +192,13 @@
     }
 
     /**
+     * Returns the way the selection mode was invoked.
+     */
+    public @InvocationMethod int getInvocationMethod() {
+        return mInvocationMethod;
+    }
+
+    /**
      * Returns the signature of the text classifier result associated with this event.
      */
     public String getSignature() {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e7a4c02..6ab09d6 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -111,7 +111,8 @@
         mSelectionTracker.onOriginalSelection(
                 getText(mTextView),
                 mTextView.getSelectionStart(),
-                mTextView.getSelectionEnd());
+                mTextView.getSelectionEnd(),
+                false /*isLink*/);
         cancelAsyncTask();
         if (skipTextClassification()) {
             startSelectionActionMode(null);
@@ -134,7 +135,11 @@
      * Starts Link ActionMode.
      */
     public void startLinkActionModeAsync(TextLinks.TextLink textLink) {
-        //TODO: tracking/logging
+        mSelectionTracker.onOriginalSelection(
+                getText(mTextView),
+                mTextView.getSelectionStart(),
+                mTextView.getSelectionEnd(),
+                true /*isLink*/);
         cancelAsyncTask();
         if (skipTextClassification()) {
             startLinkActionMode(null);
@@ -487,7 +492,8 @@
         /**
          * Called when the original selection happens, before smart selection is triggered.
          */
-        public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) {
+        public void onOriginalSelection(
+                CharSequence text, int selectionStart, int selectionEnd, boolean isLink) {
             // If we abandoned a selection and created a new one very shortly after, we may still
             // have a pending request to log ABANDON, which we flush here.
             mDelayedLogAbandon.flush();
@@ -496,7 +502,8 @@
             mOriginalEnd = mSelectionEnd = selectionEnd;
             mAllowReset = false;
             maybeInvalidateLogger();
-            mLogger.logSelectionStarted(text, selectionStart);
+            mLogger.logSelectionStarted(text, selectionStart,
+                    isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL);
         }
 
         /**
@@ -679,7 +686,9 @@
             return Logger.WIDGET_UNSELECTABLE_TEXTVIEW;
         }
 
-        public void logSelectionStarted(CharSequence text, int index) {
+        public void logSelectionStarted(
+                CharSequence text, int index,
+                @SelectionEvent.InvocationMethod int invocationMethod) {
             try {
                 Preconditions.checkNotNull(text);
                 Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
@@ -688,7 +697,7 @@
                 }
                 mTokenIterator.setText(mText);
                 mStartIndex = index;
-                mLogger.logSelectionStartedEvent(0);
+                mLogger.logSelectionStartedEvent(invocationMethod, 0);
             } catch (Exception e) {
                 // Avoid crashes due to logging.
                 Log.d(LOG_TAG, e.getMessage());
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1cf75a0..8612916 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -573,6 +573,10 @@
     <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
     <protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
 
+    <!-- Time zone rules update intents fired by the system server -->
+    <protected-broadcast android:name="com.android.intent.action.timezone.RULES_UPDATE_OPERATION" />
+    <protected-broadcast android:name="com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK" />
+
     <!-- Made protected in P (was introduced in JB-MR2) -->
     <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
     <protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
@@ -2966,11 +2970,16 @@
     <permission android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows an application to delete cache files.
-    <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Old permission for deleting an app's cache files, no longer used,
+         but signals for us to quietly ignore calls instead of throwing an exception. -->
     <permission android:name="android.permission.DELETE_CACHE_FILES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to delete cache files.
+         @hide -->
+    <permission android:name="android.permission.INTERNAL_DELETE_CACHE_FILES"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to delete packages.
          <p>Not for use by third-party applications.
          <p>Starting in {@link android.os.Build.VERSION_CODES#N}, user confirmation is requested
@@ -3796,15 +3805,6 @@
     <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
         android:protectionLevel="signature|development|instant|appop" />
 
-    <!-- Allows a regular application to use {@link android.app.Service#startForeground
-         Service.startForeground}.
-         <p>Protection level: normal
-    -->
-    <permission android:name="android.permission.FOREGROUND_SERVICE"
-        android:description="@string/permdesc_foregroundService"
-        android:label="@string/permlab_foregroundService"
-        android:protectionLevel="normal|instant" />
-
     <!-- @hide Allows system components to access all app shortcuts. -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
         android:protectionLevel="signature" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2b7b056..ec81df7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -916,11 +916,6 @@
     <string name="permdesc_persistentActivity" product="default">Allows the app to make parts of itself persistent in memory.  This can limit memory available to other apps slowing down the phone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_foregroundService">run foreground service</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_getPackageSize">measure app storage space</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 53c22f6..7d5c60a 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -51,7 +51,6 @@
     <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
     <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 90fcaab..279e05f 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -634,8 +634,39 @@
      * @throws ResourceBusyException if required resources are in use
      */
     @NonNull
-    public native byte[] openSession() throws NotProvisionedException,
-            ResourceBusyException;
+    public byte[] openSession() throws NotProvisionedException,
+            ResourceBusyException {
+        return openSession(getMaxSecurityLevel());
+    }
+
+    /**
+     * Open a new session at a requested security level. The security level
+     * represents the robustness of the device's DRM implementation. By default,
+     * sessions are opened at the native security level of the device.
+     * Overriding the security level is necessary when the decrypted frames need
+     * to be manipulated, such as for image compositing. The security level
+     * parameter must be lower than the native level. Reducing the security
+     * level will typically limit the content to lower resolutions, as
+     * determined by the license policy. If the requested level is not
+     * supported, the next lower supported security level will be set. The level
+     * can be queried using {@link #getSecurityLevel}. A session
+     * ID is returned.
+     *
+     * @param level the new security level, one of
+     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
+     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
+     * {@link #HW_SECURE_ALL}.
+     *
+     * @throws NotProvisionedException if provisioning is needed
+     * @throws ResourceBusyException if required resources are in use
+     * @throws IllegalArgumentException if the requested security level is
+     * higher than the native level or lower than the lowest supported level or
+     * if the device does not support specifying the security level when opening
+     * a session
+     */
+    @NonNull
+    public native byte[] openSession(@SecurityLevel int level) throws
+            NotProvisionedException, ResourceBusyException;
 
     /**
      * Close a session on the MediaDrm object that was previously opened
@@ -1109,7 +1140,7 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
-                        HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+            HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
     public @interface SecurityLevel {}
 
     /**
@@ -1119,39 +1150,55 @@
     public static final int SECURITY_LEVEL_UNKNOWN = 0;
 
     /**
-     *  Software-based whitebox crypto
+     * DRM key management uses software-based whitebox crypto.
      */
     public static final int SW_SECURE_CRYPTO = 1;
 
     /**
-     * Software-based whitebox crypto and an obfuscated decoder
+     * DRM key management and decoding use software-based whitebox crypto.
      */
-     public static final int SW_SECURE_DECODE = 2;
+    public static final int SW_SECURE_DECODE = 2;
 
     /**
-     * DRM key management and crypto operations are performed within a
-     * hardware backed trusted execution environment
+     * DRM key management and crypto operations are performed within a hardware
+     * backed trusted execution environment.
      */
     public static final int HW_SECURE_CRYPTO = 3;
 
     /**
-     * DRM key management, crypto operations and decoding of content
-     * are performed within a hardware backed trusted execution environment
+     * DRM key management, crypto operations and decoding of content are
+     * performed within a hardware backed trusted execution environment.
      */
-     public static final int HW_SECURE_DECODE = 4;
+    public static final int HW_SECURE_DECODE = 4;
 
     /**
      * DRM key management, crypto operations, decoding of content and all
-     * handling of the media (compressed and uncompressed) is handled within
-     * a hardware backed trusted execution environment.
+     * handling of the media (compressed and uncompressed) is handled within a
+     * hardware backed trusted execution environment.
      */
     public static final int HW_SECURE_ALL = 5;
 
     /**
-     * Return the current security level of a session. A session
-     * has an initial security level determined by the robustness of
-     * the DRM system's implementation on the device. The security
-     * level may be adjusted using {@link #setSecurityLevel}.
+     * The maximum security level supported by the device. This is the default
+     * security level when a session is opened.
+     * @hide
+     */
+    public static final int SECURITY_LEVEL_MAX = 6;
+
+    /**
+     * The maximum security level supported by the device. This is the default
+     * security level when a session is opened.
+     */
+    @SecurityLevel
+    public static final int getMaxSecurityLevel() {
+        return SECURITY_LEVEL_MAX;
+    }
+
+    /**
+     * Return the current security level of a session. A session has an initial
+     * security level determined by the robustness of the DRM system's
+     * implementation on the device. The security level may be changed at the
+     * time a session is opened using {@link #openSession}.
      * @param sessionId the session to query.
      * <p>
      * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
@@ -1163,21 +1210,6 @@
     public native int getSecurityLevel(@NonNull byte[] sessionId);
 
     /**
-     * Set the security level of a session. This can be useful if specific
-     * attributes of a lower security level are needed by an application,
-     * such as image manipulation or compositing. Reducing the security
-     * level will typically limit decryption to lower content resolutions,
-     * depending on the license policy.
-     * @param sessionId the session to set the security level on.
-     * @param level the new security level, one of
-     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
-     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
-     * {@link #HW_SECURE_ALL}.
-     */
-    public native void setSecurityLevel(@NonNull byte[] sessionId,
-            @SecurityLevel int level);
-
-    /**
      * String property name: identifies the maker of the DRM plugin
      */
     public static final String PROPERTY_VENDOR = "vendor";
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4f06caa..d7f51d4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -145,6 +145,7 @@
 
 struct SecurityLevels {
     jint kSecurityLevelUnknown;
+    jint kSecurityLevelMax;
     jint kSecurityLevelSwSecureCrypto;
     jint kSecurityLevelSwSecureDecode;
     jint kSecurityLevelHwSecureCrypto;
@@ -683,6 +684,10 @@
     GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I");
     gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
 
+    jmethodID getMaxSecurityLevel;
+    GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I");
+    gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel);
+
     FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
     GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
     GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
@@ -813,7 +818,7 @@
 }
 
 static jbyteArray android_media_MediaDrm_openSession(
-    JNIEnv *env, jobject thiz) {
+        JNIEnv *env, jobject thiz, jint jlevel) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
     if (drm == NULL) {
@@ -823,7 +828,26 @@
     }
 
     Vector<uint8_t> sessionId;
-    status_t err = drm->openSession(sessionId);
+    DrmPlugin::SecurityLevel level;
+
+    if (jlevel == gSecurityLevels.kSecurityLevelMax) {
+        level = DrmPlugin::kSecurityLevelMax;
+    }  else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
+        level = DrmPlugin::kSecurityLevelSwSecureCrypto;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
+        level = DrmPlugin::kSecurityLevelSwSecureDecode;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
+        level = DrmPlugin::kSecurityLevelHwSecureCrypto;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
+        level = DrmPlugin::kSecurityLevelHwSecureDecode;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
+        level = DrmPlugin::kSecurityLevelHwSecureAll;
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
+        return NULL;
+    }
+
+    status_t err = drm->openSession(level, sessionId);
 
     if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
         return NULL;
@@ -1345,40 +1369,6 @@
 }
 
 
-static void android_media_MediaDrm_setSecurityLevel(JNIEnv *env,
-        jobject thiz, jbyteArray jsessionId, jint jlevel) {
-    sp<IDrm> drm = GetDrm(env, thiz);
-
-    if (!CheckSession(env, drm, jsessionId)) {
-        return;
-    }
-
-    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
-    DrmPlugin::SecurityLevel level;
-
-    if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
-        level = DrmPlugin::kSecurityLevelSwSecureCrypto;
-    } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
-        level = DrmPlugin::kSecurityLevelSwSecureDecode;
-    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
-        level = DrmPlugin::kSecurityLevelHwSecureCrypto;
-    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
-        level = DrmPlugin::kSecurityLevelHwSecureDecode;
-    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
-        level = DrmPlugin::kSecurityLevelHwSecureAll;
-    } else {
-        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
-        return;
-    }
-
-    status_t err = drm->setSecurityLevel(sessionId, level);
-
-    if (throwExceptionAsNecessary(env, err, "Failed to set security level")) {
-        return;
-    }
-}
-
-
 static jstring android_media_MediaDrm_getPropertyString(
     JNIEnv *env, jobject thiz, jstring jname) {
     sp<IDrm> drm = GetDrm(env, thiz);
@@ -1724,7 +1714,7 @@
     { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
       (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
 
-    { "openSession", "()[B",
+    { "openSession", "(I)[B",
       (void *)android_media_MediaDrm_openSession },
 
     { "closeSession", "([B)V",
@@ -1785,9 +1775,6 @@
     { "getSecurityLevel", "([B)I",
       (void *)android_media_MediaDrm_getSecurityLevel },
 
-    { "setSecurityLevel", "([BI)V",
-      (void *)android_media_MediaDrm_setSecurityLevel },
-
     { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
       (void *)android_media_MediaDrm_getPropertyString },
 
diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk
index 88b85e0..12972f1 100644
--- a/packages/CtsShim/Android.mk
+++ b/packages/CtsShim/Android.mk
@@ -32,9 +32,10 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
 
-my_archs := arm x86
-my_src_arch := $(call get-prebuilt-src-arch, $(my_archs))
-LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShimPriv.apk
+LOCAL_SRC_FILES_arm := apk/arm/CtsShimPriv.apk
+LOCAL_SRC_FILES_arm64 := apk/arm/CtsShimPriv.apk
+LOCAL_SRC_FILES_x86 := apk/x86/CtsShimPriv.apk
+LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShimPriv.apk
 
 include $(BUILD_PREBUILT)
 
@@ -53,9 +54,10 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
 
-my_archs := arm x86
-my_src_arch := $(call get-prebuilt-src-arch, $(my_archs))
-LOCAL_SRC_FILES := apk/$(my_src_arch)/CtsShim.apk
+LOCAL_SRC_FILES_arm := apk/arm/CtsShim.apk
+LOCAL_SRC_FILES_arm64 := apk/arm/CtsShim.apk
+LOCAL_SRC_FILES_x86 := apk/x86/CtsShim.apk
+LOCAL_SRC_FILES_x86_64 := apk/x86/CtsShim.apk
 
 include $(BUILD_PREBUILT)
 
diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml
index c0a59b3..8d79f62 100644
--- a/packages/MtpDocumentsProvider/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/AndroidManifest.xml
@@ -3,7 +3,6 @@
           package="com.android.mtp"
           android:sharedUserId="android.media">
     <uses-feature android:name="android.hardware.usb.host" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index ddb49b6..7b09ef7 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -83,4 +83,12 @@
     <dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
     <!-- Zen mode panel: bottom padding, a bit less than qs_panel_padding -->
     <dimen name="zen_mode_condition_detail_bottom_padding">4dp</dimen>
+
+    <!-- SignalDrawable -->
+    <dimen name="signal_icon_size">17dp</dimen>
+    <!-- How far to inset the rounded edges -->
+    <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
+
+
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
rename to packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
index 15ef742..846e30d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/SignalDrawable.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.settingslib.graph;
 
 import android.animation.ArgbEvaluator;
 import android.annotation.IntRange;
@@ -36,7 +36,6 @@
 
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
-import com.android.systemui.qs.SlashDrawable;
 
 public class SignalDrawable extends Drawable {
 
@@ -458,6 +457,7 @@
     }
 
     private final class SlashArtist {
+        private static final float CORNER_RADIUS = 1f;
         // These values are derived in un-rotated (vertical) orientation
         private static final float SLASH_WIDTH = 1.8384776f;
         private static final float SLASH_HEIGHT = 22f;
@@ -478,7 +478,7 @@
 
         void draw(int height, int width, @NonNull Canvas canvas, Paint paint) {
             Matrix m = new Matrix();
-            final float radius = scale(SlashDrawable.CORNER_RADIUS, width);
+            final float radius = scale(CORNER_RADIUS, width);
             updateRect(
                     scale(LEFT, width),
                     scale(TOP, height),
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4d49899..5c8d745 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -44,7 +44,6 @@
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.USE_RESERVED_DISK" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <!-- System tool permissions granted to the shell. -->
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/packages/SystemUI/res/color/accent_tint_color_selector.xml b/packages/SystemUI/res/color/accent_tint_color_selector.xml
new file mode 100644
index 0000000..85af186
--- /dev/null
+++ b/packages/SystemUI/res/color/accent_tint_color_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="?android:attr/colorButtonNormal"/>
+
+    <item android:color="?android:attr/colorAccent"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index bab4eba7..803659f 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -38,59 +38,64 @@
             android:layout_height="wrap_content"
             android:clipChildren="false"
             android:clipToPadding="false"
-            android:paddingTop="10dp"
-            android:paddingBottom="10dp"
             android:background="@drawable/rounded_bg_full"
             android:translationZ="@dimen/volume_panel_elevation"
             android:orientation="horizontal" >
                 <!-- volume rows added and removed here! :-) -->
         </LinearLayout>
 
-        <LinearLayout
+        <FrameLayout
             android:id="@+id/footer"
             android:layout_width="@dimen/volume_dialog_panel_width"
             android:layout_height="@dimen/volume_dialog_panel_width"
-            android:clipChildren="false"
-            android:clipToPadding="false"
             android:layout_marginTop="6dp"
             android:layout_marginBottom="6dp"
             android:layout_below="@id/volume_dialog_rows"
-            android:background="@drawable/rounded_bg_full"
-            android:gravity="center"
-            android:layout_gravity="end"
-            android:translationZ="@dimen/volume_panel_elevation"
-            android:clickable="true"
-            android:orientation="vertical" >
+            android:background="@drawable/rounded_bg_full">
 
-            <TextView
-                android:id="@+id/ringer_title"
-                android:text="@string/ring_toggle_title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLines="1"
-                android:layout_centerVertical="true"
-                android:textColor="?android:attr/colorControlNormal"
-                android:textAppearance="@style/TextAppearance.Volume.Header" />
+            <LinearLayout
+                android:id="@+id/footer_linear_layout"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:clipChildren="false"
+                android:clipToPadding="false"
+                android:gravity="center"
+                android:layout_gravity="end"
+                android:translationZ="@dimen/volume_panel_elevation"
+                android:clickable="true"
+                android:orientation="vertical" >
 
-            <com.android.keyguard.AlphaOptimizedImageButton
-                android:id="@+id/ringer_icon"
-                style="@style/VolumeButtons"
-                android:background="?android:selectableItemBackgroundBorderless"
-                android:layout_width="@dimen/volume_dialog_panel_width"
-                android:layout_height="@dimen/volume_button_size"
-                android:tint="?android:attr/colorAccent"
-                android:soundEffectsEnabled="false" />
+                <TextView
+                    android:id="@+id/ringer_title"
+                    android:text="@string/ring_toggle_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:layout_centerVertical="true"
+                    android:textColor="?android:attr/colorControlNormal"
+                    android:textAppearance="@style/TextAppearance.Volume.Header" />
 
-            <TextView
-                android:id="@+id/ringer_status"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLines="1"
-                android:textColor="?android:attr/colorControlNormal"
-                android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="?android:selectableItemBackgroundBorderless"
+                    android:layout_width="@dimen/volume_dialog_panel_width"
+                    android:layout_height="@dimen/volume_button_size"
+                    android:tint="@color/accent_tint_color_selector"
+                    android:soundEffectsEnabled="false" />
 
-        </LinearLayout>
+                <TextView
+                    android:id="@+id/ringer_status"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:textColor="?android:attr/colorControlNormal"
+                    android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
+            </LinearLayout>
+
+            <include layout="@layout/volume_dnd_icon"/>
+        </FrameLayout>
     </LinearLayout>
 </com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 70654a8..fb9355a 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -13,89 +13,98 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:tag="row"
     android:layout_height="wrap_content"
     android:layout_width="@dimen/volume_dialog_panel_width"
     android:clipChildren="true"
     android:clipToPadding="true"
-    android:theme="@style/qs_theme"
-    android:gravity="center"
-    android:orientation="vertical" >
+    android:theme="@style/qs_theme">
 
     <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
         android:gravity="center"
-        android:padding="5dp">
-        <TextView
-            android:id="@+id/volume_row_header"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLength="10"
-            android:maxLines="1"
-            android:textColor="?android:attr/colorControlNormal"
-            android:textAppearance="@style/TextAppearance.Volume.Header" />
+        android:orientation="vertical" >
+
         <LinearLayout
-            android:id="@+id/output_chooser"
             android:orientation="vertical"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:minWidth="48dp"
-            android:minHeight="48dp"
-            android:paddingTop="10dp"
-            android:background="?android:selectableItemBackgroundBorderless"
-            android:gravity="center">
+            android:gravity="center"
+            android:padding="5dp">
             <TextView
-                android:id="@+id/volume_row_connected_device"
-                android:visibility="gone"
+                android:id="@+id/volume_row_header"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:maxLength="10"
                 android:ellipsize="end"
+                android:maxLength="10"
                 android:maxLines="1"
-                android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
-            <com.android.keyguard.AlphaOptimizedImageButton
-                android:id="@+id/output_chooser_button"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
+                android:textColor="?android:attr/colorControlNormal"
+                android:textAppearance="@style/TextAppearance.Volume.Header" />
+            <LinearLayout
+                android:id="@+id/output_chooser"
+                android:orientation="vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="48dp"
+                android:minHeight="48dp"
+                android:paddingTop="10dp"
                 android:background="?android:selectableItemBackgroundBorderless"
-                android:contentDescription="@string/accessibility_output_chooser"
-                style="@style/VolumeButtons"
-                android:clickable="false"
-                android:layout_centerVertical="true"
-                android:src="@drawable/ic_swap"
-                android:soundEffectsEnabled="false" />
+                android:gravity="center">
+                <TextView
+                    android:id="@+id/volume_row_connected_device"
+                    android:visibility="gone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:maxLength="10"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/output_chooser_button"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:background="?android:selectableItemBackgroundBorderless"
+                    android:contentDescription="@string/accessibility_output_chooser"
+                    style="@style/VolumeButtons"
+                    android:clickable="false"
+                    android:layout_centerVertical="true"
+                    android:src="@drawable/ic_swap"
+                    android:soundEffectsEnabled="false"/>
+            </LinearLayout>
         </LinearLayout>
-    </LinearLayout>
-    <FrameLayout
-        android:id="@+id/volume_row_slider_frame"
-        android:padding="0dp"
-        android:layout_width="@dimen/volume_dialog_panel_width"
-        android:layoutDirection="rtl"
-        android:layout_height="@dimen/volume_dialog_panel_width">
-        <SeekBar
-            android:id="@+id/volume_row_slider"
-            android:clickable="true"
+        <FrameLayout
+            android:id="@+id/volume_row_slider_frame"
             android:padding="0dp"
-            android:layout_margin="0dp"
             android:layout_width="@dimen/volume_dialog_panel_width"
-            android:layout_height="@dimen/volume_dialog_panel_width"
             android:layoutDirection="rtl"
-            android:layout_gravity="center"
-            android:rotation="90" />
-    </FrameLayout>
+            android:layout_height="@dimen/volume_dialog_panel_width">
+            <SeekBar
+                android:id="@+id/volume_row_slider"
+                android:clickable="true"
+                android:padding="0dp"
+                android:layout_margin="0dp"
+                android:layout_width="@dimen/volume_dialog_panel_width"
+                android:layout_height="@dimen/volume_dialog_panel_width"
+                android:layoutDirection="rtl"
+                android:layout_gravity="center"
+                android:rotation="90" />
+        </FrameLayout>
 
-    <com.android.keyguard.AlphaOptimizedImageButton
-        android:id="@+id/volume_row_icon"
-        style="@style/VolumeButtons"
-        android:padding="10dp"
-        android:layout_width="@dimen/volume_button_size"
-        android:layout_height="@dimen/volume_button_size"
-        android:background="?android:selectableItemBackgroundBorderless"
-        android:soundEffectsEnabled="false" />
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/volume_row_icon"
+            style="@style/VolumeButtons"
+            android:padding="10dp"
+            android:layout_width="@dimen/volume_button_size"
+            android:layout_height="@dimen/volume_button_size"
+            android:background="?android:selectableItemBackgroundBorderless"
+            android:soundEffectsEnabled="false" />
+    </LinearLayout>
 
-</LinearLayout>
\ No newline at end of file
+    <include layout="@layout/volume_dnd_icon"/>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
new file mode 100644
index 0000000..215b230
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml
@@ -0,0 +1,30 @@
+<!--
+     Copyright (C) 2018 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="14dp"
+    android:layout_height="14dp"
+    android:layout_marginTop="6dp"
+    android:layout_marginRight="6dp"
+    android:layout_gravity="right|top">
+
+    <ImageView
+        android:id="@+id/dnd_icon"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:src="@drawable/ic_dnd"
+        android:tint="?android:attr/textColorTertiary"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c351b94..3f6c85f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -855,8 +855,6 @@
     <dimen name="default_gear_space">18dp</dimen>
     <dimen name="cell_overlay_padding">18dp</dimen>
 
-    <dimen name="signal_icon_size">17dp</dimen>
-
     <dimen name="hwui_edge_margin">16dp</dimen>
 
     <dimen name="global_actions_panel_width">120dp</dimen>
@@ -885,11 +883,6 @@
     <dimen name="nav_quick_scrub_track_edge_padding">42dp</dimen>
     <dimen name="nav_quick_scrub_track_thickness">2dp</dimen>
 
-    <!-- Intended corner radius when drawing the mobile signal -->
-    <dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen>
-    <!-- How far to inset the rounded edges -->
-    <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
-
     <!-- Home button padding for sizing -->
     <dimen name="home_padding">15dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
index 5f260938..e7eefe8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
@@ -19,12 +19,12 @@
 import android.service.quicksettings.Tile;
 import android.widget.ImageView;
 
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.Icon;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 
 import java.util.Objects;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index a7fb61a..cb6e5a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -38,9 +38,9 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index 677fa81..0304086 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -16,10 +16,10 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.ImageView;
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ScalingDrawableWrapper;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 8516278..f0854ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -35,8 +35,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.EriInfo;
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index fb5c447..8881ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -68,6 +68,7 @@
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
@@ -109,7 +110,9 @@
     private ViewGroup mDialogRowsView;
     private ViewGroup mFooter;
     private ImageButton mRingerIcon;
+    private ImageView mZenIcon;
     private TextView mRingerStatus;
+    private TextView mRingerTitle;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -216,6 +219,8 @@
         mFooter = mDialog.findViewById(R.id.footer);
         mRingerIcon = mFooter.findViewById(R.id.ringer_icon);
         mRingerStatus = mFooter.findViewById(R.id.ringer_status);
+        mRingerTitle = mFooter.findViewById(R.id.ringer_title);
+        mZenIcon = mFooter.findViewById(R.id.dnd_icon);
 
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
@@ -349,6 +354,7 @@
         if (stream == STREAM_ACCESSIBILITY) {
             row.header.setFilters(new InputFilter[] {new InputFilter.LengthFilter(13)});
         }
+        row.dndIcon = row.view.findViewById(R.id.dnd_icon);
         row.slider =  row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.anim = null;
@@ -559,6 +565,8 @@
             if (ss == null) {
                 return;
             }
+
+            enableRingerViewsH(mState.zenMode == Global.ZEN_MODE_OFF || !mState.disallowRinger);
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerStatus.setText(R.string.volume_ringer_status_vibrate);
@@ -603,6 +611,28 @@
         }
     }
 
+    /**
+     * Toggles enable state of views in a VolumeRow (not including seekbar, outputChooser or icon)
+     * Hides/shows zen icon
+     * @param enable whether to enable volume row views and hide dnd icon
+     */
+    private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
+        row.header.setEnabled(enable);
+        row.dndIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
+    }
+
+    /**
+     * Toggles enable state of footer/ringer views
+     * Hides/shows zen icon
+     * @param enable whether to enable ringer views and hide dnd icon
+     */
+    private void enableRingerViewsH(boolean enable) {
+        mRingerTitle.setEnabled(enable);
+        mRingerStatus.setEnabled(enable);
+        mRingerIcon.setEnabled(enable);
+        mZenIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
+    }
+
     private void trimObsoleteH() {
         if (D.BUG) Log.d(TAG, "trimObsoleteH");
         for (int i = mRows.size() - 1; i >= 0; i--) {
@@ -748,6 +778,7 @@
         if (zenMuted) {
             row.tracking = false;
         }
+        enableVolumeRowViewsH(row, !zenMuted);
 
         // update slider
         final boolean enableSlider = !zenMuted;
@@ -1178,5 +1209,6 @@
         private int lastAudibleLevel = 1;
         private View outputChooser;
         private TextView connectedDevice;
+        private ImageView dndIcon;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index f685b1f..8aab837 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -43,9 +43,9 @@
 import android.util.Log;
 
 import com.android.internal.telephony.cdma.EriInfo;
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 3ad107c..550f4a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -30,9 +30,9 @@
 
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SignalDrawable;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 230f69d..14404f5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1043,14 +1043,20 @@
                         throw new SecurityException("Instant app " + r.appInfo.packageName
                                 + " does not have permission to create foreground services");
                     default:
-                        mAm.enforcePermission(
-                                android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
-                                r.app.pid, r.appInfo.uid, "startForeground");
+                        try {
+                            if (AppGlobals.getPackageManager().checkPermission(
+                                    android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
+                                    r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
+                                            != PackageManager.PERMISSION_GRANTED) {
+                                throw new SecurityException("Instant app " + r.appInfo.packageName
+                                        + " does not have permission to create foreground"
+                                        + "services");
+                            }
+                        } catch (RemoteException e) {
+                            throw new SecurityException("Failed to check instant app permission." ,
+                                    e);
+                        }
                 }
-            } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
-                mAm.enforcePermission(
-                        android.Manifest.permission.FOREGROUND_SERVICE,
-                        r.app.pid, r.appInfo.uid, "startForeground");
             }
             if (r.fgRequired) {
                 if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cd8b6d7..99904b5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8889,20 +8889,6 @@
     /**
      * This can be called with or without the global lock held.
      */
-    void enforcePermission(String permission, int pid, int uid, String func) {
-        if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-
-        String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid
-                + " requires " + permission;
-        Slog.w(TAG, msg);
-        throw new SecurityException(msg);
-    }
-
-    /**
-     * This can be called with or without the global lock held.
-     */
     void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
         if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
             enforceCallingPermission(permission, func);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 55c0f5a..3e43d8e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -206,7 +206,7 @@
     private static final int UPDATE_NETWORK_STATE = 4;
     private static final int INJECT_NTP_TIME = 5;
     private static final int DOWNLOAD_XTRA_DATA = 6;
-    private static final int UPDATE_LOCATION = 7;
+    private static final int UPDATE_LOCATION = 7;  // Handle external location from network listener
     private static final int ADD_LISTENER = 8;
     private static final int REMOVE_LISTENER = 9;
     private static final int INJECT_NTP_TIME_FINISHED = 10;
@@ -259,6 +259,42 @@
         }
     }
 
+    // Simple class to hold stats reported in the Extras Bundle
+    private static class LocationExtras {
+        private int mSvCount;
+        private int mMeanCn0;
+        private int mMaxCn0;
+        private final Bundle mBundle;
+
+        public LocationExtras() {
+            mBundle = new Bundle();
+        }
+
+        public void set(int svCount, int meanCn0, int maxCn0) {
+            mSvCount = svCount;
+            mMeanCn0 = meanCn0;
+            mMaxCn0 = maxCn0;
+            setBundle(mBundle);
+        }
+
+        public void reset() {
+            set(0,0,0);
+        }
+
+        // Also used by outside methods to add to other bundles
+        public void setBundle(Bundle extras) {
+            if (extras != null) {
+                extras.putInt("satellites", mSvCount);
+                extras.putInt("meanCn0", mMeanCn0);
+                extras.putInt("maxCn0", mMaxCn0);
+            }
+        }
+
+        public Bundle getBundle() {
+            return mBundle;
+        }
+    }
+
     private Object mLock = new Object();
 
     // current status
@@ -369,7 +405,7 @@
     private final NtpTrustedTime mNtpTime;
     private final ILocationManager mILocationManager;
     private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
-    private Bundle mLocationExtras = new Bundle();
+    private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mListenerHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
@@ -713,7 +749,7 @@
         mNtpTime = NtpTrustedTime.getInstance(context);
         mILocationManager = ilocationManager;
 
-        mLocation.setExtras(mLocationExtras);
+        mLocation.setExtras(mLocationExtras.getBundle());
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -1245,29 +1281,17 @@
 
     @Override
     public int getStatus(Bundle extras) {
-        setLocationExtras(extras);
+        mLocationExtras.setBundle(extras);
         return mStatus;
     }
 
-    private void updateStatus(int status, int svCount, int meanCn0, int maxCn0) {
-        if (status != mStatus || svCount != mSvCount || meanCn0 != mMeanCn0 || maxCn0 != mMaxCn0) {
+    private void updateStatus(int status) {
+        if (status != mStatus) {
             mStatus = status;
-            mSvCount = svCount;
-            mMeanCn0 = meanCn0;
-            mMaxCn0 = maxCn0;
-            setLocationExtras(mLocationExtras);
             mStatusUpdateTime = SystemClock.elapsedRealtime();
         }
     }
 
-    private void setLocationExtras(Bundle extras) {
-        if (extras != null) {
-            extras.putInt("satellites", mSvCount);
-            extras.putInt("meanCn0", mMeanCn0);
-            extras.putInt("maxCn0", mMaxCn0);
-        }
-    }
-
     @Override
     public long getStatusUpdateTime() {
         return mStatusUpdateTime;
@@ -1563,7 +1587,8 @@
             }
 
             // reset SV count to zero
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0);
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
+            mLocationExtras.reset();
             mFixRequestTime = SystemClock.elapsedRealtime();
             if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
@@ -1586,7 +1611,8 @@
             mLastFixTime = 0;
 
             // reset SV count to zero
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0, 0, 0);
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
+            mLocationExtras.reset();
         }
     }
 
@@ -1626,7 +1652,7 @@
             // It would be nice to push the elapsed real-time timestamp
             // further down the stack, but this is still useful
             mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
-            mLocation.setExtras(mLocationExtras);
+            mLocation.setExtras(mLocationExtras.getBundle());
 
             try {
                 mILocationManager.reportLocation(mLocation, false);
@@ -1662,8 +1688,9 @@
         }
 
         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
-            // we want to time out if we do not receive a fix
-            // within the time out and we are requesting infrequent fixes
+            // For devices that use framework scheduling, a timer may be set to ensure we don't
+            // spend too much power searching for a location, when the requested update rate is slow.
+            // As we just recievied a location, we'll cancel that timer.
             if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
                 mAlarmManager.cancel(mTimeoutIntent);
             }
@@ -1672,7 +1699,7 @@
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-            updateStatus(LocationProvider.AVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
+            updateStatus(LocationProvider.AVAILABLE);
         }
 
         if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
@@ -1771,7 +1798,7 @@
             meanCn0 /= usedInFixCount;
         }
         // return number of sats used in fix instead of total reported
-        updateStatus(mStatus, usedInFixCount, meanCn0, maxCn0);
+        mLocationExtras.set(usedInFixCount, meanCn0, maxCn0);
 
         if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
                 SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) {
@@ -1779,7 +1806,7 @@
             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
+            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
         }
     }
 
@@ -2726,9 +2753,6 @@
     private float mSvElevations[] = new float[MAX_SVS];
     private float mSvAzimuths[] = new float[MAX_SVS];
     private float mSvCarrierFreqs[] = new float[MAX_SVS];
-    private int mSvCount;
-    private int mMeanCn0;
-    private int mMaxCn0;
     // preallocated to avoid memory allocation in reportNmea()
     private byte[] mNmeaBuffer = new byte[120];
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61effb8..3cd24f9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19118,8 +19118,21 @@
     public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
             final IPackageDataObserver observer) {
         final int callingUid = Binder.getCallingUid();
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.DELETE_CACHE_FILES, null);
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES)
+                != PackageManager.PERMISSION_GRANTED) {
+            // If the caller has the old delete cache permission, silently ignore.  Else throw.
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.DELETE_CACHE_FILES)
+                    == PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Calling uid " + callingUid + " does not have " +
+                        android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES +
+                        ", silently ignoring");
+                return;
+            }
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
+        }
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 /* requireFullPermission= */ true, /* checkShell= */ false,
                 "delete application cache files");
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index 0e8d8bc..8f4cada 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -59,7 +59,7 @@
     private static final String TAG = "timezone.PackageTracker";
 
     private final PackageManagerHelper mPackageManagerHelper;
-    private final IntentHelper mIntentHelper;
+    private final PackageTrackerIntentHelper mIntentHelper;
     private final ConfigHelper mConfigHelper;
     private final PackageStatusStorage mPackageStatusStorage;
     private final Clock mElapsedRealtimeClock;
@@ -103,13 +103,13 @@
                 helperImpl /* configHelper */,
                 helperImpl /* packageManagerHelper */,
                 new PackageStatusStorage(storageDir),
-                new IntentHelperImpl(context));
+                new PackageTrackerIntentHelperImpl(context));
     }
 
     // A constructor that can be used by tests to supply mocked / faked dependencies.
     PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper,
             PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage,
-            IntentHelper intentHelper) {
+            PackageTrackerIntentHelper intentHelper) {
         mElapsedRealtimeClock = elapsedRealtimeClock;
         mConfigHelper = configHelper;
         mPackageManagerHelper = packageManagerHelper;
diff --git a/services/core/java/com/android/server/timezone/IntentHelper.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java
similarity index 97%
rename from services/core/java/com/android/server/timezone/IntentHelper.java
rename to services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java
index 5de5432..3753ece 100644
--- a/services/core/java/com/android/server/timezone/IntentHelper.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java
@@ -21,7 +21,7 @@
  * it is not possible to test various cases with the real one because of the need to simulate
  * receiving and broadcasting intents.
  */
-interface IntentHelper {
+interface PackageTrackerIntentHelper {
 
     void initialize(String updateAppPackageName, String dataAppPackageName,
             PackageTracker packageTracker);
diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java
similarity index 93%
rename from services/core/java/com/android/server/timezone/IntentHelperImpl.java
rename to services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java
index 6e6259d..4110d88 100644
--- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java
@@ -28,16 +28,16 @@
 import android.util.Slog;
 
 /**
- * The bona fide implementation of {@link IntentHelper}.
+ * The bona fide implementation of {@link PackageTrackerIntentHelper}.
  */
-final class IntentHelperImpl implements IntentHelper {
+final class PackageTrackerIntentHelperImpl implements PackageTrackerIntentHelper {
 
-    private final static String TAG = "timezone.IntentHelperImpl";
+    private final static String TAG = "timezone.PackageTrackerIntentHelperImpl";
 
     private final Context mContext;
     private String mUpdaterAppPackageName;
 
-    IntentHelperImpl(Context context) {
+    PackageTrackerIntentHelperImpl(Context context) {
         mContext = context;
     }
 
diff --git a/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java
new file mode 100644
index 0000000..bb317cf
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.android.server.timezone;
+
+/**
+ * An easy-to-mock interface around intent sending / receiving for use by
+ * {@link RulesManagerService}; it is not possible to test various cases with the real one because
+ * of the need to simulate broadcasting intents.
+ */
+interface RulesManagerIntentHelper {
+
+    /**
+     * Send a broadcast informing listeners that a time zone operation is staged.
+     */
+    void sendTimeZoneOperationStaged();
+
+    /**
+     * Send a broadcast informing listeners that a time zone operation is no longer staged.
+     */
+    void sendTimeZoneOperationUnstaged();
+}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index be9b204..872d723 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -99,6 +99,7 @@
     private final PermissionHelper mPermissionHelper;
     private final PackageTracker mPackageTracker;
     private final Executor mExecutor;
+    private final RulesManagerIntentHelper mIntentHelper;
     private final TimeZoneDistroInstaller mInstaller;
 
     private static RulesManagerService create(Context context) {
@@ -106,16 +107,19 @@
         return new RulesManagerService(
                 helper /* permissionHelper */,
                 helper /* executor */,
+                helper /* intentHelper */,
                 PackageTracker.create(context),
                 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
     }
 
     // A constructor that can be used by tests to supply mocked / faked dependencies.
-    RulesManagerService(PermissionHelper permissionHelper,
-            Executor executor, PackageTracker packageTracker,
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    RulesManagerService(PermissionHelper permissionHelper, Executor executor,
+            RulesManagerIntentHelper intentHelper, PackageTracker packageTracker,
             TimeZoneDistroInstaller timeZoneDistroInstaller) {
         mPermissionHelper = permissionHelper;
         mExecutor = executor;
+        mIntentHelper = intentHelper;
         mPackageTracker = packageTracker;
         mInstaller = timeZoneDistroInstaller;
     }
@@ -271,6 +275,10 @@
 
                 TimeZoneDistro distro = new TimeZoneDistro(is);
                 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
+
+                // Notify interested parties that something is staged.
+                sendInstallNotificationIntentIfRequired(installerResult);
+
                 int resultCode = mapInstallerResultToApiCode(installerResult);
                 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
                 sendFinishedStatus(mCallback, resultCode);
@@ -291,6 +299,12 @@
             }
         }
 
+        private void sendInstallNotificationIntentIfRequired(int installerResult) {
+            if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) {
+                mIntentHelper.sendTimeZoneOperationStaged();
+            }
+        }
+
         private int mapInstallerResultToApiCode(int installerResult) {
             switch (installerResult) {
                 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
@@ -351,6 +365,10 @@
             boolean packageTrackerStatus = false;
             try {
                 int uninstallResult = mInstaller.stageUninstall();
+
+                // Notify interested parties that something is staged.
+                sendUninstallNotificationIntentIfRequired(uninstallResult);
+
                 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
                         || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
 
@@ -374,6 +392,20 @@
                 mOperationInProgress.set(false);
             }
         }
+
+        private void sendUninstallNotificationIntentIfRequired(int uninstallResult) {
+            switch (uninstallResult) {
+                case TimeZoneDistroInstaller.UNINSTALL_SUCCESS:
+                    mIntentHelper.sendTimeZoneOperationStaged();
+                    break;
+                case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED:
+                    mIntentHelper.sendTimeZoneOperationUnstaged();
+                    break;
+                case TimeZoneDistroInstaller.UNINSTALL_FAIL:
+                default:
+                    // No-op - unknown or nothing to notify about.
+            }
+        }
     }
 
     private void sendFinishedStatus(ICallback callback, int resultCode) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
index e8a401e..8f5c7a7 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
@@ -18,22 +18,20 @@
 
 import com.android.internal.util.DumpUtils;
 
+import android.app.timezone.RulesManager;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.content.Intent;
 import android.os.AsyncTask;
-import android.os.Binder;
-import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
-import libcore.io.Streams;
 
 /**
  * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}.
  */
-final class RulesManagerServiceHelperImpl implements PermissionHelper, Executor {
+final class RulesManagerServiceHelperImpl
+        implements PermissionHelper, Executor, RulesManagerIntentHelper {
 
     private final Context mContext;
 
@@ -55,4 +53,22 @@
     public void execute(Runnable runnable) {
         AsyncTask.execute(runnable);
     }
+
+    @Override
+    public void sendTimeZoneOperationStaged() {
+        sendOperationIntent(true /* staged */);
+    }
+
+    @Override
+    public void sendTimeZoneOperationUnstaged() {
+        sendOperationIntent(false /* staged */);
+    }
+
+    private void sendOperationIntent(boolean staged) {
+        Intent intent = new Intent(RulesManager.ACTION_RULES_UPDATE_OPERATION);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        intent.putExtra(RulesManager.EXTRA_OPERATION_STAGED, staged);
+        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 1b2f954..98fcb0b 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -169,7 +169,12 @@
         anim.addUpdateListener(animation -> {
             synchronized (mCancelLock) {
                 if (!a.mCancelled) {
-                    applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
+                    final long duration = anim.getDuration();
+                    long currentPlayTime = anim.getCurrentPlayTime();
+                    if (currentPlayTime > duration) {
+                        currentPlayTime = duration;
+                    }
+                    applyTransformation(a, mFrameTransaction, currentPlayTime);
                 }
             }
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 97d6c43..5d8aca1 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -60,7 +60,6 @@
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 9cf6392..d9f4adf 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -31,7 +31,6 @@
 import android.support.test.filters.SmallTest;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.time.Clock;
@@ -1400,7 +1399,7 @@
     /**
      * A fake IntentHelper implementation for use in tests.
      */
-    private static class FakeIntentHelper implements IntentHelper {
+    private static class FakeIntentHelper implements PackageTrackerIntentHelper {
 
         private PackageTracker mPackageTracker;
         private String mUpdateAppPackageName;
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 1cfae1e..f5969f3 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -68,6 +68,7 @@
 
     private FakeExecutor mFakeExecutor;
     private PermissionHelper mMockPermissionHelper;
+    private RulesManagerIntentHelper mMockIntentHelper;
     private PackageTracker mMockPackageTracker;
     private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller;
 
@@ -77,11 +78,13 @@
 
         mMockPackageTracker = mock(PackageTracker.class);
         mMockPermissionHelper = mock(PermissionHelper.class);
+        mMockIntentHelper = mock(RulesManagerIntentHelper.class);
         mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class);
 
         mRulesManagerService = new RulesManagerService(
                 mMockPermissionHelper,
                 mFakeExecutor,
+                mMockIntentHelper,
                 mMockPackageTracker,
                 mMockTimeZoneDistroInstaller);
     }
@@ -329,6 +332,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -353,6 +357,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -372,6 +377,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -394,6 +400,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -416,6 +423,7 @@
         callback.assertNoResultReceived();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS);
@@ -428,6 +436,7 @@
         // Verify the expected calls were made to other components.
         verifyStageInstallCalled();
         verifyPackageTrackerCalled(token, true /* success */);
+        verifyStagedOperationIntentSent();
 
         // Check the callback was called.
         callback.assertResultReceived(Callback.SUCCESS);
@@ -450,6 +459,7 @@
         // Assert nothing has happened yet.
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS);
@@ -462,6 +472,7 @@
         // Verify the expected calls were made to other components.
         verifyStageInstallCalled();
         verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
+        verifyStagedOperationIntentSent();
 
         // Check the callback was received.
         callback.assertResultReceived(Callback.SUCCESS);
@@ -486,6 +497,7 @@
         // Assert nothing has happened yet.
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR);
@@ -502,6 +514,9 @@
         boolean expectedSuccess = true;
         verifyPackageTrackerCalled(token, expectedSuccess);
 
+        // Nothing should be staged, so no intents sent.
+        verifyNoIntentsSent();
+
         // Check the callback was received.
         callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR);
     }
@@ -529,6 +544,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -548,6 +564,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -566,6 +583,7 @@
         mFakeExecutor.assertNothingQueued();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -585,6 +603,7 @@
         callback.assertNoResultReceived();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
@@ -595,6 +614,7 @@
         // Verify the expected calls were made to other components.
         verifyStageUninstallCalled();
         verifyPackageTrackerCalled(token, true /* success */);
+        verifyStagedOperationIntentSent();
 
         // Check the callback was called.
         callback.assertResultReceived(Callback.SUCCESS);
@@ -617,6 +637,7 @@
         callback.assertNoResultReceived();
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
@@ -627,6 +648,7 @@
         // Verify the expected calls were made to other components.
         verifyStageUninstallCalled();
         verifyPackageTrackerCalled(token, true /* success */);
+        verifyUnstagedOperationIntentSent();
 
         // Check the callback was called.
         callback.assertResultReceived(Callback.SUCCESS);
@@ -645,6 +667,7 @@
         // Assert nothing has happened yet.
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
@@ -655,6 +678,7 @@
         // Verify the expected calls were made to other components.
         verifyStageUninstallCalled();
         verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
+        verifyStagedOperationIntentSent();
 
         // Check the callback was received.
         callback.assertResultReceived(Callback.SUCCESS);
@@ -676,6 +700,7 @@
         // Assert nothing has happened yet.
         verifyNoInstallerCallsMade();
         callback.assertNoResultReceived();
+        verifyNoIntentsSent();
 
         // Set up the installer.
         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL);
@@ -686,6 +711,7 @@
         // Verify the expected calls were made to other components.
         verifyStageUninstallCalled();
         verifyPackageTrackerCalled(token, false /* success */);
+        verifyNoIntentsSent();
 
         // Check the callback was received.
         callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE);
@@ -714,6 +740,7 @@
         // Verify the expected calls were made to other components.
         verifyPackageTrackerCalled(token, true /* success */);
         verifyNoInstallerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -734,6 +761,7 @@
         // Assert no other calls were made.
         verifyNoInstallerCallsMade();
         verifyNoPackageTrackerCallsMade();
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -749,6 +777,7 @@
         // Assert everything required was done.
         verifyNoInstallerCallsMade();
         verifyPackageTrackerCalled(token, false /* success */);
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -761,6 +790,7 @@
         // Assert everything required was done.
         verifyNoInstallerCallsMade();
         verifyPackageTrackerCalled(null /* token */, true /* success */);
+        verifyNoIntentsSent();
     }
 
     @Test
@@ -865,6 +895,21 @@
         reset(mMockPackageTracker);
     }
 
+    private void verifyNoIntentsSent() {
+        verifyNoMoreInteractions(mMockIntentHelper);
+        reset(mMockIntentHelper);
+    }
+
+    private void verifyStagedOperationIntentSent() {
+        verify(mMockIntentHelper).sendTimeZoneOperationStaged();
+        reset(mMockIntentHelper);
+    }
+
+    private void verifyUnstagedOperationIntentSent() {
+        verify(mMockIntentHelper).sendTimeZoneOperationUnstaged();
+        reset(mMockIntentHelper);
+    }
+
     private void configureCallerHasPermission() throws Exception {
         doNothing()
                 .when(mMockPermissionHelper)
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
index ec5a9c6..23a151c 100644
--- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -32,5 +32,8 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>
+        <service
+                android:name=".TestService"
+                android:exported="true" />
     </application>
 </manifest>
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
index 1f06121..4e7bb4c 100644
--- a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestActivity.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.os.Looper;
-import android.os.MessageQueue;
 
 import com.android.frameworks.perftests.am.util.Constants;
 import com.android.frameworks.perftests.am.util.Utils;
diff --git a/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java
new file mode 100644
index 0000000..b6534fc
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/test-app/src/com/android/frameworks/perftests/amteststestapp/TestService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.frameworks.perftests.amteststestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.frameworks.perftests.am.util.Constants;
+import com.android.frameworks.perftests.am.util.Utils;
+
+public class TestService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new Binder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Utils.sendTime(intent, Constants.TYPE_SERVICE_START);
+        return super.onStartCommand(intent, flags, startId);
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
index 661abe9..cf175e0 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
@@ -16,8 +16,11 @@
 
 package com.android.frameworks.perftests.am.tests;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
 import android.perftests.utils.ManualBenchmarkState;
 import android.perftests.utils.PerfManualStatusReporter;
 import android.support.test.InstrumentationRegistry;
@@ -26,13 +29,17 @@
 import com.android.frameworks.perftests.am.util.TimeReceiver;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.LongSupplier;
 
 public class BasePerfTest {
     private static final String TAG = BasePerfTest.class.getSimpleName();
+    private static final long AWAIT_SERVICE_CONNECT_MS = 2000;
 
     private TimeReceiver mTimeReceiver;
 
@@ -52,14 +59,70 @@
         TargetPackageUtils.killTargetPackage(mContext);
     }
 
-    protected Intent createIntent(String action) {
+    protected void addReceivedTimeNs(String type) {
+        mTimeReceiver.addTimeForTypeToQueue(type, System.nanoTime());
+    }
+
+    protected Intent createServiceIntent() {
+        final Intent intent = new Intent();
+        intent.setClassName(TargetPackageUtils.PACKAGE_NAME,
+                TargetPackageUtils.SERVICE_NAME);
+        putTimeReceiverBinderExtra(intent);
+        return intent;
+    }
+
+    protected ServiceConnection bindAndWaitForConnectedService() {
+        return bindAndWaitForConnectedService(0);
+    }
+
+    protected ServiceConnection bindAndWaitForConnectedService(int flags) {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        final ServiceConnection serviceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                countDownLatch.countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        };
+
+        final Intent intent = createServiceIntent();
+        final boolean success = mContext.bindService(intent, serviceConnection,
+                Context.BIND_AUTO_CREATE | flags);
+        Assert.assertTrue("Could not bind to service", success);
+
+        try {
+            boolean connectedSuccess = countDownLatch.await(AWAIT_SERVICE_CONNECT_MS,
+                    TimeUnit.MILLISECONDS);
+            Assert.assertTrue("Timeout when waiting for ServiceConnection.onServiceConnected()",
+                    connectedSuccess);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+
+        return serviceConnection;
+    }
+
+    protected void unbindFromService(ServiceConnection serviceConnection) {
+        if (serviceConnection != null) {
+            mContext.unbindService(serviceConnection);
+        }
+    }
+
+    protected Intent createBroadcastIntent(String action) {
         final Intent intent = new Intent(action);
         intent.addFlags(
                 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
-        intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder());
+        putTimeReceiverBinderExtra(intent);
         return intent;
     }
 
+    protected void putTimeReceiverBinderExtra(Intent intent) {
+        intent.putExtras(mTimeReceiver.createReceiveTimeExtraBinder());
+    }
+
     private void setUpIteration() {
         mTimeReceiver.clear();
         TargetPackageUtils.killTargetPackage(mContext);
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
index 795f498..f7dab03 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
@@ -33,7 +33,8 @@
         runPerfFunction(() -> {
             startTargetPackage();
 
-            final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+            final Intent intent = createBroadcastIntent(
+                    Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
 
             final long startTime = System.nanoTime();
 
@@ -48,7 +49,8 @@
     @Test
     public void manifestBroadcastNotRunning() {
         runPerfFunction(() -> {
-            final Intent intent = createIntent(Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
+            final Intent intent = createBroadcastIntent(
+                    Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
 
             final long startTime = System.nanoTime();
 
@@ -65,7 +67,8 @@
         runPerfFunction(() -> {
             startTargetPackage();
 
-            final Intent intent = createIntent(Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
+            final Intent intent = createBroadcastIntent(
+                    Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
 
             final long startTime = System.nanoTime();
 
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java
new file mode 100644
index 0000000..6d2935a
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceBindPerfTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ServiceBindPerfTest extends BasePerfTest {
+    /**
+     * Create and return a ServiceConnection that will add the current time with type
+     * Constants.TYPE_SERVICE_CONNECTED.
+     */
+    private ServiceConnection createServiceConnectionReportTime() {
+        return new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                addReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+            }
+        };
+    }
+
+    /**
+     * Try to bind to the service with the input parameters, throwing a RuntimeException with the
+     * errorMessage on failure.
+     */
+    private void bindService(Intent intent, ServiceConnection serviceConnection, int flags) {
+        final boolean success = mContext.bindService(intent, serviceConnection, flags);
+        Assert.assertTrue("Could not bind to service", success);
+    }
+
+    /**
+     * Benchmark time from Context.bindService() to Service.onBind() when target package is not
+     * running.
+     */
+    @Test
+    public void bindServiceNotRunning() {
+        runPerfFunction(() -> {
+            final Intent intent = createServiceIntent();
+            final ServiceConnection serviceConnection = createServiceConnectionReportTime();
+
+            final long startTimeNs = System.nanoTime();
+            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+            try {
+                final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED);
+                return endTimeNs - startTimeNs;
+            } finally {
+                unbindFromService(serviceConnection);
+            }
+        });
+    }
+
+    /**
+     * Benchmark time from Context.bindService() to Service.onBind() when target package is running.
+     */
+    @Test
+    public void bindServiceRunning() {
+        runPerfFunction(() -> {
+            startTargetPackage();
+
+            final Intent intent = createServiceIntent();
+            final ServiceConnection serviceConnection = createServiceConnectionReportTime();
+
+            final long startTimeNs = System.nanoTime();
+            bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+            try {
+                final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED);
+                return endTimeNs - startTimeNs;
+            } finally {
+                unbindFromService(serviceConnection);
+            }
+        });
+    }
+
+    /**
+     * Benchmark time from Context.bindService() to Service.onBind() when service is already bound
+     * to.
+     */
+    @Test
+    public void bindServiceAlreadyBound() {
+        runPerfFunction(() -> {
+            startTargetPackage();
+
+            final Intent intent = createServiceIntent();
+            final ServiceConnection alreadyBoundServiceConnection = bindAndWaitForConnectedService();
+
+            try {
+                final ServiceConnection serviceConnection = createServiceConnectionReportTime();
+
+                final long startTimeNs = System.nanoTime();
+                try {
+                    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+                    final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED);
+                    return endTimeNs - startTimeNs;
+                } finally {
+                    unbindFromService(serviceConnection);
+                }
+            } finally {
+                unbindFromService(alreadyBoundServiceConnection);
+            }
+        });
+    }
+
+    /**
+     * Benchmark time from Context.bindService() (without BIND_ALLOW_OOM_MANAGEMENT) to
+     * Service.onBind() when service is already bound to with BIND_ALLOW_OOM_MANAGEMENT.
+     */
+    @Test
+    public void bindServiceAllowOomManagement() {
+        runPerfFunction(() -> {
+            final Intent intentNoOom = createServiceIntent();
+            final ServiceConnection serviceConnectionOom = bindAndWaitForConnectedService(
+                    Context.BIND_ALLOW_OOM_MANAGEMENT);
+
+            try {
+                final ServiceConnection serviceConnectionNoOom =
+                        createServiceConnectionReportTime();
+                try {
+                    final long startTimeNs = System.nanoTime();
+                    bindService(intentNoOom, serviceConnectionNoOom, Context.BIND_AUTO_CREATE);
+                    final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_CONNECTED);
+
+                    return endTimeNs - startTimeNs;
+                } finally {
+                    unbindFromService(serviceConnectionNoOom);
+                }
+            } finally {
+                unbindFromService(serviceConnectionOom);
+            }
+        });
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
new file mode 100644
index 0000000..626ee02
--- /dev/null
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/ServiceStartPerfTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package com.android.frameworks.perftests.am.tests;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.perftests.am.util.Constants;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ServiceStartPerfTest extends BasePerfTest {
+
+    /**
+     * Tries to start the service with the given intent, throwing a RuntimeException with the
+     * errorMessage on failure.
+     */
+    private void startService(Intent intent) {
+        final ComponentName componentName = mContext.startService(intent);
+        Assert.assertNotNull("Could not start service", componentName);
+    }
+
+    /**
+     * Benchmark time from Context.startService() to Service.onStartCommand() when target process is
+     * not running.
+     */
+    @Test
+    public void startServiceNotRunning() {
+        runPerfFunction(() -> {
+            final Intent intent = createServiceIntent();
+
+            final long startTimeNs = System.nanoTime();
+
+            startService(intent);
+
+            final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START);
+            return endTimeNs - startTimeNs;
+        });
+    }
+
+    /**
+     * Benchmark time from Context.startService() to Service.onStartCommand() when target process is
+     * running.
+     */
+    @Test
+    public void startServiceProcessRunning() {
+        runPerfFunction(() -> {
+            startTargetPackage();
+
+            final Intent intent = createServiceIntent();
+
+            final long startTimeNs = System.nanoTime();
+            startService(intent);
+            final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START);
+
+            return endTimeNs - startTimeNs;
+        });
+    }
+
+    /**
+     * Benchmark time from Context.startService() to Service.onStartCommand() when service is
+     * already bound to.
+     */
+    @Test
+    public void startServiceAlreadyBound() {
+        runPerfFunction(() -> {
+            final ServiceConnection alreadyBoundServiceConnection =
+                    bindAndWaitForConnectedService();
+            try {
+                final Intent intent = createServiceIntent();
+
+                final long startTimeNs = System.nanoTime();
+                startService(intent);
+                final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START);
+
+                return endTimeNs - startTimeNs;
+            } finally {
+                unbindFromService(alreadyBoundServiceConnection);
+            }
+        });
+    }
+
+    /**
+     * Benchmark time from Context.startService() with FLAG_GRANT_READ_URI_PERMISSION to
+     * Service.onStartCommand() when target process is running.
+     */
+    @Test
+    public void startServiceProcessRunningReadUriPermission() {
+        runPerfFunction(() -> {
+            final ServiceConnection alreadyBoundServiceConnection =
+                    bindAndWaitForConnectedService();
+            try {
+                final Intent intent = createServiceIntent();
+                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+                final long startTimeNs = System.nanoTime();
+                startService(intent);
+                final long endTimeNs = getReceivedTimeNs(Constants.TYPE_SERVICE_START);
+
+                return endTimeNs - startTimeNs;
+            } finally {
+                unbindFromService(alreadyBoundServiceConnection);
+            }
+        });
+    }
+}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
index 26a8e7b..3db8abc 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java
@@ -27,6 +27,7 @@
 
     public static final String PACKAGE_NAME = "com.android.frameworks.perftests.amteststestapp";
     public static final String ACTIVITY_NAME = PACKAGE_NAME + ".TestActivity";
+    public static final String SERVICE_NAME = PACKAGE_NAME + ".TestService";
 
     private static final long WAIT_TIME_MS = 100L;
 
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
index 9cf6ee7..a86a5c7 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TimeReceiver.java
@@ -45,20 +45,23 @@
         }
     }
 
+    public void addTimeForTypeToQueue(String type, long timeNs) {
+        if (type == null) {
+            throw new IllegalArgumentException("type is null when adding time to queue");
+        }
+        if (timeNs < 0) {
+            throw new RuntimeException(
+                    "time is negative/non-existant (" + timeNs + ") when adding time to queue");
+        }
+        mQueue.add(new ReceivedMessage(type, timeNs));
+    }
+
     public Bundle createReceiveTimeExtraBinder() {
         Bundle extras = new Bundle();
         extras.putBinder(Constants.EXTRA_RECEIVER_CALLBACK, new ITimeReceiverCallback.Stub() {
             @Override
             public void sendTime(String type, long timeNs) throws RemoteException {
-                if (type == null) {
-                    throw new RuntimeException("receivedType is null");
-                }
-                if (timeNs < 0) {
-                    throw new RuntimeException(
-                            "receivedTime is negative/non-existant: " + timeNs);
-                }
-                Log.i(TAG, type + " " + timeNs);
-                mQueue.add(new ReceivedMessage(type, timeNs));
+                addTimeForTypeToQueue(type, timeNs);
             }
         });
         return extras;
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
index f35c2fd..ffb3f84 100644
--- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Constants.java
@@ -19,6 +19,9 @@
 public class Constants {
     public static final String TYPE_TARGET_PACKAGE_START = "target_package_start";
     public static final String TYPE_BROADCAST_RECEIVE = "broadcast_receive";
+    public static final String TYPE_SERVICE_BIND = "service_bind";
+    public static final String TYPE_SERVICE_START = "service_start";
+    public static final String TYPE_SERVICE_CONNECTED = "service_connection_connect";
 
     public static final String ACTION_BROADCAST_MANIFEST_RECEIVE =
             "com.android.frameworks.perftests.ACTION_BROADCAST_MANIFEST_RECEIVE";
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index d62ef9e..2591aaf 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworkperf">
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-sdk android:minSdkVersion="5" />
 
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 8697f1b..c6824ec 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -5,7 +5,6 @@
     android:versionName="1.0" >
 
     <uses-sdk android:minSdkVersion="19"/>
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />