1/ Support nested message and repeated fields in statsd.
2/ Filter gauge fields by FieldMatcher.
3/ Wire up wakelock attribution chain.
4/ e2e test: wakelock duration metric with aggregated predicate dimensions.
5/ e2e test: count metric with multiple metric condition links for 2 predicates and 1 non-sliced predicate.

Test: statsd unit test passed.

Change-Id: I89db31cb068184a54e0a892fad710966d3127bc9
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 0b6f8f2..6da1243 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -13,92 +13,104 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "HashableDimensionKey.h"
+#include "dimension.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
+android::hash_t hashDimensionsValue(const DimensionsValue& value) {
+    android::hash_t hash = 0;
+    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)));
+            }
+            break;
+        }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            break;
+    }
+    return JenkinsHashWhiten(hash);
+}
+
 using std::string;
 
+
 string HashableDimensionKey::toString() const {
     string flattened;
-    for (const auto& pair : mKeyValuePairs) {
-        flattened += std::to_string(pair.key());
-        flattened += ":";
-        switch (pair.value_case()) {
-            case KeyValuePair::ValueCase::kValueStr:
-                flattened += pair.value_str();
-                break;
-            case KeyValuePair::ValueCase::kValueInt:
-                flattened += std::to_string(pair.value_int());
-                break;
-            case KeyValuePair::ValueCase::kValueLong:
-                flattened += std::to_string(pair.value_long());
-                break;
-            case KeyValuePair::ValueCase::kValueBool:
-                flattened += std::to_string(pair.value_bool());
-                break;
-            case KeyValuePair::ValueCase::kValueFloat:
-                flattened += std::to_string(pair.value_float());
-                break;
-            default:
-                break;
-        }
-        flattened += "|";
-    }
+    DimensionsValueToString(getDimensionsValue(), &flattened);
     return flattened;
 }
 
-bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
-    const auto& keyValue2 = that.getKeyValuePairs();
-    if (mKeyValuePairs.size() != keyValue2.size()) {
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+    if (s1.field() != s2.field()) {
         return false;
     }
-
-    for (size_t i = 0; i < keyValue2.size(); i++) {
-        const auto& kv1 = mKeyValuePairs[i];
-        const auto& kv2 = keyValue2[i];
-        if (kv1.key() != kv2.key()) {
-            return false;
-        }
-
-        if (kv1.value_case() != kv2.value_case()) {
-            return false;
-        }
-
-        switch (kv1.value_case()) {
-            case KeyValuePair::ValueCase::kValueStr:
-                if (kv1.value_str() != kv2.value_str()) {
-                    return false;
-                }
-                break;
-            case KeyValuePair::ValueCase::kValueInt:
-                if (kv1.value_int() != kv2.value_int()) {
-                    return false;
-                }
-                break;
-            case KeyValuePair::ValueCase::kValueLong:
-                if (kv1.value_long() != kv2.value_long()) {
-                    return false;
-                }
-                break;
-            case KeyValuePair::ValueCase::kValueBool:
-                if (kv1.value_bool() != kv2.value_bool()) {
-                    return false;
-                }
-                break;
-            case KeyValuePair::ValueCase::kValueFloat: {
-                if (kv1.value_float() != kv2.value_float()) {
-                    return false;
-                }
-                break;
-            }
-            case KeyValuePair::ValueCase::VALUE_NOT_SET:
-                break;
-        }
+    if (s1.value_case() != s1.value_case()) {
+        return false;
     }
-    return true;
+    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 &= compareDimensionsValue(s1.value_tuple().dimensions_value(i),
+                                                    s2.value_tuple().dimensions_value(i));
+                }
+                return allMatched;
+            }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+        default:
+            return true;
+    }
+}
+
+bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
+    return compareDimensionsValue(getDimensionsValue(), that.getDimensionsValue());
 };
 
 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {