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" />