Merge "Make StatsdStats print nicer" into main
diff --git a/.gitignore b/.gitignore
index ccff052..3c2d0cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,8 @@
**/.idea
**/*.iml
**/*.ipr
+
+# VSCode
+**/.vscode/
+**/packages.modules.StatsD.code-workspace
+
diff --git a/OWNERS b/OWNERS
index fb7e5b1..09ff156 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 366902
jeffreyhuang@google.com
monicamwang@google.com
muhammadq@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index fae7a94..ec71d8a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -30,7 +30,7 @@
"name" : "statsd_test"
}
],
- "hwasan-postsubmit" : [
+ "hwasan-presubmit" : [
{
"name" : "FrameworkStatsdTest"
},
diff --git a/aidl/Android.bp b/aidl/Android.bp
index ede81df..c79f0b8 100644
--- a/aidl/Android.bp
+++ b/aidl/Android.bp
@@ -33,6 +33,7 @@
"android/os/IPullAtomResultReceiver.aidl",
"android/os/IStatsCompanionService.aidl",
"android/os/IStatsd.aidl",
+ "android/os/IStatsQueryCallback.aidl",
"android/os/StatsDimensionsValueParcel.aidl",
"android/util/PropertyParcel.aidl",
"android/util/StatsEventParcel.aidl",
diff --git a/aidl/android/os/IPendingIntentRef.aidl b/aidl/android/os/IPendingIntentRef.aidl
index 000a699..5a0f5d7 100644
--- a/aidl/android/os/IPendingIntentRef.aidl
+++ b/aidl/android/os/IPendingIntentRef.aidl
@@ -43,4 +43,10 @@
oneway void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
long subscriptionRuleId, in String[] cookies,
in StatsDimensionsValueParcel dimensionsValueParcel);
+
+ /**
+ * Send a broadcast to the specified PendingIntent notifying it that the list of restricted
+ * metrics has changed.
+ */
+ oneway void sendRestrictedMetricsChangedBroadcast(in long[] metricIds);
}
diff --git a/aidl/android/os/IStatsManagerService.aidl b/aidl/android/os/IStatsManagerService.aidl
index b27c2ec..f8f9122 100644
--- a/aidl/android/os/IStatsManagerService.aidl
+++ b/aidl/android/os/IStatsManagerService.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.os.IPullAtomCallback;
+import android.os.IStatsQueryCallback;
/**
* Binder interface to communicate with the Java-based statistics service helper.
@@ -106,6 +107,7 @@
* wire-encoded of ConfigMetricsReportList.
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+ * @deprecated use #getDataFd() instead
*/
byte[] getData(in long key, in String packageName);
@@ -134,6 +136,31 @@
/** Tell StatsManagerService to unregister the puller for the given atom tag from statsd. */
oneway void unregisterPullAtomCallback(int atomTag);
+ /** Section for restricted-logging methods. */
+
+ /** Queries data from underlying statsd sql store. */
+ oneway void querySql(in String sqlQuery, in int minSqlClientVersion,
+ in @nullable byte[] policyConfig, in IStatsQueryCallback queryCallback,
+ in long configKey, in String configPackage);
+
+ /**
+ * Registers the operation that is called whenever there is a change in the restricted metrics
+ * for a specified config that are present for this client. This operation allows statsd to
+ * inform the client about the current restricted metrics available to be queried for
+ * the specified config.
+ *
+ * Requires Manifest.permission.READ_RESTRICTED_STATS.
+ */
+ long[] setRestrictedMetricsChangedOperation(in PendingIntent pendingIntent, in long configKey,
+ in String configPackage);
+
+ /**
+ * Removes the restricted metrics changed operation for the specified config key/package.
+ *
+ * Requires Manifest.permission.READ_RESTRICTED_STATS.
+ */
+ void removeRestrictedMetricsChangedOperation(in long configKey, in String configPackage);
+
/**
* Same as #getData(in long key, in String packageName), but the data is stored in a file
* descriptor.
diff --git a/aidl/android/os/IStatsQueryCallback.aidl b/aidl/android/os/IStatsQueryCallback.aidl
new file mode 100644
index 0000000..911c816
--- /dev/null
+++ b/aidl/android/os/IStatsQueryCallback.aidl
@@ -0,0 +1,28 @@
+/* *
+ * Copyright (C) 2022 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 android.os;
+
+/**
+ * Binder interface to hold a Callback for Stats SQL queries.
+ * {@hide}
+ */
+interface IStatsQueryCallback {
+ oneway void sendResults(in String[] queryData, in String[] columnNames,
+ in int[] columnTypes, int rowCount);
+
+ oneway void sendFailure(String error);
+}
diff --git a/aidl/android/os/IStatsd.aidl b/aidl/android/os/IStatsd.aidl
index b9a134f..658bd6a 100644
--- a/aidl/android/os/IStatsd.aidl
+++ b/aidl/android/os/IStatsd.aidl
@@ -21,6 +21,7 @@
import android.os.IPullAtomCallback;
import android.os.ParcelFileDescriptor;
import android.util.PropertyParcel;
+import android.os.IStatsQueryCallback;
/**
* Binder interface to communicate with the statistics management service.
@@ -94,6 +95,7 @@
* wire-encoded of ConfigMetricsReportList.
*
* Requires Manifest.permission.DUMP.
+ * @deprecated use #getDataFd() instead for Android T+
*/
byte[] getData(in long key, int callingUid);
@@ -242,6 +244,33 @@
*/
oneway void updateProperties(in PropertyParcel[] properties);
+ /** Section for restricted-logging methods. */
+ /**
+ * Queries data from underlying statsd sql store.
+ */
+ oneway void querySql(in String sqlQuery, in int minSqlClientVersion,
+ in @nullable byte[] policyConfig, in IStatsQueryCallback queryCallback,
+ in long configKey, in String configPackage, in int callingUid);
+
+ /**
+ * Registers the operation that is called whenever there is a change in the restricted metrics
+ * for a specified config that are present for this client. This operation allows statsd to
+ * inform the client about the current restricted metrics available to be queried for the
+ * specified config.
+ *
+ * Requires Manifest.permission.READ_RESTRICTED_STATS
+ */
+ long[] setRestrictedMetricsChangedOperation(in long configKey, in String configPackage,
+ in IPendingIntentRef pir, int callingUid);
+
+ /**
+ * Removes the restricted metrics changed operation for the specified config package/id.
+ *
+ * Requires Manifest.permission.READ_RESTRICTED_STATS.
+ */
+ void removeRestrictedMetricsChangedOperation(in long configKey, in String configPackage,
+ in int callingUid);
+
/** Section for atoms subscription methods. */
/**
* Adds a subscription for atom events.
diff --git a/apex/tests/libstatspull/Android.bp b/apex/tests/libstatspull/Android.bp
index cdac762..d3079e0 100644
--- a/apex/tests/libstatspull/Android.bp
+++ b/apex/tests/libstatspull/Android.bp
@@ -22,7 +22,7 @@
"androidx.test.rules",
"platformprotoslite",
"statsdprotolite",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.runner.stubs",
@@ -34,7 +34,7 @@
srcs: [
"src/**/*.java",
"protos/**/*.proto",
- ],
+ ],
test_suites: [
"device-tests",
"mts",
@@ -45,7 +45,7 @@
compile_multilib: "both",
}
-cc_library_shared {
+cc_test_library {
name: "libstatspull_testhelper",
srcs: ["jni/stats_pull_helper.cpp"],
cflags: [
@@ -54,12 +54,12 @@
"-Wthread-safety",
],
shared_libs: [
- "libbinder_ndk",
- "statsd-aidl-ndk",
+ "libstatspull",
+ "libstatssocket",
],
+ header_libs: ["libnativehelper_header_only"],
static_libs: [
"libbase",
- "libstatspull_private",
- "libstatssocket_private",
],
+ test_for: ["com.android.os.statsd"],
}
diff --git a/framework/Android.bp b/framework/Android.bp
index d3f037d..7ffa7b5 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -21,7 +21,7 @@
name: "statslog-statsd-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module statsd" +
- " --javaPackage com.android.internal.statsd --javaClass StatsdStatsLog --minApiLevel 30",
+ " --javaPackage com.android.internal.statsd --javaClass StatsdStatsLog",
out: ["com/android/internal/statsd/StatsdStatsLog.java"],
}
@@ -30,6 +30,9 @@
srcs: [
":statslog-statsd-java-gen",
],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
visibility: [
"//cts/hostsidetests/statsd/apps:__subpackages__",
"//vendor:__subpackages__",
@@ -59,6 +62,7 @@
],
libs: [
+ "androidx.annotation_annotation",
"framework-configinfrastructure",
],
@@ -99,15 +103,6 @@
min_sdk_version: "30",
}
-java_api_contribution {
- name: "framework-statsd-public-stubs",
- api_surface: "public",
- api_file: "api/current.txt",
- visibility: [
- "//build/orchestrator/apis",
- ],
-}
-
// JNI library for StatsLog.write
cc_library_shared {
name: "libstats_jni",
diff --git a/framework/api/lint-baseline.txt b/framework/api/lint-baseline.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/framework/api/lint-baseline.txt
diff --git a/framework/api/module-lib-lint-baseline.txt b/framework/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..2ae37de
--- /dev/null
+++ b/framework/api/module-lib-lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+BroadcastBehavior: android.app.StatsManager#ACTION_STATSD_STARTED:
+ Field 'ACTION_STATSD_STARTED' is missing @BroadcastBehavior
+
+
+SdkConstant: android.app.StatsManager#ACTION_STATSD_STARTED:
+ Field 'ACTION_STATSD_STARTED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index c432a7f..41b5891 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -1,6 +1,18 @@
// Signature format: 2.0
package android.app {
+ public class StatsCursor extends android.database.AbstractCursor {
+ method @NonNull public String[] getColumnNames();
+ method public int getCount();
+ method public double getDouble(int);
+ method public float getFloat(int);
+ method public int getInt(int);
+ method public long getLong(int);
+ method public short getShort(int);
+ method @NonNull public String getString(int);
+ method public boolean isNull(int);
+ }
+
public final class StatsManager {
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean addConfiguration(long, byte[]);
@@ -10,6 +22,7 @@
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.READ_RESTRICTED_STATS) public void query(long, @NonNull String, @NonNull android.app.StatsQuery, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.StatsCursor,android.app.StatsManager.StatsQueryException>) throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
@@ -18,12 +31,14 @@
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void setPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_RESTRICTED_STATS) public long[] setRestrictedMetricsChangedOperation(long, @NonNull String, @Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
field public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
field public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
field public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
+ field public static final String EXTRA_STATS_RESTRICTED_METRIC_IDS = "android.app.extra.STATS_RESTRICTED_METRIC_IDS";
field public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
field public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
field public static final int PULL_SKIP = 1; // 0x1
@@ -48,11 +63,33 @@
method public int onPullAtom(int, @NonNull java.util.List<android.util.StatsEvent>);
}
+ public static class StatsManager.StatsQueryException extends android.util.AndroidException {
+ ctor public StatsManager.StatsQueryException(@NonNull String);
+ ctor public StatsManager.StatsQueryException(@NonNull String, @NonNull Throwable);
+ }
+
public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
ctor public StatsManager.StatsUnavailableException(String);
ctor public StatsManager.StatsUnavailableException(String, Throwable);
}
+ public final class StatsQuery {
+ method @IntRange(from=0) public int getMinSqlClientVersion();
+ method @Nullable public byte[] getPolicyConfig();
+ method @NonNull public String getRawSql();
+ method public int getSqlDialect();
+ field public static final int DIALECT_SQLITE = 1; // 0x1
+ field public static final int DIALECT_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class StatsQuery.Builder {
+ ctor public StatsQuery.Builder(@NonNull String);
+ method @NonNull public android.app.StatsQuery build();
+ method @NonNull public android.app.StatsQuery.Builder setMinSqlClientVersion(@IntRange(from=0) int);
+ method @NonNull public android.app.StatsQuery.Builder setPolicyConfig(@NonNull byte[]);
+ method @NonNull public android.app.StatsQuery.Builder setSqlDialect(int);
+ }
+
}
package android.os {
@@ -112,12 +149,26 @@
method @Deprecated public static void writeRaw(@NonNull byte[], int);
field public static final byte ANNOTATION_ID_DEFAULT_STATE = 6; // 0x6
field public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4; // 0x4
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14; // 0xe
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17; // 0x11
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12; // 0xc
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11; // 0xb
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18; // 0x12
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13; // 0xd
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10; // 0xa
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15; // 0xf
+ field public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16; // 0x10
field public static final byte ANNOTATION_ID_IS_UID = 1; // 0x1
field public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3; // 0x3
field public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; // 0x5
+ field public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9; // 0x9
field public static final byte ANNOTATION_ID_STATE_NESTED = 8; // 0x8
field public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7; // 0x7
field public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; // 0x2
+ field public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3; // 0x3
+ field public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1; // 0x1
+ field public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4; // 0x4
+ field public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2; // 0x2
}
}
diff --git a/framework/api/system-lint-baseline.txt b/framework/api/system-lint-baseline.txt
new file mode 100644
index 0000000..2ae37de
--- /dev/null
+++ b/framework/api/system-lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+BroadcastBehavior: android.app.StatsManager#ACTION_STATSD_STARTED:
+ Field 'ACTION_STATSD_STARTED' is missing @BroadcastBehavior
+
+
+SdkConstant: android.app.StatsManager#ACTION_STATSD_STARTED:
+ Field 'ACTION_STATSD_STARTED' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/framework/java/android/app/StatsCursor.java b/framework/java/android/app/StatsCursor.java
new file mode 100644
index 0000000..29cd241
--- /dev/null
+++ b/framework/java/android/app/StatsCursor.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 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 android.app;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SuppressLint;
+import android.database.AbstractCursor;
+import android.database.MatrixCursor;
+
+/**
+ * Custom cursor implementation to hold a cross-process cursor to pass data to caller.
+ *
+ * @hide
+ */
+@SystemApi
+public class StatsCursor extends AbstractCursor {
+ private final MatrixCursor mMatrixCursor;
+ private final int[] mColumnTypes;
+ private final String[] mColumnNames;
+ private final int mRowCount;
+
+ /**
+ * @hide
+ **/
+ public StatsCursor(String[] queryData, String[] columnNames, int[] columnTypes, int rowCount) {
+ mColumnTypes = columnTypes;
+ mColumnNames = columnNames;
+ mRowCount = rowCount;
+ mMatrixCursor = new MatrixCursor(columnNames);
+ for (int i = 0; i < rowCount; i++) {
+ MatrixCursor.RowBuilder builder = mMatrixCursor.newRow();
+ for (int j = 0; j < columnNames.length; j++) {
+ int dataIndex = i * columnNames.length + j;
+ builder.add(columnNames[j], queryData[dataIndex]);
+ }
+ }
+ }
+
+ /**
+ * Returns the numbers of rows in the cursor.
+ *
+ * @return the number of rows in the cursor.
+ */
+ @Override
+ public int getCount() {
+ return mRowCount;
+ }
+
+ /**
+ * Returns a string array holding the names of all of the columns in the
+ * result set in the order in which they were listed in the result.
+ *
+ * @return the names of the columns returned in this query.
+ */
+ @Override
+ @NonNull
+ public String[] getColumnNames() {
+ return mColumnNames;
+ }
+
+ /**
+ * Returns the value of the requested column as a String.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as a String.
+ */
+ @Override
+ @NonNull
+ public String getString(int column) {
+ return mMatrixCursor.getString(column);
+ }
+
+ /**
+ * Returns the value of the requested column as a short.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as a short.
+ */
+ @Override
+ @SuppressLint("NoByteOrShort")
+ public short getShort(int column) {
+ return mMatrixCursor.getShort(column);
+ }
+
+ /**
+ * Returns the value of the requested column as an int.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as an int.
+ */
+ @Override
+ public int getInt(int column) {
+ return mMatrixCursor.getInt(column);
+ }
+
+ /**
+ * Returns the value of the requested column as a long.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as a long.
+ */
+ @Override
+ public long getLong(int column) {
+ return mMatrixCursor.getLong(column);
+ }
+
+ /**
+ * Returns the value of the requested column as a float.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as a float.
+ */
+ @Override
+ public float getFloat(int column) {
+ return mMatrixCursor.getFloat(column);
+ }
+
+ /**
+ * Returns the value of the requested column as a double.
+ *
+ * @param column the zero-based index of the target column.
+ * @return the value of that column as a double.
+ */
+ @Override
+ public double getDouble(int column) {
+ return mMatrixCursor.getDouble(column);
+ }
+
+ /**
+ * Returns <code>true</code> if the value in the indicated column is null.
+ *
+ * @param column the zero-based index of the target column.
+ * @return whether the column value is null.
+ */
+ @Override
+ public boolean isNull(int column) {
+ return mMatrixCursor.isNull(column);
+ }
+
+ /**
+ * Returns the data type of the given column's value.
+ *
+ * @param column the zero-based index of the target column.
+ * @return column value type
+ */
+ @Override
+ public int getType(int column) {
+ return mColumnTypes[column];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ return mMatrixCursor.moveToPosition(newPosition);
+ }
+}
diff --git a/framework/java/android/app/StatsManager.java b/framework/java/android/app/StatsManager.java
index 43e085e..0fc9f00 100644
--- a/framework/java/android/app/StatsManager.java
+++ b/framework/java/android/app/StatsManager.java
@@ -17,6 +17,7 @@
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.Manifest.permission.READ_RESTRICTED_STATS;
import static android.provider.DeviceConfig.NAMESPACE_STATSD_JAVA;
import android.annotation.CallbackExecutor;
@@ -26,9 +27,12 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
import android.os.IStatsManagerService;
+import android.os.IStatsQueryCallback;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.StatsFrameworkInitializer;
@@ -38,6 +42,8 @@
import android.util.StatsEvent;
import android.util.StatsEventParcel;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
@@ -104,6 +110,12 @@
"android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
/**
+ * Long array extra of the restricted metric ids present for the client.
+ */
+ public static final String EXTRA_STATS_RESTRICTED_METRIC_IDS =
+ "android.app.extra.STATS_RESTRICTED_METRIC_IDS";
+
+ /**
* Broadcast Action: Statsd has started.
* Configurations and PendingIntents can now be sent to it.
*/
@@ -372,6 +384,115 @@
}
}
+ /**
+ * Registers the operation that is called whenever there is a change in the restricted metrics
+ * for a specified config that are present for this client. This operation allows statsd to
+ * inform the client about the current restricted metric ids available to be queried for the
+ * specified config. This call can block on statsd.
+ *
+ * If there is no config in statsd that matches the provided config package and key, an empty
+ * list is returned. The pending intent will be tracked, and the operation will be called
+ * whenever a matching config is added.
+ *
+ * @param configKey The configKey passed by the package that added the config in
+ * StatsManager#addConfig
+ * @param configPackage The package that added the config in StatsManager#addConfig
+ * @param pendingIntent the PendingIntent to use when broadcasting info to caller.
+ * May be null, in which case it removes any associated pending intent
+ * for this client.
+ * @return A list of metric ids identifying the restricted metrics that are currently available
+ * to be queried for the specified config.
+ * If the pendingIntent is null, this will be an empty list.
+ * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+ */
+ @RequiresPermission(READ_RESTRICTED_STATS)
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public @NonNull long[] setRestrictedMetricsChangedOperation(long configKey,
+ @NonNull String configPackage,
+ @Nullable PendingIntent pendingIntent)
+ throws StatsUnavailableException {
+ synchronized (sLock) {
+ try {
+ IStatsManagerService service = getIStatsManagerServiceLocked();
+ if (pendingIntent == null) {
+ service.removeRestrictedMetricsChangedOperation(configKey, configPackage);
+ return new long[0];
+ } else {
+ return service.setRestrictedMetricsChangedOperation(pendingIntent,
+ configKey, configPackage);
+ }
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to connect to statsmanager "
+ + "when registering restricted metrics listener.");
+ throw new StatsUnavailableException("could not connect", e);
+ } catch (SecurityException e) {
+ throw new StatsUnavailableException(e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Queries the underlying service based on query received and populates the OutcomeReceiver via
+ * callback. This call is blocking on statsd being available, but is otherwise nonblocking.
+ * i.e. the call can return before the query processing is done.
+ * <p>
+ * Two types of tables are supported: Metric tables and the device information table.
+ * </p>
+ * <p>
+ * The device information table is named device_info and contains the following columns:
+ * sdkVersion, model, product, hardware, device, osBuild, fingerprint, brand, manufacturer, and
+ * board. These columns correspond to {@link Build.VERSION.SDK_INT}, {@link Build.MODEL},
+ * {@link Build.PRODUCT}, {@link Build.HARDWARE}, {@link Build.DEVICE}, {@link Build.ID},
+ * {@link Build.FINGERPRINT}, {@link Build.BRAND}, {@link Build.MANUFACTURER},
+ * {@link Build.BOARD} respectively.
+ * </p>
+ * <p>
+ * The metric tables are named metric_METRIC_ID where METRIC_ID is the metric id that is part
+ * of the wire encoded config passed to {@link #addConfig(long, byte[])}. If the metric id is
+ * negative, then the '-' character is replaced with 'n' in the table name. Each metric table
+ * contains the 3 columns followed by n columns of the following form: atomId,
+ * elapsedTimestampNs, wallTimestampNs, field_1, field_2, field_3 ... field_n. These
+ * columns correspond to to the id of the atom from frameworks/proto_logging/stats/atoms.proto,
+ * time when the atom is recorded, and the data fields within each atom.
+ * </p>
+ * @param configKey The configKey passed by the package that added
+ * the config being queried in StatsManager#addConfig
+ * @param configPackage The package that added the config being queried in
+ * StatsManager#addConfig
+ * @param query the query object encapsulating a sql-string and necessary config to query
+ * underlying sql-based data store.
+ * @param executor the executor on which outcomeReceiver will be invoked.
+ * @param outcomeReceiver the receiver to be populated with cursor pointing to result data.
+ */
+ @RequiresPermission(READ_RESTRICTED_STATS)
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void query(long configKey, @NonNull String configPackage, @NonNull StatsQuery query,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<StatsCursor, StatsQueryException> outcomeReceiver)
+ throws StatsUnavailableException {
+ if(query.getSqlDialect() != StatsQuery.DIALECT_SQLITE) {
+ executor.execute(() -> {
+ outcomeReceiver.onError(new StatsQueryException("Unsupported Sql Dialect"));
+ });
+ return;
+ }
+
+ StatsQueryCallbackInternal callbackInternal =
+ new StatsQueryCallbackInternal(outcomeReceiver, executor);
+ synchronized (sLock) {
+ try {
+ IStatsManagerService service = getIStatsManagerServiceLocked();
+ service.querySql(query.getRawSql(), query.getMinSqlClientVersion(),
+ query.getPolicyConfig(), callbackInternal, configKey,
+ configPackage);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new StatsUnavailableException("could not connect", e);
+ }
+ }
+ }
+
+
// TODO: Temporary for backwards compatibility. Remove.
/**
* @deprecated Use {@link #setFetchReportsOperation(PendingIntent, long)}
@@ -734,6 +855,52 @@
return mStatsManagerService;
}
+ private static class StatsQueryCallbackInternal extends IStatsQueryCallback.Stub {
+ OutcomeReceiver<StatsCursor, StatsQueryException> queryCallback;
+ Executor mExecutor;
+
+ StatsQueryCallbackInternal(OutcomeReceiver<StatsCursor, StatsQueryException> queryCallback,
+ @NonNull @CallbackExecutor Executor executor) {
+ this.queryCallback = queryCallback;
+ this.mExecutor = executor;
+ }
+
+ @Override
+ public void sendResults(String[] queryData, String[] columnNames, int[] columnTypes,
+ int rowCount) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new IllegalStateException(
+ "StatsManager#query is not available before Android U");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ StatsCursor cursor = new StatsCursor(queryData, columnNames, columnTypes,
+ rowCount);
+ queryCallback.onResult(cursor);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void sendFailure(String error) {
+ if (!SdkLevel.isAtLeastU()) {
+ throw new IllegalStateException(
+ "StatsManager#query is not available before Android U");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ queryCallback.onError(new StatsQueryException(error));
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
/**
* Exception thrown when communication with the stats service fails (eg if it is not available).
* This might be thrown early during boot before the stats service has started or if it crashed.
@@ -748,12 +915,22 @@
}
}
- private static final String USE_FILE_DESCRIPTOR_FLAG = "use_file_descriptor";
+ /**
+ * Exception thrown when executing a query in statsd fails for any reason. This might be thrown
+ * if the query is malformed or if there is a database error when executing the query.
+ */
+ public static class StatsQueryException extends AndroidException {
+ public StatsQueryException(@NonNull String reason) {
+ super("Failed to query statsd: " + reason);
+ }
+
+ public StatsQueryException(@NonNull String reason, @NonNull Throwable e) {
+ super("Failed to query statsd: " + reason, e);
+ }
+ }
private static boolean getUseFileDescriptor() {
- return SdkLevel.isAtLeastT()
- && DeviceConfig.getBoolean(
- NAMESPACE_STATSD_JAVA, USE_FILE_DESCRIPTOR_FLAG, /* defaultValue= */ false);
+ return SdkLevel.isAtLeastT();
}
private static final int MAX_BUFFER_SIZE = 1024 * 1024 * 20; // 20MB
diff --git a/framework/java/android/app/StatsQuery.java b/framework/java/android/app/StatsQuery.java
new file mode 100644
index 0000000..59f8597
--- /dev/null
+++ b/framework/java/android/app/StatsQuery.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 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 android.app;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents a query that contains information required for StatsManager to return relevant metric
+ * data.
+ *
+ * @hide
+ */
+@SystemApi
+public final class StatsQuery {
+ /**
+ * Default value for SQL dialect.
+ */
+ public static final int DIALECT_UNKNOWN = 0;
+
+ /**
+ * Query passed is of SQLite dialect.
+ */
+ public static final int DIALECT_SQLITE = 1;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"DIALECT_"}, value = {DIALECT_UNKNOWN, DIALECT_SQLITE})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SqlDialect {
+ }
+
+ private final int sqlDialect;
+ private final String rawSql;
+ private final int minClientSqlVersion;
+ private final byte[] policyConfig;
+ private StatsQuery(int sqlDialect, @NonNull String rawSql, int minClientSqlVersion,
+ @Nullable byte[] policyConfig) {
+ this.sqlDialect = sqlDialect;
+ this.rawSql = rawSql;
+ this.minClientSqlVersion = minClientSqlVersion;
+ this.policyConfig = policyConfig;
+ }
+
+ /**
+ * Returns the SQL dialect of the query.
+ */
+ public @SqlDialect int getSqlDialect() {
+ return sqlDialect;
+ }
+
+ /**
+ * Returns the raw SQL of the query.
+ */
+ @NonNull
+ public String getRawSql() {
+ return rawSql;
+ }
+
+ /**
+ * Returns the minimum SQL client library version required to execute the query.
+ */
+ @IntRange(from = 0)
+ public int getMinSqlClientVersion() {
+ return minClientSqlVersion;
+ }
+
+ /**
+ * Returns the wire-encoded StatsPolicyConfig proto that contains information to verify the
+ * query against a policy defined on the underlying data. Returns null if no policy was set.
+ */
+ @Nullable
+ public byte[] getPolicyConfig() {
+ return policyConfig;
+ }
+
+ /**
+ * Builder for constructing a StatsQuery object.
+ * <p>Usage:</p>
+ * <code>
+ * StatsQuery statsQuery = new StatsQuery.Builder("SELECT * from table")
+ * .setSqlDialect(StatsQuery.DIALECT_SQLITE)
+ * .setMinClientSqlVersion(1)
+ * .build();
+ * </code>
+ */
+ public static final class Builder {
+ private int sqlDialect;
+ private String rawSql;
+ private int minSqlClientVersion;
+ private byte[] policyConfig;
+
+ /**
+ * Returns a new StatsQuery.Builder object for constructing StatsQuery for
+ * StatsManager#query
+ */
+ public Builder(@NonNull final String rawSql) {
+ if (rawSql == null) {
+ throw new IllegalArgumentException("rawSql must not be null");
+ }
+ this.rawSql = rawSql;
+ this.sqlDialect = DIALECT_SQLITE;
+ this.minSqlClientVersion = 1;
+ this.policyConfig = null;
+ }
+
+ /**
+ * Sets the SQL dialect of the query.
+ *
+ * @param sqlDialect The SQL dialect of the query.
+ */
+ @NonNull
+ public Builder setSqlDialect(@SqlDialect final int sqlDialect) {
+ this.sqlDialect = sqlDialect;
+ return this;
+ }
+
+ /**
+ * Sets the minimum SQL client library version required to execute the query.
+ *
+ * @param minSqlClientVersion The minimum SQL client version required to execute the query.
+ */
+ @NonNull
+ public Builder setMinSqlClientVersion(@IntRange(from = 0) final int minSqlClientVersion) {
+ if (minSqlClientVersion < 0) {
+ throw new IllegalArgumentException("minSqlClientVersion must be a "
+ + "positive integer");
+ }
+ this.minSqlClientVersion = minSqlClientVersion;
+ return this;
+ }
+
+ /**
+ * Sets the wire-encoded StatsPolicyConfig proto that contains information to verify the
+ * query against a policy defined on the underlying data.
+ *
+ * @param policyConfig The wire-encoded StatsPolicyConfig proto.
+ */
+ @NonNull
+ public Builder setPolicyConfig(@NonNull final byte[] policyConfig) {
+ this.policyConfig = policyConfig;
+ return this;
+ }
+
+ /**
+ * Builds a new instance of {@link StatsQuery}.
+ *
+ * @return A new instance of {@link StatsQuery}.
+ */
+ @NonNull
+ public StatsQuery build() {
+ return new StatsQuery(sqlDialect, rawSql, minSqlClientVersion, policyConfig);
+ }
+ }
+}
diff --git a/framework/java/android/util/StatsLog.java b/framework/java/android/util/StatsLog.java
index 28884c1..f38751a 100644
--- a/framework/java/android/util/StatsLog.java
+++ b/framework/java/android/util/StatsLog.java
@@ -20,17 +20,24 @@
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
+import android.os.Build;
import android.os.IStatsd;
import android.os.Process;
import android.util.proto.ProtoOutputStream;
+import androidx.annotation.RequiresApi;
+
import com.android.internal.statsd.StatsdStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
* define custom metrics inside statsd.
@@ -46,80 +53,290 @@
private static final int EXPERIMENT_IDS_FIELD_ID = 1;
/**
- * Annotation ID constant for logging UID field.
- *
- * @hide
- */
+ * Annotation ID constant for logging UID field.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_IS_UID = 1;
/**
- * Annotation ID constant to indicate logged atom event's timestamp should be truncated.
- *
- * @hide
- */
+ * Annotation ID constant to indicate logged atom event's timestamp should be truncated.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
/**
- * Annotation ID constant for a state atom's primary field.
- *
- * @hide
- */
+ * Annotation ID constant for a state atom's primary field.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3;
/**
- * Annotation ID constant for state atom's state field.
- *
- * @hide
- */
+ * Annotation ID constant for state atom's state field.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4;
/**
- * Annotation ID constant to indicate the first UID in the attribution chain
- * is a primary field.
- * Should only be used for attribution chain fields.
- *
- * @hide
- */
+ * Annotation ID constant to indicate the first UID in the attribution chain
+ * is a primary field.
+ * Should only be used for attribution chain fields.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
/**
- * Annotation ID constant to indicate which state is default for the state atom.
- *
- * @hide
- */
+ * Annotation ID constant to indicate which state is default for the state atom.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_DEFAULT_STATE = 6;
/**
- * Annotation ID constant to signal all states should be reset to the default state.
- *
- * @hide
- */
+ * Annotation ID constant to signal all states should be reset to the default state.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
/**
- * Annotation ID constant to indicate state changes need to account for nesting.
- * This should only be used with binary state atoms.
- *
- * @hide
- */
+ * Annotation ID constant to indicate state changes need to account for nesting.
+ * This should only be used with binary state atoms.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
@SuppressLint("NoByteOrShort")
@SystemApi
public static final byte ANNOTATION_ID_STATE_NESTED = 8;
+ /**
+ * Annotation ID constant to indicate the restriction category of an atom.
+ * This annotation must only be attached to the atom id. This is an int annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains peripheral device info.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains app usage information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains app activity information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains health connect information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains accessibility information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains system search information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains user engagement information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains ambient sensing information.
+ * This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17;
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains demographic classification
+ * information. This is a bool annotation.
+ *
+ * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
+ * accept byte as the type for annotation ids to save space.
+ *
+ * @hide
+ */
+ @SuppressLint("NoByteOrShort")
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18;
+
+
+ /** @hide */
+ @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = {
+ RESTRICTION_CATEGORY_DIAGNOSTIC,
+ RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE,
+ RESTRICTION_CATEGORY_AUTHENTICATION,
+ RESTRICTION_CATEGORY_FRAUD_AND_ABUSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RestrictionCategory {}
+
+ /**
+ * Restriction category for atoms about diagnostics.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1;
+
+ /**
+ * Restriction category for atoms about system intelligence.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2;
+
+ /**
+ * Restriction category for atoms about authentication.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3;
+
+ /**
+ * Restriction category for atoms about fraud and abuse.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4;
+
private StatsLog() {
}
diff --git a/framework/test/hostsidetests/Android.bp b/framework/test/hostsidetests/Android.bp
index e0b7eb0..b3e253c 100644
--- a/framework/test/hostsidetests/Android.bp
+++ b/framework/test/hostsidetests/Android.bp
@@ -24,7 +24,7 @@
"androidx.test.runner",
"androidx.test.core",
"statsdprotolite",
- "truth-prebuilt",
+ "truth",
"framework-statsd.stubs.module_lib",
],
compile_multilib: "both",
@@ -42,7 +42,7 @@
"androidx.test.runner",
"androidx.test.core",
"statsdprotolite",
- "truth-prebuilt",
+ "truth",
"framework-statsd.stubs.module_lib",
],
compile_multilib: "both",
@@ -54,10 +54,10 @@
java_test_host {
name: "FrameworkStatsdHostTest",
- srcs: [ "src/**/*.java" ],
+ srcs: ["src/**/*.java"],
libs: [
"tradefed",
- "truth-prebuilt",
+ "truth",
],
test_suites: [
"device-tests",
diff --git a/framework/test/hostsidetests/app/src/com/android/tests/statsd/framework/appnopermission/StatsdPermissionTest.java b/framework/test/hostsidetests/app/src/com/android/tests/statsd/framework/appnopermission/StatsdPermissionTest.java
index d8c43b2..d42ab9e 100644
--- a/framework/test/hostsidetests/app/src/com/android/tests/statsd/framework/appnopermission/StatsdPermissionTest.java
+++ b/framework/test/hostsidetests/app/src/com/android/tests/statsd/framework/appnopermission/StatsdPermissionTest.java
@@ -61,7 +61,11 @@
e = assertThrows(
StatsManager.StatsUnavailableException.class, () -> statsManager.getReports(1234));
- assertThat(e).hasCauseThat().isInstanceOf(SecurityException.class);
+ // expectations are:
+ // - for Android T+ receive IllegalStateException
+ // - for previous versions receive SecurityExceptions
+ assertThat(e.getCause().getClass()).
+ isAnyOf(SecurityException.class, IllegalStateException.class);
e = assertThrows(
StatsManager.StatsUnavailableException.class, () -> statsManager.getStatsMetadata());
@@ -101,4 +105,4 @@
// Setting a pull atom callback is not tested because it is oneway,
// so the security exception would not propagate to the client.
}
-}
\ No newline at end of file
+}
diff --git a/framework/test/unittests/Android.bp b/framework/test/unittests/Android.bp
index 243328c..14424f8 100644
--- a/framework/test/unittests/Android.bp
+++ b/framework/test/unittests/Android.bp
@@ -19,11 +19,11 @@
android_test {
name: "FrameworkStatsdTest",
sdk_version: "module_current",
- srcs: [ "**/*.java" ],
+ srcs: ["**/*.java"],
manifest: "AndroidManifest.xml",
static_libs: [
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
"modules-utils-build",
],
libs: [
diff --git a/framework/test/unittests/AndroidTest.xml b/framework/test/unittests/AndroidTest.xml
index e32f16e..4ab7b64 100644
--- a/framework/test/unittests/AndroidTest.xml
+++ b/framework/test/unittests/AndroidTest.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<configuration description="Runs Tests for Statsd.">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="test-file-name" value="FrameworkStatsdTest.apk" />
<option name="install-arg" value="-g" />
</target_preparer>
diff --git a/lib/libstatsgtestmatchers/Android.bp b/lib/libstatsgtestmatchers/Android.bp
index 38839e0..ae26066 100644
--- a/lib/libstatsgtestmatchers/Android.bp
+++ b/lib/libstatsgtestmatchers/Android.bp
@@ -24,8 +24,8 @@
cc_test_library {
name: "libstatsgtestmatchers",
srcs: [
- ":statsd_internal_protos",
- ":libstats_internal_protos",
+ ":libstats_log_protos",
+ ":libstats_subscription_protos",
],
export_include_dirs: ["include"],
proto: {
@@ -46,7 +46,7 @@
"-Wall",
"-Werror",
"-Wthread-safety",
- "-Wdeprecated-declarations",
+ "-Wno-deprecated-declarations",
],
visibility: [
"//packages/modules/StatsD/lib/libstatspull",
diff --git a/lib/libstatsgtestmatchers/include/gtest_matchers.h b/lib/libstatsgtestmatchers/include/gtest_matchers.h
index c6729de..3d9b792 100644
--- a/lib/libstatsgtestmatchers/include/gtest_matchers.h
+++ b/lib/libstatsgtestmatchers/include/gtest_matchers.h
@@ -206,20 +206,39 @@
EQ_MATCHER(PluggedStateChanged, PROPERTY_EQ(PluggedStateChanged, state));
TYPE_PRINTER(PluggedStateChanged, PROPERTY_PRINT(state));
+EQ_MATCHER(WakelockStateChanged,
+ REPEATED_PROPERTY_MATCHER(WakelockStateChanged, attribution_node, EqAttributionNode),
+ PROPERTY_EQ(WakelockStateChanged, type),
+ PROPERTY_EQ(WakelockStateChanged, tag),
+ PROPERTY_EQ(WakelockStateChanged, state),
+ PROPERTY_EQ(WakelockStateChanged, process_state)
+);
+TYPE_PRINTER(WakelockStateChanged,
+ REPEATED_PROPERTY_PRINT(attribution_node)
+ PROPERTY_PRINT(type)
+ PROPERTY_PRINT(tag)
+ PROPERTY_PRINT(state)
+ PROPERTY_PRINT(process_state)
+);
+
EQ_MATCHER(Atom,
PROPERTY_MATCHER(Atom, screen_state_changed, EqScreenStateChanged),
- PROPERTY_MATCHER(Atom, test_atom_reported, EqTestAtomReported)
+ PROPERTY_MATCHER(Atom, test_atom_reported, EqTestAtomReported),
+ PROPERTY_MATCHER(Atom, wakelock_state_changed, EqWakelockStateChanged)
);
TYPE_PRINTER(Atom,
PROPERTY_PRINT(screen_state_changed)
PROPERTY_PRINT(test_atom_reported)
+ PROPERTY_PRINT(wakelock_state_changed)
);
EQ_MATCHER(ShellData,
- REPEATED_PROPERTY_MATCHER(ShellData, atom, EqAtom)
+ REPEATED_PROPERTY_MATCHER(ShellData, atom, EqAtom),
+ REPEATED_PROPERTY_EQ(ShellData, elapsed_timestamp_nanos)
);
TYPE_PRINTER(ShellData,
REPEATED_PROPERTY_PRINT(atom)
+ REPEATED_PROPERTY_PRINT(elapsed_timestamp_nanos)
);
// clang-format on
diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp
index de0ae47..28c0a70 100644
--- a/lib/libstatspull/Android.bp
+++ b/lib/libstatspull/Android.bp
@@ -37,38 +37,21 @@
shared_libs: [
"libbinder_ndk",
"liblog",
+ "libstatssocket",
],
static_libs: [
"libutils",
"statsd-aidl-ndk",
],
- target: {
- android: {
- shared_libs: ["libstatssocket"],
- },
- host: {
- static_libs: ["libstatssocket"],
- },
- },
}
-cc_library {
+cc_library_shared {
name: "libstatspull",
defaults: [
"libstatspull_defaults",
],
host_supported: true,
target: {
- android: {
- static: {
- enabled: false,
- },
- },
- host: {
- shared: {
- enabled: false,
- },
- },
darwin: {
enabled: false,
},
@@ -94,20 +77,6 @@
export_include_dirs: ["include"],
}
-// ONLY USE IN TESTS.
-cc_library_static {
- name: "libstatspull_private",
- defaults: [
- "libstatspull_defaults",
- ],
- cflags: [
- "-DLIB_STATS_PULL_TESTS_FLAG",
- ],
- visibility: [
- "//packages/modules/StatsD/apex/tests/libstatspull",
- ],
-}
-
// Note: These unit tests only test PullAtomMetadata and subscriptions
// For full E2E tests of pullers, use LibStatsPullTests
cc_test {
@@ -169,4 +138,5 @@
],
require_root: true,
min_sdk_version: "30",
+ test_for: ["com.android.os.statsd"],
}
diff --git a/lib/libstatspull/libstatspull.map.txt b/lib/libstatspull/libstatspull.map.txt
index 30641e4..a76342c 100644
--- a/lib/libstatspull/libstatspull.map.txt
+++ b/lib/libstatspull/libstatspull.map.txt
@@ -1,21 +1,21 @@
LIBSTATSPULL {
global:
- AStatsManager_PullAtomMetadata_obtain; # apex # introduced=30
- AStatsManager_PullAtomMetadata_release; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setCoolDownMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getCoolDownMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setTimeoutMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getTimeoutMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setAdditiveFields; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getNumAdditiveFields; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getAdditiveFields; # apex # introduced=30
- AStatsEventList_addStatsEvent; # apex # introduced=30
- AStatsManager_setPullAtomCallback; # apex # introduced=30
- AStatsManager_clearPullAtomCallback; # apex # introduced=30
+ AStatsManager_PullAtomMetadata_obtain; # apex introduced=30
+ AStatsManager_PullAtomMetadata_release; # apex introduced=30
+ AStatsManager_PullAtomMetadata_setCoolDownMillis; # apex introduced=30
+ AStatsManager_PullAtomMetadata_getCoolDownMillis; # apex introduced=30
+ AStatsManager_PullAtomMetadata_setTimeoutMillis; # apex introduced=30
+ AStatsManager_PullAtomMetadata_getTimeoutMillis; # apex introduced=30
+ AStatsManager_PullAtomMetadata_setAdditiveFields; # apex introduced=30
+ AStatsManager_PullAtomMetadata_getNumAdditiveFields; # apex introduced=30
+ AStatsManager_PullAtomMetadata_getAdditiveFields; # apex introduced=30
+ AStatsEventList_addStatsEvent; # apex introduced=30
+ AStatsManager_setPullAtomCallback; # apex introduced=30
+ AStatsManager_clearPullAtomCallback; # apex introduced=30
- AStatsManager_addSubscription; # apex # introduced=UpsideDownCake
- AStatsManager_removeSubscription; # apex # introduced=UpsideDownCake
- AStatsManager_flushSubscription; # apex # introduced=UpsideDownCake
+ AStatsManager_addSubscription; # apex introduced=UpsideDownCake
+ AStatsManager_removeSubscription; # apex introduced=UpsideDownCake
+ AStatsManager_flushSubscription; # apex introduced=UpsideDownCake
local:
*;
};
diff --git a/lib/libstatspull/stats_pull_atom_callback.cpp b/lib/libstatspull/stats_pull_atom_callback.cpp
index 3fdf243..b880f0a 100644
--- a/lib/libstatspull/stats_pull_atom_callback.cpp
+++ b/lib/libstatspull/stats_pull_atom_callback.cpp
@@ -121,8 +121,6 @@
// Convert stats_events into StatsEventParcels.
std::vector<StatsEventParcel> parcels;
- // Resolves fuzz build failure in b/161575591.
-#if defined(__ANDROID_APEX__) || defined(LIB_STATS_PULL_TESTS_FLAG)
for (int i = 0; i < statsEventList.data.size(); i++) {
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
@@ -133,7 +131,6 @@
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
}
-#endif
Status status = resultReceiver->pullFinished(atomTag, success, parcels);
if (!status.isOk()) {
@@ -276,11 +273,8 @@
public:
~CallbackOperationsHandler() {
- for (auto& workThread : mWorkThreads) {
- if (workThread.joinable()) {
- mCondition.notify_one();
- workThread.join();
- }
+ if (mWorkThread.joinable()) {
+ mWorkThread.join();
}
}
@@ -296,9 +290,7 @@
registerCmd->callback = std::move(callback);
pushToQueue(std::move(registerCmd));
- std::thread registerThread(&CallbackOperationsHandler::processCommands, this,
- statsProvider);
- mWorkThreads.push_back(std::move(registerThread));
+ startWorkerThread();
}
void unregisterCallback(int atomTag) {
@@ -307,15 +299,13 @@
unregisterCmd->atomTag = atomTag;
pushToQueue(std::move(unregisterCmd));
- std::thread unregisterThread(&CallbackOperationsHandler::processCommands, this,
- statsProvider);
- mWorkThreads.push_back(std::move(unregisterThread));
+ startWorkerThread();
}
private:
- std::vector<std::thread> mWorkThreads;
+ std::atomic_bool mThreadAlive = false;
+ std::thread mWorkThread;
- std::condition_variable mCondition;
std::mutex mMutex;
std::queue<std::unique_ptr<Cmd>> mCmdQueue;
@@ -323,11 +313,20 @@
}
void pushToQueue(std::unique_ptr<Cmd> cmd) {
- {
- std::unique_lock<std::mutex> lock(mMutex);
- mCmdQueue.push(std::move(cmd));
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCmdQueue.push(std::move(cmd));
+ }
+
+ void startWorkerThread() {
+ // Only spawn one thread to manage requests
+ if (mThreadAlive) {
+ return;
}
- mCondition.notify_one();
+ mThreadAlive = true;
+ if (mWorkThread.joinable()) {
+ mWorkThread.join();
+ }
+ mWorkThread = std::thread(&CallbackOperationsHandler::processCommands, this, statsProvider);
}
void processCommands(std::shared_ptr<StatsdProvider> statsProvider) {
@@ -337,37 +336,41 @@
*/
const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
- /**
- * To guarantee sequential commands processing we need to lock mutex queue
- */
- std::unique_lock<std::mutex> lock(mMutex);
- /**
- * This should never really block in practice, since the command was already queued
- * from the main thread by registerCallback or unregisterCallback.
- * We are putting command to the queue, and only after a worker thread is created,
- * which will pop a single command from a queue and will be terminated after processing.
- * It makes producer/consumer as 1:1 match
- */
- if (mCmdQueue.empty()) {
- mCondition.wait(lock, [this] { return !this->mCmdQueue.empty(); });
- }
-
- std::unique_ptr<Cmd> cmd = std::move(mCmdQueue.front());
- mCmdQueue.pop();
-
if (!statsService) {
- // Statsd not available - dropping command request
+ // Statsd not available - dropping all submitted command requests
+ std::queue<std::unique_ptr<Cmd>> emptyQueue;
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCmdQueue.swap(emptyQueue);
+ mThreadAlive = false;
return;
}
- switch (cmd->type) {
- case Cmd::CMD_REGISTER: {
- registerStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider, cmd->callback);
- break;
+ while (true) {
+ std::unique_ptr<Cmd> cmd = nullptr;
+ {
+ /**
+ * To guarantee sequential commands processing we need to lock mutex queue
+ */
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCmdQueue.empty()) {
+ mThreadAlive = false;
+ return;
+ }
+
+ cmd = std::move(mCmdQueue.front());
+ mCmdQueue.pop();
}
- case Cmd::CMD_UNREGISTER: {
- unregisterStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider);
- break;
+
+ switch (cmd->type) {
+ case Cmd::CMD_REGISTER: {
+ registerStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider,
+ cmd->callback);
+ break;
+ }
+ case Cmd::CMD_UNREGISTER: {
+ unregisterStatsPullAtomCallbackBlocking(cmd->atomTag, statsProvider);
+ break;
+ }
}
}
}
diff --git a/lib/libstatssocket/Android.bp b/lib/libstatssocket/Android.bp
index a6d103e..fce6a75 100644
--- a/lib/libstatssocket/Android.bp
+++ b/lib/libstatssocket/Android.bp
@@ -25,41 +25,39 @@
name: "libstatssocket_defaults",
srcs: [
"stats_buffer_writer.c",
+ "stats_buffer_writer_queue.cpp",
"stats_event.c",
"stats_socket.c",
"statsd_writer.cpp",
+ "stats_socket_loss_reporter.cpp",
+ "utils.cpp",
],
+ generated_sources: ["stats_statsdsocketlog.cpp"],
+ generated_headers: ["stats_statsdsocketlog.h"],
export_include_dirs: ["include"],
- header_libs: ["liblog_headers"],
+ header_libs: [
+ "libcutils_headers",
+ "liblog_headers",
+ ],
cflags: [
"-Wall",
"-Werror",
"-Wthread-safety",
+
+ // for local testing & benchmarking with statsd_benchmark
+ // "-DENABLE_BENCHMARK_SUPPORT",
],
static_libs: [
"libbase",
],
}
-cc_library {
+cc_library_shared {
name: "libstatssocket",
defaults: [
"libstatssocket_defaults",
],
host_supported: true,
- target: {
- // On android, libstatssocket should only be linked as a shared lib
- android: {
- static: {
- enabled: false,
- },
- },
- host: {
- shared: {
- enabled: false,
- },
- },
- },
stl: "libc++_static",
// enumerate stable entry points for APEX use
@@ -76,18 +74,6 @@
min_sdk_version: "30",
}
-//TODO (b/149842105): Figure out if there is a better solution for this.
-cc_test_library {
- name: "libstatssocket_private",
- defaults: [
- "libstatssocket_defaults",
- ],
- visibility: [
- "//packages/modules/StatsD/apex/tests/libstatspull",
- "//packages/modules/StatsD/statsd",
- ],
-}
-
cc_library_headers {
name: "libstatssocket_headers",
export_include_dirs: ["include"],
@@ -103,7 +89,11 @@
srcs: [
"tests/stats_event_test.cpp",
"tests/stats_writer_test.cpp",
+ "tests/stats_buffer_writer_queue_test.cpp",
+ "tests/stats_socketlog_test.cpp",
],
+ generated_sources: ["stats_statsdsocketlog.cpp"],
+ generated_headers: ["stats_statsdsocketlog.h"],
cflags: [
"-Wall",
"-Werror",
@@ -112,10 +102,10 @@
static_libs: [
"libbase",
"libgmock",
- "libstatssocket_private",
],
shared_libs: [
"libutils",
+ "libstatssocket",
],
test_suites: [
"device-tests",
@@ -135,4 +125,56 @@
},
require_root: true,
min_sdk_version: "30",
+ test_for: ["com.android.os.statsd"],
+}
+
+genrule {
+ name: "stats_statsdsocketlog.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) " +
+ "--header $(genDir)/stats_statsdsocketlog.h " +
+ "--module statsdsocket " +
+ "--namespace android,os,statsdsocket",
+ out: [
+ "stats_statsdsocketlog.h",
+ ],
+}
+
+genrule {
+ name: "stats_statsdsocketlog.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) " +
+ "--cpp $(genDir)/stats_statsdsocketlog.cpp " +
+ "--module statsdsocket " +
+ "--namespace android,os,statsdsocket " +
+ "--importHeader stats_statsdsocketlog.h",
+ out: [
+ "stats_statsdsocketlog.cpp",
+ ],
+}
+
+cc_fuzz {
+ name: "statsevent_fuzzer",
+ defaults: [
+ "libstatssocket_defaults",
+ ],
+ srcs: [
+ "fuzzers/stats_event_fuzzer.cpp",
+ ],
+ local_include_dirs: [
+ "include",
+ ],
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ fuzz_config: {
+ cc: [
+ "singhtejinder@google.com",
+ "sharaienko@google.com",
+ ],
+ },
}
diff --git a/lib/libstatssocket/fuzzers/stats_event_fuzzer.cpp b/lib/libstatssocket/fuzzers/stats_event_fuzzer.cpp
new file mode 100644
index 0000000..975ecf7
--- /dev/null
+++ b/lib/libstatssocket/fuzzers/stats_event_fuzzer.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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 <fuzzer/FuzzedDataProvider.h>
+
+#include "include/stats_event.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ // This only tests the libstatssocket APIs.
+ // Since fuzzing is not supported across processes, it does not fuzz statsd.
+ // See statsd_fuzzer.
+
+ AStatsEvent* event = AStatsEvent_obtain();
+
+ AStatsEvent_setAtomId(event, fdp.ConsumeIntegral<int32_t>());
+ // atom-level annotation
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+
+ while (fdp.remaining_bytes() > 0) {
+ AStatsEvent_writeInt32(event, fdp.ConsumeIntegral<int32_t>());
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+ AStatsEvent_addInt32Annotation(event, fdp.ConsumeIntegral<int32_t>(),
+ fdp.ConsumeIntegral<int32_t>());
+ AStatsEvent_writeBool(event, fdp.ConsumeBool());
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+ AStatsEvent_addInt32Annotation(event, fdp.ConsumeIntegral<int32_t>(),
+ fdp.ConsumeIntegral<int32_t>());
+ AStatsEvent_writeFloat(event, fdp.ConsumeFloatingPoint<float>());
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+ AStatsEvent_addInt32Annotation(event, fdp.ConsumeIntegral<int32_t>(),
+ fdp.ConsumeIntegral<int32_t>());
+ AStatsEvent_writeInt64(event, fdp.ConsumeIntegral<int64_t>());
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+ AStatsEvent_addInt32Annotation(event, fdp.ConsumeIntegral<int32_t>(),
+ fdp.ConsumeIntegral<int32_t>());
+ AStatsEvent_writeString(event, fdp.ConsumeRandomLengthString().c_str());
+ AStatsEvent_addBoolAnnotation(event, fdp.ConsumeIntegral<int32_t>(), fdp.ConsumeBool());
+ AStatsEvent_addInt32Annotation(event, fdp.ConsumeIntegral<int32_t>(),
+ fdp.ConsumeIntegral<int32_t>());
+ }
+
+ AStatsEvent_write(event);
+ AStatsEvent_release(event);
+ return 0;
+}
diff --git a/lib/libstatssocket/include/stats_annotations.h b/lib/libstatssocket/include/stats_annotations.h
index e812af0..2963db9 100644
--- a/lib/libstatssocket/include/stats_annotations.h
+++ b/lib/libstatssocket/include/stats_annotations.h
@@ -80,6 +80,116 @@
* Introduced in API 31.
*/
ASTATSLOG_ANNOTATION_ID_STATE_NESTED = 8,
+
+ /**
+ * Annotation ID constant to indicate the restriction category of an atom.
+ * This annotation must only be attached to the atom id. This is an int annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY = 9,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains peripheral device info.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains app usage information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains app activity information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains health connect information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains accessibility information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains system search information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains user engagement information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains ambient sensing information.
+ * This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17,
+
+ /**
+ * Annotation ID to indicate that a field of an atom contains demographic classification
+ * information. This is a bool annotation.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18,
};
+enum AStatsLogRestrictionCategory : uint32_t {
+ /**
+ * Restriction category for atoms about diagnostics.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC = 1,
+
+ /**
+ * Restriction category for atoms about system intelligence.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2,
+
+ /**
+ * Restriction category for atoms about authentication.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_RESTRICTION_CATEGORY_AUTHENTICATION = 3,
+
+ /**
+ * Restriction category for atoms about fraud and abuse.
+ *
+ * Introduced in API 34.
+ */
+ ASTATSLOG_RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4,
+
+};
__END_DECLS
diff --git a/lib/libstatssocket/include/stats_event.h b/lib/libstatssocket/include/stats_event.h
index 23e1419..f131bf5 100644
--- a/lib/libstatssocket/include/stats_event.h
+++ b/lib/libstatssocket/include/stats_event.h
@@ -73,6 +73,10 @@
/**
* Writes the StatsEvent to the stats log.
+ * For all UIDs except system server:
+ * - Returns number of bytes written into the socket, or socket error code.
+ * For the system_server the write is done via intermediate queue:
+ * - Returns 1 if event was added into the queue, 0 otherwise.
*
* After calling this, AStatsEvent_release must be called,
* and is the only function that can be safely called.
diff --git a/lib/libstatssocket/libstatssocket.map.txt b/lib/libstatssocket/libstatssocket.map.txt
index aa6eb30..f48e159 100644
--- a/lib/libstatssocket/libstatssocket.map.txt
+++ b/lib/libstatssocket/libstatssocket.map.txt
@@ -1,25 +1,25 @@
LIBSTATSSOCKET {
global:
- AStatsEvent_obtain; # apex # introduced=30
- AStatsEvent_build; # apex # introduced=30
- AStatsEvent_write; # apex # introduced=30
- AStatsEvent_release; # apex # introduced=30
- AStatsEvent_setAtomId; # apex # introduced=30
- AStatsEvent_writeInt32; # apex # introduced=30
- AStatsEvent_writeInt64; # apex # introduced=30
- AStatsEvent_writeFloat; # apex # introduced=30
- AStatsEvent_writeBool; # apex # introduced=30
- AStatsEvent_writeByteArray; # apex # introduced=30
- AStatsEvent_writeString; # apex # introduced=30
- AStatsEvent_writeAttributionChain; # apex # introduced=30
- AStatsEvent_writeInt32Array; # apex # introduced=Tiramisu
- AStatsEvent_writeInt64Array; # apex # introduced=Tiramisu
- AStatsEvent_writeFloatArray; # apex # introduced=Tiramisu
- AStatsEvent_writeBoolArray; # apex # introduced=Tiramisu
- AStatsEvent_writeStringArray; # apex # introduced=Tiramisu
- AStatsEvent_addBoolAnnotation; # apex # introduced=30
- AStatsEvent_addInt32Annotation; # apex # introduced=30
- AStatsSocket_close; # apex # introduced=30
+ AStatsEvent_obtain; # apex introduced=30
+ AStatsEvent_build; # apex introduced=30
+ AStatsEvent_write; # apex introduced=30
+ AStatsEvent_release; # apex introduced=30
+ AStatsEvent_setAtomId; # apex introduced=30
+ AStatsEvent_writeInt32; # apex introduced=30
+ AStatsEvent_writeInt64; # apex introduced=30
+ AStatsEvent_writeFloat; # apex introduced=30
+ AStatsEvent_writeBool; # apex introduced=30
+ AStatsEvent_writeByteArray; # apex introduced=30
+ AStatsEvent_writeString; # apex introduced=30
+ AStatsEvent_writeAttributionChain; # apex introduced=30
+ AStatsEvent_writeInt32Array; # apex introduced=Tiramisu
+ AStatsEvent_writeInt64Array; # apex introduced=Tiramisu
+ AStatsEvent_writeFloatArray; # apex introduced=Tiramisu
+ AStatsEvent_writeBoolArray; # apex introduced=Tiramisu
+ AStatsEvent_writeStringArray; # apex introduced=Tiramisu
+ AStatsEvent_addBoolAnnotation; # apex introduced=30
+ AStatsEvent_addInt32Annotation; # apex introduced=30
+ AStatsSocket_close; # apex introduced=30
local:
*;
};
diff --git a/lib/libstatssocket/stats_buffer_writer.c b/lib/libstatssocket/stats_buffer_writer.c
index 5ad2ec0..33e2ef7 100644
--- a/lib/libstatssocket/stats_buffer_writer.c
+++ b/lib/libstatssocket/stats_buffer_writer.c
@@ -15,9 +15,13 @@
*/
#include "include/stats_buffer_writer.h"
+
#include <errno.h>
#include <sys/time.h>
#include <sys/uio.h>
+
+#include "stats_buffer_writer_impl.h"
+#include "stats_buffer_writer_queue.h"
#include "statsd_writer.h"
static const uint32_t kStatsEventTag = 1937006964;
@@ -27,6 +31,12 @@
static int __write_to_statsd_init(struct iovec* vec, size_t nr);
static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+/**
+ * @brief Logs the error code associated with atom loss
+ *
+ * @param error To distinguish source of error, the errno code values must be negative,
+ * while the libstatssocket internal error codes are positive
+ */
void note_log_drop(int error, int atomId) {
statsdLoggerWrite.noteDrop(error, atomId);
}
@@ -45,6 +55,19 @@
}
int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
+ const int kQueueOverflowErrorCode = 1;
+ if (should_write_via_queue(atomId)) {
+ const bool ret = write_buffer_to_statsd_queue(buffer, size, atomId);
+ if (!ret) {
+ // to account on the loss, note atom drop with predefined internal error code
+ note_log_drop(kQueueOverflowErrorCode, atomId);
+ }
+ return ret;
+ }
+ return write_buffer_to_statsd_impl(buffer, size, atomId, true);
+}
+
+int write_buffer_to_statsd_impl(void* buffer, size_t size, uint32_t atomId, bool doNoteDrop) {
int ret = 1;
struct iovec vecs[2];
@@ -55,7 +78,7 @@
ret = __write_to_statsd(vecs, 2);
- if (ret < 0) {
+ if (ret < 0 && doNoteDrop) {
note_log_drop(ret, atomId);
}
diff --git a/lib/libstatssocket/stats_buffer_writer_impl.h b/lib/libstatssocket/stats_buffer_writer_impl.h
new file mode 100644
index 0000000..a9b8a48
--- /dev/null
+++ b/lib/libstatssocket/stats_buffer_writer_impl.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+int write_buffer_to_statsd_impl(void* buffer, size_t size, uint32_t atomId, bool doNoteDrop);
+
+__END_DECLS
diff --git a/lib/libstatssocket/stats_buffer_writer_queue.cpp b/lib/libstatssocket/stats_buffer_writer_queue.cpp
new file mode 100644
index 0000000..cf21c4e
--- /dev/null
+++ b/lib/libstatssocket/stats_buffer_writer_queue.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 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 "stats_buffer_writer_queue.h"
+
+#include <private/android_filesystem_config.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <queue>
+#include <thread>
+
+#include "stats_buffer_writer_impl.h"
+#include "stats_buffer_writer_queue_impl.h"
+#include "utils.h"
+
+BufferWriterQueue::BufferWriterQueue() : mWorkThread(&BufferWriterQueue::processCommands, this) {
+}
+
+BufferWriterQueue::~BufferWriterQueue() {
+ terminate();
+ // at this stage there can be N elements in the queue for which memory needs to be freed
+ // explicitly
+ drainQueue();
+}
+
+bool BufferWriterQueue::write(const uint8_t* buffer, size_t size, uint32_t atomId) {
+ Cmd cmd = createWriteBufferCmd(buffer, size, atomId);
+ if (cmd.buffer == NULL) {
+ return false;
+ }
+ return pushToQueue(cmd);
+}
+
+size_t BufferWriterQueue::getQueueSize() const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ return mCmdQueue.size();
+}
+
+bool BufferWriterQueue::pushToQueue(const Cmd& cmd) {
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCmdQueue.size() >= kQueueMaxSizeLimit) {
+ // TODO (b/258003151): add logging info about internal queue overflow with appropriate
+ // error code
+ return false;
+ }
+ mCmdQueue.push(cmd);
+ }
+ mCondition.notify_one();
+ return true;
+}
+
+BufferWriterQueue::Cmd BufferWriterQueue::createWriteBufferCmd(const uint8_t* buffer, size_t size,
+ uint32_t atomId) {
+ BufferWriterQueue::Cmd writeCmd;
+ writeCmd.atomId = atomId;
+ writeCmd.buffer = (uint8_t*)malloc(size);
+ if (writeCmd.buffer == NULL) {
+ return writeCmd;
+ }
+ memcpy(writeCmd.buffer, buffer, size);
+ writeCmd.size = size;
+ return writeCmd;
+}
+
+void BufferWriterQueue::terminate() {
+ if (mWorkThread.joinable()) {
+ mDoTerminate = true;
+ Cmd terminateCmd;
+ terminateCmd.buffer = NULL;
+ pushToQueue(terminateCmd);
+ mWorkThread.join();
+ }
+}
+
+void BufferWriterQueue::drainQueue() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (!mCmdQueue.empty()) {
+ free(mCmdQueue.front().buffer);
+ mCmdQueue.pop();
+ }
+}
+
+void BufferWriterQueue::processCommands() {
+ while (true) {
+ // temporary local thread copy
+ Cmd cmd;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCmdQueue.empty()) {
+ mCondition.wait(lock, [this] { return !this->mCmdQueue.empty(); });
+ }
+ cmd = mCmdQueue.front();
+ }
+
+ if (cmd.buffer == NULL) {
+ // null buffer ptr used as a marker of the termination request
+ return;
+ }
+
+ const bool writeSuccess = handleCommand(cmd);
+ if (writeSuccess) {
+ // no event drop is observed otherwise command remains in the queue
+ // and worker thread will try to log later on
+
+ // call free() explicitly here to free memory before the mutex lock
+ free(cmd.buffer);
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ // this will lead to Cmd destructor call which will be no-op since now the
+ // buffer is NULL
+ mCmdQueue.pop();
+ }
+ }
+ // TODO (b/258003151): add logging info about retry count
+
+ if (mDoTerminate) {
+ return;
+ }
+
+ // attempt to enforce the logging frequency constraints
+ // in case of failed write due to socket overflow the sleep can be longer
+ // to not overload socket continuously
+ if (!writeSuccess) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(kDelayOnFailedWriteMs));
+ }
+ }
+}
+
+bool BufferWriterQueue::handleCommand(const Cmd& cmd) const {
+ // skip log drop if occurs, since the atom remains in the queue and write will be retried
+ return write_buffer_to_statsd_impl(cmd.buffer, cmd.size, cmd.atomId, /*doNoteDrop*/ false) > 0;
+}
+
+bool write_buffer_to_statsd_queue(const uint8_t* buffer, size_t size, uint32_t atomId) {
+ static BufferWriterQueue queue;
+ return queue.write(buffer, size, atomId);
+}
+
+#ifdef ENABLE_BENCHMARK_SUPPORT
+bool should_write_via_queue(uint32_t atomId) {
+#else
+bool should_write_via_queue(uint32_t /*atomId*/) {
+#endif
+ const uint32_t appUid = getuid();
+
+ // hard-coded push all system server atoms to queue
+ if (appUid == AID_SYSTEM) {
+ return true;
+ }
+
+#ifdef ENABLE_BENCHMARK_SUPPORT
+ // some hand-picked atoms to be pushed into the queue
+ switch (atomId) {
+ case 47: // APP_BREADCRUMB_REPORTED for statsd_benchmark purpose
+ return true;
+ default:
+ return false;
+ }
+#endif // ENABLE_BENCHMARK_SUPPORT
+ return false;
+}
diff --git a/lib/libstatssocket/stats_buffer_writer_queue.h b/lib/libstatssocket/stats_buffer_writer_queue.h
new file mode 100644
index 0000000..41afcc7
--- /dev/null
+++ b/lib/libstatssocket/stats_buffer_writer_queue.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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 <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+bool write_buffer_to_statsd_queue(const uint8_t* buffer, size_t size, uint32_t atomId);
+
+bool should_write_via_queue(uint32_t atomId);
+
+__END_DECLS
diff --git a/lib/libstatssocket/stats_buffer_writer_queue_impl.h b/lib/libstatssocket/stats_buffer_writer_queue_impl.h
new file mode 100644
index 0000000..a5c6873
--- /dev/null
+++ b/lib/libstatssocket/stats_buffer_writer_queue_impl.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <queue>
+#include <thread>
+
+class BufferWriterQueue {
+public:
+ constexpr static int kDelayOnFailedWriteMs = 5;
+ constexpr static int kQueueMaxSizeLimit = 4800; // 2X max_dgram_qlen
+
+ BufferWriterQueue();
+ virtual ~BufferWriterQueue();
+
+ bool write(const uint8_t* buffer, size_t size, uint32_t atomId);
+
+ size_t getQueueSize() const;
+
+ void drainQueue();
+
+ struct Cmd {
+ uint8_t* buffer = NULL;
+ int atomId = 0;
+ int size = 0;
+ };
+
+ virtual bool handleCommand(const Cmd& cmd) const;
+
+private:
+ std::condition_variable mCondition;
+ mutable std::mutex mMutex;
+ std::queue<Cmd> mCmdQueue;
+ std::atomic_bool mDoTerminate = false;
+ std::thread mWorkThread;
+
+ static Cmd createWriteBufferCmd(const uint8_t* buffer, size_t size, uint32_t atomId);
+
+ bool pushToQueue(const Cmd& cmd);
+
+ void terminate();
+
+ void processCommands();
+};
diff --git a/lib/libstatssocket/stats_event.c b/lib/libstatssocket/stats_event.c
index 9bb4c52..ade1b93 100644
--- a/lib/libstatssocket/stats_event.c
+++ b/lib/libstatssocket/stats_event.c
@@ -15,10 +15,12 @@
*/
#include "include/stats_event.h"
+
#include <stdlib.h>
#include <string.h>
-#include <time.h>
+
#include "stats_buffer_writer.h"
+#include "utils.h"
#define LOGGER_ENTRY_MAX_PAYLOAD 4068
// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
@@ -81,13 +83,6 @@
size_t bufSize;
};
-static int64_t get_elapsed_realtime_ns() {
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_BOOTTIME, &t);
- return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
-}
-
AStatsEvent* AStatsEvent_obtain() {
AStatsEvent* event = malloc(sizeof(AStatsEvent));
event->lastFieldPos = 0;
diff --git a/lib/libstatssocket/stats_socket_loss_reporter.cpp b/lib/libstatssocket/stats_socket_loss_reporter.cpp
new file mode 100644
index 0000000..5e2d272
--- /dev/null
+++ b/lib/libstatssocket/stats_socket_loss_reporter.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 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 <stats_event.h>
+#include <stats_socket_loss_reporter.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "stats_statsdsocketlog.h"
+#include "utils.h"
+
+StatsSocketLossReporter::StatsSocketLossReporter() : mUid(getuid()) {
+}
+
+StatsSocketLossReporter::~StatsSocketLossReporter() {
+ // try to dump loss stats since there might be pending data which have been not sent earlier
+ // due to:
+ // - cool down timer was active
+ // - no input atoms to trigger loss info dump after cooldown timer expired
+ dumpAtomsLossStats(true);
+}
+
+StatsSocketLossReporter& StatsSocketLossReporter::getInstance() {
+ static StatsSocketLossReporter instance;
+ return instance;
+}
+
+void StatsSocketLossReporter::noteDrop(int32_t error, int32_t atomId) {
+ using namespace android::os::statsdsocket;
+
+ const int64_t currentRealtimeTsNanos = get_elapsed_realtime_ns();
+
+ // The intention is to skip self counting, however the timestamps still need to be updated
+ // to know when was last failed attempt to log atom.
+ // This is required for more accurate cool down timer work
+ if (mFirstTsNanos == 0) {
+ mFirstTsNanos.store(currentRealtimeTsNanos, std::memory_order_relaxed);
+ }
+ mLastTsNanos.store(currentRealtimeTsNanos, std::memory_order_relaxed);
+
+ if (atomId == STATS_SOCKET_LOSS_REPORTED) {
+ // avoid self counting due to write to socket might fail during dumpAtomsLossStats()
+ // also due to mutex is not re-entrant and is already locked by dumpAtomsLossStats() API,
+ // return to avoid deadlock
+ // alternative is to consider std::recursive_mutex
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // using unordered_map is more CPU efficient vs vectors, however will require some
+ // postprocessing before writing into the socket
+ const LossInfoKey key = std::make_pair(error, atomId);
+ auto counterIt = mLossInfo.find(key);
+ if (counterIt != mLossInfo.end()) {
+ ++counterIt->second;
+ } else if (mLossInfo.size() < kMaxAtomTagsCount) {
+ mLossInfo[key] = 1;
+ } else {
+ mOverflowCounter++;
+ }
+}
+
+void StatsSocketLossReporter::dumpAtomsLossStats(bool forceDump) {
+ using namespace android::os::statsdsocket;
+
+ const int64_t currentRealtimeTsNanos = get_elapsed_realtime_ns();
+
+ if (!forceDump && isCooldownTimerActive(currentRealtimeTsNanos)) {
+ // To avoid socket flooding with more STATS_SOCKET_LOSS_REPORTED atoms,
+ // which have high probability of write failures, the cooldown timer approach is applied:
+ // - start cooldown timer for 10us for every failed dump
+ // - before writing STATS_SOCKET_LOSS_REPORTED do check the timestamp to keep some delay
+ return;
+ }
+
+ // intention to hold mutex here during the stats_write() to avoid data copy overhead
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mLossInfo.size() == 0) {
+ return;
+ }
+
+ // populate temp vectors to be written into the socket
+ std::vector<int> errors(mLossInfo.size());
+ std::vector<int> tags(mLossInfo.size());
+ std::vector<int> counts(mLossInfo.size());
+
+ auto lossInfoIt = mLossInfo.begin();
+ for (size_t i = 0; i < mLossInfo.size(); i++, lossInfoIt++) {
+ const LossInfoKey& key = lossInfoIt->first;
+ errors[i] = key.first;
+ tags[i] = key.second;
+ counts[i] = lossInfoIt->second;
+ }
+
+ // below call might lead to socket loss event - intention is to avoid self counting
+ const int ret = stats_write(STATS_SOCKET_LOSS_REPORTED, mUid, mFirstTsNanos, mLastTsNanos,
+ mOverflowCounter, errors, tags, counts);
+ if (ret > 0) {
+ // Otherwise, in case of failure we preserve all socket loss information between dumps.
+ // When above write failed - the socket loss stats are not discarded
+ // and would be re-send during next attempt.
+ mOverflowCounter = 0;
+ mLossInfo.clear();
+
+ mFirstTsNanos.store(0, std::memory_order_relaxed);
+ mLastTsNanos.store(0, std::memory_order_relaxed);
+ }
+ // since the delay before next attempt is significantly larger than this API call
+ // duration it is ok to have correctness of timestamp in a range of 10us
+ startCooldownTimer(currentRealtimeTsNanos);
+}
+
+void StatsSocketLossReporter::startCooldownTimer(int64_t elapsedRealtimeNanos) {
+ mCooldownTimerFinishAtNanos = elapsedRealtimeNanos + kCoolDownTimerDurationNanos;
+}
+
+bool StatsSocketLossReporter::isCooldownTimerActive(int64_t elapsedRealtimeNanos) const {
+ return mCooldownTimerFinishAtNanos > elapsedRealtimeNanos;
+}
diff --git a/lib/libstatssocket/stats_socket_loss_reporter.h b/lib/libstatssocket/stats_socket_loss_reporter.h
new file mode 100644
index 0000000..b857321
--- /dev/null
+++ b/lib/libstatssocket/stats_socket_loss_reporter.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <thread>
+#include <unordered_map>
+
+class StatsSocketLossReporter {
+public:
+ static StatsSocketLossReporter& getInstance();
+
+ void noteDrop(int32_t error, int32_t atomId);
+
+ /**
+ * @brief Dump loss info into statsd as a STATS_SOCKET_LOSS_REPORTED atom instance
+ *
+ * @param forceDump skip cooldown timer evaluation
+ * @return true if atom have been written into the socket successfully
+ * @return false if atom have been written into the socket with an error
+ */
+ void dumpAtomsLossStats(bool forceDump = false);
+
+ ~StatsSocketLossReporter();
+
+private:
+ StatsSocketLossReporter();
+
+ void startCooldownTimer(int64_t elapsedRealtimeNanos);
+ bool isCooldownTimerActive(int64_t elapsedRealtimeNanos) const;
+
+ const int32_t mUid;
+ std::atomic_int64_t mFirstTsNanos = 0;
+ std::atomic_int64_t mLastTsNanos = 0;
+ std::atomic_int64_t mCooldownTimerFinishAtNanos = 0;
+
+ // Loss info data will be logged to statsd as a regular AStatsEvent
+ // which means it needs to obey event size limitations (4kB)
+ // for N tag ids the loss info might take N * 12 + 8 + 8 + 4 bytes
+ // defining guardrail as a 100 tag ids should limit the atom size to
+ // 100 * 12 + 8 + 8 + 4 ~ 1.2kB
+ const size_t kMaxAtomTagsCount = 100;
+
+ const int64_t kCoolDownTimerDurationNanos = 10 * 1000 * 1000; // 10ms
+
+ struct HashPair final {
+ template <class TFirst, class TSecond>
+ size_t operator()(const std::pair<TFirst, TSecond>& p) const noexcept {
+ uintmax_t hash = std::hash<TFirst>{}(p.first);
+ hash <<= sizeof(uintmax_t) * 4;
+ hash ^= std::hash<TSecond>{}(p.second);
+ return std::hash<uintmax_t>{}(hash);
+ }
+ };
+
+ // guards access to below mLossInfo
+ mutable std::mutex mMutex;
+
+ using LossInfoKey = std::pair<int, int>; // [error, tag]
+
+ // Represents loss info as a counter per [error, tag] pair
+ std::unordered_map<LossInfoKey, int, HashPair> mLossInfo;
+
+ // tracks guardrail kMaxAtomTagsCount hit count
+ int32_t mOverflowCounter = 0;
+};
diff --git a/lib/libstatssocket/statsd_writer.cpp b/lib/libstatssocket/statsd_writer.cpp
index a3c2dd6..4133e26 100644
--- a/lib/libstatssocket/statsd_writer.cpp
+++ b/lib/libstatssocket/statsd_writer.cpp
@@ -33,6 +33,8 @@
#include <time.h>
#include <unistd.h>
+#include "stats_socket_loss_reporter.h"
+#include "utils.h"
// Compatibility shims for glibc-2.17 in the Android tree.
#ifndef __BIONIC__
@@ -105,7 +107,7 @@
if (sock < 0) {
ret = -errno;
} else {
- int sndbuf = 1 * 1024 * 1024; // set max send buffer size 1MB
+ const int sndbuf = 2 * 1024 * 1024; // set max send buffer size 2MB
socklen_t bufLen = sizeof(sndbuf);
// SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
// Proceed to connect even setsockopt fails.
@@ -166,6 +168,8 @@
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
+
+ StatsSocketLossReporter::getInstance().noteDrop(error, tag);
}
static int statsdIsClosed() {
@@ -239,6 +243,10 @@
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ } else {
+ // try to send socket loss info only when socket connection established
+ // and it is proved by previous write that socket is available
+ StatsSocketLossReporter::getInstance().dumpAtomsLossStats();
}
}
}
diff --git a/lib/libstatssocket/tests/stats_buffer_writer_queue_test.cpp b/lib/libstatssocket/tests/stats_buffer_writer_queue_test.cpp
new file mode 100644
index 0000000..008009b
--- /dev/null
+++ b/lib/libstatssocket/tests/stats_buffer_writer_queue_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+
+#include "stats_buffer_writer_queue_impl.h"
+#include "stats_event.h"
+#include "utils.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Return;
+using testing::StrictMock;
+
+namespace {
+
+constexpr static int WAIT_MS = 100;
+
+static AStatsEvent* generateTestEvent() {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 5);
+ AStatsEvent_write(event);
+ return event;
+}
+
+class BasicBufferWriterQueueMock : public BufferWriterQueue {
+public:
+ BasicBufferWriterQueueMock() = default;
+ MOCK_METHOD(bool, handleCommand, (const BufferWriterQueue::Cmd& cmd), (const override));
+};
+
+typedef StrictMock<BasicBufferWriterQueueMock> BufferWriterQueueMock;
+
+} // namespace
+
+TEST(StatsBufferWriterQueueTest, TestWriteSuccess) {
+ AStatsEvent* event = generateTestEvent();
+
+ size_t eventBufferSize = 0;
+ const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
+ EXPECT_GE(eventBufferSize, 0);
+ EXPECT_TRUE(buffer != nullptr);
+
+ const uint32_t atomId = AStatsEvent_getAtomId(event);
+
+ BufferWriterQueueMock queue;
+ EXPECT_CALL(queue, handleCommand(_)).WillOnce(Return(true));
+ // simulate failed write to stats socket
+ const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
+ AStatsEvent_release(event);
+ EXPECT_TRUE(addedToQueue);
+ // to yeld to the queue worker thread
+ std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
+
+ queue.drainQueue();
+ EXPECT_EQ(queue.getQueueSize(), 0);
+}
+
+TEST(StatsBufferWriterQueueTest, TestWriteOverflow) {
+ AStatsEvent* event = generateTestEvent();
+
+ size_t eventBufferSize = 0;
+ const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
+ EXPECT_GE(eventBufferSize, 0);
+ EXPECT_TRUE(buffer != nullptr);
+
+ const uint32_t atomId = AStatsEvent_getAtomId(event);
+
+ BufferWriterQueueMock queue;
+ EXPECT_CALL(queue, handleCommand(_)).WillRepeatedly(Return(false));
+ // simulate failed write to stats socket
+ for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
+ const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
+ EXPECT_TRUE(addedToQueue);
+ }
+
+ const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
+ AStatsEvent_release(event);
+ EXPECT_FALSE(addedToQueue);
+
+ EXPECT_EQ(queue.getQueueSize(), BufferWriterQueueMock::kQueueMaxSizeLimit);
+
+ queue.drainQueue();
+ EXPECT_EQ(queue.getQueueSize(), 0);
+}
+
+TEST(StatsBufferWriterQueueTest, TestSleepOnOverflow) {
+ AStatsEvent* event = generateTestEvent();
+
+ size_t eventBufferSize = 0;
+ const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
+ EXPECT_GE(eventBufferSize, 0);
+ EXPECT_TRUE(buffer != nullptr);
+
+ const uint32_t atomId = AStatsEvent_getAtomId(event);
+
+ std::vector<int64_t> attemptsTs;
+
+ BufferWriterQueueMock queue;
+ ON_CALL(queue, handleCommand(_))
+ .WillByDefault(DoAll(
+ [&attemptsTs](const BufferWriterQueue::Cmd&) {
+ // store timestamp for command handler invocations
+ attemptsTs.push_back(get_elapsed_realtime_ns());
+ return false;
+ },
+ Return(false)));
+
+ EXPECT_CALL(queue, handleCommand(_)).Times(AnyNumber());
+
+ // simulate failed write to stats socket to fill the queue
+ for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
+ const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
+ EXPECT_TRUE(addedToQueue);
+ }
+ AStatsEvent_release(event);
+ // to yeld to the queue worker thread
+ std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
+
+ // to eliminate extra commands handling on the worker thread
+ queue.drainQueue();
+ EXPECT_EQ(queue.getQueueSize(), 0);
+
+ EXPECT_GE(attemptsTs.size(), 2);
+ for (int i = 0; i < attemptsTs.size() - 1; i++) {
+ EXPECT_GE(attemptsTs[i + 1] - attemptsTs[i],
+ BufferWriterQueueMock::kDelayOnFailedWriteMs * 1000000);
+ }
+}
+
+TEST(StatsBufferWriterQueueTest, TestTerminateNonEmptyQueue) {
+ AStatsEvent* event = generateTestEvent();
+
+ size_t eventBufferSize = 0;
+ const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
+ EXPECT_GE(eventBufferSize, 0);
+ EXPECT_TRUE(buffer != nullptr);
+
+ const uint32_t atomId = AStatsEvent_getAtomId(event);
+
+ BufferWriterQueueMock queue;
+ EXPECT_CALL(queue, handleCommand(_)).WillRepeatedly(Return(false));
+ // simulate failed write to stats socket
+ for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
+ const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
+ EXPECT_TRUE(addedToQueue);
+ }
+ AStatsEvent_release(event);
+ EXPECT_EQ(queue.getQueueSize(), BufferWriterQueueMock::kQueueMaxSizeLimit);
+ queue.drainQueue();
+ EXPECT_EQ(queue.getQueueSize(), 0);
+}
diff --git a/lib/libstatssocket/tests/stats_socketlog_test.cpp b/lib/libstatssocket/tests/stats_socketlog_test.cpp
new file mode 100644
index 0000000..b4bf723
--- /dev/null
+++ b/lib/libstatssocket/tests/stats_socketlog_test.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 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 "stats_statsdsocketlog.h"
+#include "utils.h"
+
+TEST(StatsStatsdSocketLog, TestToSocketLossError) {
+ using namespace android::os::statsdsocket;
+
+ EXPECT_EQ(-EAGAIN, toSocketLossError(EAGAIN));
+ EXPECT_EQ(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_UNKNOWN,
+ toSocketLossError(ERANGE));
+}
diff --git a/lib/libstatssocket/utils.cpp b/lib/libstatssocket/utils.cpp
new file mode 100644
index 0000000..8f8775f
--- /dev/null
+++ b/lib/libstatssocket/utils.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023, 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 "utils.h"
+
+#include <errno.h>
+#include <time.h>
+
+#include "stats_statsdsocketlog.h"
+
+int64_t get_elapsed_realtime_ns() {
+ struct timespec t;
+ t.tv_sec = t.tv_nsec = 0;
+ clock_gettime(CLOCK_BOOTTIME, &t);
+ return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
+}
+
+int toSocketLossError(int errno_code) {
+ using namespace android::os::statsdsocket;
+
+ // compile time checks
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EPERM == -EPERM,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EINTR == -EINTR,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EIO == -EIO,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EBADF == -EBADF,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(
+ STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EAGAIN == -EAGAIN,
+ "Socket Loss Error codes mapping function needs to be updated"); // same as EWOULDBLOCK
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EFAULT == -EFAULT,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_ENODEV == -ENODEV,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EINVAL == -EINVAL,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EFBIG == -EFBIG,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_ENOSPC == -ENOSPC,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EPIPE == -EPIPE,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EDESTADDRREQ ==
+ -EDESTADDRREQ,
+ "Socket Loss Error codes mapping function needs to be updated");
+ static_assert(STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_ON_WRITE_EDQUOT == -EDQUOT,
+ "Socket Loss Error codes mapping function needs to be updated");
+
+ switch (errno_code) {
+ case EPERM:
+ case EINTR:
+ case EIO:
+ case EBADF:
+ case EAGAIN:
+ case EFAULT:
+ case ENODEV:
+ case EINVAL:
+ case EFBIG:
+ case ENOSPC:
+ case EPIPE:
+ case EDESTADDRREQ:
+ case EDQUOT:
+ return -errno_code;
+ default:
+ return STATS_SOCKET_LOSS_REPORTED__ERRORS__SOCKET_LOSS_ERROR_UNKNOWN;
+ }
+}
diff --git a/lib/libstatssocket/utils.h b/lib/libstatssocket/utils.h
new file mode 100644
index 0000000..1eada99
--- /dev/null
+++ b/lib/libstatssocket/utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023, 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 <stddef.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+int64_t get_elapsed_realtime_ns();
+
+int toSocketLossError(int errno_code);
+
+__END_DECLS
diff --git a/perfetto/README.md b/perfetto/README.md
new file mode 100644
index 0000000..765b743
--- /dev/null
+++ b/perfetto/README.md
@@ -0,0 +1,16 @@
+To push config to be used at device boot automatically
+
+```
+adb push perfetto/atrace.pbtxt /data/misc/perfetto-configs/boottrace.pbtxt
+adb shell setprop persist.debug.perfetto.boottrace 1
+```
+
+The output trace will be written at /data/misc/perfetto-traces/boottrace.perfetto-trace.
+The file will be removed before a new trace is started.
+
+```
+adb pull /data/misc/perfetto-traces/boottrace.perfetto-trace
+```
+
+# Links
+- https://perfetto.dev/docs/case-studies/android-boot-tracing
diff --git a/perfetto/atrace.pbtxt b/perfetto/atrace.pbtxt
new file mode 100644
index 0000000..e5c512c
--- /dev/null
+++ b/perfetto/atrace.pbtxt
@@ -0,0 +1,30 @@
+buffers: {
+ size_kb: 522240
+ fill_policy: DISCARD
+}
+buffers: {
+ size_kb: 8192
+ fill_policy: RING_BUFFER
+}
+data_sources: {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 1
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ atrace_categories: "binder_driver"
+ atrace_categories: "binder_lock"
+ atrace_categories: "sm"
+ atrace_categories: "ss"
+ atrace_apps: "*"
+ }
+ }
+}
+duration_ms: 180000
diff --git a/service/java/com/android/server/stats/StatsCompanion.java b/service/java/com/android/server/stats/StatsCompanion.java
index dc477a5..d0954ea 100644
--- a/service/java/com/android/server/stats/StatsCompanion.java
+++ b/service/java/com/android/server/stats/StatsCompanion.java
@@ -112,6 +112,7 @@
private static final int CODE_DATA_BROADCAST = 1;
private static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
private static final int CODE_SUBSCRIBER_BROADCAST = 1;
+ private static final int CODE_RESTRICTED_METRICS_BROADCAST = 1;
private final PendingIntent mPendingIntent;
private final Context mContext;
@@ -184,5 +185,24 @@
+ "; presumably it had been cancelled.");
}
}
+
+ @Override
+ public void sendRestrictedMetricsChangedBroadcast(long[] metricIds) {
+ enforceStatsdCallingUid();
+ Intent intent = new Intent();
+ intent.putExtra(StatsManager.EXTRA_STATS_RESTRICTED_METRIC_IDS, metricIds);
+ try {
+ mPendingIntent.send(mContext, CODE_RESTRICTED_METRICS_BROADCAST, intent, null,
+ null);
+ if (DEBUG) {
+ Log.d(TAG,
+ "Sent restricted metrics broadcast with metric ids " + Arrays.toString(
+ metricIds));
+ }
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG,
+ "Unable to send restricted metrics changed broadcast using PendingIntent");
+ }
+ }
}
}
diff --git a/service/java/com/android/server/stats/StatsCompanionService.java b/service/java/com/android/server/stats/StatsCompanionService.java
index 360889b..6e920c5 100644
--- a/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/service/java/com/android/server/stats/StatsCompanionService.java
@@ -742,18 +742,21 @@
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
+ mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null,
+ /* scheduler= */ mHandler);
// Setup receiver for user initialize (which happens once for a new user)
// and if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
+ mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null,
+ /* scheduler= */ mHandler);
// Setup receiver for device reboots or shutdowns.
filter = new IntentFilter(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
+ mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null,
+ /* scheduler= */ mHandler);
// Register listener for statsd_java properties updates.
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_STATSD_JAVA,
diff --git a/service/java/com/android/server/stats/StatsManagerService.java b/service/java/com/android/server/stats/StatsManagerService.java
index d55a12a..a25c91f 100644
--- a/service/java/com/android/server/stats/StatsManagerService.java
+++ b/service/java/com/android/server/stats/StatsManagerService.java
@@ -26,6 +26,7 @@
import android.os.Binder;
import android.os.IPullAtomCallback;
import android.os.IStatsManagerService;
+import android.os.IStatsQueryCallback;
import android.os.IStatsd;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
@@ -38,6 +39,7 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -72,6 +74,9 @@
@GuardedBy("mLock")
private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
new ArrayMap<>();
+ @GuardedBy("mLock")
+ private ArrayMap<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>>
+ mRestrictedMetricsPirMap = new ArrayMap<>();
public StatsManagerService(Context context) {
super();
@@ -110,6 +115,39 @@
}
}
+ private static class ConfigKeyWithPackage {
+ private final String mConfigPackage;
+ private final long mConfigId;
+
+ ConfigKeyWithPackage(String configPackage, long configId) {
+ mConfigPackage = configPackage;
+ mConfigId = configId;
+ }
+
+ public String getConfigPackage() {
+ return mConfigPackage;
+ }
+
+ public long getConfigId() {
+ return mConfigId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConfigPackage, mConfigId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ConfigKeyWithPackage) {
+ ConfigKeyWithPackage other = (ConfigKeyWithPackage) obj;
+ return this.mConfigPackage.equals(other.getConfigPackage())
+ && this.mConfigId == other.getConfigId();
+ }
+ return false;
+ }
+ }
+
private static class PullerKey {
private final int mUid;
private final int mAtomTag;
@@ -443,23 +481,27 @@
@Override
public void getDataFd(long key, String packageName, ParcelFileDescriptor writeFd)
throws IllegalStateException, RemoteException {
- enforceDumpAndUsageStatsPermission(packageName);
- PowerManager powerManager = mContext.getSystemService(PowerManager.class);
- PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- /*tag=*/ StatsManagerService.class.getCanonicalName());
- final int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- wl.acquire();
- try {
- IStatsd statsd = waitForStatsd();
- if (statsd != null) {
- // create another intermediate pipe for statsd
- getDataWithFd(statsd, key, callingUid, writeFd);
- return;
+ try (writeFd) {
+ enforceDumpAndUsageStatsPermission(packageName);
+ PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ /*tag=*/ StatsManagerService.class.getCanonicalName());
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ wl.acquire();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ // will create another intermediate pipe for statsd
+ getDataFdFromStatsd(statsd, key, callingUid, writeFd.getFileDescriptor());
+ return;
+ }
+ } finally {
+ wl.release();
+ Binder.restoreCallingIdentity(token);
}
- } finally {
- wl.release();
- Binder.restoreCallingIdentity(token);
+ } catch (IOException e) {
+ throw new IllegalStateException("IOException during getDataFd() call", e);
}
throw new IllegalStateException("Failed to connect to statsd to getDataFd");
}
@@ -506,10 +548,97 @@
throw new IllegalStateException("Failed to connect to statsd to removeConfig");
}
+ @Override
+ public long[] setRestrictedMetricsChangedOperation(PendingIntent pendingIntent,
+ long configId, String configPackage) {
+ enforceRestrictedStatsPermission();
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+ ConfigKeyWithPackage key = new ConfigKeyWithPackage(configPackage, configId);
+ // Add the PIR to a map so we can re-register if statsd is unavailable.
+ synchronized (mLock) {
+ ArrayMap<Integer, PendingIntentRef> innerMap = mRestrictedMetricsPirMap.getOrDefault(
+ key, new ArrayMap<>());
+ innerMap.put(callingUid, pir);
+ mRestrictedMetricsPirMap.put(key, innerMap);
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ return statsd.setRestrictedMetricsChangedOperation(configId, configPackage, pir,
+ callingUid);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setRestrictedMetricsChangedOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return new long[]{};
+ }
+
+ @Override
+ public void removeRestrictedMetricsChangedOperation(long configId, String configPackage) {
+ enforceRestrictedStatsPermission();
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ ConfigKeyWithPackage key = new ConfigKeyWithPackage(configPackage, configId);
+ synchronized (mLock) {
+ ArrayMap<Integer, PendingIntentRef> innerMap = mRestrictedMetricsPirMap.getOrDefault(
+ key, new ArrayMap<>());
+ innerMap.remove(callingUid);
+ if (innerMap.isEmpty()) {
+ mRestrictedMetricsPirMap.remove(key);
+ }
+ }
+ try {
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd != null) {
+ statsd.removeRestrictedMetricsChangedOperation(configId, configPackage, callingUid);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to removeRestrictedMetricsChangedOperation with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void querySql(String sqlQuery, int minSqlClientVersion, byte[] policyConfig,
+ IStatsQueryCallback queryCallback, long configKey, String configPackage) {
+ int callingUid = Binder.getCallingUid();
+ enforceRestrictedStatsPermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IStatsd statsd = waitForStatsd();
+ if (statsd != null) {
+ statsd.querySql(
+ sqlQuery,
+ minSqlClientVersion,
+ policyConfig,
+ queryCallback,
+ configKey,
+ configPackage,
+ callingUid);
+ } else {
+ queryCallback.sendFailure("Could not connect to statsd from system server");
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void setStatsCompanionService(StatsCompanionService statsCompanionService) {
mStatsCompanionService = statsCompanionService;
}
+ /** Checks that the caller has READ_RESTRICTED_STATS permission. */
+ private void enforceRestrictedStatsPermission() {
+ mContext.enforceCallingPermission(Manifest.permission.READ_RESTRICTED_STATS, null);
+ }
+
/**
* Checks that the caller has both DUMP and PACKAGE_USAGE_STATS permissions. Also checks that
* the caller has USAGE_STATS_PERMISSION_OPS for the specified packageName if it is not null.
@@ -618,6 +747,8 @@
registerAllDataFetchOperations(statsd);
registerAllActiveConfigsChangedOperations(statsd);
registerAllBroadcastSubscribers(statsd);
+ registerAllRestrictedMetricsChangedOperations(statsd);
+ // TODO (b/269419485): register all restricted metric operations.
} catch (RemoteException e) {
Log.e(TAG, "StatsManager failed to (re-)register data with statsd");
} finally {
@@ -686,7 +817,7 @@
}
for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
- mBroadcastSubscriberPirMap.entrySet()) {
+ broadcastSubscriberCopy.entrySet()) {
ConfigKey configKey = entry.getKey();
for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
@@ -695,6 +826,29 @@
}
}
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllRestrictedMetricsChangedOperations(IStatsd statsd)
+ throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a deep copy of
+ // the data with the lock held before iterating through the map.
+ ArrayMap<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> restrictedMetricsCopy =
+ new ArrayMap<>();
+ synchronized (mLock) {
+ for (Map.Entry<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> entry :
+ mRestrictedMetricsPirMap.entrySet()) {
+ restrictedMetricsCopy.put(entry.getKey(), new ArrayMap(entry.getValue()));
+ }
+ }
+
+ for (Map.Entry<ConfigKeyWithPackage, ArrayMap<Integer, PendingIntentRef>> entry :
+ restrictedMetricsCopy.entrySet()) {
+ ConfigKeyWithPackage configKey = entry.getKey();
+ for (Map.Entry<Integer, PendingIntentRef> uidEntry : entry.getValue().entrySet()) {
+ statsd.setRestrictedMetricsChangedOperation(configKey.getConfigId(),
+ configKey.getConfigPackage(), uidEntry.getValue(), uidEntry.getKey());
+ }
+ }
+ }
private static final int CHUNK_SIZE = 1024 * 64; // 64 kB
/**
@@ -703,8 +857,8 @@
* No exception handling in this API since they will not be propagated back to caller
* to make debugging easier, since this API part of oneway binder call flow.
*/
- public static void getDataWithFd(IStatsd service, long configKey, int callingUid,
- ParcelFileDescriptor dstFd)
+ private static void getDataFdFromStatsd(IStatsd service, long configKey, int callingUid,
+ FileDescriptor dstFd)
throws IllegalStateException, RemoteException {
ParcelFileDescriptor[] pipe;
try {
@@ -728,10 +882,13 @@
throw new IllegalStateException("Failed to close FD.", e);
}
+ // There are many possible exceptions below, to not forget close pipe descriptors
+ // wrapping in the try-with-resources statement
try (FileInputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(readFd);
DataInputStream srcDataStream = new DataInputStream(inputStream);
- FileOutputStream outStream = new ParcelFileDescriptor.AutoCloseOutputStream(dstFd);
+ FileOutputStream outStream = new FileOutputStream(dstFd);
DataOutputStream dstDataStream = new DataOutputStream(outStream)) {
+
byte[] chunk = new byte[CHUNK_SIZE];
int chunkLen = 0;
int readBytes = 0;
diff --git a/statsd/Android.bp b/statsd/Android.bp
index 181a4e3..2199a5c 100644
--- a/statsd/Android.bp
+++ b/statsd/Android.bp
@@ -25,7 +25,34 @@
"-Wno-deprecated-declarations",
"-Wthread-safety",
],
+ tidy: true,
+ tidy_flags: [
+ // Only check our headers
+ "-header-filter=^packages/modules/StatsD/statsd",
+ ],
+ tidy_checks: [
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "google-*",
+ "misc-*",
+ "performance-*",
+ "-bugprone-narrowing-conversions", // lots of unsigned -> int conversions
+ "-cert-err34-c",
+ "-cert-msc30-c", // warning: rand() has limited randomness; use C++11 random library
+ "-cert-msc50-cpp", // warning: rand() has limited randomness; use C++11 random library
+ ],
+ tidy_checks_as_errors: [
+ "android-*",
+ "bugprone-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "google-*",
+ "misc-*",
+ "performance-*",
+ ],
srcs: [
"src/active_config_list.proto",
"src/anomaly/AlarmMonitor.cpp",
@@ -48,6 +75,7 @@
"src/external/StatsPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/TrainInfoPuller.cpp",
+ "src/external/Uprobestats.cpp",
"src/FieldValue.cpp",
"src/flags/FlagProvider.cpp",
"src/guardrail/StatsdStats.cpp",
@@ -55,6 +83,7 @@
"src/HashableDimensionKey.cpp",
"src/logd/LogEvent.cpp",
"src/logd/LogEventQueue.cpp",
+ "src/logd/logevent_util.cpp",
"src/matchers/CombinationAtomMatchingTracker.cpp",
"src/matchers/EventMatcherWizard.cpp",
"src/matchers/matcher_util.cpp",
@@ -65,6 +94,7 @@
"src/metrics/duration_helper/OringDurationTracker.cpp",
"src/metrics/DurationMetricProducer.cpp",
"src/metrics/EventMetricProducer.cpp",
+ "src/metrics/RestrictedEventMetricProducer.cpp",
"src/metrics/GaugeMetricProducer.cpp",
"src/metrics/KllMetricProducer.cpp",
"src/metrics/MetricProducer.cpp",
@@ -81,6 +111,7 @@
"src/state/StateManager.cpp",
"src/state/StateTracker.cpp",
"src/stats_log_util.cpp",
+ "src/stats_policy_config.proto",
"src/statscompanion_util.cpp",
"src/statsd_config.proto",
"src/statsd_metadata.proto",
@@ -92,6 +123,9 @@
"src/subscriber/SubscriberReporter.cpp",
"src/uid_data.proto",
"src/utils/MultiConditionTrigger.cpp",
+ "src/utils/DbUtils.cpp",
+ "src/utils/Regex.cpp",
+ "src/utils/RestrictedPolicyManager.cpp",
"src/utils/ShardOffsetProvider.cpp",
],
@@ -110,11 +144,13 @@
"libutils",
"server_configurable_flags",
"statsd-aidl-ndk",
+ "libsqlite_static_noicu",
],
shared_libs: [
"libbinder_ndk",
"libincident",
"liblog",
+ "libstatssocket",
],
header_libs: [
"libgtest_prod_headers",
@@ -124,7 +160,10 @@
genrule {
name: "statslog_statsd.h",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
+ cmd: "$(location stats-log-api-gen) " +
+ "--header $(genDir)/statslog_statsd.h " +
+ "--module statsd " +
+ "--namespace android,os,statsd,util",
out: [
"statslog_statsd.h",
],
@@ -133,7 +172,11 @@
genrule {
name: "statslog_statsd.cpp",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
+ cmd: "$(location stats-log-api-gen) " +
+ "--cpp $(genDir)/statslog_statsd.cpp " +
+ "--module statsd " +
+ "--namespace android,os,statsd,util " +
+ "--importHeader statslog_statsd.h",
out: [
"statslog_statsd.cpp",
],
@@ -142,7 +185,10 @@
genrule {
name: "statslog_statsdtest.h",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util",
+ cmd: "$(location stats-log-api-gen) " +
+ "--header $(genDir)/statslog_statsdtest.h " +
+ "--module statsdtest " +
+ "--namespace android,os,statsd,util",
out: [
"statslog_statsdtest.h",
],
@@ -151,7 +197,11 @@
genrule {
name: "statslog_statsdtest.cpp",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h",
+ cmd: "$(location stats-log-api-gen) " +
+ "--cpp $(genDir)/statslog_statsdtest.cpp " +
+ "--module statsdtest " +
+ "--namespace android,os,statsd,util " +
+ "--importHeader statslog_statsdtest.h",
out: [
"statslog_statsdtest.cpp",
],
@@ -208,25 +258,12 @@
// "-O0",
],
- product_variables: {
- eng: {
- // Enable sanitizer ONLY on eng builds
- //sanitize: {
- // address: true,
- //},
- },
- },
-
proto: {
type: "lite",
static: true,
},
stl: "libc++_static",
- shared_libs: [
- "libstatssocket",
- ],
-
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
@@ -234,13 +271,59 @@
min_sdk_version: "30",
}
+cc_defaults {
+ name: "statsd_test_defaults",
+ defaults: ["statsd_defaults"],
+ srcs: [
+ // atom_field_options.proto needs field_options.proto, but that is
+ // not included in libprotobuf-cpp-lite, so compile it here.
+ ":libprotobuf-internal-protos",
+ ":libstats_internal_protos",
+
+ "src/shell/shell_data.proto",
+ "src/stats_log.proto",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-function",
+ "-Wno-unused-parameter",
+ "-Wno-unused-variable",
+ ],
+ static_libs: [
+ "libgmock",
+ "libstatslog_statsdtest",
+ ],
+ proto: {
+ type: "lite",
+ include_dirs: [
+ "external/protobuf/src",
+ "frameworks/proto_logging/stats",
+ ],
+ static: true,
+ },
+}
+
+cc_library_static {
+ name: "libstats_test_utils",
+ defaults: ["statsd_test_defaults"],
+ srcs: [
+ "tests/statsd_test_util.cpp",
+ ],
+ tidy_timeout_srcs: [
+ "tests/statsd_test_util.cpp",
+ ],
+}
+
// ==============
// statsd_test
// ==============
cc_test {
name: "statsd_test",
- defaults: ["statsd_defaults"],
+ defaults: ["statsd_test_defaults"],
test_suites: [
"device-tests",
"mts-statsd",
@@ -259,16 +342,6 @@
},
},
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-enum-compare",
- "-Wno-missing-field-initializers",
- "-Wno-unused-function",
- "-Wno-unused-parameter",
- "-Wno-unused-variable",
- ],
-
require_root: true,
tidy_timeout_srcs: [
@@ -298,23 +371,16 @@
"tests/metrics/MaxDurationTracker_test.cpp",
"tests/metrics/NumericValueMetricProducer_test.cpp",
"tests/metrics/OringDurationTracker_test.cpp",
+ "tests/metrics/RestrictedEventMetricProducer_test.cpp",
"tests/MetricsManager_test.cpp",
"tests/metrics/parsing_utils/config_update_utils_test.cpp",
"tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
"tests/state/StateTracker_test.cpp",
- "tests/statsd_test_util.cpp",
"tests/StatsLogProcessor_test.cpp",
"tests/UidMap_test.cpp",
],
srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
- ":libstats_internal_protos",
-
- "src/shell/shell_data.proto",
- "src/stats_log.proto",
"tests/AlarmMonitor_test.cpp",
"tests/anomaly/AlarmTracker_test.cpp",
"tests/anomaly/AnomalyTracker_test.cpp",
@@ -338,6 +404,9 @@
"tests/e2e/MetricActivation_e2e_test.cpp",
"tests/e2e/MetricConditionLink_e2e_test.cpp",
"tests/e2e/PartialBucket_e2e_test.cpp",
+ "tests/e2e/RestrictedConfig_e2e_test.cpp",
+ "tests/e2e/RestrictedEventMetric_e2e_test.cpp",
+ "tests/e2e/StringReplace_e2e_test.cpp",
"tests/e2e/ValueMetric_pull_e2e_test.cpp",
"tests/e2e/WakelockDuration_e2e_test.cpp",
"tests/external/puller_util_test.cpp",
@@ -362,6 +431,7 @@
"tests/metrics/metrics_test_helper.cpp",
"tests/metrics/OringDurationTracker_test.cpp",
"tests/metrics/NumericValueMetricProducer_test.cpp",
+ "tests/metrics/RestrictedEventMetricProducer_test.cpp",
"tests/metrics/parsing_utils/config_update_utils_test.cpp",
"tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
"tests/subscriber/SubscriberReporter_test.cpp",
@@ -369,7 +439,6 @@
"tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
"tests/state/StateTracker_test.cpp",
- "tests/statsd_test_util.cpp",
"tests/statsd_test_util_test.cpp",
"tests/SocketListener_test.cpp",
"tests/StatsLogProcessor_test.cpp",
@@ -377,25 +446,16 @@
"tests/storage/StorageManager_test.cpp",
"tests/UidMap_test.cpp",
"tests/utils/MultiConditionTrigger_test.cpp",
+ "tests/utils/DbUtils_test.cpp",
],
static_libs: [
- "libgmock",
"libstatsgtestmatchers",
- "libstatslog_statsdtest",
- "libstatssocket_private",
+ "libstats_test_utils",
],
- proto: {
- type: "lite",
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- static: true,
- },
min_sdk_version: "30",
-
+ test_for: ["com.android.os.statsd"],
}
//#############################
@@ -404,14 +464,11 @@
cc_benchmark {
name: "statsd_benchmark",
- defaults: ["statsd_defaults"],
+ defaults: ["statsd_test_defaults"],
srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
- ":libstats_internal_protos",
-
+ "benchmark/data_structures_benchmark.cpp",
+ "benchmark/db_benchmark.cpp",
"benchmark/duration_metric_benchmark.cpp",
"benchmark/filter_value_benchmark.cpp",
"benchmark/get_dimensions_for_condition_benchmark.cpp",
@@ -419,43 +476,23 @@
"benchmark/log_event_benchmark.cpp",
"benchmark/log_event_filter_benchmark.cpp",
"benchmark/main.cpp",
- "benchmark/metric_util.cpp",
+ "benchmark/on_log_event_benchmark.cpp",
"benchmark/stats_write_benchmark.cpp",
- "src/stats_log.proto",
+ "benchmark/loss_info_container_benchmark.cpp",
+ "benchmark/string_transform_benchmark.cpp",
],
- proto: {
- type: "lite",
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- },
-
cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-unused-variable",
- "-Wno-unused-function",
-
// Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
"-Wno-varargs",
],
static_libs: [
- "libplatformprotos",
- "libstatssocket_private",
+ "libgtest",
+ "libstats_test_utils",
],
- shared_libs: [
- "libprotobuf-cpp-lite",
- "libstatslog",
- ],
-
- header_libs: [
- "libgtest_prod_headers",
- ],
+ test_for: ["com.android.os.statsd"],
}
// ==== java proto device library (for test only) ==============================
@@ -547,6 +584,68 @@
],
}
+cc_fuzz {
+ name: "statsd_service_fuzzer",
+ defaults: [
+ "statsd_defaults",
+ "service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
+ ],
+ srcs: [
+ "fuzzers/statsd_service_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libstatssocket",
+ "libvndksupport",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ fuzz_config: {
+ triage_assignee: "waghpawan@google.com",
+ cc: [
+ "singhtejinder@google.com",
+ "sharaienko@google.com",
+ ],
+ },
+ proto: {
+ type: "lite",
+ static: true,
+ },
+}
+
+cc_fuzz {
+ name: "statsd_fuzzer",
+ defaults: [
+ "statsd_defaults",
+ ],
+ srcs: [
+ "fuzzers/statsd_socket_data_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libstatssocket",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ fuzz_config: {
+ cc: [
+ "singhtejinder@google.com",
+ "sharaienko@google.com",
+ ],
+ },
+ proto: {
+ type: "lite",
+ static: true,
+ },
+}
+
// Filegroup for subscription protos.
filegroup {
name: "libstats_subscription_protos",
@@ -557,4 +656,3 @@
"src/shell/shell_data.proto",
],
}
-
diff --git a/statsd/benchmark/data_structures_benchmark.cpp b/statsd/benchmark/data_structures_benchmark.cpp
new file mode 100644
index 0000000..5ee451c
--- /dev/null
+++ b/statsd/benchmark/data_structures_benchmark.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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 <cstdlib>
+#include <ctime>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+template <typename ContainerType>
+void benchmarkFunctionForVector(std::vector<ContainerType>& vec, int capacity) {
+ ContainerType result = false;
+ for (int i = 0; i < capacity; i++) {
+ vec[i] = !result;
+ result = !result;
+ }
+
+ int resultInt = 0;
+ for (int i = 0; i < capacity; i++) {
+ resultInt += vec[i];
+ }
+
+ // Make sure the variable is not optimized away by compiler
+ benchmark::DoNotOptimize(vec);
+ benchmark::DoNotOptimize(resultInt);
+}
+
+template <typename ContainerType>
+void benchmarkStdFillForVector(std::vector<ContainerType>& vec, int capacity) {
+ std::fill(vec.begin(), vec.end(), true);
+ int resultInt = 0;
+ for (int i = 0; i < capacity; i++) {
+ resultInt += vec[i];
+ }
+
+ // Make sure the variable is not optimized away by compiler
+ benchmark::DoNotOptimize(vec);
+ benchmark::DoNotOptimize(resultInt);
+}
+
+} // namespace
+
+static void BM_BasicVectorBoolUsage(benchmark::State& state) {
+ const int capacity = state.range(0);
+ std::vector<bool> vec(capacity);
+
+ while (state.KeepRunning()) {
+ benchmarkFunctionForVector<bool>(vec, capacity);
+ }
+}
+BENCHMARK(BM_BasicVectorBoolUsage)->Args({5})->Args({10})->Args({20})->Args({50})->Args({100});
+
+static void BM_VectorBoolStdFill(benchmark::State& state) {
+ const int capacity = state.range(0);
+ std::vector<bool> vec(capacity);
+
+ while (state.KeepRunning()) {
+ benchmarkStdFillForVector<bool>(vec, capacity);
+ }
+}
+BENCHMARK(BM_VectorBoolStdFill)->Args({5})->Args({10})->Args({20})->Args({50})->Args({100});
+
+static void BM_BasicVectorInt8Usage(benchmark::State& state) {
+ const int capacity = state.range(0);
+ std::vector<int8_t> vec(capacity);
+
+ while (state.KeepRunning()) {
+ benchmarkFunctionForVector<int8_t>(vec, capacity);
+ }
+}
+BENCHMARK(BM_BasicVectorInt8Usage)->Args({5})->Args({10})->Args({20})->Args({50})->Args({100});
+
+static void BM_VectorInt8StdFill(benchmark::State& state) {
+ const int capacity = state.range(0);
+ std::vector<int8_t> vec(capacity);
+
+ while (state.KeepRunning()) {
+ benchmarkStdFillForVector<int8_t>(vec, capacity);
+ }
+}
+BENCHMARK(BM_VectorInt8StdFill)->Args({5})->Args({10})->Args({20})->Args({50})->Args({100});
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/benchmark/db_benchmark.cpp b/statsd/benchmark/db_benchmark.cpp
new file mode 100644
index 0000000..b9b1694
--- /dev/null
+++ b/statsd/benchmark/db_benchmark.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 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 "benchmark/benchmark.h"
+#include "tests/statsd_test_util.h"
+#include "utils/DbUtils.h"
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+namespace dbutils {
+
+static void BM_insertAtomsIntoDbTablesNewConnection(benchmark::State& state) {
+ ConfigKey key = ConfigKey(111, 222);
+ int64_t metricId = 0;
+ int64_t bucketStartTimeNs = 10000000000;
+
+ unique_ptr<LogEvent> event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs, android::view::DISPLAY_STATE_OFF);
+ vector<LogEvent> logEvents;
+ for (int j = 0; j < state.range(1); ++j) {
+ logEvents.push_back(*event.get());
+ }
+ string err;
+ for (auto s : state) {
+ for (int metricId = 0; metricId < state.range(0); ++metricId) {
+ state.PauseTiming();
+ deleteDb(key);
+ createTableIfNeeded(key, metricId, *event.get());
+ state.ResumeTiming();
+ insert(key, metricId, logEvents, err);
+ }
+ }
+ deleteDb(key);
+}
+
+BENCHMARK(BM_insertAtomsIntoDbTablesNewConnection)
+ ->Args({1, 10})
+ ->Args({1, 50})
+ ->Args({1, 100})
+ ->Args({1, 500})
+ ->Args({10, 10})
+ ->Args({10, 20});
+
+static void BM_insertAtomsIntoDbTablesReuseConnection(benchmark::State& state) {
+ ConfigKey key = ConfigKey(111, 222);
+ int64_t metricId = 0;
+ int64_t bucketStartTimeNs = 10000000000;
+
+ unique_ptr<LogEvent> event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs, android::view::DISPLAY_STATE_OFF);
+ vector<LogEvent> logEvents;
+ for (int j = 0; j < state.range(1); ++j) {
+ logEvents.push_back(*event.get());
+ }
+ sqlite3* dbHandle = getDb(key);
+ string err;
+ for (auto s : state) {
+ for (int metricId = 0; metricId < state.range(0); ++metricId) {
+ state.PauseTiming();
+ deleteTable(key, metricId);
+ createTableIfNeeded(key, metricId, *event.get());
+ state.ResumeTiming();
+ insert(key, metricId, logEvents, err);
+ }
+ }
+ closeDb(dbHandle);
+ deleteDb(key);
+}
+
+BENCHMARK(BM_insertAtomsIntoDbTablesReuseConnection)
+ ->Args({1, 10})
+ ->Args({1, 50})
+ ->Args({1, 100})
+ ->Args({1, 500})
+ ->Args({10, 10})
+ ->Args({10, 20});
+
+static void BM_createDbTables(benchmark::State& state) {
+ ConfigKey key = ConfigKey(111, 222);
+ int64_t metricId = 0;
+ int64_t bucketStartTimeNs = 10000000000;
+
+ unique_ptr<LogEvent> event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs, android::view::DISPLAY_STATE_OFF);
+ vector<LogEvent> logEvents{*event.get()};
+ string err;
+ for (auto s : state) {
+ state.PauseTiming();
+ deleteTable(key, metricId);
+ state.ResumeTiming();
+ createTableIfNeeded(key, metricId, *event.get());
+ insert(key, metricId, logEvents, err);
+ }
+ deleteDb(key);
+}
+
+BENCHMARK(BM_createDbTables);
+} // namespace dbutils
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/benchmark/duration_metric_benchmark.cpp b/statsd/benchmark/duration_metric_benchmark.cpp
index 2d315d9..a87b5ee 100644
--- a/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/statsd/benchmark/duration_metric_benchmark.cpp
@@ -14,12 +14,13 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-#include "metric_util.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
@@ -39,15 +40,15 @@
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
+ dimensions->set_field(util::SCHEDULED_JOB_STATE_CHANGED);
dimensions->add_child()->set_field(2); // job name field.
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(util::SYNC_STATE_CHANGED, {Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
@@ -68,10 +69,10 @@
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
auto dimensionWhat = metric->mutable_dimensions_in_what();
- dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
+ dimensionWhat->set_field(util::SCHEDULED_JOB_STATE_CHANGED);
dimensionWhat->add_child()->set_field(2); // job name field.
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ *metric->mutable_dimensions_in_condition() =
+ CreateAttributionUidAndTagDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
@@ -87,14 +88,13 @@
auto scheduledJobPredicate = CreateScheduledJobPredicate();
auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+ *dimensions =
+ CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
dimensions->add_child()->set_field(2); // job name field.
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ *syncDimension = CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
if (addExtraDimensionInCondition) {
syncDimension->add_child()->set_field(2 /* name field*/);
}
@@ -116,16 +116,15 @@
metric->set_what(scheduledJobPredicate.id());
metric->set_condition(combinationPredicate->id());
metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+ *metric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
auto links = metric->add_links();
links->set_condition(isSyncingPredicate.id());
*links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+ CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
*links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
return config;
}
@@ -208,8 +207,8 @@
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
@@ -299,8 +298,8 @@
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ auto processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
diff --git a/statsd/benchmark/filter_value_benchmark.cpp b/statsd/benchmark/filter_value_benchmark.cpp
index 743ccc4..0642070 100644
--- a/statsd/benchmark/filter_value_benchmark.cpp
+++ b/statsd/benchmark/filter_value_benchmark.cpp
@@ -19,9 +19,9 @@
#include "HashableDimensionKey.h"
#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "metric_util.h"
#include "stats_event.h"
#include "stats_log_util.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
diff --git a/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index 7a45565..9957066 100644
--- a/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -19,9 +19,9 @@
#include "HashableDimensionKey.h"
#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "metric_util.h"
#include "stats_event.h"
#include "stats_log_util.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
diff --git a/statsd/benchmark/loss_info_container_benchmark.cpp b/statsd/benchmark/loss_info_container_benchmark.cpp
new file mode 100644
index 0000000..634a173
--- /dev/null
+++ b/statsd/benchmark/loss_info_container_benchmark.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2023 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 <cstdlib>
+#include <ctime>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+std::vector<int> generateRandomIds(int count, int maxRange) {
+ std::srand(std::time(nullptr));
+
+ std::unordered_set<int> unique_values;
+
+ while (unique_values.size() <= count) {
+ unique_values.insert(std::rand() % maxRange);
+ }
+
+ std::vector<int> result(unique_values.begin(), unique_values.end());
+
+ return result;
+}
+
+const int kMaxAtomId = 100000;
+const int kMaxErrorCode = 20;
+
+std::vector<std::pair<std::vector<int>, std::vector<int>>> generateIdsAndErrorsVectors(
+ const std::vector<int>& idsCounts, const std::vector<int>& errorsCounts) {
+ std::vector<std::pair<std::vector<int>, std::vector<int>>> result;
+ for (const int idCount : idsCounts) {
+ for (const int errorCount : errorsCounts) {
+ auto ids = generateRandomIds(idCount, kMaxAtomId);
+ auto errors = generateRandomIds(errorCount, kMaxErrorCode);
+ result.push_back(std::make_pair(ids, errors));
+ }
+ }
+ return result;
+}
+
+const std::vector<std::pair<std::vector<int>, std::vector<int>>> kRandomIdsAndErrorsPairs =
+ generateIdsAndErrorsVectors({1, 5, 10, 50}, {1, 2, 5, 10});
+
+struct TestVector {
+ std::vector<int> errors;
+ std::vector<int> tags;
+};
+
+std::vector<TestVector> generateTestVector(
+ int count,
+ const std::vector<std::pair<std::vector<int>, std::vector<int>>>& idsAndErrorsPairs) {
+ std::srand(std::time(nullptr));
+
+ std::vector<TestVector> result;
+
+ for (const auto& idsAndErrors : idsAndErrorsPairs) {
+ TestVector testVector;
+
+ testVector.errors.reserve(count);
+ testVector.tags.reserve(count);
+
+ for (int i = 0; i < count; i++) {
+ const int randomAtomIdFromReferenceList =
+ idsAndErrors.first[std::rand() % idsAndErrors.first.size()];
+ const int randomErrorFromReferenceList =
+ idsAndErrors.second[std::rand() % idsAndErrors.second.size()];
+
+ testVector.errors.push_back(randomErrorFromReferenceList);
+ testVector.tags.push_back(randomAtomIdFromReferenceList);
+ }
+ result.push_back(testVector);
+ }
+
+ return result;
+}
+
+constexpr int kTestVectorSize = 4096;
+constexpr int kMaxAtomTagsCount = 100;
+
+const std::vector<TestVector> kTestVectors =
+ generateTestVector(kTestVectorSize, kRandomIdsAndErrorsPairs);
+
+struct LossInfoVector {
+ // using vectors is more memory efficient
+ // using vectors fits well with the dump API implementation - no need to transform data
+ // before writing into AStatsEvent since it is aligned with repeated int32 fields
+ std::vector<int> errors;
+ std::vector<int> tags;
+ std::vector<int> counts;
+
+ bool noteLossInfo(int error, int tag) {
+ // linear search is Ok here since we do not expect to see many tags, usually 1-5 per uid
+ // exception is system server where we see 10s atoms
+ size_t locatedTagIndex = 0;
+ for (locatedTagIndex = 0; locatedTagIndex < errors.size(); ++locatedTagIndex) {
+ // is there already logged an atom with tag == atomId
+ if (errors[locatedTagIndex] == error && tags[locatedTagIndex] == tag) {
+ ++counts[locatedTagIndex];
+ return true;
+ }
+ }
+
+ // if pair [error, atomId] is not found and guardrail is not reached yet store loss
+ // counter
+ if (locatedTagIndex == errors.size() && tags.size() < kMaxAtomTagsCount) {
+ errors.push_back(error);
+ tags.push_back(tag);
+ counts.push_back(1);
+ } else {
+ return false;
+ }
+ return true;
+ }
+};
+
+using LossInfoKey = std::pair<int, int>; // [error, tag]
+
+template <typename T>
+struct LossInfoMap {
+ // using maps is more CPU efficient however will require some postprocessing before
+ // writing into the socket
+ T countsPerErrorTag;
+
+ bool noteLossInfo(int error, int tag) {
+ LossInfoKey key = std::make_pair(error, tag);
+ auto counterIt = countsPerErrorTag.find(key);
+
+ if (counterIt != countsPerErrorTag.end()) {
+ ++counterIt->second;
+ } else if (countsPerErrorTag.size() < kMaxAtomTagsCount) {
+ countsPerErrorTag[key] = 1;
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+} // namespace
+
+struct hash_pair final {
+ template <class TFirst, class TSecond>
+ size_t operator()(const std::pair<TFirst, TSecond>& p) const noexcept {
+ uintmax_t hash = std::hash<TFirst>{}(p.first);
+ hash <<= sizeof(uintmax_t) * 4;
+ hash ^= std::hash<TSecond>{}(p.second);
+ return std::hash<uintmax_t>{}(hash);
+ }
+};
+
+static void BM_LossInfoCollectionAndDumpViaVector(benchmark::State& state) {
+ const TestVector& testVector = kTestVectors[state.range(0)];
+ LossInfoVector lossInfo;
+
+ while (state.KeepRunning()) {
+ int res = 0;
+ for (int i = 0; i < kTestVectorSize; i++) {
+ res += lossInfo.noteLossInfo(testVector.errors[i], testVector.tags[i]);
+ }
+ benchmark::DoNotOptimize(res);
+ }
+}
+BENCHMARK(BM_LossInfoCollectionAndDumpViaVector)
+ ->Args({0})
+ ->Args({1})
+ ->Args({2})
+ ->Args({3})
+ ->Args({4})
+ ->Args({5})
+ ->Args({6})
+ ->Args({7})
+ ->Args({8})
+ ->Args({9})
+ ->Args({10})
+ ->Args({11})
+ ->Args({12})
+ ->Args({13})
+ ->Args({14})
+ ->Args({15});
+
+static void BM_LossInfoCollectionAndDumpViaUnorderedMap(benchmark::State& state) {
+ const TestVector& testVector = kTestVectors[state.range(0)];
+ LossInfoMap<std::unordered_map<LossInfoKey, int, hash_pair>> lossInfo;
+
+ while (state.KeepRunning()) {
+ int res = 0;
+ for (int i = 0; i < kTestVectorSize; i++) {
+ res += lossInfo.noteLossInfo(testVector.errors[i], testVector.tags[i]);
+ }
+ benchmark::DoNotOptimize(res);
+ }
+}
+BENCHMARK(BM_LossInfoCollectionAndDumpViaUnorderedMap)
+ ->Args({0})
+ ->Args({1})
+ ->Args({2})
+ ->Args({3})
+ ->Args({4})
+ ->Args({5})
+ ->Args({6})
+ ->Args({7})
+ ->Args({8})
+ ->Args({9})
+ ->Args({10})
+ ->Args({11})
+ ->Args({12})
+ ->Args({13})
+ ->Args({14})
+ ->Args({15});
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/benchmark/metric_util.cpp b/statsd/benchmark/metric_util.cpp
deleted file mode 100644
index 66c4364..0000000
--- a/statsd/benchmark/metric_util.cpp
+++ /dev/null
@@ -1,380 +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 "metric_util.h"
-
-#include "stats_event.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(atomId);
- return atom_matcher;
-}
-
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
- ScheduledJobStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateStartScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
- ScheduledJobStateChanged::STARTED);
-}
-
-AtomMatcher CreateFinishScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
- ScheduledJobStateChanged::FINISHED);
-}
-
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("UidProcessStateChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
- WakelockStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateAcquireWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
-}
-
-AtomMatcher CreateReleaseWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
-}
-
-AtomMatcher CreateScreenStateChangedAtomMatcher(
- const string& name, android::view::DisplayStateEnum state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(1); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateScreenTurnedOnAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-}
-
-AtomMatcher CreateScreenTurnedOffAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
- ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
-}
-
-AtomMatcher CreateSyncStateChangedAtomMatcher(
- const string& name, SyncStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateSyncStartAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
-}
-
-AtomMatcher CreateSyncEndAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
-}
-
-AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
- const string& name, ActivityForegroundStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // Activity field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateMoveToBackgroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
-}
-
-AtomMatcher CreateMoveToForegroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND);
-}
-
-Predicate CreateScheduledJobPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
- return predicate;
-}
-
-Predicate CreateBatterySaverModePredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("BatterySaverIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
- return predicate;
-}
-
-Predicate CreateScreenIsOnPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScreenIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
- return predicate;
-}
-
-Predicate CreateScreenIsOffPredicate() {
- Predicate predicate;
- predicate.set_id(1111123);
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
- return predicate;
-}
-
-Predicate CreateHoldingWakelockPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("HoldingWakelock"));
- predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
- return predicate;
-}
-
-Predicate CreateIsSyncingPredicate() {
- Predicate predicate;
- predicate.set_id(33333333333333);
- predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
- return predicate;
-}
-
-Predicate CreateIsInBackgroundPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("IsInBackground"));
- predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
- return predicate;
-}
-
-void addPredicateToPredicateCombination(const Predicate& predicate,
- Predicate* combinationPredicate) {
- combinationPredicate->mutable_combination()->add_predicate(predicate.id());
-}
-
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- }
- return dimensions;
-}
-
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- child->add_child()->set_field(2);
- }
- return dimensions;
-}
-
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const int field : fields) {
- dimensions.add_child()->set_field(field);
- }
- return dimensions;
-}
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags) {
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
-}
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
-}
-
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- uint64_t timestampNs, const android::view::DisplayStateEnum state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
- const vector<int>& attributionUids, const vector<string>& attributionTags,
- const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, jobName.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::STARTED, timestampNs);
-}
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::FINISHED, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name,
- const SyncStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::ON);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::OFF);
-}
-
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- sp<StatsLogProcessor> processor = new StatsLogProcessor(
- uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; },
- [](const int&, const vector<int64_t>&) { return true; },
- /*logEventFilter=*/nullptr);
- processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
- return processor;
-}
-
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
- std::sort(events->begin(), events->end(),
- [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
- return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
- });
-}
-
-int64_t StringToId(const string& str) {
- return static_cast<int64_t>(std::hash<std::string>()(str));
-}
-
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/statsd/benchmark/metric_util.h b/statsd/benchmark/metric_util.h
deleted file mode 100644
index 693bf45..0000000
--- a/statsd/benchmark/metric_util.h
+++ /dev/null
@@ -1,140 +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 "src/stats_log.pb.h"
-#include "src/statsd_config.pb.h"
-#include "src/StatsLogProcessor.h"
-#include "src/logd/LogEvent.h"
-#include "stats_event.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Create AtomMatcher proto to simply match a specific atom type.
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
-
-// Create AtomMatcher proto for scheduled job state changed.
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
-
-// Create AtomMatcher proto for starting a scheduled job.
-AtomMatcher CreateStartScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for a scheduled job is done.
-AtomMatcher CreateFinishScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for screen brightness state changed.
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
-
-// Create AtomMatcher proto for acquiring wakelock.
-AtomMatcher CreateAcquireWakelockAtomMatcher();
-
-// Create AtomMatcher proto for releasing wakelock.
-AtomMatcher CreateReleaseWakelockAtomMatcher() ;
-
-// Create AtomMatcher proto for screen turned on.
-AtomMatcher CreateScreenTurnedOnAtomMatcher();
-
-// Create AtomMatcher proto for screen turned off.
-AtomMatcher CreateScreenTurnedOffAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned on.
-AtomMatcher CreateSyncStartAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned off.
-AtomMatcher CreateSyncEndAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to background.
-AtomMatcher CreateMoveToBackgroundAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to foreground.
-AtomMatcher CreateMoveToForegroundAtomMatcher();
-
-// Create Predicate proto for screen is off.
-Predicate CreateScreenIsOffPredicate();
-
-// Create Predicate proto for a running scheduled job.
-Predicate CreateScheduledJobPredicate();
-
-// Create Predicate proto for holding wakelock.
-Predicate CreateHoldingWakelockPredicate();
-
-// Create a Predicate proto for app syncing.
-Predicate CreateIsSyncingPredicate();
-
-// Create a Predicate proto for app is in background.
-Predicate CreateIsInBackgroundPredicate();
-
-// Add a predicate to the predicate combination.
-void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
-
-// Create dimensions from primitive fields.
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
-
-// Create dimensions by attribution uid and tag.
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-// Create dimensions by attribution uid only.
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags);
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
-
-// Create log event for screen state changed.
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- uint64_t timestampNs, const android::view::DisplayStateEnum state);
-
-// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name);
-
-// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key);
-
-// Util function to sort the log events by timestamp.
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
-
-int64_t StringToId(const string& str);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/statsd/benchmark/on_log_event_benchmark.cpp b/statsd/benchmark/on_log_event_benchmark.cpp
new file mode 100644
index 0000000..d3e5709
--- /dev/null
+++ b/statsd/benchmark/on_log_event_benchmark.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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 "benchmark/benchmark.h"
+#include "tests/statsd_test_util.h"
+
+using namespace std;
+namespace android {
+namespace os {
+namespace statsd {
+
+static void BM_OnLogEvent(benchmark::State& state) {
+ StatsdConfig config;
+ auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+ *config.add_event_metric() =
+ createEventMetric("Event", wakelockAcquireMatcher.id(), /* condition */ nullopt);
+
+ for (int atomId = 1000; atomId < 2000; atomId++) {
+ auto matcher = CreateSimpleAtomMatcher("name" + to_string(atomId), atomId);
+ *config.add_atom_matcher() = CreateSimpleAtomMatcher("name" + to_string(atomId), atomId);
+ *config.add_event_metric() = createEventMetric("Event" + to_string(atomId), matcher.id(),
+ /* condition */ nullopt);
+ }
+
+ ConfigKey cfgKey;
+ std::vector<std::unique_ptr<LogEvent>> events;
+ vector<int> attributionUids = {111};
+ vector<string> attributionTags = {"App1"};
+ for (int i = 1; i <= 10; i++) {
+ events.push_back(CreateAcquireWakelockEvent(2 + i, attributionUids, attributionTags,
+ "wl" + to_string(i)));
+ }
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+ for (auto _ : state) {
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ }
+}
+BENCHMARK(BM_OnLogEvent);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/benchmark/stats_write_benchmark.cpp b/statsd/benchmark/stats_write_benchmark.cpp
index f5a0cd5..b84b0cd 100644
--- a/statsd/benchmark/stats_write_benchmark.cpp
+++ b/statsd/benchmark/stats_write_benchmark.cpp
@@ -13,28 +13,45 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <statslog_statsdtest.h>
+
#include "benchmark/benchmark.h"
-#include <statslog.h>
namespace android {
namespace os {
namespace statsd {
-static void BM_StatsWrite(benchmark::State& state) {
- const char* reason = "test";
- int64_t boot_end_time = 1234567;
- int64_t total_duration = 100;
- int64_t bootloader_duration = 10;
- int64_t time_since_last_boot = 99999999;
+static void BM_StatsEventObtain(benchmark::State& state) {
while (state.KeepRunning()) {
- android::util::stats_write(
- android::util::BOOT_SEQUENCE_REPORTED, reason, reason,
- boot_end_time, total_duration, bootloader_duration, time_since_last_boot);
- total_duration++;
+ AStatsEvent* event = AStatsEvent_obtain();
+ benchmark::DoNotOptimize(event);
+ AStatsEvent_release(event);
+ }
+}
+BENCHMARK(BM_StatsEventObtain);
+
+static void BM_StatsWrite(benchmark::State& state) {
+ int32_t parent_uid = 0;
+ int32_t isolated_uid = 100;
+ int32_t event = 1;
+ while (state.KeepRunning()) {
+ util::stats_write(util::ISOLATED_UID_CHANGED, parent_uid, isolated_uid, event++);
}
}
BENCHMARK(BM_StatsWrite);
+static void BM_StatsWriteViaQueue(benchmark::State& state) {
+ // writes dedicated atom which known to be put into the queue for the test purpose
+ int32_t uid = 0;
+ int32_t label = 100;
+ int32_t a_state = 1;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(
+ util::stats_write(util::APP_BREADCRUMB_REPORTED, uid, label, a_state++));
+ }
+}
+BENCHMARK(BM_StatsWriteViaQueue);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/benchmark/string_transform_benchmark.cpp b/statsd/benchmark/string_transform_benchmark.cpp
new file mode 100644
index 0000000..0c5f5c6
--- /dev/null
+++ b/statsd/benchmark/string_transform_benchmark.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 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 <memory>
+#include <regex>
+#include <string>
+
+#include "benchmark/benchmark.h"
+// #include "re2/re2.h"
+#include "utils/Regex.h"
+
+using android::os::statsd::Regex;
+using namespace std;
+
+static void removeTrailingCharacters(string& str, const string& characters) {
+ str.erase(str.find_last_not_of(characters) + 1, string::npos);
+}
+
+static void removeLeadingCharacters(string& str, const char charToRemove) {
+ str.erase(0, min(str.find_first_not_of(charToRemove), str.size() - 1));
+}
+
+static void removeTrailingNumbers(string& str) {
+ int i = str.length() - 1;
+ while (i >= 0 && isdigit(str[i])) {
+ str.erase(i, 1);
+ i--;
+ }
+}
+
+static void BM_RemoveTrailingCharacters(benchmark::State& state) {
+ const string prefix(state.range(0), 'a' + rand() % 26);
+ const string suffix(state.range(1), '0' + rand() % 10);
+ const string input = prefix + suffix;
+ const string characters("0123456789");
+ for (auto _ : state) {
+ string str = input;
+ removeTrailingCharacters(str, characters);
+ benchmark::DoNotOptimize(str);
+ }
+}
+BENCHMARK(BM_RemoveTrailingCharacters)->RangeMultiplier(2)->RangePair(0, 20, 0, 20);
+
+static void BM_RemoveTrailingNumbers(benchmark::State& state) {
+ const string prefix(state.range(0), 'a' + rand() % 26);
+ const string suffix(state.range(1), '0' + rand() % 10);
+ const string input = prefix + suffix;
+ for (auto _ : state) {
+ string str = input;
+ removeTrailingNumbers(str);
+ benchmark::DoNotOptimize(str);
+ }
+}
+BENCHMARK(BM_RemoveTrailingNumbers)->RangeMultiplier(2)->RangePair(0, 20, 0, 20);
+
+static void BM_RemoveTrailingNumbersCppRegex(benchmark::State& state) {
+ static const regex re(R"([\d]+$)");
+ const string prefix(state.range(0), 'a' + rand() % 26);
+ const string suffix(state.range(1), '0' + rand() % 10);
+ const string input = prefix + suffix;
+ for (auto _ : state) {
+ string str = input;
+ benchmark::DoNotOptimize(regex_replace(str, re, ""));
+ }
+}
+BENCHMARK(BM_RemoveTrailingNumbersCppRegex)->RangeMultiplier(2)->RangePair(0, 20, 0, 20);
+
+static void BM_RemoveTrailingNumbersCRegex(benchmark::State& state) {
+ unique_ptr<Regex> re = Regex::create(R"([0-9]+$)");
+ const string prefix(state.range(0), 'a' + rand() % 26);
+ const string suffix(state.range(1), '0' + rand() % 10);
+ const string input = prefix + suffix;
+ for (auto _ : state) {
+ string str = input;
+ benchmark::DoNotOptimize(re->replace(str, ""));
+ }
+}
+BENCHMARK(BM_RemoveTrailingNumbersCRegex)->RangeMultiplier(2)->RangePair(0, 20, 0, 20);
+
+// To run RE2 benchmark locally, libregex_re2 under external/regex_re2 needs to be made visible to
+// statsd_benchmark.
+// static void BM_RemoveTrailingNumbersRe2(benchmark::State& state) {
+// using namespace re2;
+// static const RE2 re2{R"([\d]+$)"};
+// const string prefix(state.range(0), 'a' + rand() % 26);
+// const string suffix(state.range(1), '0' + rand() % 10);
+// const string input = prefix + suffix;
+// for (auto _ : state) {
+// string str = input;
+// RE2::Replace(&str, re2, "");
+// benchmark::DoNotOptimize(str);
+// }
+// }
+// BENCHMARK(BM_RemoveTrailingNumbersRe2)->RangeMultiplier(2)->RangePair(0, 20, 0, 20);
diff --git a/statsd/fuzzers/statsd_service_fuzzer.cpp b/statsd/fuzzers/statsd_service_fuzzer.cpp
new file mode 100644
index 0000000..7baca73
--- /dev/null
+++ b/statsd/fuzzers/statsd_service_fuzzer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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 <android/binder_interface_utils.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+
+#include "StatsService.h"
+#include "packages/UidMap.h"
+
+using namespace android;
+using namespace android::os::statsd;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
+ std::shared_ptr<LogEventQueue> eventQueue =
+ std::make_shared<LogEventQueue>(8000 /*buffer limit. Same as StatsD binary*/);
+ sp<UidMap> uidMap = UidMap::getInstance();
+ shared_ptr<StatsService> binder =
+ SharedRefBase::make<StatsService>(uidMap, eventQueue, logEventFilter);
+ fuzzService(binder->asBinder().get(), FuzzedDataProvider(data, size));
+ return 0;
+}
diff --git a/statsd/fuzzers/statsd_socket_data_fuzzer.cpp b/statsd/fuzzers/statsd_socket_data_fuzzer.cpp
new file mode 100644
index 0000000..bfdfd8b
--- /dev/null
+++ b/statsd/fuzzers/statsd_socket_data_fuzzer.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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 "socket/StatsSocketListener.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void fuzzSocket(const uint8_t* data, size_t size) {
+ LogEventQueue queue(50000);
+ LogEventFilter filter;
+ filter.setFilteringEnabled(false);
+
+ StatsSocketListener::processSocketMessage((const char*)data, size, 0, 0, queue, filter);
+
+ StatsSocketListener::processStatsEventBuffer(data, size, 0, 0, queue, filter);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ using namespace android::os::statsd;
+
+ fuzzSocket(data, size);
+
+ return 0;
+}
diff --git a/statsd/src/FieldValue.cpp b/statsd/src/FieldValue.cpp
index 02a2c5e..84fad92 100644
--- a/statsd/src/FieldValue.cpp
+++ b/statsd/src/FieldValue.cpp
@@ -60,6 +60,17 @@
return false;
}
+std::vector<Matcher> dedupFieldMatchers(const std::vector<Matcher>& fieldMatchers) {
+ std::vector<Matcher> dedupedFieldMatchers;
+ for (size_t i = 0; i < fieldMatchers.size(); i++) {
+ if (std::find(dedupedFieldMatchers.begin(), dedupedFieldMatchers.end(), fieldMatchers[i]) ==
+ dedupedFieldMatchers.end()) {
+ dedupedFieldMatchers.push_back(fieldMatchers[i]);
+ }
+ }
+ return dedupedFieldMatchers;
+}
+
void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask,
std::vector<Matcher>* output) {
if (depth > kMaxLogDepth) {
@@ -348,28 +359,30 @@
}
Value& Value::operator=(const Value& that) {
- type = that.type;
- switch (type) {
- case INT:
- int_value = that.int_value;
- break;
- case LONG:
- long_value = that.long_value;
- break;
- case FLOAT:
- float_value = that.float_value;
- break;
- case DOUBLE:
- double_value = that.double_value;
- break;
- case STRING:
- str_value = that.str_value;
- break;
- case STORAGE:
- storage_value = that.storage_value;
- break;
- default:
- break;
+ if (this != &that) {
+ type = that.type;
+ switch (type) {
+ case INT:
+ int_value = that.int_value;
+ break;
+ case LONG:
+ long_value = that.long_value;
+ break;
+ case FLOAT:
+ float_value = that.float_value;
+ break;
+ case DOUBLE:
+ double_value = that.double_value;
+ break;
+ case STRING:
+ str_value = that.str_value;
+ break;
+ case STORAGE:
+ storage_value = that.storage_value;
+ break;
+ default:
+ break;
+ }
}
return *this;
}
@@ -480,6 +493,7 @@
return eq;
}
+/* Is dimension_a a subset of dimension_b. */
bool subsetDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b) {
if (dimension_a.size() > dimension_b.size()) {
@@ -490,6 +504,18 @@
for (size_t j = 0; j < dimension_b.size(); ++j) {
if (dimension_a[i] == dimension_b[j]) {
found = true;
+ break;
+ }
+
+ // Check equality of repeated fields with different positions.
+ // Only position FIRST and LAST are considered subsets of position ALL.
+ if (dimension_b[j].hasAllPositionMatcher() &&
+ (dimension_a[i].hasFirstPositionMatcher() ||
+ dimension_a[i].hasLastPositionMatcher())) {
+ if (dimension_a[i].isEqualWithoutPositionBits(dimension_b[j])) {
+ found = true;
+ break;
+ }
}
}
if (!found) {
diff --git a/statsd/src/FieldValue.h b/statsd/src/FieldValue.h
index 5906942..66b5475 100644
--- a/statsd/src/FieldValue.h
+++ b/statsd/src/FieldValue.h
@@ -16,7 +16,6 @@
#pragma once
#include "src/statsd_config.pb.h"
-#include "annotations.h"
namespace android {
namespace os {
@@ -241,7 +240,21 @@
}
bool hasAllPositionMatcher() const {
- return mMatcher.getDepth() >= 1 && mMatcher.getRawPosAtDepth(1) == 0;
+ return mMatcher.getDepth() >= 1 && mMatcher.getRawPosAtDepth(1) == 0 &&
+ getRawMaskAtDepth(1) == 0x7f;
+ }
+
+ bool hasFirstPositionMatcher() const {
+ return mMatcher.getDepth() >= 1 && mMatcher.getRawPosAtDepth(1) == 1;
+ }
+
+ bool hasLastPositionMatcher() const {
+ return mMatcher.getDepth() >= 1 && mMatcher.isLastPosMatcher(1);
+ }
+
+ bool isEqualWithoutPositionBits(const Matcher& that) const {
+ return ((mMatcher.getField() & kClearAllPositionMatcherMask) ==
+ (that.getMatcher().getField() & kClearAllPositionMatcherMask));
}
inline bool operator!=(const Matcher& that) const {
@@ -358,7 +371,6 @@
class Annotations {
public:
Annotations() {
- setNested(true); // Nested = true by default
}
// This enum stores where particular annotations can be found in the
@@ -451,6 +463,8 @@
/* returns uid if the field is uid field, or -1 if the field is not a uid field */
int getUidIfExists(const FieldValue& value);
+std::vector<Matcher> dedupFieldMatchers(const std::vector<Matcher>& fieldMatchers);
+
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
diff --git a/statsd/src/HashableDimensionKey.cpp b/statsd/src/HashableDimensionKey.cpp
index 447a731..837d9e9 100644
--- a/statsd/src/HashableDimensionKey.cpp
+++ b/statsd/src/HashableDimensionKey.cpp
@@ -337,16 +337,8 @@
}
bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
- 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;
+ // according to http://go/cppref/cpp/container/vector/operator_cmp
+ return mValues == that.mValues;
};
bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
diff --git a/statsd/src/HashableDimensionKey.h b/statsd/src/HashableDimensionKey.h
index b5df456..5753d98 100644
--- a/statsd/src/HashableDimensionKey.h
+++ b/statsd/src/HashableDimensionKey.h
@@ -142,7 +142,7 @@
class AtomDimensionKey {
public:
- explicit AtomDimensionKey(const int32_t atomTag, const HashableDimensionKey& atomFieldValues)
+ explicit AtomDimensionKey(int32_t atomTag, const HashableDimensionKey& atomFieldValues)
: mAtomTag(atomTag), mAtomFieldValues(atomFieldValues){};
AtomDimensionKey(){};
@@ -269,22 +269,16 @@
} // namespace os
} // namespace android
-namespace std {
-
-using android::os::statsd::AtomDimensionKey;
-using android::os::statsd::HashableDimensionKey;
-using android::os::statsd::MetricDimensionKey;
-
template <>
-struct hash<HashableDimensionKey> {
- std::size_t operator()(const HashableDimensionKey& key) const {
+struct std::hash<android::os::statsd::HashableDimensionKey> {
+ std::size_t operator()(const android::os::statsd::HashableDimensionKey& key) const {
return hashDimension(key);
}
};
template <>
-struct hash<MetricDimensionKey> {
- std::size_t operator()(const MetricDimensionKey& key) const {
+struct std::hash<android::os::statsd::MetricDimensionKey> {
+ std::size_t operator()(const android::os::statsd::MetricDimensionKey& key) const {
android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey()));
return android::JenkinsHashWhiten(hash);
@@ -292,11 +286,10 @@
};
template <>
-struct hash<AtomDimensionKey> {
- std::size_t operator()(const AtomDimensionKey& key) const {
+struct std::hash<android::os::statsd::AtomDimensionKey> {
+ std::size_t operator()(const android::os::statsd::AtomDimensionKey& key) const {
android::hash_t hash = hashDimension(key.getAtomFieldValues());
hash = android::JenkinsHashMix(hash, key.getAtomTag());
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std
diff --git a/statsd/src/StatsLogProcessor.cpp b/statsd/src/StatsLogProcessor.cpp
index 61d32fc..02356e8 100644
--- a/statsd/src/StatsLogProcessor.cpp
+++ b/statsd/src/StatsLogProcessor.cpp
@@ -36,6 +36,7 @@
#include "stats_util.h"
#include "statslog_statsd.h"
#include "storage/StorageManager.h"
+#include "utils/api_tracing.h"
using namespace android;
using android::base::StringPrintf;
@@ -53,12 +54,16 @@
namespace os {
namespace statsd {
+using aidl::android::os::IStatsQueryCallback;
+
// for ConfigMetricsReportList
const int FIELD_ID_CONFIG_KEY = 1;
const int FIELD_ID_REPORTS = 2;
// for ConfigKey
const int FIELD_ID_UID = 1;
const int FIELD_ID_ID = 2;
+const int FIELD_ID_REPORT_NUMBER = 3;
+const int FIELD_ID_STATSD_STATS_ID = 4;
// for ConfigMetricsReport
// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp
const int FIELD_ID_UID_MAP = 2;
@@ -68,7 +73,8 @@
const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
const int FIELD_ID_DUMP_REPORT_REASON = 8;
const int FIELD_ID_STRINGS = 9;
-const int FIELD_ID_DATA_CORRUPTED_REASON = 10;
+const int FIELD_ID_DATA_CORRUPTED_REASON = 11;
+const int FIELD_ID_ESTIMATED_DATA_BYTES = 12;
// for ActiveConfigList
const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
@@ -90,14 +96,20 @@
const sp<AlarmMonitor>& anomalyAlarmMonitor, const sp<AlarmMonitor>& periodicAlarmMonitor,
const int64_t timeBaseNs, const std::function<bool(const ConfigKey&)>& sendBroadcast,
const std::function<bool(const int&, const vector<int64_t>&)>& activateBroadcast,
+ const std::function<void(const ConfigKey&, const string&, const vector<int64_t>&)>&
+ sendRestrictedMetricsBroadcast,
const std::shared_ptr<LogEventFilter>& logEventFilter)
- : mUidMap(uidMap),
+ : mLastTtlTime(0),
+ mLastFlushRestrictedTime(0),
+ mLastDbGuardrailEnforcementTime(0),
+ mUidMap(uidMap),
mPullerManager(pullerManager),
mAnomalyAlarmMonitor(anomalyAlarmMonitor),
mPeriodicAlarmMonitor(periodicAlarmMonitor),
mLogEventFilter(logEventFilter),
mSendBroadcast(sendBroadcast),
mSendActivationBroadcast(activateBroadcast),
+ mSendRestrictedMetricsBroadcast(sendRestrictedMetricsBroadcast),
mTimeBaseNs(timeBaseNs),
mLargestTimestampSeen(0),
mLastTimestampSeen(0) {
@@ -124,14 +136,14 @@
}
void StatsLogProcessor::processFiredAnomalyAlarmsLocked(
- const int64_t& timestampNs,
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mMetricsManagers) {
itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
}
}
void StatsLogProcessor::onPeriodicAlarmFired(
- const int64_t& timestampNs,
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
for (const auto& itr : mMetricsManagers) {
@@ -378,6 +390,7 @@
}
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+ ATRACE_CALL();
OnLogEvent(event, getElapsedRealtimeNs());
}
@@ -440,16 +453,28 @@
informAnomalyAlarmFiredLocked(NanoToMillis(elapsedRealtimeNs));
}
- const int64_t curTimeSec = getElapsedRealtimeSec();
+ const int64_t curTimeSec = NanoToSeconds(elapsedRealtimeNs);
if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
mLastPullerCacheClearTimeSec = curTimeSec;
}
+ flushRestrictedDataIfNecessaryLocked(elapsedRealtimeNs);
+ enforceDataTtlsIfNecessaryLocked(getWallClockNs(), elapsedRealtimeNs);
+ enforceDbGuardrailsIfNecessaryLocked(getWallClockNs(), elapsedRealtimeNs);
+
+ if (!validateAppBreadcrumbEvent(*event)) {
+ return;
+ }
+
std::unordered_set<int> uidsWithActiveConfigsChanged;
std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid;
+
// pass the event to metrics managers.
for (auto& pair : mMetricsManagers) {
+ if (event->isRestricted() && !pair.second->hasRestrictedMetricsDelegate()) {
+ continue;
+ }
int uid = pair.first.GetUid();
int64_t configId = pair.first.GetId();
bool isPrevActive = pair.second->isActive();
@@ -533,9 +558,22 @@
void StatsLogProcessor::OnConfigUpdatedLocked(const int64_t timestampNs, const ConfigKey& key,
const StatsdConfig& config, bool modularUpdate) {
VLOG("Updated configuration for key %s", key.ToString().c_str());
- // Create new config if this is not a modular update or if this is a new config.
const auto& it = mMetricsManagers.find(key);
bool configValid = false;
+ if (isAtLeastU() && it != mMetricsManagers.end()) {
+ if (it->second->hasRestrictedMetricsDelegate() !=
+ config.has_restricted_metrics_delegate_package_name()) {
+ // Not a modular update if has_restricted_metrics_delegate changes
+ modularUpdate = false;
+ }
+ if (!modularUpdate && it->second->hasRestrictedMetricsDelegate()) {
+ StatsdStats::getInstance().noteDbDeletionConfigUpdated(key);
+ // Always delete the old db if restricted metrics config is not a
+ // modular update.
+ dbutils::deleteDb(key);
+ }
+ }
+ // Create new config if this is not a modular update or if this is a new config.
if (!modularUpdate || it == mMetricsManagers.end()) {
sp<MetricsManager> newMetricsManager =
new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
@@ -543,8 +581,23 @@
configValid = newMetricsManager->isConfigValid();
if (configValid) {
newMetricsManager->init();
- mUidMap->OnConfigUpdated(key);
newMetricsManager->refreshTtl(timestampNs);
+ // Sdk check for U+ is unnecessary because config with restricted metrics delegate
+ // will be invalid on non U+ devices.
+ if (newMetricsManager->hasRestrictedMetricsDelegate()) {
+ mSendRestrictedMetricsBroadcast(key,
+ newMetricsManager->getRestrictedMetricsDelegate(),
+ newMetricsManager->getAllMetricIds());
+ string err;
+ if (!dbutils::updateDeviceInfoTable(key, err)) {
+ ALOGE("Failed to create device_info table for configKey %s, err: %s",
+ key.ToString().c_str(), err.c_str());
+ StatsdStats::getInstance().noteDeviceInfoTableCreationFailed(key);
+ }
+ } else if (it != mMetricsManagers.end() && it->second->hasRestrictedMetricsDelegate()) {
+ mSendRestrictedMetricsBroadcast(key, it->second->getRestrictedMetricsDelegate(),
+ {});
+ }
mMetricsManagers[key] = newMetricsManager;
VLOG("StatsdConfig valid");
}
@@ -552,15 +605,32 @@
// Preserve the existing MetricsManager, update necessary components and metadata in place.
configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs,
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
- if (configValid) {
- mUidMap->OnConfigUpdated(key);
+ if (configValid && it->second->hasRestrictedMetricsDelegate()) {
+ mSendRestrictedMetricsBroadcast(key, it->second->getRestrictedMetricsDelegate(),
+ it->second->getAllMetricIds());
}
}
+
+ if (configValid && !config.has_restricted_metrics_delegate_package_name()) {
+ // We do not need to track uid map changes for restricted metrics since the uidmap is not
+ // stored in the sqlite db.
+ mUidMap->OnConfigUpdated(key);
+ } else if (configValid && config.has_restricted_metrics_delegate_package_name()) {
+ mUidMap->OnConfigRemoved(key);
+ }
if (!configValid) {
// If there is any error in the config, don't use it.
// Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
+ // Send an empty restricted metrics broadcast if the previous config was restricted.
+ if (isAtLeastU() && it != mMetricsManagers.end() &&
+ it->second->hasRestrictedMetricsDelegate()) {
+ mSendRestrictedMetricsBroadcast(key, it->second->getRestrictedMetricsDelegate(), {});
+ StatsdStats::getInstance().noteDbConfigInvalid(key);
+ dbutils::deleteDb(key);
+ }
mMetricsManagers.erase(key);
+ mUidMap->OnConfigRemoved(key);
}
updateLogEventFilterLocked();
@@ -576,10 +646,10 @@
return it->second->byteSize();
}
-void StatsLogProcessor::dumpStates(int out, bool verbose) {
+void StatsLogProcessor::dumpStates(int out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMetricsMutex);
dprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
- for (auto metricsManager : mMetricsManagers) {
+ for (const auto& metricsManager : mMetricsManagers) {
metricsManager.second->dumpStates(out, verbose);
}
}
@@ -594,6 +664,12 @@
const DumpLatency dumpLatency, ProtoOutputStream* proto) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ auto it = mMetricsManagers.find(key);
+ if (it != mMetricsManagers.end() && it->second->hasRestrictedMetricsDelegate()) {
+ VLOG("Unexpected call to StatsLogProcessor::onDumpReport for restricted metrics.");
+ return;
+ }
+
// Start of ConfigKey.
uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
@@ -602,7 +678,6 @@
// End of ConfigKey.
bool keepFile = false;
- auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end() && it->second->shouldPersistLocalHistory()) {
keepFile = true;
}
@@ -628,6 +703,18 @@
} else {
ALOGW("Config source %s does not exist", key.ToString().c_str());
}
+
+ if (erase_data) {
+ ++mDumpReportNumbers[key];
+ }
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_REPORT_NUMBER, mDumpReportNumbers[key]);
+
+ proto->write(FIELD_TYPE_INT32 | FIELD_ID_STATSD_STATS_ID,
+ StatsdStats::getInstance().getStatsdStatsId());
+ if (erase_data) {
+ StatsdStats::getInstance().noteMetricsReportSent(key, proto->size(),
+ mDumpReportNumbers[key]);
+ }
}
/*
@@ -646,8 +733,6 @@
flushProtoToBuffer(proto, outData);
VLOG("output data size %zu", outData->size());
}
-
- StatsdStats::getInstance().noteMetricsReportSent(key, proto.size());
}
/*
@@ -676,11 +761,19 @@
if (it == mMetricsManagers.end()) {
return;
}
+ if (it->second->hasRestrictedMetricsDelegate()) {
+ VLOG("Unexpected call to StatsLogProcessor::onConfigMetricsReportLocked for restricted "
+ "metrics.");
+ // Do not call onDumpReport for restricted metrics.
+ return;
+ }
int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
std::set<string> str_set;
+ int64_t totalSize = it->second->byteSize();
+
ProtoOutputStream tempProto;
// First, fill in ConfigMetricsReport using current data on memory, which
// starts from filling in StatsLogReport's.
@@ -717,6 +810,9 @@
// Data corrupted reason
writeDataCorruptedReasons(tempProto);
+ // Estimated memory bytes
+ tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_DATA_BYTES, totalSize);
+
flushProtoToBuffer(tempProto, buffer);
// save buffer to disk if needed
@@ -766,16 +862,23 @@
if (it != mMetricsManagers.end()) {
WriteDataToDiskLocked(key, getElapsedRealtimeNs(), getWallClockNs(), CONFIG_REMOVED,
NO_TIME_CONSTRAINTS);
+ if (isAtLeastU() && it->second->hasRestrictedMetricsDelegate()) {
+ StatsdStats::getInstance().noteDbDeletionConfigRemoved(key);
+ dbutils::deleteDb(key);
+ mSendRestrictedMetricsBroadcast(key, it->second->getRestrictedMetricsDelegate(), {});
+ }
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
}
StatsdStats::getInstance().noteConfigRemoved(key);
mLastBroadcastTimes.erase(key);
+ mLastByteSizeTimes.erase(key);
+ mDumpReportNumbers.erase(key);
int uid = key.GetUid();
bool lastConfigForUid = true;
- for (auto it : mMetricsManagers) {
+ for (const auto& it : mMetricsManagers) {
if (it.first.GetUid() == uid) {
lastConfigForUid = false;
break;
@@ -792,6 +895,219 @@
updateLogEventFilterLocked();
}
+// TODO(b/267501143): Add unit tests when metric producer is ready
+void StatsLogProcessor::enforceDataTtlsIfNecessaryLocked(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs) {
+ if (!isAtLeastU()) {
+ return;
+ }
+ if (elapsedRealtimeNs - mLastTtlTime < StatsdStats::kMinTtlCheckPeriodNs) {
+ return;
+ }
+ enforceDataTtlsLocked(wallClockNs, elapsedRealtimeNs);
+}
+
+void StatsLogProcessor::flushRestrictedDataIfNecessaryLocked(const int64_t elapsedRealtimeNs) {
+ if (!isAtLeastU()) {
+ return;
+ }
+ if (elapsedRealtimeNs - mLastFlushRestrictedTime < StatsdStats::kMinFlushRestrictedPeriodNs) {
+ return;
+ }
+ flushRestrictedDataLocked(elapsedRealtimeNs);
+}
+
+void StatsLogProcessor::querySql(const string& sqlQuery, const int32_t minSqlClientVersion,
+ const optional<vector<uint8_t>>& policyConfig,
+ const shared_ptr<IStatsQueryCallback>& callback,
+ const int64_t configId, const string& configPackage,
+ const int32_t callingUid) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ string err = "";
+
+ if (!isAtLeastU()) {
+ ALOGW("Restricted metrics query invoked on U- device");
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, std::nullopt, callingUid,
+ InvalidQueryReason(FLAG_DISABLED));
+ return;
+ }
+
+ const int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
+
+ // TODO(b/268416460): validate policyConfig here
+
+ if (minSqlClientVersion > dbutils::getDbVersion()) {
+ callback->sendFailure(StringPrintf(
+ "Unsupported sqlite version. Installed Version: %d, Requested Version: %d.",
+ dbutils::getDbVersion(), minSqlClientVersion));
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, std::nullopt, callingUid,
+ InvalidQueryReason(UNSUPPORTED_SQLITE_VERSION));
+ return;
+ }
+
+ set<int32_t> configPackageUids;
+ const auto& uidMapItr = UidMap::sAidToUidMapping.find(configPackage);
+ if (uidMapItr != UidMap::sAidToUidMapping.end()) {
+ configPackageUids.insert(uidMapItr->second);
+ } else {
+ configPackageUids = mUidMap->getAppUid(configPackage);
+ }
+
+ InvalidQueryReason invalidQueryReason;
+ set<ConfigKey> keysToQuery = getRestrictedConfigKeysToQueryLocked(
+ callingUid, configId, configPackageUids, err, invalidQueryReason);
+
+ if (keysToQuery.empty()) {
+ callback->sendFailure(err);
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, std::nullopt, callingUid,
+ InvalidQueryReason(invalidQueryReason));
+ return;
+ }
+
+ if (keysToQuery.size() > 1) {
+ err = "Ambiguous ConfigKey";
+ callback->sendFailure(err);
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, std::nullopt, callingUid,
+ InvalidQueryReason(AMBIGUOUS_CONFIG_KEY));
+ return;
+ }
+
+ flushRestrictedDataLocked(elapsedRealtimeNs);
+ enforceDataTtlsLocked(getWallClockNs(), elapsedRealtimeNs);
+
+ std::vector<std::vector<std::string>> rows;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ if (!dbutils::query(*(keysToQuery.begin()), sqlQuery, rows, columnTypes, columnNames, err)) {
+ callback->sendFailure(StringPrintf("failed to query db %s:", err.c_str()));
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, keysToQuery.begin()->GetUid(), callingUid,
+ InvalidQueryReason(QUERY_FAILURE), err.c_str());
+ return;
+ }
+
+ vector<string> queryData;
+ queryData.reserve(rows.size() * columnNames.size());
+ // TODO(b/268415904): avoid this vector transformation.
+ if (columnNames.size() != columnTypes.size()) {
+ callback->sendFailure("Inconsistent row sizes");
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, keysToQuery.begin()->GetUid(), callingUid,
+ InvalidQueryReason(INCONSISTENT_ROW_SIZE));
+ }
+ for (size_t i = 0; i < rows.size(); ++i) {
+ if (rows[i].size() != columnNames.size()) {
+ callback->sendFailure("Inconsistent row sizes");
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configId, configPackage, keysToQuery.begin()->GetUid(), callingUid,
+ InvalidQueryReason(INCONSISTENT_ROW_SIZE));
+ return;
+ }
+ queryData.insert(std::end(queryData), std::make_move_iterator(std::begin(rows[i])),
+ std::make_move_iterator(std::end(rows[i])));
+ }
+ callback->sendResults(queryData, columnNames, columnTypes, rows.size());
+ StatsdStats::getInstance().noteQueryRestrictedMetricSucceed(
+ configId, configPackage, keysToQuery.begin()->GetUid(), callingUid,
+ /*queryLatencyNs=*/getElapsedRealtimeNs() - elapsedRealtimeNs);
+}
+
+set<ConfigKey> StatsLogProcessor::getRestrictedConfigKeysToQueryLocked(
+ const int32_t callingUid, const int64_t configId, const set<int32_t>& configPackageUids,
+ string& err, InvalidQueryReason& invalidQueryReason) {
+ set<ConfigKey> matchedConfigKeys;
+ for (auto uid : configPackageUids) {
+ ConfigKey configKey(uid, configId);
+ if (mMetricsManagers.find(configKey) != mMetricsManagers.end()) {
+ matchedConfigKeys.insert(configKey);
+ }
+ }
+
+ set<ConfigKey> excludedKeys;
+ for (auto& configKey : matchedConfigKeys) {
+ auto it = mMetricsManagers.find(configKey);
+ if (!it->second->validateRestrictedMetricsDelegate(callingUid)) {
+ excludedKeys.insert(configKey);
+ };
+ }
+
+ set<ConfigKey> result;
+ std::set_difference(matchedConfigKeys.begin(), matchedConfigKeys.end(), excludedKeys.begin(),
+ excludedKeys.end(), std::inserter(result, result.end()));
+ if (matchedConfigKeys.empty()) {
+ err = "No configs found matching the config key";
+ invalidQueryReason = InvalidQueryReason(CONFIG_KEY_NOT_FOUND);
+ } else if (result.empty()) {
+ err = "No matching configs for restricted metrics delegate";
+ invalidQueryReason = InvalidQueryReason(CONFIG_KEY_WITH_UNMATCHED_DELEGATE);
+ }
+
+ return result;
+}
+
+void StatsLogProcessor::EnforceDataTtls(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs) {
+ if (!isAtLeastU()) {
+ return;
+ }
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ enforceDataTtlsLocked(wallClockNs, elapsedRealtimeNs);
+}
+
+void StatsLogProcessor::enforceDataTtlsLocked(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs) {
+ for (const auto& itr : mMetricsManagers) {
+ itr.second->enforceRestrictedDataTtls(wallClockNs);
+ }
+ mLastTtlTime = elapsedRealtimeNs;
+}
+
+void StatsLogProcessor::enforceDbGuardrailsIfNecessaryLocked(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs) {
+ if (elapsedRealtimeNs - mLastDbGuardrailEnforcementTime <
+ StatsdStats::kMinDbGuardrailEnforcementPeriodNs) {
+ return;
+ }
+ StorageManager::enforceDbGuardrails(STATS_RESTRICTED_DATA_DIR, wallClockNs / NS_PER_SEC,
+ StatsdStats::kMaxFileSize);
+ mLastDbGuardrailEnforcementTime = elapsedRealtimeNs;
+}
+
+void StatsLogProcessor::fillRestrictedMetrics(const int64_t configId, const string& configPackage,
+ const int32_t delegateUid, vector<int64_t>* output) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+
+ set<int32_t> configPackageUids;
+ const auto& uidMapItr = UidMap::sAidToUidMapping.find(configPackage);
+ if (uidMapItr != UidMap::sAidToUidMapping.end()) {
+ configPackageUids.insert(uidMapItr->second);
+ } else {
+ configPackageUids = mUidMap->getAppUid(configPackage);
+ }
+ string err;
+ InvalidQueryReason invalidQueryReason;
+ set<ConfigKey> keysToGetMetrics = getRestrictedConfigKeysToQueryLocked(
+ delegateUid, configId, configPackageUids, err, invalidQueryReason);
+
+ for (const ConfigKey& key : keysToGetMetrics) {
+ vector<int64_t> metricIds = mMetricsManagers[key]->getAllMetricIds();
+ output->insert(output->end(), metricIds.begin(), metricIds.end());
+ }
+}
+
+void StatsLogProcessor::flushRestrictedDataLocked(const int64_t elapsedRealtimeNs) {
+ for (const auto& it : mMetricsManagers) {
+ // no-op if metricsManager is not restricted
+ it.second->flushRestrictedData();
+ }
+
+ mLastFlushRestrictedTime = elapsedRealtimeNs;
+}
+
void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key,
MetricsManager& metricsManager) {
int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
@@ -804,22 +1120,31 @@
// We suspect that the byteSize() computation is expensive, so we set a rate limit.
size_t totalBytes = metricsManager.byteSize();
+
mLastByteSizeTimes[key] = elapsedRealtimeNs;
+ const size_t kBytesPerConfig = metricsManager.hasRestrictedMetricsDelegate()
+ ? StatsdStats::kBytesPerRestrictedConfigTriggerFlush
+ : metricsManager.getTriggerGetDataBytes();
bool requestDump = false;
if (totalBytes > metricsManager.getMaxMetricsBytes()) {
// Too late. We need to start clearing data.
metricsManager.dropData(elapsedRealtimeNs);
StatsdStats::getInstance().noteDataDropped(key, totalBytes);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
- } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
+ } else if ((totalBytes > kBytesPerConfig) ||
(mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) {
- // Request to send a broadcast if:
+ // Request to dump if:
// 1. in memory data > threshold OR
// 2. config has old data report on disk.
requestDump = true;
}
if (requestDump) {
+ if (metricsManager.hasRestrictedMetricsDelegate()) {
+ metricsManager.flushRestrictedData();
+ // No need to send broadcast for restricted metrics.
+ return;
+ }
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
@@ -846,6 +1171,10 @@
!mMetricsManagers.find(key)->second->shouldWriteToDisk()) {
return;
}
+ if (mMetricsManagers.find(key)->second->hasRestrictedMetricsDelegate()) {
+ mMetricsManagers.find(key)->second->flushRestrictedData();
+ return;
+ }
vector<uint8_t> buffer;
onConfigMetricsReportLocked(key, timestampNs, wallClockNs,
true /* include_current_partial_bucket*/, true /* erase_data */,
@@ -1098,7 +1427,7 @@
}
}
-void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
+void StatsLogProcessor::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk,
const int uid, const int64_t version) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Received app upgrade");
@@ -1108,7 +1437,7 @@
}
}
-void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
+void StatsLogProcessor::notifyAppRemoved(const int64_t eventTimeNs, const string& apk,
const int uid) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Received app removed");
@@ -1118,7 +1447,7 @@
}
}
-void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
+void StatsLogProcessor::onUidMapReceived(const int64_t eventTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Received uid map");
StateManager::getInstance().updateLogSources(mUidMap);
@@ -1127,7 +1456,8 @@
}
}
-void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
+void StatsLogProcessor::onStatsdInitCompleted(const int64_t elapsedTimeNs) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMetricsMutex);
VLOG("Received boot completed signal");
for (const auto& it : mMetricsManagers) {
@@ -1167,17 +1497,14 @@
// we add also atoms which could be pushed by statsd itself to simplify the logic
// to handle metric configs update: APP_BREADCRUMB_REPORTED & ANOMALY_DETECTED
LogEventFilter::AtomIdSet allAtomIds{
- util::BINARY_PUSH_STATE_CHANGED, util::DAVEY_OCCURRED,
- util::ISOLATED_UID_CHANGED, util::APP_BREADCRUMB_REPORTED,
- util::WATCHDOG_ROLLBACK_OCCURRED, util::ANOMALY_DETECTED};
+ util::BINARY_PUSH_STATE_CHANGED, util::ISOLATED_UID_CHANGED,
+ util::APP_BREADCRUMB_REPORTED, util::WATCHDOG_ROLLBACK_OCCURRED,
+ util::ANOMALY_DETECTED, util::STATS_SOCKET_LOSS_REPORTED};
return allAtomIds;
}
void StatsLogProcessor::updateLogEventFilterLocked() const {
VLOG("StatsLogProcessor: Updating allAtomIds");
- if (!mLogEventFilter) {
- return;
- }
LogEventFilter::AtomIdSet allAtomIds = getDefaultAtomIdSet();
for (const auto& metricsManager : mMetricsManagers) {
metricsManager.second->addAllAtomIds(allAtomIds);
@@ -1198,6 +1525,42 @@
}
}
+bool StatsLogProcessor::validateAppBreadcrumbEvent(const LogEvent& event) const {
+ if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
+ // Check that app breadcrumb reported fields are valid.
+ status_t err = NO_ERROR;
+
+ // Uid is 3rd from last field and must match the caller's uid,
+ // unless that caller is statsd itself (statsd is allowed to spoof uids).
+ const long appHookUid = event.GetLong(event.size() - 2, &err);
+ if (err != NO_ERROR) {
+ VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
+ return false;
+ }
+
+ // Because the uid within the LogEvent may have been mapped from
+ // isolated to host, map the loggerUid similarly before comparing.
+ const int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
+ if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
+ VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
+ appHookUid, loggerUid);
+ return false;
+ }
+
+ // The state must be from 0,3. This part of code must be manually updated.
+ const long appHookState = event.GetLong(event.size(), &err);
+ if (err != NO_ERROR) {
+ VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
+ return false;
+ } else if (appHookState < 0 || appHookState > 3) {
+ VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
+ return false;
+ }
+ }
+
+ return true;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/StatsLogProcessor.h b/statsd/src/StatsLogProcessor.h
index 135c108..b6d66af 100644
--- a/statsd/src/StatsLogProcessor.h
+++ b/statsd/src/StatsLogProcessor.h
@@ -16,40 +16,42 @@
#pragma once
+#include <aidl/android/os/BnStatsd.h>
#include <gtest/gtest_prod.h>
+#include <stdio.h>
+
+#include <unordered_map>
+
#include "config/ConfigListener.h"
+#include "external/StatsPullerManager.h"
#include "logd/LogEvent.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
#include "socket/LogEventFilter.h"
-#include "external/StatsPullerManager.h"
-
#include "src/statsd_config.pb.h"
#include "src/statsd_metadata.pb.h"
-#include <stdio.h>
-#include <unordered_map>
-
namespace android {
namespace os {
namespace statsd {
-
class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener {
public:
StatsLogProcessor(
const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, const int64_t timeBaseNs,
+ const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, int64_t timeBaseNs,
const std::function<bool(const ConfigKey&)>& sendBroadcast,
const std::function<bool(const int&, const vector<int64_t>&)>& sendActivationBroadcast,
+ const std::function<void(const ConfigKey&, const string&, const vector<int64_t>&)>&
+ sendRestrictedMetricsBroadcast,
const std::shared_ptr<LogEventFilter>& logEventFilter);
virtual ~StatsLogProcessor();
void OnLogEvent(LogEvent* event);
- void OnConfigUpdated(const int64_t timestampNs, const int64_t wallClockNs, const ConfigKey& key,
+ void OnConfigUpdated(const int64_t timestampNs, int64_t wallClockNs, const ConfigKey& key,
const StatsdConfig& config, bool modularUpdate = true);
// For testing only.
void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
@@ -60,28 +62,28 @@
void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs);
- void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const int64_t wallClockNs,
+ void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs, int64_t wallClockNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
vector<uint8_t>* outData);
- void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const int64_t wallClockNs,
+ void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs, int64_t wallClockNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
ProtoOutputStream* proto);
// For testing only.
- void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
+ void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
vector<uint8_t>* outData);
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
void onPeriodicAlarmFired(
- const int64_t& timestampNs,
+ int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
/* Flushes data to disk. Data on memory will be gone after written to disk. */
void WriteDataToDisk(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
- const int64_t elapsedRealtimeNs, const int64_t wallClockNs);
+ const int64_t elapsedRealtimeNs, int64_t wallClockNs);
/* Persist configs containing metrics with active activations to disk. */
void SaveActiveConfigsToDisk(int64_t currentTimeNs);
@@ -110,23 +112,26 @@
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
+ /* Enforces ttls for restricted metrics */
+ void EnforceDataTtls(const int64_t wallClockNs, int64_t elapsedRealtimeNs);
+
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
/* Notify all MetricsManagers of app upgrades */
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override;
+ void notifyAppUpgrade(int64_t eventTimeNs, const string& apk, int uid,
+ int64_t version) override;
/* Notify all MetricsManagers of app removals */
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+ void notifyAppRemoved(int64_t eventTimeNs, const string& apk, int uid) override;
/* Notify all MetricsManagers of uid map snapshots received */
- void onUidMapReceived(const int64_t& eventTimeNs) override;
+ void onUidMapReceived(int64_t eventTimeNs) override;
/* Notify all metrics managers of boot completed
* This will force a bucket split when the boot is finished.
*/
- void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+ void onStatsdInitCompleted(int64_t elapsedTimeNs);
// Reset all configs.
void resetConfigs();
@@ -135,7 +140,7 @@
return mUidMap;
}
- void dumpStates(int outFd, bool verbose);
+ void dumpStates(int outFd, bool verbose) const;
void informPullAlarmFired(const int64_t timestampNs);
@@ -144,12 +149,9 @@
inline void setPrintLogs(bool enabled) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mPrintAllLogs = enabled;
-
- if (mLogEventFilter) {
- // Turning on print logs turns off pushed event filtering to enforce
- // complete log event buffer parsing
- mLogEventFilter->setFilteringEnabled(!enabled);
- }
+ // Turning on print logs turns off pushed event filtering to enforce
+ // complete log event buffer parsing
+ mLogEventFilter->setFilteringEnabled(!enabled);
}
// Add a specific config key to the possible configs to dump ASAP.
@@ -159,6 +161,14 @@
void cancelAnomalyAlarm();
+ void querySql(const string& sqlQuery, const int32_t minSqlClientVersion,
+ const optional<vector<uint8_t>>& policyConfig,
+ const shared_ptr<aidl::android::os::IStatsQueryCallback>& callback,
+ const int64_t configId, const string& configPackage, const int32_t callingUid);
+
+ void fillRestrictedMetrics(const int64_t configId, const string& configPackage,
+ const int32_t delegateUid, vector<int64_t>* output);
+
/* Returns pre-defined list of atoms to parse by LogEventFilter */
static LogEventFilter::AtomIdSet getDefaultAtomIdSet();
@@ -189,6 +199,18 @@
// Tracks when we last checked the bytes consumed for each config key.
std::unordered_map<ConfigKey, int64_t> mLastByteSizeTimes;
+ // Tracks the number of times a config with a specified config key has been dumped.
+ std::unordered_map<ConfigKey, int32_t> mDumpReportNumbers;
+
+ // Tracks when we last checked the ttl for restricted metrics.
+ int64_t mLastTtlTime;
+
+ // Tracks when we last flushed restricted metrics.
+ int64_t mLastFlushRestrictedTime;
+
+ // Tracks when we last checked db guardrails.
+ int64_t mLastDbGuardrailEnforcementTime;
+
// Tracks which config keys has metric reports on disk
std::set<ConfigKey> mOnDiskDataConfigs;
@@ -226,25 +248,42 @@
metadata::StatsMetadataList* metadataList);
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency, const int64_t elapsedRealtimeNs,
+ const DumpLatency dumpLatency, int64_t elapsedRealtimeNs,
const int64_t wallClockNs);
- void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
- const int64_t wallClockNs, const DumpReportReason dumpReportReason,
+ void WriteDataToDiskLocked(const ConfigKey& key, int64_t timestampNs, const int64_t wallClockNs,
+ const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
void onConfigMetricsReportLocked(
- const ConfigKey& key, const int64_t dumpTimeStampNs, const int64_t wallClockNs,
+ const ConfigKey& key, int64_t dumpTimeStampNs, int64_t wallClockNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
/*if dataSavedToDisk is true, it indicates the caller will write the data to disk
(e.g., before reboot). So no need to further persist local history.*/
const bool dataSavedToDisk, vector<uint8_t>* proto);
+ /* Check if it is time enforce data ttls for restricted metrics, and if it is, enforce ttls
+ * on all restricted metrics. */
+ void enforceDataTtlsIfNecessaryLocked(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs);
+
+ // Enforces ttls on all restricted metrics.
+ void enforceDataTtlsLocked(const int64_t wallClockNs, int64_t elapsedRealtimeNs);
+
+ // Enforces that dbs are within guardrail parameters.
+ void enforceDbGuardrailsIfNecessaryLocked(const int64_t wallClockNs,
+ const int64_t elapsedRealtimeNs);
+
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
+ set<ConfigKey> getRestrictedConfigKeysToQueryLocked(int32_t callingUid, const int64_t configId,
+ const set<int32_t>& configPackageUids,
+ string& err,
+ InvalidQueryReason& invalidQueryReason);
+
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
@@ -264,8 +303,8 @@
// Gets experiment ids on disk for associated train and updates them
// depending on rollback type. Then writes them back to disk and returns
// them.
- std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
- const string& packageName);
+ std::vector<int64_t> processWatchdogRollbackOccurred(int32_t rollbackTypeIn,
+ const string& packageName);
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);
@@ -278,14 +317,20 @@
/* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
void processFiredAnomalyAlarmsLocked(
- const int64_t& timestampNs,
+ int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
+ void flushRestrictedDataLocked(const int64_t elapsedRealtimeNs);
+
+ void flushRestrictedDataIfNecessaryLocked(const int64_t elapsedRealtimeNs);
+
/* Tells LogEventFilter about atom ids to parse */
void updateLogEventFilterLocked() const;
void writeDataCorruptedReasons(ProtoOutputStream& proto);
+ bool validateAppBreadcrumbEvent(const LogEvent& event) const;
+
// Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data.
std::function<bool(const ConfigKey& key)> mSendBroadcast;
@@ -294,6 +339,12 @@
// are currently active.
std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast;
+ // Function used to send a broadcast if necessary so the receiver can be notified of the
+ // restricted metrics for the given config.
+ std::function<void(const ConfigKey& key, const string& delegatePackage,
+ const vector<int64_t>& restrictedMetricIds)>
+ mSendRestrictedMetricsBroadcast;
+
const int64_t mTimeBaseNs;
// Largest timestamp of the events that we have processed.
@@ -317,6 +368,7 @@
bool mPrintAllLogs = false;
+ friend class StatsLogProcessorTestRestricted;
FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
@@ -333,6 +385,17 @@
FRIEND_TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap);
FRIEND_TEST(StatsLogProcessorTest, TestReportIncludesSubConfig);
FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, TestInconsistentRestrictedMetricsConfigUpdate);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, TestRestrictedLogEventPassed);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, TestRestrictedLogEventNotPassed);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, RestrictedMetricsManagerOnDumpReportNotCalled);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, NonRestrictedMetricsManagerOnDumpReportCalled);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, RestrictedMetricOnDumpReportEmpty);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, NonRestrictedMetricOnDumpReportNotEmpty);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, RestrictedMetricNotWriteToDisk);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, NonRestrictedMetricWriteToDisk);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, RestrictedMetricFlushIfReachMemoryLimit);
+ FRIEND_TEST(StatsLogProcessorTestRestricted, RestrictedMetricNotFlushIfNotReachMemoryLimit);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3);
@@ -352,6 +415,19 @@
FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2ePulledTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2ePulledTest, TestConditionChangeToTrueSamplePulledEvents);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestEnforceTtlRemovesOldEvents);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestFlagDisabled);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestLogEventsEnforceTtls);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestQueryEnforceTtls);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestLogEventsDoesNotEnforceTtls);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestNotFlushed);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestFlushInWriteDataToDisk);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestFlushPeriodically);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestTTlsEnforceDbGuardrails);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestOnLogEventMalformedDbNameDeleted);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestEnforceDbGuardrails);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestEnforceDbGuardrailsDoesNotDeleteBeforeGuardrail);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestRestrictedMetricLoadsTtlFromDisk);
FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyCountDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
@@ -409,10 +485,16 @@
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithValueFieldPositionALL);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithMultipleAggTypes);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithDefaultAggType);
FRIEND_TEST(KllMetricE2eTest, TestInitWithKllFieldPositionALL);
FRIEND_TEST(StatsServiceStatsdInitTest, StatsServiceStatsdInitTest);
+
+ FRIEND_TEST(StringReplaceE2eTest, TestPulledDimension);
+ FRIEND_TEST(StringReplaceE2eTest, TestPulledWhat);
+ FRIEND_TEST(StringReplaceE2eTest, TestMultipleMatchersForAtom);
};
} // namespace statsd
diff --git a/statsd/src/StatsService.cpp b/statsd/src/StatsService.cpp
index 6450115..9cbd7b1 100644
--- a/statsd/src/StatsService.cpp
+++ b/statsd/src/StatsService.cpp
@@ -18,28 +18,33 @@
#include "Log.h"
#include "StatsService.h"
-#include "stats_log_util.h"
-#include "android-base/stringprintf.h"
-#include "config/ConfigKey.h"
-#include "config/ConfigManager.h"
-#include "guardrail/StatsdStats.h"
-#include "storage/StorageManager.h"
-#include "subscriber/SubscriberReporter.h"
#include <android-base/file.h>
#include <android-base/strings.h>
#include <android/binder_ibinder_platform.h>
#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
#include <src/statsd_config.pb.h>
#include <src/uid_data.pb.h>
-#include <private/android_filesystem_config.h>
#include <statslog_statsd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/random.h>
#include <sys/system_properties.h>
#include <unistd.h>
#include <utils/String16.h>
+#include "android-base/stringprintf.h"
+#include "config/ConfigKey.h"
+#include "config/ConfigManager.h"
+#include "flags/FlagProvider.h"
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "storage/StorageManager.h"
+#include "subscriber/SubscriberReporter.h"
+#include "utils/DbUtils.h"
+#include "utils/api_tracing.h"
+
using namespace android;
using android::base::StringPrintf;
@@ -196,6 +201,27 @@
VLOG("StatsService::active configs broadcast failed for uid %d", uid);
return false;
},
+ [this](const ConfigKey& key, const string& delegatePackage,
+ const vector<int64_t>& restrictedMetrics) {
+ set<string> configPackages;
+ set<int32_t> delegateUids;
+ for (const auto& kv : UidMap::sAidToUidMapping) {
+ if (kv.second == static_cast<uint32_t>(key.GetUid())) {
+ configPackages.insert(kv.first);
+ }
+ if (kv.first == delegatePackage) {
+ delegateUids.insert(kv.second);
+ }
+ }
+ if (configPackages.empty()) {
+ configPackages = mUidMap->getAppNamesFromUid(key.GetUid(), true);
+ }
+ if (delegateUids.empty()) {
+ delegateUids = mUidMap->getAppUid(delegatePackage);
+ }
+ mConfigManager->SendRestrictedMetricsBroadcast(configPackages, key.GetId(),
+ delegateUids, restrictedMetrics);
+ },
logEventFilter);
mUidMap->setListener(mProcessor);
@@ -204,12 +230,19 @@
init_system_properties();
if (mEventQueue != nullptr) {
- std::thread pushedEventThread([this] { readLogs(); });
- pushedEventThread.detach();
+ mLogsReaderThread = std::make_unique<std::thread>([this] { readLogs(); });
+ if (mLogsReaderThread) {
+ pthread_setname_np(mLogsReaderThread->native_handle(), "statsd.reader");
+ }
}
}
StatsService::~StatsService() {
+ ATRACE_CALL();
+ if (mEventQueue != nullptr) {
+ stopReadingLogs();
+ mLogsReaderThread->join();
+ }
}
/* Runs on a dedicated thread to process pushed events. */
@@ -218,6 +251,13 @@
while (1) {
// Block until an event is available.
auto event = mEventQueue->waitPop();
+
+ // Below flag will be set when statsd is exiting and log event will be pushed to break
+ // out of waitPop.
+ if (mIsStopRequested) {
+ break;
+ }
+
// Pass it to StatsLogProcess to all configs/metrics
// At this point, the LogEventQueue is not blocked, so that the socketListener
// can read events from the socket and write to buffer to avoid data drop.
@@ -841,8 +881,8 @@
int32_t state = atoi(args[6].c_str());
vector<int64_t> experimentIds;
if (argCount == 8) {
- vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ",");
- for (string experimentIdString : experimentIdsString) {
+ vector<string> experimentIdsStrings = android::base::Split(string(args[7].c_str()), ",");
+ for (const string& experimentIdString : experimentIdsStrings) {
int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10);
experimentIds.push_back(experimentId);
}
@@ -949,6 +989,7 @@
}
Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
// Parse fd into proto.
@@ -966,6 +1007,7 @@
Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
const string& versionString, const string& installer,
const vector<uint8_t>& certificateHash) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackage was called");
@@ -976,6 +1018,7 @@
}
Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackageRemoved was called");
@@ -991,6 +1034,7 @@
}
Status StatsService::informAlarmForSubscriberTriggeringFired() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
@@ -1007,6 +1051,7 @@
}
Status StatsService::informPollAlarmFired() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informPollAlarmFired was called");
@@ -1016,6 +1061,7 @@
}
Status StatsService::systemRunning() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
// When system_server is up and running, schedule the dropbox task to run.
@@ -1025,6 +1071,7 @@
}
Status StatsService::informDeviceShutdown() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informDeviceShutdown");
int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
@@ -1046,6 +1093,7 @@
}
Status StatsService::statsCompanionReady() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::statsCompanionReady was called");
@@ -1064,6 +1112,7 @@
}
Status StatsService::bootCompleted() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::bootCompleted was called");
@@ -1087,12 +1136,17 @@
}
void StatsService::Startup() {
+ ATRACE_CALL();
mConfigManager->Startup();
+ int64_t wallClockNs = getWallClockNs();
+ int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
mProcessor->LoadActiveConfigsFromDisk();
- mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs());
+ mProcessor->LoadMetadataFromDisk(wallClockNs, elapsedRealtimeNs);
+ mProcessor->EnforceDataTtls(wallClockNs, elapsedRealtimeNs);
}
void StatsService::Terminate() {
+ ATRACE_CALL();
ALOGI("StatsService::Terminating");
if (mProcessor != nullptr) {
int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
@@ -1113,6 +1167,7 @@
}
Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
getDataChecked(key, callingUid, output);
return Status::ok();
@@ -1120,6 +1175,7 @@
Status StatsService::getDataFd(int64_t key, const int32_t callingUid,
const ScopedFileDescriptor& fd) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
vector<uint8_t> reportData;
getDataChecked(key, callingUid, &reportData);
@@ -1156,6 +1212,7 @@
}
Status StatsService::getMetadata(vector<uint8_t>* output) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
@@ -1164,6 +1221,7 @@
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
if (addConfigurationChecked(callingUid, key, config)) {
@@ -1187,6 +1245,7 @@
Status StatsService::removeDataFetchOperation(int64_t key,
const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfigReceiver(configKey);
@@ -1196,6 +1255,7 @@
Status StatsService::setDataFetchOperation(int64_t key,
const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
ConfigKey configKey(callingUid, key);
@@ -1211,6 +1271,7 @@
Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid,
vector<int64_t>* output) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
@@ -1223,6 +1284,7 @@
}
Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
@@ -1230,6 +1292,7 @@
}
Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
ConfigKey configKey(callingUid, key);
@@ -1241,6 +1304,7 @@
int64_t subscriberId,
const shared_ptr<IPendingIntentRef>& pir,
const int32_t callingUid) {
+ ATRACE_CALL();
VLOG("StatsService::setBroadcastSubscriber called.");
ENFORCE_UID(AID_SYSTEM);
@@ -1258,6 +1322,7 @@
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const int32_t callingUid) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unsetBroadcastSubscriber called.");
@@ -1268,6 +1333,7 @@
}
Status StatsService::allPullersFromBootRegistered() {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::allPullersFromBootRegistered was called");
@@ -1279,6 +1345,7 @@
int64_t timeoutMillis,
const std::vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::registerPullAtomCallback called.");
mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
@@ -1291,6 +1358,7 @@
int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
const std::vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ ATRACE_CALL();
if (!checkPermission(kPermissionRegisterPullAtom)) {
return exception(
EX_SECURITY,
@@ -1306,6 +1374,7 @@
}
Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unregisterPullAtomCallback called.");
mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
@@ -1313,6 +1382,7 @@
}
Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
+ ATRACE_CALL();
if (!checkPermission(kPermissionRegisterPullAtom)) {
return exception(
EX_SECURITY,
@@ -1326,6 +1396,7 @@
}
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
+ ATRACE_CALL();
ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
@@ -1355,6 +1426,7 @@
}
void StatsService::statsCompanionServiceDied(void* cookie) {
+ ATRACE_CALL();
auto thiz = static_cast<StatsService*>(cookie);
thiz->statsCompanionServiceDiedImpl();
}
@@ -1388,8 +1460,61 @@
mPullerManager->SetStatsCompanionService(nullptr);
}
+Status StatsService::setRestrictedMetricsChangedOperation(const int64_t configId,
+ const string& configPackage,
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
+ vector<int64_t>* output) {
+ ATRACE_CALL();
+ ENFORCE_UID(AID_SYSTEM);
+ if (!isAtLeastU()) {
+ ALOGW("setRestrictedMetricsChangedOperation invoked on U- device");
+ return Status::ok();
+ }
+ mConfigManager->SetRestrictedMetricsChangedReceiver(configPackage, configId, callingUid, pir);
+ if (output != nullptr) {
+ mProcessor->fillRestrictedMetrics(configId, configPackage, callingUid, output);
+ } else {
+ ALOGW("StatsService::setRestrictedMetricsChangedOperation output was nullptr");
+ }
+ return Status::ok();
+}
+
+Status StatsService::removeRestrictedMetricsChangedOperation(const int64_t configId,
+ const string& configPackage,
+ const int32_t callingUid) {
+ ATRACE_CALL();
+ ENFORCE_UID(AID_SYSTEM);
+ if (!isAtLeastU()) {
+ ALOGW("removeRestrictedMetricsChangedOperation invoked on U- device");
+ return Status::ok();
+ }
+ mConfigManager->RemoveRestrictedMetricsChangedReceiver(configPackage, configId, callingUid);
+ return Status::ok();
+}
+
+Status StatsService::querySql(const string& sqlQuery, const int32_t minSqlClientVersion,
+ const optional<vector<uint8_t>>& policyConfig,
+ const shared_ptr<IStatsQueryCallback>& callback,
+ const int64_t configKey, const string& configPackage,
+ const int32_t callingUid) {
+ ATRACE_CALL();
+ ENFORCE_UID(AID_SYSTEM);
+ if (callback == nullptr) {
+ ALOGW("querySql called with null callback.");
+ StatsdStats::getInstance().noteQueryRestrictedMetricFailed(
+ configKey, configPackage, std::nullopt, callingUid,
+ InvalidQueryReason(NULL_CALLBACK));
+ return Status::ok();
+ }
+ mProcessor->querySql(sqlQuery, minSqlClientVersion, policyConfig, callback, configKey,
+ configPackage, callingUid);
+ return Status::ok();
+}
+
Status StatsService::addSubscription(const vector<uint8_t>& subscriptionConfig,
const shared_ptr<IStatsSubscriptionCallback>& callback) {
+ ATRACE_CALL();
ENFORCE_SID(kTracedProbesSid);
initShellSubscriber();
@@ -1399,6 +1524,7 @@
}
Status StatsService::removeSubscription(const shared_ptr<IStatsSubscriptionCallback>& callback) {
+ ATRACE_CALL();
ENFORCE_SID(kTracedProbesSid);
if (mShellSubscriber != nullptr) {
@@ -1408,6 +1534,7 @@
}
Status StatsService::flushSubscription(const shared_ptr<IStatsSubscriptionCallback>& callback) {
+ ATRACE_CALL();
ENFORCE_SID(kTracedProbesSid);
if (mShellSubscriber != nullptr) {
@@ -1423,6 +1550,14 @@
}
}
+void StatsService::stopReadingLogs() {
+ mIsStopRequested = true;
+ // Push this event so that readLogs will process and break out of the loop
+ // after the stop is requested.
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ mEventQueue->push(std::move(logEvent));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/StatsService.h b/statsd/src/StatsService.h
index fbc687c..0a167dd 100644
--- a/statsd/src/StatsService.h
+++ b/statsd/src/StatsService.h
@@ -46,6 +46,7 @@
using aidl::android::os::BnStatsd;
using aidl::android::os::IPendingIntentRef;
using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsQueryCallback;
using aidl::android::os::IStatsSubscriptionCallback;
using aidl::android::util::PropertyParcel;
using ::ndk::ScopedAIBinder_DeathRecipient;
@@ -145,7 +146,7 @@
/**
* Binder call to remove the active configs changed operation for the specified package..
*/
- virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
+ virtual Status removeActiveConfigsChangedOperation(int32_t callingUid) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
@@ -212,6 +213,33 @@
virtual Status updateProperties(const vector<PropertyParcel>& properties);
/**
+ * Binder call to let clients register the restricted metrics changed operation for the given
+ * config and calling uid.
+ */
+ virtual Status setRestrictedMetricsChangedOperation(const int64_t configKey,
+ const string& configPackage,
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
+ vector<int64_t>* output);
+
+ /**
+ * Binder call to remove the restricted metrics changed operation for the specified config
+ * and calling uid.
+ */
+ virtual Status removeRestrictedMetricsChangedOperation(const int64_t configKey,
+ const string& configPackage,
+ const int32_t callingUid);
+
+ /**
+ * Binder call to query data in statsd sql store.
+ */
+ virtual Status querySql(const string& sqlQuery, const int32_t minSqlClientVersion,
+ const optional<vector<uint8_t>>& policyConfig,
+ const shared_ptr<IStatsQueryCallback>& callback,
+ const int64_t configKey, const string& configPackage,
+ const int32_t callingUid);
+
+ /**
* Binder call to add a subscription.
*/
virtual Status addSubscription(const vector<uint8_t>& subscriptionConfig,
@@ -387,6 +415,13 @@
*/
void onStatsdInitCompleted();
+ /*
+ * This method is used to stop log reader thread.
+ */
+ void stopReadingLogs();
+
+ std::atomic<bool> mIsStopRequested = false;
+
/**
* Tracks the uid <--> package name mapping.
*/
@@ -431,6 +466,8 @@
shared_ptr<LogEventQueue> mEventQueue;
std::shared_ptr<LogEventFilter> mLogEventFilter;
+ std::unique_ptr<std::thread> mLogsReaderThread;
+
MultiConditionTrigger mBootCompleteTrigger;
static const inline string kBootCompleteTag = "BOOT_COMPLETE";
static const inline string kUidMapReceivedTag = "UID_MAP";
@@ -442,6 +479,7 @@
friend class StatsServiceConfigTest;
friend class StatsServiceStatsdInitTest;
+ friend class RestrictedConfigE2ETest;
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
@@ -461,7 +499,10 @@
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitByDefault);
-
+ FRIEND_TEST(RestrictedConfigE2ETest, NonRestrictedConfigGetReport);
+ FRIEND_TEST(RestrictedConfigE2ETest, RestrictedConfigNoReport);
+ FRIEND_TEST(RestrictedConfigE2ETest,
+ TestSendRestrictedMetricsChangedBroadcastMultipleMatchedConfigs);
FRIEND_TEST(ConfigUpdateE2eTest, TestAnomalyDurationMetric);
FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
diff --git a/statsd/src/annotations.h b/statsd/src/annotations.h
deleted file mode 100644
index cf7f543..0000000
--- a/statsd/src/annotations.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 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
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const uint8_t ANNOTATION_ID_IS_UID = 1;
-const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
-const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3;
-const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4;
-const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
-const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
-const uint8_t ANNOTATION_ID_STATE_NESTED = 8;
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/statsd/src/anomaly/AlarmMonitor.cpp b/statsd/src/anomaly/AlarmMonitor.cpp
index 4822b54..6359c5f 100644
--- a/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/statsd/src/anomaly/AlarmMonitor.cpp
@@ -36,7 +36,7 @@
AlarmMonitor::~AlarmMonitor() {}
void AlarmMonitor::setStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {
+ const shared_ptr<IStatsCompanionService>& statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
@@ -51,7 +51,7 @@
}
}
-void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
+void AlarmMonitor::add(const sp<const InternalAlarm>& alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to add a null alarm.");
@@ -71,7 +71,7 @@
}
}
-void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
+void AlarmMonitor::remove(const sp<const InternalAlarm>& alarm) {
std::lock_guard<std::mutex> lock(mLock);
if (alarm == nullptr) {
ALOGW("Asked to remove a null alarm.");
diff --git a/statsd/src/anomaly/AlarmMonitor.h b/statsd/src/anomaly/AlarmMonitor.h
index 5c34e38..ee955c9 100644
--- a/statsd/src/anomaly/AlarmMonitor.h
+++ b/statsd/src/anomaly/AlarmMonitor.h
@@ -49,7 +49,7 @@
/** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
struct SmallerTimestamp {
- bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
+ bool operator()(const sp<const InternalAlarm>& a, const sp<const InternalAlarm>& b) const {
return (a->timestampSec < b->timestampSec);
}
};
@@ -77,19 +77,19 @@
* If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
* update IStatsCompanionService (until such time as it is set non-null).
*/
- void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
+ void setStatsCompanionService(const shared_ptr<IStatsCompanionService>& statsCompanionService);
/**
* Adds the given alarm (reference) to the queue.
*/
- void add(sp<const InternalAlarm> alarm);
+ void add(const sp<const InternalAlarm>& alarm);
/**
* Removes the given alarm (reference) from the queue.
* Note that alarm comparison is reference-based; if another alarm exists
* with the same timestampSec, that alarm will still remain in the queue.
*/
- void remove(sp<const InternalAlarm> alarm);
+ void remove(const sp<const InternalAlarm>& alarm);
/**
* Returns and removes all alarms whose timestamp <= the given timestampSec.
diff --git a/statsd/src/anomaly/AlarmTracker.cpp b/statsd/src/anomaly/AlarmTracker.cpp
index e8c70fa..9d1aa10 100644
--- a/statsd/src/anomaly/AlarmTracker.cpp
+++ b/statsd/src/anomaly/AlarmTracker.cpp
@@ -69,14 +69,20 @@
}
void AlarmTracker::informAlarmsFired(
- const int64_t& timestampNs,
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
if (firedAlarms.empty() || mInternalAlarm == nullptr ||
firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
return;
}
- if (!mSubscriptions.empty()) {
- VLOG("AlarmTracker triggers the subscribers.");
+ if (!mSubscriptions.empty() &&
+ (mAlarmConfig.probability_of_informing() >= 1 ||
+ (mAlarmConfig.probability_of_informing() < 1 &&
+ ((float)rand() / (float)RAND_MAX) < mAlarmConfig.probability_of_informing()))) {
+ // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
+ // The config writer was advised to use -0.1 and 1.1 for never/always.
+
+ ALOGI("Fate decided that an alarm will trigger subscribers.");
triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
0 /* metricValue N/A */, mConfigKey, mSubscriptions);
}
diff --git a/statsd/src/anomaly/AlarmTracker.h b/statsd/src/anomaly/AlarmTracker.h
index 4b8fab3..c89099f 100644
--- a/statsd/src/anomaly/AlarmTracker.h
+++ b/statsd/src/anomaly/AlarmTracker.h
@@ -42,7 +42,8 @@
void addSubscription(const Subscription& subscription);
- void informAlarmsFired(const int64_t& timestampNs,
+ void informAlarmsFired(
+ int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
protected:
diff --git a/statsd/src/anomaly/AnomalyTracker.cpp b/statsd/src/anomaly/AnomalyTracker.cpp
index fe390ca..2829bff 100644
--- a/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/statsd/src/anomaly/AnomalyTracker.cpp
@@ -63,7 +63,7 @@
return bucketNum % mNumOfPastBuckets;
}
-void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
+void AnomalyTracker::advanceMostRecentBucketTo(const int64_t bucketNum) {
VLOG("advanceMostRecentBucketTo() called.");
if (mNumOfPastBuckets <= 0) {
return;
@@ -89,9 +89,8 @@
mMostRecentBucketNum = bucketNum;
}
-void AnomalyTracker::addPastBucket(const MetricDimensionKey& key,
- const int64_t& bucketValue,
- const int64_t& bucketNum) {
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t bucketValue,
+ const int64_t bucketNum) {
VLOG("addPastBucket(bucketValue) called.");
if (mNumOfPastBuckets == 0 ||
bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
@@ -119,8 +118,8 @@
}
}
-void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket,
- const int64_t& bucketNum) {
+void AnomalyTracker::addPastBucket(const std::shared_ptr<DimToValMap>& bucket,
+ const int64_t bucketNum) {
VLOG("addPastBucket(bucket) called.");
if (mNumOfPastBuckets == 0 ||
bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
@@ -147,9 +146,8 @@
}
}
-
void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key,
- const int64_t& bucketValue) {
+ const int64_t bucketValue) {
auto itr = mSumOverPastBuckets.find(key);
if (itr == mSumOverPastBuckets.end()) {
return;
@@ -171,7 +169,7 @@
}
int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
- const int64_t& bucketNum) const {
+ const int64_t bucketNum) const {
if (bucketNum < 0 || mMostRecentBucketNum < 0
|| bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
|| bucketNum > mMostRecentBucketNum) {
@@ -194,10 +192,8 @@
return 0;
}
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
-
+bool AnomalyTracker::detectAnomaly(const int64_t currentBucketNum, const MetricDimensionKey& key,
+ const int64_t currentBucketValue) {
// currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is.
if (currentBucketNum > mMostRecentBucketNum + 1) {
advanceMostRecentBucketTo(currentBucketNum - 1);
@@ -206,7 +202,7 @@
getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
}
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+void AnomalyTracker::declareAnomaly(const int64_t timestampNs, int64_t metricId,
const MetricDimensionKey& key, int64_t metricValue) {
// TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
// real time right now.
@@ -214,6 +210,19 @@
VLOG("Skipping anomaly declaration since within refractory period");
return;
}
+
+ // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
+ util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(), mConfigKey.GetId(), mAlert.id());
+
+ if (mAlert.probability_of_informing() < 1 &&
+ ((float)rand() / (float)RAND_MAX) >= mAlert.probability_of_informing()) {
+ // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
+ // The config writer was advised to use -0.1 and 1.1 for never/always.
+ ALOGI("Fate decided that an alert will not trigger subscribers or start the refactory "
+ "period countdown.");
+ return;
+ }
+
if (mAlert.has_refractory_period_secs()) {
mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
+ mAlert.refractory_period_secs();
@@ -231,22 +240,17 @@
}
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
-
- // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
- util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
- mConfigKey.GetId(), mAlert.id());
}
-void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
- const int64_t& currBucketNum, int64_t metricId,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
+void AnomalyTracker::detectAndDeclareAnomaly(const int64_t timestampNs, const int64_t currBucketNum,
+ int64_t metricId, const MetricDimensionKey& key,
+ const int64_t currentBucketValue) {
if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
declareAnomaly(timestampNs, metricId, key, currentBucketValue);
}
}
-bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs,
+bool AnomalyTracker::isInRefractoryPeriod(const int64_t timestampNs,
const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
diff --git a/statsd/src/anomaly/AnomalyTracker.h b/statsd/src/anomaly/AnomalyTracker.h
index 281c1f6..d57a4b8 100644
--- a/statsd/src/anomaly/AnomalyTracker.h
+++ b/statsd/src/anomaly/AnomalyTracker.h
@@ -34,7 +34,6 @@
using std::optional;
using std::shared_ptr;
-using std::unordered_map;
// Does NOT allow negative values.
class AnomalyTracker : public virtual RefBase {
@@ -55,32 +54,30 @@
// If a bucket for bucketNum already exists, it will be replaced.
// Also, advances to bucketNum (if not in the past), effectively filling any intervening
// buckets with 0s.
- void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum);
+ void addPastBucket(const std::shared_ptr<DimToValMap>& bucket, const int64_t bucketNum);
// Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the
// given bucketValue. If the bucket does not exist, it will be created.
// Also, advances to bucketNum (if not in the past), effectively filling any intervening
// buckets with 0s.
- void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
- const int64_t& bucketNum);
+ void addPastBucket(const MetricDimensionKey& key, int64_t bucketValue, int64_t bucketNum);
// Returns true if, based on past buckets plus the new currentBucketValue (which generally
// represents the partially-filled current bucket), an anomaly has happened.
// Also advances to currBucketNum-1.
- bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
- const int64_t& currentBucketValue);
+ bool detectAnomaly(int64_t currBucketNum, const MetricDimensionKey& key,
+ int64_t currentBucketValue);
// Informs incidentd about the detected alert.
- void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+ void declareAnomaly(int64_t timestampNs, int64_t metricId, const MetricDimensionKey& key,
int64_t metricValue);
// Detects if, based on past buckets plus the new currentBucketValue (which generally
// represents the partially-filled current bucket), an anomaly has happened, and if so,
// declares an anomaly and informs relevant subscribers.
// Also advances to currBucketNum-1.
- void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
- int64_t metricId, const MetricDimensionKey& key,
- const int64_t& currentBucketValue);
+ void detectAndDeclareAnomaly(int64_t timestampNs, int64_t currBucketNum, int64_t metricId,
+ const MetricDimensionKey& key, int64_t currentBucketValue);
// Init the AlarmMonitor which is shared across anomaly trackers.
virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -91,7 +88,7 @@
int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
// Returns the value for a past bucket, or 0 if that bucket doesn't exist.
- int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
+ int64_t getPastBucketValue(const MetricDimensionKey& key, int64_t bucketNum) const;
// Returns the anomaly threshold set in the configuration.
inline int64_t getAnomalyThreshold() const {
@@ -115,14 +112,14 @@
// Sets an alarm for the given timestamp.
// Replaces previous alarm if one already exists.
- virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) {
+ virtual void startAlarm(const MetricDimensionKey& dimensionKey, int64_t eventTime) {
return; // The base AnomalyTracker class doesn't have alarms.
}
// Stops the alarm.
// If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
// declare the anomaly now.
- virtual void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) {
+ virtual void stopAlarm(const MetricDimensionKey& dimensionKey, int64_t timestampNs) {
return; // The base AnomalyTracker class doesn't have alarms.
}
@@ -133,7 +130,8 @@
// Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
// and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
- virtual void informAlarmsFired(const int64_t& timestampNs,
+ virtual void informAlarmsFired(
+ int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
return; // The base AnomalyTracker class doesn't have alarms.
}
@@ -185,12 +183,12 @@
// declared for that dimension) ends, in seconds. From this moment and onwards, anomalies
// can be declared again.
// Entries may be, but are not guaranteed to be, removed after the period is finished.
- unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+ std::unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
// Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old.
// Specifically, since it is now too old, removes the data for
// [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets].
- void advanceMostRecentBucketTo(const int64_t& bucketNum);
+ void advanceMostRecentBucketTo(int64_t bucketNum);
// Add the information in the given bucket to mSumOverPastBuckets.
void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
@@ -200,10 +198,10 @@
void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
// From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0.
- void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
+ void subtractValueFromSum(const MetricDimensionKey& key, int64_t bucketValue);
// Returns true if in the refractory period, else false.
- bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const;
+ bool isInRefractoryPeriod(int64_t timestampNs, const MetricDimensionKey& key) const;
// Calculates the corresponding bucket index within the circular array.
// Requires bucketNum >= 0.
@@ -217,7 +215,6 @@
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
- FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDurationDetectionE2eTest, TestDurationMetric_SUM_partial_bucket);
diff --git a/statsd/src/anomaly/DurationAnomalyTracker.cpp b/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 66c71ad..82e5bc8 100644
--- a/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -36,7 +36,7 @@
}
void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
- const int64_t& timestampNs) {
+ const int64_t timestampNs) {
// Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
@@ -57,7 +57,7 @@
}
void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey,
- const int64_t& timestampNs) {
+ const int64_t timestampNs) {
const auto itr = mAlarms.find(dimensionKey);
if (itr == mAlarms.end()) {
return;
@@ -84,16 +84,16 @@
mAlarms.clear();
}
-void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs,
+void DurationAnomalyTracker::informAlarmsFired(
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
-
if (firedAlarms.empty() || mAlarms.empty()) return;
// Find the intersection of firedAlarms and mAlarms.
// The for loop is inefficient, since it loops over all keys, but that's okay since it is very
// seldomly called. The alternative would be having InternalAlarms store information about the
// DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
// is rarely ever called.
- unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
+ std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
for (const auto& kv : mAlarms) {
if (firedAlarms.count(kv.second) > 0) {
matchedAlarms.insert({kv.first, kv.second});
diff --git a/statsd/src/anomaly/DurationAnomalyTracker.h b/statsd/src/anomaly/DurationAnomalyTracker.h
index a523782..73e741e 100644
--- a/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -23,8 +23,6 @@
namespace os {
namespace statsd {
-using std::unordered_map;
-
class DurationAnomalyTracker : public virtual AnomalyTracker {
public:
DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
@@ -34,12 +32,12 @@
// Sets an alarm for the given timestamp.
// Replaces previous alarm if one already exists.
- void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override;
+ void startAlarm(const MetricDimensionKey& dimensionKey, int64_t eventTime) override;
// Stops the alarm.
// If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
// declare the anomaly now.
- void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) override;
+ void stopAlarm(const MetricDimensionKey& dimensionKey, int64_t timestampNs) override;
// Stop all the alarms owned by this tracker. Does not declare any anomalies.
void cancelAllAlarms() override;
@@ -48,7 +46,8 @@
// and removes it from firedAlarms. The AlarmMonitor is not informed.
// Note that this will generally be called from a different thread from the other functions;
// the caller is responsible for thread safety.
- void informAlarmsFired(const int64_t& timestampNs,
+ void informAlarmsFired(
+ int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
protected:
diff --git a/statsd/src/anomaly/subscriber_util.cpp b/statsd/src/anomaly/subscriber_util.cpp
index 34f3320..19d0ea5 100644
--- a/statsd/src/anomaly/subscriber_util.cpp
+++ b/statsd/src/anomaly/subscriber_util.cpp
@@ -17,7 +17,10 @@
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
+#include "subscriber_util.h"
+
#include "external/Perfetto.h"
+#include "external/Uprobestats.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
@@ -25,8 +28,9 @@
namespace os {
namespace statsd {
-void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
- int64_t metricValue, const ConfigKey& configKey,
+void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+ const MetricDimensionKey& dimensionKey, int64_t metricValue,
+ const ConfigKey& configKey,
const std::vector<Subscription>& subscriptions) {
VLOG("informSubscribers called.");
if (subscriptions.empty()) {
@@ -55,6 +59,11 @@
ALOGW("Failed to generate perfetto traces.");
}
break;
+ case Subscription::SubscriberInformationCase::kUprobestatsDetails:
+ if (!StartUprobeStats(subscription.uprobestats_details())) {
+ ALOGW("Failed to start uprobestats.");
+ }
+ break;
case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription,
dimensionKey);
diff --git a/statsd/src/anomaly/subscriber_util.h b/statsd/src/anomaly/subscriber_util.h
index 4d4c83b..d55581d 100644
--- a/statsd/src/anomaly/subscriber_util.h
+++ b/statsd/src/anomaly/subscriber_util.h
@@ -24,7 +24,7 @@
namespace os {
namespace statsd {
-void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+void triggerSubscribers(const int64_t ruleId, int64_t metricId,
const MetricDimensionKey& dimensionKey, int64_t metricValue,
const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
diff --git a/statsd/src/condition/CombinationConditionTracker.cpp b/statsd/src/condition/CombinationConditionTracker.cpp
index 829ae06..af4a18a 100644
--- a/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/statsd/src/condition/CombinationConditionTracker.cpp
@@ -25,7 +25,7 @@
using std::unordered_map;
using std::vector;
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
+CombinationConditionTracker::CombinationConditionTracker(const int64_t id, const int index,
const uint64_t protoHash)
: ConditionTracker(id, index, protoHash) {
VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
@@ -38,7 +38,7 @@
optional<InvalidConfigReason> CombinationConditionTracker::init(
const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack,
+ const unordered_map<int64_t, int>& conditionIdIndexMap, vector<uint8_t>& stack,
vector<ConditionState>& conditionCache) {
VLOG("Combination predicate init() %lld", (long long)mConditionId);
if (mInitialized) {
@@ -203,7 +203,7 @@
const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& nonSlicedConditionCache,
- std::vector<bool>& conditionChangedCache) {
+ std::vector<uint8_t>& conditionChangedCache) {
// value is up to date.
if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
return;
diff --git a/statsd/src/condition/CombinationConditionTracker.h b/statsd/src/condition/CombinationConditionTracker.h
index 82463f0..3ed4a83 100644
--- a/statsd/src/condition/CombinationConditionTracker.h
+++ b/statsd/src/condition/CombinationConditionTracker.h
@@ -26,18 +26,18 @@
class CombinationConditionTracker : public ConditionTracker {
public:
- CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
+ CombinationConditionTracker(int64_t id, int index, const uint64_t protoHash);
~CombinationConditionTracker();
optional<InvalidConfigReason> init(
const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& conditionCache) override;
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap,
+ std::vector<uint8_t>& stack, std::vector<ConditionState>& conditionCache) override;
optional<InvalidConfigReason> onConfigUpdated(
- const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<Predicate>& allConditionProtos, int index,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
@@ -46,7 +46,7 @@
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
+ std::vector<uint8_t>& changedCache) override;
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
diff --git a/statsd/src/condition/ConditionTracker.h b/statsd/src/condition/ConditionTracker.h
index 2aa3ff0..b09d350 100644
--- a/statsd/src/condition/ConditionTracker.h
+++ b/statsd/src/condition/ConditionTracker.h
@@ -31,7 +31,7 @@
class ConditionTracker : public virtual RefBase {
public:
- ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
+ ConditionTracker(int64_t id, int index, const uint64_t protoHash)
: mConditionId(id),
mIndex(index),
mInitialized(false),
@@ -57,8 +57,8 @@
virtual optional<InvalidConfigReason> init(
const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& conditionCache) = 0;
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap,
+ std::vector<uint8_t>& stack, std::vector<ConditionState>& conditionCache) = 0;
// Update appropriate state on config updates. Primarily, all indices need to be updated.
// This predicate and all of its children are guaranteed to be preserved across the update.
@@ -73,7 +73,7 @@
// conditionTrackerMap: map of condition tracker id to index after the config update.
// returns whether or not the update is successful.
virtual optional<InvalidConfigReason> onConfigUpdated(
- const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<Predicate>& allConditionProtos, int index,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap) {
@@ -95,7 +95,7 @@
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& conditionChanged) = 0;
+ std::vector<uint8_t>& conditionChanged) = 0;
// Query the condition with parameters.
// [conditionParameters]: a map from condition name to the HashableDimensionKey to query the
diff --git a/statsd/src/condition/SimpleConditionTracker.cpp b/statsd/src/condition/SimpleConditionTracker.cpp
index ccb2946..5b87f13 100644
--- a/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/statsd/src/condition/SimpleConditionTracker.cpp
@@ -27,7 +27,7 @@
using std::unordered_map;
SimpleConditionTracker::SimpleConditionTracker(
- const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
+ const ConfigKey& key, const int64_t id, const uint64_t protoHash, const int index,
const SimplePredicate& simplePredicate,
const unordered_map<int64_t, int>& atomMatchingTrackerMap)
: ConditionTracker(id, index, protoHash),
@@ -59,7 +59,7 @@
optional<InvalidConfigReason> SimpleConditionTracker::init(
const vector<Predicate>& allConditionConfig,
const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack,
+ const unordered_map<int64_t, int>& conditionIdIndexMap, vector<uint8_t>& stack,
vector<ConditionState>& conditionCache) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
@@ -145,7 +145,7 @@
}
void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
- std::vector<bool>& conditionChangedCache) {
+ std::vector<uint8_t>& conditionChangedCache) {
// Unless the default condition is false, and there was nothing started, otherwise we have
// triggered a condition change.
conditionChangedCache[mIndex] =
@@ -164,13 +164,13 @@
conditionCache[mIndex] = ConditionState::kFalse;
}
-bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) const {
if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
// if the condition is not sliced or the key is not new, we are good!
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mSlicedConditionState.size() >= StatsdStats::kDimensionKeySizeSoftLimit) {
size_t newTupleCount = mSlicedConditionState.size() + 1;
StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
@@ -265,12 +265,11 @@
*conditionChangedCache);
}
-void SimpleConditionTracker::evaluateCondition(
- const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<uint8_t>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
diff --git a/statsd/src/condition/SimpleConditionTracker.h b/statsd/src/condition/SimpleConditionTracker.h
index 4687d8e..8af89cb 100644
--- a/statsd/src/condition/SimpleConditionTracker.h
+++ b/statsd/src/condition/SimpleConditionTracker.h
@@ -29,7 +29,7 @@
class SimpleConditionTracker : public ConditionTracker {
public:
- SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+ SimpleConditionTracker(const ConfigKey& key, int64_t id, const uint64_t protoHash,
const int index, const SimplePredicate& simplePredicate,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
@@ -38,11 +38,11 @@
optional<InvalidConfigReason> init(
const std::vector<Predicate>& allConditionConfig,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& conditionCache) override;
+ const std::unordered_map<int64_t, int>& conditionIdIndexMap,
+ std::vector<uint8_t>& stack, std::vector<ConditionState>& conditionCache) override;
optional<InvalidConfigReason> onConfigUpdated(
- const std::vector<Predicate>& allConditionProtos, const int index,
+ const std::vector<Predicate>& allConditionProtos, int index,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
@@ -51,7 +51,7 @@
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
+ std::vector<uint8_t>& changedCache) override;
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
@@ -120,12 +120,12 @@
const std::unordered_map<int64_t, int>& logTrackerMap);
void handleStopAll(std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache);
+ std::vector<uint8_t>& changedCache);
void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
ConditionState* conditionCache, bool* changedCache);
- bool hitGuardRail(const HashableDimensionKey& newKey);
+ bool hitGuardRail(const HashableDimensionKey& newKey) const;
void dumpState();
diff --git a/statsd/src/config/ConfigKey.cpp b/statsd/src/config/ConfigKey.cpp
index 4a2bd27..3b0b835 100644
--- a/statsd/src/config/ConfigKey.cpp
+++ b/statsd/src/config/ConfigKey.cpp
@@ -26,7 +26,7 @@
ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) {
}
-ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) {
+ConfigKey::ConfigKey(int uid, const int64_t id) : mId(id), mUid(uid) {
}
ConfigKey::~ConfigKey() {
diff --git a/statsd/src/config/ConfigKey.h b/statsd/src/config/ConfigKey.h
index 0e5a7e3..e8df355 100644
--- a/statsd/src/config/ConfigKey.h
+++ b/statsd/src/config/ConfigKey.h
@@ -34,13 +34,13 @@
public:
ConfigKey();
ConfigKey(const ConfigKey& that);
- ConfigKey(int uid, const int64_t& id);
+ ConfigKey(int uid, int64_t id);
~ConfigKey();
inline int GetUid() const {
return mUid;
}
- inline const int64_t& GetId() const {
+ inline int64_t GetId() const {
return mId;
}
@@ -73,17 +73,10 @@
/**
* A hash function for ConfigKey so it can be used for unordered_map/set.
- * Unfortunately this has to go in std namespace because C++ is fun!
*/
-namespace std {
-
-using android::os::statsd::ConfigKey;
-
template <>
-struct hash<ConfigKey> {
- std::size_t operator()(const ConfigKey& key) const {
+struct std::hash<android::os::statsd::ConfigKey> {
+ std::size_t operator()(const android::os::statsd::ConfigKey& key) const {
return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId())));
}
};
-
-} // namespace std
diff --git a/statsd/src/config/ConfigKeyWithPackage.h b/statsd/src/config/ConfigKeyWithPackage.h
new file mode 100644
index 0000000..9cf507b
--- /dev/null
+++ b/statsd/src/config/ConfigKeyWithPackage.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::hash;
+using std::string;
+
+/**
+ * A config key that uses a package name instead of a uid. Generally, ConfigKey which uses a uid
+ * should be used. This is currently only used for restricted metrics changed operation.
+ */
+class ConfigKeyWithPackage {
+public:
+ ConfigKeyWithPackage(const string& package, int64_t id) : mPackage(package), mId(id) {
+ }
+
+ inline string GetPackage() const {
+ return mPackage;
+ }
+ inline int64_t GetId() const {
+ return mId;
+ }
+
+ inline bool operator<(const ConfigKeyWithPackage& that) const {
+ if (mPackage != that.mPackage) {
+ return mPackage < that.mPackage;
+ }
+ return mId < that.mId;
+ };
+
+ inline bool operator==(const ConfigKeyWithPackage& that) const {
+ return mPackage == that.mPackage && mId == that.mId;
+ };
+
+private:
+ string mPackage;
+ int64_t mId;
+};
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/config/ConfigListener.cpp b/statsd/src/config/ConfigListener.cpp
index 21a3f16..f0406fc 100644
--- a/statsd/src/config/ConfigListener.cpp
+++ b/statsd/src/config/ConfigListener.cpp
@@ -20,12 +20,6 @@
namespace os {
namespace statsd {
-ConfigListener::ConfigListener() {
-}
-
-ConfigListener::~ConfigListener() {
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/config/ConfigListener.h b/statsd/src/config/ConfigListener.h
index b29e0be..fbebf82 100644
--- a/statsd/src/config/ConfigListener.h
+++ b/statsd/src/config/ConfigListener.h
@@ -30,13 +30,13 @@
*/
class ConfigListener : public virtual RefBase {
public:
- ConfigListener();
- virtual ~ConfigListener();
+ ConfigListener() = default;
+ virtual ~ConfigListener() = default;
/**
* A configuration was added or updated.
*/
- virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
+ virtual void OnConfigUpdated(int64_t timestampNs, const ConfigKey& key,
const StatsdConfig& config, bool modularUpdate = true) = 0;
/**
diff --git a/statsd/src/config/ConfigManager.cpp b/statsd/src/config/ConfigManager.cpp
index 570cd45..d1f93c7 100644
--- a/statsd/src/config/ConfigManager.cpp
+++ b/statsd/src/config/ConfigManager.cpp
@@ -33,14 +33,14 @@
namespace os {
namespace statsd {
-using std::pair;
using std::string;
using std::vector;
+using Status = ::ndk::ScopedAStatus;
+
#define STATS_SERVICE_DIR "/data/misc/stats-service"
using android::base::StringPrintf;
-using std::unique_ptr;
ConfigManager::ConfigManager() {
}
@@ -51,8 +51,8 @@
void ConfigManager::Startup() {
map<ConfigKey, StatsdConfig> configsFromDisk;
StorageManager::readConfigFromDisk(configsFromDisk);
- for (const auto& pair : configsFromDisk) {
- UpdateConfig(pair.first, pair.second);
+ for (const auto& config : configsFromDisk) {
+ UpdateConfig(config.first, config.second);
}
}
@@ -152,6 +152,83 @@
}
}
+void ConfigManager::SetRestrictedMetricsChangedReceiver(const string& configPackage,
+ const int64_t configId,
+ const int32_t callingUid,
+ const shared_ptr<IPendingIntentRef>& pir) {
+ lock_guard<mutex> lock(mMutex);
+ ConfigKeyWithPackage configKey(configPackage, configId);
+ mRestrictedMetricsChangedReceivers[configKey][callingUid] = pir;
+}
+
+void ConfigManager::RemoveRestrictedMetricsChangedReceiver(const string& configPackage,
+ const int64_t configId,
+ const int32_t callingUid) {
+ lock_guard<mutex> lock(mMutex);
+ ConfigKeyWithPackage configKey(configPackage, configId);
+ const auto& it = mRestrictedMetricsChangedReceivers.find(configKey);
+ if (it != mRestrictedMetricsChangedReceivers.end()) {
+ it->second.erase(callingUid);
+ if (it->second.empty()) {
+ mRestrictedMetricsChangedReceivers.erase(it);
+ }
+ }
+}
+
+void ConfigManager::RemoveRestrictedMetricsChangedReceiver(
+ const ConfigKeyWithPackage& key, const int32_t delegateUid,
+ const shared_ptr<IPendingIntentRef>& pir) {
+ lock_guard<mutex> lock(mMutex);
+ const auto& it = mRestrictedMetricsChangedReceivers.find(key);
+ if (it != mRestrictedMetricsChangedReceivers.end()) {
+ const auto& pirIt = it->second.find(delegateUid);
+ if (pirIt != it->second.end() && pirIt->second == pir) {
+ it->second.erase(delegateUid);
+ if (it->second.empty()) {
+ mRestrictedMetricsChangedReceivers.erase(it);
+ }
+ }
+ }
+}
+
+void ConfigManager::SendRestrictedMetricsBroadcast(const set<string>& configPackages,
+ const int64_t configId,
+ const set<int32_t>& delegateUids,
+ const vector<int64_t>& metricIds) {
+ map<ConfigKeyWithPackage, map<int32_t, shared_ptr<IPendingIntentRef>>> intentsToSend;
+ {
+ lock_guard<mutex> lock(mMutex);
+ // Invoke the pending intent for all matching configs, as long as the listening delegates
+ // match the allowed delegate uids specified by the config.
+ for (const string& configPackage : configPackages) {
+ ConfigKeyWithPackage key(configPackage, configId);
+ const auto& it = mRestrictedMetricsChangedReceivers.find(key);
+ if (it != mRestrictedMetricsChangedReceivers.end()) {
+ for (const auto& [delegateUid, pir] : it->second) {
+ if (delegateUids.find(delegateUid) != delegateUids.end()) {
+ intentsToSend[key][delegateUid] = pir;
+ }
+ }
+ }
+ }
+ }
+
+ // Invoke the pending intents without holding the lock.
+ for (const auto& [key, innerMap] : intentsToSend) {
+ for (const auto& [delegateUid, pir] : innerMap) {
+ Status status = pir->sendRestrictedMetricsChangedBroadcast(metricIds);
+ if (status.isOk()) {
+ VLOG("ConfigManager::SendRestrictedMetricsBroadcast succeeded");
+ }
+ if (status.getExceptionCode() == EX_TRANSACTION_FAILED &&
+ status.getStatus() == STATUS_DEAD_OBJECT) {
+ // Must also be called without the lock, since remove will acquire the lock.
+ RemoveRestrictedMetricsChangedReceiver(key, delegateUid, pir);
+ }
+ }
+ }
+}
+
void ConfigManager::RemoveConfig(const ConfigKey& key) {
vector<sp<ConfigListener>> broadcastList;
{
@@ -181,6 +258,7 @@
StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str());
}
+// TODO(b/xxx): consider removing all receivers associated with this uid.
void ConfigManager::RemoveConfigs(int uid) {
vector<ConfigKey> removed;
vector<sp<ConfigListener>> broadcastList;
diff --git a/statsd/src/config/ConfigManager.h b/statsd/src/config/ConfigManager.h
index 9a0f504..5a213b7 100644
--- a/statsd/src/config/ConfigManager.h
+++ b/statsd/src/config/ConfigManager.h
@@ -16,14 +16,17 @@
#pragma once
-#include "config/ConfigKey.h"
-#include "config/ConfigListener.h"
-
#include <aidl/android/os/IPendingIntentRef.h>
+#include <stdio.h>
+
+#include <map>
#include <mutex>
+#include <set>
#include <string>
-#include <stdio.h>
+#include "config/ConfigKey.h"
+#include "config/ConfigKeyWithPackage.h"
+#include "config/ConfigListener.h"
using aidl::android::os::IPendingIntentRef;
using std::shared_ptr;
@@ -113,6 +116,27 @@
const shared_ptr<IPendingIntentRef>& pir);
/**
+ * Sets the pending intent that is notified whenever the list of restricted metrics changes
+ */
+ void SetRestrictedMetricsChangedReceiver(const string& configPackage, int64_t configId,
+ const int32_t callingUid,
+ const shared_ptr<IPendingIntentRef>& pir);
+
+ /**
+ * Erase any restricted metrics changed pending intents associated with this config key & uid.
+ */
+ void RemoveRestrictedMetricsChangedReceiver(const string& configPackage, int64_t configId,
+ const int32_t callingUid);
+
+ /**
+ * Sends a restricted metrics broadcast for the valid config keys and delegate package
+ */
+ void SendRestrictedMetricsBroadcast(const std::set<string>& configPackages,
+ const int64_t configId,
+ const std::set<int32_t>& delegateUids,
+ const std::vector<int64_t>& metricIds);
+
+ /**
* A configuration was removed.
*
* Reports this to listeners.
@@ -166,9 +190,24 @@
std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
+ * Each uid can subscribe up to one receiver for a particular config to receive the restricted
+ * metrics for that config. The receiver is specified as IPendingIntentRef.
+ */
+ std::map<ConfigKeyWithPackage, std::map<int32_t, shared_ptr<IPendingIntentRef>>>
+ mRestrictedMetricsChangedReceivers;
+
+ /**
* The ConfigListeners that will be told about changes.
*/
std::vector<sp<ConfigListener>> mListeners;
+
+ /**
+ * Erase the restricted metrics changed pending intents associated with this config key & uid if
+ * it is equal to the provided pending intent.
+ */
+ void RemoveRestrictedMetricsChangedReceiver(const ConfigKeyWithPackage& key,
+ const int32_t delegateUid,
+ const shared_ptr<IPendingIntentRef>& pir);
};
} // namespace statsd
diff --git a/statsd/src/config/ConfigMetadataProvider.h b/statsd/src/config/ConfigMetadataProvider.h
new file mode 100644
index 0000000..451503a
--- /dev/null
+++ b/statsd/src/config/ConfigMetadataProvider.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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 <utils/RefBase.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ConfigMetadataProvider : virtual public RefBase {
+public:
+ virtual ~ConfigMetadataProvider() {
+ }
+
+ virtual bool useV2SoftMemoryCalculation() = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/external/PullResultReceiver.h b/statsd/src/external/PullResultReceiver.h
index ceaae80..f2d0a4e 100644
--- a/statsd/src/external/PullResultReceiver.h
+++ b/statsd/src/external/PullResultReceiver.h
@@ -29,8 +29,8 @@
class PullResultReceiver : public BnPullAtomResultReceiver {
public:
- PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
- pullFinishCallback);
+ PullResultReceiver(
+ function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback);
~PullResultReceiver();
/**
diff --git a/statsd/src/external/StatsCallbackPuller.cpp b/statsd/src/external/StatsCallbackPuller.cpp
index 7d5d04b..a1c8df2 100644
--- a/statsd/src/external/StatsCallbackPuller.cpp
+++ b/statsd/src/external/StatsCallbackPuller.cpp
@@ -37,7 +37,7 @@
StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
const int64_t coolDownNs, int64_t timeoutNs,
- const vector<int> additiveFields)
+ const vector<int>& additiveFields)
: StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
VLOG("StatsCallbackPuller created for tag %d", tagId);
}
diff --git a/statsd/src/external/StatsCallbackPuller.h b/statsd/src/external/StatsCallbackPuller.h
index 43d35fc..65acb00 100644
--- a/statsd/src/external/StatsCallbackPuller.h
+++ b/statsd/src/external/StatsCallbackPuller.h
@@ -29,8 +29,8 @@
class StatsCallbackPuller : public StatsPuller {
public:
explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
- const int64_t coolDownNs, const int64_t timeoutNs,
- const std::vector<int> additiveFields);
+ const int64_t coolDownNs, int64_t timeoutNs,
+ const std::vector<int>& additiveFields);
private:
PullErrorCode PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
diff --git a/statsd/src/external/StatsPuller.cpp b/statsd/src/external/StatsPuller.cpp
index 395e660..e0fce5e 100644
--- a/statsd/src/external/StatsPuller.cpp
+++ b/statsd/src/external/StatsPuller.cpp
@@ -18,10 +18,12 @@
#include "Log.h"
#include "StatsPuller.h"
+
#include "StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "puller_util.h"
#include "stats_log_util.h"
+#include "utils/api_tracing.h"
namespace android {
namespace os {
@@ -33,7 +35,7 @@
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
- const std::vector<int> additiveFields)
+ const std::vector<int>& additiveFields)
: mTagId(tagId),
mPullTimeoutNs(pullTimeoutNs),
mCoolDownNs(coolDownNs),
@@ -44,6 +46,7 @@
PullErrorCode StatsPuller::Pull(const int64_t eventTimeNs,
std::vector<std::shared_ptr<LogEvent>>* data) {
+ ATRACE_CALL();
lock_guard<std::mutex> lock(mLock);
const int64_t elapsedTimeNs = getElapsedRealtimeNs();
const int64_t systemUptimeMillis = getSystemUptimeMillis();
diff --git a/statsd/src/external/StatsPuller.h b/statsd/src/external/StatsPuller.h
index d8c7eb3..4a38382 100644
--- a/statsd/src/external/StatsPuller.h
+++ b/statsd/src/external/StatsPuller.h
@@ -41,10 +41,9 @@
class StatsPuller : public virtual RefBase {
public:
- explicit StatsPuller(const int tagId,
- const int64_t coolDownNs = NS_PER_SEC,
+ explicit StatsPuller(const int tagId, int64_t coolDownNs = NS_PER_SEC,
const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
- const std::vector<int> additiveFields = std::vector<int>());
+ const std::vector<int>& additiveFields = std::vector<int>());
virtual ~StatsPuller() {}
@@ -69,7 +68,7 @@
static void SetUidMap(const sp<UidMap>& uidMap);
virtual void SetStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {};
+ const shared_ptr<IStatsCompanionService>& statsCompanionService){};
protected:
const int mTagId;
diff --git a/statsd/src/external/StatsPullerManager.cpp b/statsd/src/external/StatsPullerManager.cpp
index 8fabcad..bba32b9 100644
--- a/statsd/src/external/StatsPullerManager.cpp
+++ b/statsd/src/external/StatsPullerManager.cpp
@@ -54,12 +54,14 @@
bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
vector<shared_ptr<LogEvent>>* data) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
return PullLocked(tagId, configKey, eventTimeNs, data);
}
bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
return PullLocked(tagId, uids, eventTimeNs, data);
}
@@ -135,7 +137,7 @@
}
void StatsPullerManager::SetStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {
+ const shared_ptr<IStatsCompanionService>& statsCompanionService) {
std::lock_guard<std::mutex> _l(mLock);
shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
@@ -148,8 +150,8 @@
}
void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
- int64_t intervalNs) {
+ const wp<PullDataReceiver>& receiver,
+ int64_t nextPullTimeNs, int64_t intervalNs) {
std::lock_guard<std::mutex> _l(mLock);
auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -184,7 +186,7 @@
}
void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver) {
+ const wp<PullDataReceiver>& receiver) {
std::lock_guard<std::mutex> _l(mLock);
auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
if (receiversIt == mReceivers.end()) {
@@ -202,13 +204,13 @@
}
void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider) {
+ const wp<PullUidProvider>& provider) {
std::lock_guard<std::mutex> _l(mLock);
mPullUidProviders[configKey] = provider;
}
void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider) {
+ const wp<PullUidProvider>& provider) {
std::lock_guard<std::mutex> _l(mLock);
const auto& it = mPullUidProviders.find(configKey);
if (it != mPullUidProviders.end() && it->second == provider) {
@@ -217,6 +219,7 @@
}
void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
int64_t wallClockNs = getWallClockNs();
@@ -231,8 +234,8 @@
// receiver to the list that will pull on this alarm.
// If pullNecessary is false, check if next pull time needs to be updated.
sp<PullDataReceiver> receiverPtr = receiverInfo.receiver.promote();
- const bool pullNecessary = receiverPtr != nullptr && receiverPtr->isPullNeeded();
- if (receiverInfo.nextPullTimeNs <= elapsedTimeNs && pullNecessary) {
+ if (receiverInfo.nextPullTimeNs <= elapsedTimeNs && receiverPtr != nullptr &&
+ receiverPtr->isPullNeeded()) {
receivers.push_back(&receiverInfo);
} else {
if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
@@ -294,6 +297,7 @@
}
int StatsPullerManager::ForceClearPullerCache() {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
@@ -303,6 +307,7 @@
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
@@ -315,6 +320,7 @@
const int64_t coolDownNs, const int64_t timeoutNs,
const vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& callback) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
@@ -339,6 +345,7 @@
}
void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> _l(mLock);
PullerKey key = {.uid = uid, .atomTag = atomTag};
if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
diff --git a/statsd/src/external/StatsPullerManager.h b/statsd/src/external/StatsPullerManager.h
index 80a1331..9f5a41c 100644
--- a/statsd/src/external/StatsPullerManager.h
+++ b/statsd/src/external/StatsPullerManager.h
@@ -70,20 +70,21 @@
// Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
// and then every intervalNs thereafter.
virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ const wp<PullDataReceiver>& receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
// Stop listening on a tagId.
virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver);
+ const wp<PullDataReceiver>& receiver);
// Registers a pull uid provider for the config key. When pulling atoms, it will be used to
// determine which uids to pull from.
- virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
+ virtual void RegisterPullUidProvider(const ConfigKey& configKey,
+ const wp<PullUidProvider>& provider);
// Unregister a pull uid provider.
virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider);
+ const wp<PullUidProvider>& provider);
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
@@ -101,11 +102,11 @@
// registered for any of the uids for this atom.
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
- virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ virtual bool Pull(int tagId, const ConfigKey& configKey, int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// Same as above, but directly specify the allowed uids to pull from.
- virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ virtual bool Pull(int tagId, const vector<int32_t>& uids, int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// Clear pull data cache immediately.
@@ -114,9 +115,9 @@
// Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
- void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
+ void SetStatsCompanionService(const shared_ptr<IStatsCompanionService>& statsCompanionService);
- void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
+ void RegisterPullAtomCallback(const int uid, const int32_t atomTag, int64_t coolDownNs,
const int64_t timeoutNs, const vector<int32_t>& additiveFields,
const shared_ptr<IPullAtomCallback>& callback);
@@ -151,10 +152,10 @@
// mapping from Config Key to the PullUidProvider for that config
std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
- bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ bool PullLocked(int tagId, const ConfigKey& configKey, int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
- bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ bool PullLocked(int tagId, const vector<int32_t>& uids, int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data);
// locks for data receiver and StatsCompanionService changes
diff --git a/statsd/src/external/Uprobestats.cpp b/statsd/src/external/Uprobestats.cpp
new file mode 100644
index 0000000..ad6613c
--- /dev/null
+++ b/statsd/src/external/Uprobestats.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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 STATSD_DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <private/android_filesystem_config.h>
+
+#include <string>
+
+#include "src/statsd_config.pb.h" // Alert
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool StartUprobeStats(const UprobestatsDetails& config) {
+ // TODO: Add an implementation.
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/external/Uprobestats.h b/statsd/src/external/Uprobestats.h
new file mode 100644
index 0000000..e045e8f
--- /dev/null
+++ b/statsd/src/external/Uprobestats.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 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
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Starts the uprobestats process.
+bool StartUprobeStats(const UprobestatsDetails& config);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/flags/FlagProvider.h b/statsd/src/flags/FlagProvider.h
index 813dae5..7b77c04 100644
--- a/statsd/src/flags/FlagProvider.h
+++ b/statsd/src/flags/FlagProvider.h
@@ -112,9 +112,15 @@
friend class KllMetricE2eAbTest;
friend class MetricsManagerTest;
friend class StatsLogProcessorTest;
+ friend class StatsLogProcessorTestRestricted;
+ friend class RestrictedEventMetricProducerTest;
+ friend class RestrictedConfigE2ETest;
+ friend class RestrictedEventMetricE2eTest;
+ friend class LogEvent_FieldRestrictionTest;
FRIEND_TEST(ConfigUpdateE2eTest, TestEventMetric);
FRIEND_TEST(ConfigUpdateE2eTest, TestGaugeMetric);
+ FRIEND_TEST(ConfigUpdateE2eTest, TestConfigUpdateRestrictedDelegateCleared);
FRIEND_TEST(EventMetricE2eTest, TestEventMetricDataAggregated);
FRIEND_TEST(EventMetricProducerTest, TestOneAtomTagAggregatedEvents);
FRIEND_TEST(EventMetricProducerTest, TestTwoAtomTagAggregatedEvents);
@@ -126,6 +132,13 @@
FRIEND_TEST(FlagProviderTest_SPlus, TestGetFlagBoolServerFlagEmptyDefaultTrue);
FRIEND_TEST(FlagProviderTest_SPlus_RealValues, TestGetBootFlagBoolServerFlagTrue);
FRIEND_TEST(FlagProviderTest_SPlus_RealValues, TestGetBootFlagBoolServerFlagFalse);
+ FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfig);
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestFlagDisabled);
+ FRIEND_TEST(LogEventTest, TestRestrictionCategoryAnnotation);
+ FRIEND_TEST(LogEventTest, TestInvalidRestrictionCategoryAnnotation);
+ FRIEND_TEST(LogEvent_FieldRestrictionTest, TestFieldRestrictionAnnotation);
+ FRIEND_TEST(LogEvent_FieldRestrictionTest, TestInvalidAnnotationIntType);
+ FRIEND_TEST(LogEvent_FieldRestrictionTest, TestInvalidAnnotationAtomLevel);
};
} // namespace statsd
diff --git a/statsd/src/guardrail/StatsdStats.cpp b/statsd/src/guardrail/StatsdStats.cpp
index 626114f..2d9586f 100644
--- a/statsd/src/guardrail/StatsdStats.cpp
+++ b/statsd/src/guardrail/StatsdStats.cpp
@@ -57,8 +57,22 @@
const int FIELD_ID_LOGGER_ERROR_STATS = 16;
const int FIELD_ID_OVERFLOW = 18;
const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS = 20;
const int FIELD_ID_SHARD_OFFSET = 21;
+const int FIELD_ID_STATSD_STATS_ID = 22;
const int FIELD_ID_SUBSCRIPTION_STATS = 23;
+const int FIELD_ID_SOCKET_LOSS_STATS = 24;
+const int FIELD_ID_QUEUE_STATS = 25;
+
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CALLING_UID = 1;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_ID = 2;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_UID = 3;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_PACKAGE = 4;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_INVALID_QUERY_REASON = 5;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_QUERY_WALL_TIME_NS = 6;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_HAS_ERROR = 7;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_ERROR = 8;
+const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_LATENCY_NS = 9;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
@@ -80,6 +94,9 @@
const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2;
const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3;
+const int FIELD_ID_QUEUE_MAX_SIZE_OBSERVED = 1;
+const int FIELD_ID_QUEUE_MAX_SIZE_OBSERVED_ELAPSED_NANOS = 2;
+
const int FIELD_ID_CONFIG_STATS_UID = 1;
const int FIELD_ID_CONFIG_STATS_ID = 2;
const int FIELD_ID_CONFIG_STATS_CREATION = 3;
@@ -106,6 +123,19 @@
const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23;
const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1;
const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2;
+const int FIELD_ID_CONFIG_STATS_RESTRICTED_METRIC_STATS = 25;
+const int FIELD_ID_CONFIG_STATS_DEVICE_INFO_TABLE_CREATION_FAILED = 26;
+const int FIELD_ID_CONFIG_STATS_RESTRICTED_DB_CORRUPTED_COUNT = 27;
+const int FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_FLUSH_LATENCY = 28;
+const int FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_DB_SIZE_TIME_SEC = 29;
+const int FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_DB_SIZE_BYTES = 30;
+const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_NUMBER = 31;
+const int FIELD_ID_DB_DELETION_STAT_FAILED = 32;
+const int FIELD_ID_DB_DELETION_SIZE_EXCEEDED_LIMIT = 33;
+const int FIELD_ID_DB_DELETION_CONFIG_INVALID = 34;
+const int FIELD_ID_DB_DELETION_TOO_OLD = 35;
+const int FIELD_ID_DB_DELETION_CONFIG_REMOVED = 36;
+const int FIELD_ID_DB_DELETION_CONFIG_UPDATED = 37;
const int FIELD_ID_INVALID_CONFIG_REASON_ENUM = 1;
const int FIELD_ID_INVALID_CONFIG_REASON_METRIC_ID = 2;
@@ -133,6 +163,33 @@
const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1;
const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
+// SocketLossStats
+const int FIELD_ID_SOCKET_LOSS_STATS_PER_UID = 1;
+const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS = 2;
+
+// for LossStatsOverflowCounters proto
+const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_UID = 1;
+const int FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_COUNT = 2;
+
+// for LossStatsPerUid proto
+const int FIELD_ID_SOCKET_LOSS_STATS_UID = 1;
+const int FIELD_ID_SOCKET_LOSS_STATS_FIRST_TIMESTAMP_NANOS = 2;
+const int FIELD_ID_SOCKET_LOSS_STATS_LAST_TIMESTAMP_NANOS = 3;
+const int FIELD_ID_SOCKET_LOSS_ATOM_ID_LOSS_STATS = 4;
+
+// for AtomIdLossStats proto
+const int FIELD_ID_ATOM_ID_LOSS_STATS_ATOM_ID = 1;
+const int FIELD_ID_ATOM_ID_LOSS_STATS_ERROR = 2;
+const int FIELD_ID_ATOM_ID_LOSS_STATS_COUNT = 3;
+
+// for RestrictedMetricStats proto
+const int FIELD_ID_RESTRICTED_STATS_METRIC_ID = 1;
+const int FIELD_ID_RESTRICTED_STATS_INSERT_ERROR = 2;
+const int FIELD_ID_RESTRICTED_STATS_TABLE_CREATION_ERROR = 3;
+const int FIELD_ID_RESTRICTED_STATS_TABLE_DELETION_ERROR = 4;
+const int FIELD_ID_RESTRICTED_STATS_FLUSH_LATENCY = 5;
+const int FIELD_ID_RESTRICTED_STATS_CATEGORY_CHANGED_COUNT = 6;
+
const int FIELD_ID_SUBSCRIPTION_STATS_PER_SUBSCRIPTION_STATS = 1;
const int FIELD_ID_SUBSCRIPTION_STATS_PULL_THREAD_WAKEUP_COUNT = 2;
@@ -149,7 +206,7 @@
{util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
-StatsdStats::StatsdStats() {
+StatsdStats::StatsdStats() : mStatsdStatsId(rand()) {
mPushedAtomStats.resize(kMaxPushedAtomId + 1);
mStartTimeSec = getWallClockSec();
}
@@ -308,6 +365,15 @@
noteAtomDroppedLocked(atomId);
}
+void StatsdStats::noteEventQueueSize(int32_t size, int64_t eventTimestampNs) {
+ lock_guard<std::mutex> lock(mLock);
+
+ if (mEventQueueMaxSizeObserved < size) {
+ mEventQueueMaxSizeObserved = size;
+ mEventQueueMaxSizeObservedElapsedNanos = eventTimestampNs;
+ }
+}
+
void StatsdStats::noteAtomDroppedLocked(int32_t atomId) {
constexpr int kMaxPushedAtomDroppedStatsSize = kMaxPushedAtomId + kMaxNonPlatformPushedAtoms;
if (mPushedAtomDropsStats.size() < kMaxPushedAtomDroppedStatsSize ||
@@ -316,6 +382,34 @@
}
}
+void StatsdStats::noteAtomSocketLoss(const SocketLossInfo& lossInfo) {
+ ALOGW("SocketLossEvent detected: %lld (firstLossTsNanos), %lld (lastLossTsNanos)",
+ (long long)lossInfo.firstLossTsNanos, (long long)lossInfo.lastLossTsNanos);
+ lock_guard<std::mutex> lock(mLock);
+
+ if (mSocketLossStats.size() == kMaxSocketLossStatsSize) {
+ // erase the oldest record
+ mSocketLossStats.pop_front();
+ }
+ mSocketLossStats.emplace_back(lossInfo.uid, lossInfo.firstLossTsNanos,
+ lossInfo.lastLossTsNanos);
+ for (size_t i = 0; i < lossInfo.atomIds.size(); i++) {
+ ALOGW("For uid %d atom %d was lost %d times with error %d", lossInfo.uid,
+ lossInfo.atomIds[i], lossInfo.counts[i], lossInfo.errors[i]);
+ mSocketLossStats.back().mLossCountPerErrorAtomId.emplace_back(
+ lossInfo.atomIds[i], lossInfo.errors[i], lossInfo.counts[i]);
+ }
+
+ if (lossInfo.overflowCounter > 0) {
+ auto overflowPerUid = mSocketLossStatsOverflowCounters.find(lossInfo.uid);
+ if (overflowPerUid != mSocketLossStatsOverflowCounters.end()) {
+ overflowPerUid->second += lossInfo.overflowCounter;
+ } else if (mSocketLossStatsOverflowCounters.size() < kMaxSocketLossStatsSize) {
+ mSocketLossStatsOverflowCounters[lossInfo.uid] = lossInfo.overflowCounter;
+ }
+ }
+}
+
void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) {
lock_guard<std::mutex> lock(mLock);
auto it = mConfigStats.find(key);
@@ -331,22 +425,104 @@
it->second->data_drop_bytes.push_back(totalBytes);
}
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) {
- noteMetricsReportSent(key, num_bytes, getWallClockSec());
+void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t numBytes,
+ const int32_t reportNumber) {
+ noteMetricsReportSent(key, numBytes, getWallClockSec(), reportNumber);
}
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes,
- int32_t timeSec) {
+void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t numBytes,
+ int32_t timeSec, const int32_t reportNumber) {
lock_guard<std::mutex> lock(mLock);
auto it = mConfigStats.find(key);
if (it == mConfigStats.end()) {
ALOGE("Config key %s not found!", key.ToString().c_str());
return;
}
+
if (it->second->dump_report_stats.size() == kMaxTimestampCount) {
it->second->dump_report_stats.pop_front();
}
- it->second->dump_report_stats.push_back(std::make_pair(timeSec, num_bytes));
+ it->second->dump_report_stats.emplace_back(timeSec, numBytes, reportNumber);
+}
+
+void StatsdStats::noteDeviceInfoTableCreationFailed(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->device_info_table_creation_failed = true;
+}
+
+void StatsdStats::noteDbCorrupted(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_corrupted_count++;
+}
+
+void StatsdStats::noteDbSizeExceeded(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_size_exceeded_limit++;
+}
+
+void StatsdStats::noteDbStatFailed(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_stat_failed++;
+}
+
+void StatsdStats::noteDbConfigInvalid(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_config_invalid++;
+}
+
+void StatsdStats::noteDbTooOld(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_too_old++;
+}
+
+void StatsdStats::noteDbDeletionConfigRemoved(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_config_removed++;
+}
+
+void StatsdStats::noteDbDeletionConfigUpdated(const ConfigKey& key) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(key);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", key.ToString().c_str());
+ return;
+ }
+ it->second->db_deletion_config_updated++;
}
void StatsdStats::noteUidMapDropped(int deltas) {
@@ -369,7 +545,7 @@
mUidMapStats.bytes_used = bytes;
}
-void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t id, int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
auto statsIt = mConfigStats.find(key);
@@ -383,7 +559,7 @@
}
}
-void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t id, int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
auto statsIt = mConfigStats.find(key);
@@ -396,8 +572,8 @@
}
}
-void StatsdStats::noteMetricDimensionInConditionSize(
- const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t id,
+ int size) {
lock_guard<std::mutex> lock(mLock);
// if name doesn't exist before, it will create the key with count 0.
auto statsIt = mConfigStats.find(key);
@@ -410,7 +586,7 @@
}
}
-void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
+void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t id) {
lock_guard<std::mutex> lock(mLock);
auto statsIt = mConfigStats.find(key);
@@ -420,7 +596,7 @@
statsIt->second->matcher_stats[id]++;
}
-void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
+void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t id) {
lock_guard<std::mutex> lock(mLock);
auto statsIt = mConfigStats.find(key);
if (statsIt == mConfigStats.end()) {
@@ -640,6 +816,137 @@
return false;
}
+void StatsdStats::noteQueryRestrictedMetricSucceed(const int64_t configId,
+ const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid,
+ const int64_t latencyNs) {
+ lock_guard<std::mutex> lock(mLock);
+
+ if (mRestrictedMetricQueryStats.size() == kMaxRestrictedMetricQueryCount) {
+ mRestrictedMetricQueryStats.pop_front();
+ }
+ mRestrictedMetricQueryStats.emplace_back(RestrictedMetricQueryStats(
+ callingUid, configId, configPackage, configUid, getWallClockNs(),
+ /*invalidQueryReason=*/std::nullopt, /*error=*/"", latencyNs));
+}
+
+void StatsdStats::noteQueryRestrictedMetricFailed(const int64_t configId,
+ const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid,
+ const InvalidQueryReason reason) {
+ lock_guard<std::mutex> lock(mLock);
+ noteQueryRestrictedMetricFailedLocked(configId, configPackage, configUid, callingUid, reason,
+ /*error=*/"");
+}
+
+void StatsdStats::noteQueryRestrictedMetricFailed(
+ const int64_t configId, const string& configPackage, const std::optional<int32_t> configUid,
+ const int32_t callingUid, const InvalidQueryReason reason, const string& error) {
+ lock_guard<std::mutex> lock(mLock);
+ noteQueryRestrictedMetricFailedLocked(configId, configPackage, configUid, callingUid, reason,
+ error);
+}
+
+void StatsdStats::noteQueryRestrictedMetricFailedLocked(
+ const int64_t configId, const string& configPackage, const std::optional<int32_t> configUid,
+ const int32_t callingUid, const InvalidQueryReason reason, const string& error) {
+ if (mRestrictedMetricQueryStats.size() == kMaxRestrictedMetricQueryCount) {
+ mRestrictedMetricQueryStats.pop_front();
+ }
+ mRestrictedMetricQueryStats.emplace_back(RestrictedMetricQueryStats(
+ callingUid, configId, configPackage, configUid, getWallClockNs(), reason, error,
+ /*queryLatencyNs=*/std::nullopt));
+}
+
+void StatsdStats::noteRestrictedMetricInsertError(const ConfigKey& configKey,
+ const int64_t metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it != mConfigStats.end()) {
+ it->second->restricted_metric_stats[metricId].insertError++;
+ }
+}
+
+void StatsdStats::noteRestrictedMetricTableCreationError(const ConfigKey& configKey,
+ const int64_t metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it != mConfigStats.end()) {
+ it->second->restricted_metric_stats[metricId].tableCreationError++;
+ }
+}
+
+void StatsdStats::noteRestrictedMetricTableDeletionError(const ConfigKey& configKey,
+ const int64_t metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it != mConfigStats.end()) {
+ it->second->restricted_metric_stats[metricId].tableDeletionError++;
+ }
+}
+
+void StatsdStats::noteRestrictedMetricFlushLatency(const ConfigKey& configKey,
+ const int64_t metricId,
+ const int64_t flushLatencyNs) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", configKey.ToString().c_str());
+ return;
+ }
+ auto& restrictedMetricStats = it->second->restricted_metric_stats[metricId];
+ if (restrictedMetricStats.flushLatencyNs.size() == kMaxRestrictedMetricFlushLatencyCount) {
+ restrictedMetricStats.flushLatencyNs.pop_front();
+ }
+ restrictedMetricStats.flushLatencyNs.push_back(flushLatencyNs);
+}
+
+void StatsdStats::noteRestrictedConfigFlushLatency(const ConfigKey& configKey,
+ const int64_t totalFlushLatencyNs) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", configKey.ToString().c_str());
+ return;
+ }
+ std::list<int64_t>& totalFlushLatencies = it->second->total_flush_latency_ns;
+ if (totalFlushLatencies.size() == kMaxRestrictedConfigFlushLatencyCount) {
+ totalFlushLatencies.pop_front();
+ }
+ totalFlushLatencies.push_back(totalFlushLatencyNs);
+}
+
+void StatsdStats::noteRestrictedConfigDbSize(const ConfigKey& configKey,
+ const int64_t elapsedTimeNs, const int64_t dbSize) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", configKey.ToString().c_str());
+ return;
+ }
+ std::list<int64_t>& totalDbSizeTimestamps = it->second->total_db_size_timestamps;
+ std::list<int64_t>& totaDbSizes = it->second->total_db_sizes;
+ if (totalDbSizeTimestamps.size() == kMaxRestrictedConfigDbSizeCount) {
+ totalDbSizeTimestamps.pop_front();
+ totaDbSizes.pop_front();
+ }
+ totalDbSizeTimestamps.push_back(elapsedTimeNs);
+ totaDbSizes.push_back(dbSize);
+}
+
+void StatsdStats::noteRestrictedMetricCategoryChanged(const ConfigKey& configKey,
+ const int64_t metricId) {
+ lock_guard<std::mutex> lock(mLock);
+ auto it = mConfigStats.find(configKey);
+ if (it == mConfigStats.end()) {
+ ALOGE("Config key %s not found!", configKey.ToString().c_str());
+ return;
+ }
+ it->second->restricted_metric_stats[metricId].categoryChangedCount++;
+}
+
void StatsdStats::noteSubscriptionStarted(int subId, int32_t pushedAtomCount,
int32_t pulledAtomCount) {
lock_guard<std::mutex> lock(mLock);
@@ -735,6 +1042,8 @@
mOverflowCount = 0;
mMinQueueHistoryNs = kInt64Max;
mMaxQueueHistoryNs = 0;
+ mEventQueueMaxSizeObserved = 0;
+ mEventQueueMaxSizeObservedElapsedNanos = 0;
for (auto& config : mConfigStats) {
config.second->broadcast_sent_time_sec.clear();
config.second->activation_time_sec.clear();
@@ -748,6 +1057,17 @@
config.second->metric_stats.clear();
config.second->metric_dimension_in_condition_stats.clear();
config.second->alert_stats.clear();
+ config.second->restricted_metric_stats.clear();
+ config.second->db_corrupted_count = 0;
+ config.second->total_flush_latency_ns.clear();
+ config.second->total_db_size_timestamps.clear();
+ config.second->total_db_sizes.clear();
+ config.second->db_deletion_size_exceeded_limit = 0;
+ config.second->db_deletion_stat_failed = 0;
+ config.second->db_deletion_config_invalid = 0;
+ config.second->db_deletion_too_old = 0;
+ config.second->db_deletion_config_removed = 0;
+ config.second->db_deletion_config_updated = 0;
}
for (auto& pullStats : mPulledAtomStats) {
pullStats.second.totalPull = 0;
@@ -775,7 +1095,10 @@
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
mPushedAtomErrorStats.clear();
+ mSocketLossStats.clear();
+ mSocketLossStatsOverflowCounters.clear();
mPushedAtomDropsStats.clear();
+ mRestrictedMetricQueryStats.clear();
mSubscriptionPullThreadWakeupCount = 0;
for (auto it = mSubscriptionStats.begin(); it != mSubscriptionStats.end();) {
@@ -816,6 +1139,13 @@
}
}
+bool StatsdStats::hasRestrictedConfigErrors(const std::shared_ptr<ConfigStats>& configStats) const {
+ return configStats->device_info_table_creation_failed || configStats->db_corrupted_count ||
+ configStats->db_deletion_size_exceeded_limit || configStats->db_deletion_stat_failed ||
+ configStats->db_deletion_config_invalid || configStats->db_deletion_too_old ||
+ configStats->db_deletion_config_removed || configStats->db_deletion_config_updated;
+}
+
bool StatsdStats::hasEventQueueOverflow() const {
lock_guard<std::mutex> lock(mLock);
return mOverflowCount != 0;
@@ -837,12 +1167,24 @@
for (const auto& configStats : mIceBox) {
dprintf(out,
"Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, valid=%d\n",
+ "#matcher=%d, #alert=%d, valid=%d",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
configStats->deletion_time_sec, configStats->reset_time_sec,
configStats->metric_count, configStats->condition_count, configStats->matcher_count,
configStats->alert_count, configStats->is_valid);
-
+ if (hasRestrictedConfigErrors(configStats)) {
+ dprintf(out,
+ ", device_info_table_creation_failed=%d, db_corrupted_count=%d, "
+ "db_size_exceeded=%d, db_stat_failed=%d, "
+ "db_config_invalid=%d, db_too_old=%d, db_deletion_config_removed=%d, "
+ "db_deletion_config_updated=%d",
+ configStats->device_info_table_creation_failed, configStats->db_corrupted_count,
+ configStats->db_deletion_size_exceeded_limit,
+ configStats->db_deletion_stat_failed, configStats->db_deletion_config_invalid,
+ configStats->db_deletion_too_old, configStats->db_deletion_config_removed,
+ configStats->db_deletion_config_updated);
+ }
+ dprintf(out, "\n");
if (!configStats->is_valid) {
dprintf(out, "\tinvalid config reason: %s\n",
InvalidConfigReasonEnum_Name(configStats->reason->reason).c_str());
@@ -868,18 +1210,54 @@
buildTimeString(*dropTimePtr).c_str(), (long long)*dropTimePtr,
(long long)*dropBytesPtr);
}
+
+ for (const auto& stats : configStats->restricted_metric_stats) {
+ dprintf(out, "Restricted MetricId %lld: ", (long long)stats.first);
+ dprintf(out, "Insert error %lld, ", (long long)stats.second.insertError);
+ dprintf(out, "Table creation error %lld, ", (long long)stats.second.tableCreationError);
+ dprintf(out, "Table deletion error %lld ", (long long)stats.second.tableDeletionError);
+ dprintf(out, "Category changed count %lld\n ",
+ (long long)stats.second.categoryChangedCount);
+ string flushLatencies = "Flush Latencies: ";
+ for (const int64_t latencyNs : stats.second.flushLatencyNs) {
+ flushLatencies.append(to_string(latencyNs).append(","));
+ }
+ flushLatencies.pop_back();
+ flushLatencies.push_back('\n');
+ dprintf(out, "%s", flushLatencies.c_str());
+ }
+
+ for (const int64_t flushLatency : configStats->total_flush_latency_ns) {
+ dprintf(out, "\tflush latency time ns: %lld\n", (long long)flushLatency);
+ }
+
+ for (const int64_t dbSize : configStats->total_db_sizes) {
+ dprintf(out, "\tdb size: %lld\n", (long long)dbSize);
+ }
}
dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
for (auto& pair : mConfigStats) {
auto& configStats = pair.second;
dprintf(out,
"Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, valid=%d\n",
+ "#matcher=%d, #alert=%d, valid=%d",
configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
configStats->deletion_time_sec, configStats->metric_count,
configStats->condition_count, configStats->matcher_count, configStats->alert_count,
configStats->is_valid);
-
+ if (hasRestrictedConfigErrors(configStats)) {
+ dprintf(out,
+ ", device_info_table_creation_failed=%d, db_corrupted_count=%d, "
+ "db_size_exceeded=%d, db_stat_failed=%d, "
+ "db_config_invalid=%d, db_too_old=%d, db_deletion_config_removed=%d, "
+ "db_deletion_config_updated=%d",
+ configStats->device_info_table_creation_failed, configStats->db_corrupted_count,
+ configStats->db_deletion_size_exceeded_limit,
+ configStats->db_deletion_stat_failed, configStats->db_deletion_config_invalid,
+ configStats->db_deletion_too_old, configStats->db_deletion_config_removed,
+ configStats->db_deletion_config_updated);
+ }
+ dprintf(out, "\n");
if (!configStats->is_valid) {
dprintf(out, "\tinvalid config reason: %s\n",
InvalidConfigReasonEnum_Name(configStats->reason->reason).c_str());
@@ -913,9 +1291,10 @@
}
for (const auto& dump : configStats->dump_report_stats) {
- dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
- buildTimeString(dump.first).c_str(), (long long)dump.first,
- (long long)dump.second);
+ dprintf(out, "\tdump report time: %s(%lld) bytes: %d reportNumber: %d\n",
+ buildTimeString(dump.mDumpReportTimeSec).c_str(),
+ (long long)dump.mDumpReportTimeSec, dump.mDumpReportSizeBytes,
+ dump.mDumpReportNumber);
}
for (const auto& stats : pair.second->matcher_stats) {
@@ -935,6 +1314,30 @@
for (const auto& stats : pair.second->alert_stats) {
dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
}
+
+ for (const auto& stats : configStats->restricted_metric_stats) {
+ dprintf(out, "Restricted MetricId %lld: ", (long long)stats.first);
+ dprintf(out, "Insert error %lld, ", (long long)stats.second.insertError);
+ dprintf(out, "Table creation error %lld, ", (long long)stats.second.tableCreationError);
+ dprintf(out, "Table deletion error %lld ", (long long)stats.second.tableDeletionError);
+ dprintf(out, "Category changed count %lld\n ",
+ (long long)stats.second.categoryChangedCount);
+ string flushLatencies = "Flush Latencies: ";
+ for (const int64_t latencyNs : stats.second.flushLatencyNs) {
+ flushLatencies.append(to_string(latencyNs).append(","));
+ }
+ flushLatencies.pop_back();
+ flushLatencies.push_back('\n');
+ dprintf(out, "%s", flushLatencies.c_str());
+ }
+
+ for (const int64_t flushLatency : configStats->total_flush_latency_ns) {
+ dprintf(out, "flush latency time ns: %lld\n", (long long)flushLatency);
+ }
+
+ for (const int64_t dbSize : configStats->total_db_sizes) {
+ dprintf(out, "\tdb size: %lld\n", (long long)dbSize);
+ }
}
dprintf(out, "********Disk Usage stats***********\n");
StorageManager::printStats(out);
@@ -965,7 +1368,7 @@
" (pull timeout)%ld, (pull exceed max delay)%ld"
" (no uid provider count)%ld, (no puller found count)%ld\n"
" (registered count) %ld, (unregistered count) %ld"
- " (atom error count) %d, (subscription pull count) %d\n",
+ " (atom error count) %d, (subscription pull count) %d, (binder call failed) %ld\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
@@ -973,7 +1376,8 @@
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
pair.second.registeredCount, pair.second.unregisteredCount,
- pair.second.atomErrorCount, pair.second.subscriptionPullCount);
+ pair.second.atomErrorCount, pair.second.subscriptionPullCount,
+ pair.second.binderCallFailCount);
if (pair.second.pullTimeoutMetadata.size() > 0) {
string uptimeMillis = "(pull timeout system uptime millis) ";
string pullTimeoutMillis = "(pull timeout elapsed time millis) ";
@@ -1017,8 +1421,31 @@
loss.mUid, loss.mPid);
}
+ if (mSocketLossStats.size()) {
+ dprintf(out, "********SocketLossStats stats***********\n");
+ for (const auto& loss : mSocketLossStats) {
+ dprintf(out, "Socket loss: %d (uid), first loss at %lld, last loss at %lld\n",
+ loss.mUid, (long long)loss.mFirstLossTsNanos, (long long)loss.mLastLossTsNanos);
+ for (const auto& counterInfo : loss.mLossCountPerErrorAtomId) {
+ dprintf(out, "\t\t %d (atomId) %d (error), %d (count)\n", counterInfo.mAtomId,
+ counterInfo.mError, counterInfo.mCount);
+ }
+ }
+ }
+
+ if (mSocketLossStatsOverflowCounters.size()) {
+ dprintf(out, "********mSocketLossStatsOverflowCounters stats***********\n");
+ for (const auto& overflow : mSocketLossStatsOverflowCounters) {
+ dprintf(out, "Socket loss overflow for %d uid is %d times\n", overflow.first,
+ overflow.second);
+ }
+ }
+
+ dprintf(out, "********EventQueueOverflow stats***********\n");
dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n",
mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs);
+ dprintf(out, "Event queue max size: %d; Observed at : %lld\n", mEventQueueMaxSizeObserved,
+ (long long)mEventQueueMaxSizeObservedElapsedNanos);
if (mActivationBroadcastGuardrailStats.size() > 0) {
dprintf(out, "********mActivationBroadcastGuardrail stats***********\n");
@@ -1040,6 +1467,29 @@
dprintf(out, "\n");
}
+ if (mRestrictedMetricQueryStats.size() > 0) {
+ dprintf(out, "********Restricted Metric Query stats***********\n");
+ for (const auto& stat : mRestrictedMetricQueryStats) {
+ if (stat.mHasError) {
+ dprintf(out,
+ "Query with error type: %d - %lld (query time ns), "
+ "%d (calling uid), %lld (config id), %s (config package), %s (error)\n",
+ stat.mInvalidQueryReason.value(), (long long)stat.mQueryWallTimeNs,
+ stat.mCallingUid, (long long)stat.mConfigId, stat.mConfigPackage.c_str(),
+ stat.mError.c_str());
+ } else {
+ dprintf(out,
+ "Query succeed - %lld (query time ns), %d (calling uid), "
+ "%lld (config id), %s (config package), %d (config uid), "
+ "%lld (queryLatencyNs)\n",
+ (long long)stat.mQueryWallTimeNs, stat.mCallingUid,
+ (long long)stat.mConfigId, stat.mConfigPackage.c_str(),
+ stat.mConfigUid.value(), (long long)stat.mQueryLatencyNs.value());
+ }
+ }
+ }
+ dprintf(out, "********Statsd Stats Id***********\n");
+ dprintf(out, "Statsd Stats Id %d\n", mStatsdStatsId);
dprintf(out, "********Shard Offset Provider stats***********\n");
dprintf(out, "Shard Offset: %u\n", ShardOffsetProvider::getInstance().getShardOffset());
}
@@ -1129,15 +1579,21 @@
}
for (const auto& dump : configStats.dump_report_stats) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME |
- FIELD_COUNT_REPEATED,
- dump.first);
+ proto->write(
+ FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME | FIELD_COUNT_REPEATED,
+ dump.mDumpReportTimeSec);
}
for (const auto& dump : configStats.dump_report_stats) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES |
- FIELD_COUNT_REPEATED,
- (long long)dump.second);
+ proto->write(
+ FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES | FIELD_COUNT_REPEATED,
+ (long long)dump.mDumpReportSizeBytes);
+ }
+
+ for (const auto& dump : configStats.dump_report_stats) {
+ proto->write(
+ FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_NUMBER | FIELD_COUNT_REPEATED,
+ dump.mDumpReportNumber);
}
for (const auto& annotation : configStats.annotations) {
@@ -1188,6 +1644,60 @@
proto->end(tmpToken);
}
+ for (const auto& pair : configStats.restricted_metric_stats) {
+ uint64_t token =
+ proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_STATS_RESTRICTED_METRIC_STATS |
+ FIELD_COUNT_REPEATED);
+
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_METRIC_ID, (long long)pair.first);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_INSERT_ERROR,
+ (long long)pair.second.insertError, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_TABLE_CREATION_ERROR,
+ (long long)pair.second.tableCreationError, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_TABLE_DELETION_ERROR,
+ (long long)pair.second.tableDeletionError, proto);
+ for (const int64_t flushLatencyNs : pair.second.flushLatencyNs) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_FLUSH_LATENCY |
+ FIELD_COUNT_REPEATED,
+ flushLatencyNs);
+ }
+ writeNonZeroStatToStream(
+ FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_STATS_CATEGORY_CHANGED_COUNT,
+ (long long)pair.second.categoryChangedCount, proto);
+ proto->end(token);
+ }
+ writeNonZeroStatToStream(
+ FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_DEVICE_INFO_TABLE_CREATION_FAILED,
+ configStats.device_info_table_creation_failed, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_RESTRICTED_DB_CORRUPTED_COUNT,
+ configStats.db_corrupted_count, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_STAT_FAILED,
+ configStats.db_deletion_size_exceeded_limit, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_SIZE_EXCEEDED_LIMIT,
+ configStats.db_deletion_size_exceeded_limit, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_CONFIG_INVALID,
+ configStats.db_deletion_config_invalid, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_TOO_OLD,
+ configStats.db_deletion_too_old, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_CONFIG_REMOVED,
+ configStats.db_deletion_config_removed, proto);
+ writeNonZeroStatToStream(FIELD_TYPE_INT32 | FIELD_ID_DB_DELETION_CONFIG_UPDATED,
+ configStats.db_deletion_config_updated, proto);
+ for (int64_t latency : configStats.total_flush_latency_ns) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_FLUSH_LATENCY |
+ FIELD_COUNT_REPEATED,
+ latency);
+ }
+ for (int64_t dbSizeTimestamp : configStats.total_db_size_timestamps) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_DB_SIZE_TIME_SEC |
+ FIELD_COUNT_REPEATED,
+ dbSizeTimestamp);
+ }
+ for (int64_t dbSize : configStats.total_db_sizes) {
+ proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_RESTRICTED_CONFIG_DB_SIZE_BYTES |
+ FIELD_COUNT_REPEATED,
+ dbSize);
+ }
proto->end(token);
}
@@ -1291,6 +1801,13 @@
proto.end(token);
}
+ uint64_t queueStatsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_QUEUE_STATS);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_QUEUE_MAX_SIZE_OBSERVED,
+ (int32_t)mEventQueueMaxSizeObserved);
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_QUEUE_MAX_SIZE_OBSERVED_ELAPSED_NANOS,
+ (long long)mEventQueueMaxSizeObservedElapsedNanos);
+ proto.end(queueStatsToken);
+
for (const auto& restart : mSystemServerRestartSec) {
proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED,
restart);
@@ -1310,9 +1827,44 @@
proto.end(token);
}
+ for (const auto& stat : mRestrictedMetricQueryStats) {
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS |
+ FIELD_COUNT_REPEATED);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CALLING_UID,
+ stat.mCallingUid);
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_ID,
+ stat.mConfigId);
+ proto.write(FIELD_TYPE_STRING | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_PACKAGE,
+ stat.mConfigPackage);
+ if (stat.mConfigUid.has_value()) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_UID,
+ stat.mConfigUid.value());
+ }
+ if (stat.mInvalidQueryReason.has_value()) {
+ proto.write(
+ FIELD_TYPE_ENUM | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_INVALID_QUERY_REASON,
+ stat.mInvalidQueryReason.value());
+ }
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_QUERY_WALL_TIME_NS,
+ stat.mQueryWallTimeNs);
+ proto.write(FIELD_TYPE_BOOL | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_HAS_ERROR,
+ stat.mHasError);
+ if (stat.mHasError && !stat.mError.empty()) {
+ proto.write(FIELD_TYPE_STRING | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_ERROR,
+ stat.mError);
+ }
+ if (stat.mQueryLatencyNs.has_value()) {
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_LATENCY_NS,
+ stat.mQueryLatencyNs.value());
+ }
+ proto.end(token);
+ }
+
proto.write(FIELD_TYPE_UINT32 | FIELD_ID_SHARD_OFFSET,
static_cast<long>(ShardOffsetProvider::getInstance().getShardOffset()));
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_STATSD_STATS_ID, mStatsdStatsId);
+
// Write subscription stats
const uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIPTION_STATS);
for (const auto& [id, subStats] : mSubscriptionStats) {
@@ -1338,6 +1890,47 @@
mSubscriptionPullThreadWakeupCount, &proto);
proto.end(token);
+ // libstatssocket specific stats
+
+ const uint64_t socketLossStatsToken =
+ proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS);
+
+ // socket loss stats info per uid/error/atom id counter
+ for (const auto& perUidLossInfo : mSocketLossStats) {
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS_PER_UID |
+ FIELD_COUNT_REPEATED);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_UID, perUidLossInfo.mUid);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_FIRST_TIMESTAMP_NANOS,
+ perUidLossInfo.mFirstLossTsNanos);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_LAST_TIMESTAMP_NANOS,
+ perUidLossInfo.mLastLossTsNanos);
+ for (const auto& counterInfo : perUidLossInfo.mLossCountPerErrorAtomId) {
+ uint64_t token =
+ proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_ATOM_ID_LOSS_STATS |
+ FIELD_COUNT_REPEATED);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_ATOM_ID,
+ counterInfo.mAtomId);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_ERROR, counterInfo.mError);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ID_LOSS_STATS_COUNT, counterInfo.mCount);
+ proto.end(token);
+ }
+ proto.end(token);
+ }
+
+ // socket loss stats overflow counters
+ for (const auto& overflowInfo : mSocketLossStatsOverflowCounters) {
+ uint64_t token =
+ proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS |
+ FIELD_COUNT_REPEATED);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_UID,
+ overflowInfo.first);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_SOCKET_LOSS_STATS_OVERFLOW_COUNTERS_COUNT,
+ overflowInfo.second);
+ proto.end(token);
+ }
+
+ proto.end(socketLossStatsToken);
+
output->clear();
proto.serializeToVector(output);
@@ -1348,11 +1941,11 @@
VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)output->size());
}
-std::pair<size_t, size_t> StatsdStats::getAtomDimensionKeySizeLimits(const int atomId) {
+std::pair<size_t, size_t> StatsdStats::getAtomDimensionKeySizeLimits(int atomId,
+ size_t defaultHardLimit) {
return kAtomDimensionKeySizeLimitMap.find(atomId) != kAtomDimensionKeySizeLimitMap.end()
? kAtomDimensionKeySizeLimitMap.at(atomId)
- : std::make_pair<size_t, size_t>(kDimensionKeySizeSoftLimit,
- kDimensionKeySizeHardLimit);
+ : std::pair<size_t, size_t>(kDimensionKeySizeSoftLimit, defaultHardLimit);
}
InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason,
diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h
index 9e7d9e3..3adb668 100644
--- a/statsd/src/guardrail/StatsdStats.h
+++ b/statsd/src/guardrail/StatsdStats.h
@@ -26,6 +26,7 @@
#include <vector>
#include "config/ConfigKey.h"
+#include "logd/logevent_util.h"
namespace android {
namespace os {
@@ -52,6 +53,25 @@
}
};
+typedef struct {
+ int64_t insertError = 0;
+ int64_t tableCreationError = 0;
+ int64_t tableDeletionError = 0;
+ std::list<int64_t> flushLatencyNs;
+ int64_t categoryChangedCount = 0;
+} RestrictedMetricStats;
+
+struct DumpReportStats {
+ DumpReportStats(int32_t dumpReportSec, int32_t dumpReportSize, int32_t reportNumber)
+ : mDumpReportTimeSec(dumpReportSec),
+ mDumpReportSizeBytes(dumpReportSize),
+ mDumpReportNumber(reportNumber) {
+ }
+ int32_t mDumpReportTimeSec = 0;
+ int32_t mDumpReportSizeBytes = 0;
+ int32_t mDumpReportNumber = 0;
+};
+
struct ConfigStats {
int32_t uid;
int64_t id;
@@ -63,6 +83,14 @@
int32_t matcher_count;
int32_t alert_count;
bool is_valid;
+ bool device_info_table_creation_failed = false;
+ int32_t db_corrupted_count = 0;
+ int32_t db_deletion_stat_failed = 0;
+ int32_t db_deletion_size_exceeded_limit = 0;
+ int32_t db_deletion_config_invalid = 0;
+ int32_t db_deletion_too_old = 0;
+ int32_t db_deletion_config_removed = 0;
+ int32_t db_deletion_config_updated = 0;
// Stores reasons for why config is valid or not
std::optional<InvalidConfigReason> reason;
@@ -78,7 +106,8 @@
std::list<int32_t> data_drop_time_sec;
// Number of bytes dropped at corresponding time.
std::list<int64_t> data_drop_bytes;
- std::list<std::pair<int32_t, int64_t>> dump_report_stats;
+
+ std::list<DumpReportStats> dump_report_stats;
// Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
std::map<const int64_t, int> matcher_stats;
@@ -105,6 +134,17 @@
// Stores the config ID for each sub-config used.
std::list<std::pair<const int64_t, const int32_t>> annotations;
+
+ // Maps metric ID of restricted metric to its stats.
+ std::map<int64_t, RestrictedMetricStats> restricted_metric_stats;
+
+ std::list<int64_t> total_flush_latency_ns;
+
+ // Stores the last 20 timestamps for computing sqlite db size.
+ std::list<int64_t> total_db_size_timestamps;
+
+ // Stores the last 20 sizes of the sqlite db.
+ std::list<int64_t> total_db_sizes;
};
struct UidMapStats {
@@ -131,6 +171,8 @@
const static int kDimensionKeySizeSoftLimit = 500;
static constexpr int kDimensionKeySizeHardLimit = 800;
+ static constexpr int kDimensionKeySizeHardLimitMin = 800;
+ static constexpr int kDimensionKeySizeHardLimitMax = 3000;
// Per atom dimension key size limit
static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
@@ -138,8 +180,8 @@
const static int kMaxConfigCountPerUid = 20;
const static int kMaxAlertCountPerConfig = 200;
const static int kMaxConditionCountPerConfig = 500;
- const static int kMaxMetricCountPerConfig = 2000;
- const static int kMaxMatcherCountPerConfig = 2500;
+ const static int kMaxMetricCountPerConfig = 3000;
+ const static int kMaxMatcherCountPerConfig = 3500;
// The max number of old config stats we keep.
const static int kMaxIceBoxSize = 20;
@@ -154,6 +196,14 @@
const static int kMaxPullAtomPackages = 100;
+ const static int kMaxRestrictedMetricQueryCount = 20;
+
+ const static int kMaxRestrictedMetricFlushLatencyCount = 20;
+
+ const static int kMaxRestrictedConfigFlushLatencyCount = 20;
+
+ const static int kMaxRestrictedConfigDbSizeCount = 20;
+
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
static const size_t kDefaultMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
@@ -161,9 +211,17 @@
// Hard limit for custom memory allowed for storing metrics per configuration.
static const size_t kHardMaxMetricsBytesPerConfig = 20 * 1024 * 1024;
+ // Max memory allowed for storing metrics per configuration before triggering a intent to fetch
+ // data.
+ static const size_t kHardMaxTriggerGetDataBytes = 10 * 1024 * 1024;
+
// Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
// data subscriber that it's time to call getData.
- static const size_t kBytesPerConfigTriggerGetData = 192 * 1024;
+ static const size_t kDefaultBytesPerConfigTriggerGetData = 192 * 1024;
+
+ // Soft memory limit per restricted configuration. Once this limit is exceeded,
+ // we begin flush in-memory restricted metrics to database.
+ static const size_t kBytesPerRestrictedConfigTriggerFlush = 25 * 1024;
// Cap the UID map's memory usage to this. This should be fairly high since the UID information
// is critical for understanding the metrics.
@@ -178,6 +236,15 @@
/* Min period between two checks of byte size per config key in nanoseconds. */
static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
+ /* Min period between two checks of restricted metrics TTLs. */
+ static const int64_t kMinTtlCheckPeriodNs = 60 * 60 * NS_PER_SEC;
+
+ /* Min period between two flush operations of restricted metrics. */
+ static const int64_t kMinFlushRestrictedPeriodNs = 60 * 60 * NS_PER_SEC;
+
+ /* Min period between two db guardrail check operations of restricted metrics. */
+ static const int64_t kMinDbGuardrailEnforcementPeriodNs = 60 * 60 * NS_PER_SEC;
+
/* Minimum period between two activation broadcasts in nanoseconds. */
static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC;
@@ -205,9 +272,12 @@
// Maximum number of pushed atoms error statsd stats will track.
static const int kMaxPushedAtomErrorStatsSize = 100;
+ // Maximum number of socket loss stats to track.
+ static const int kMaxSocketLossStatsSize = 50;
+
// Maximum atom id value that we consider a platform pushed atom.
// This should be updated once highest pushed atom id in atoms.proto approaches this value.
- static const int kMaxPushedAtomId = 900;
+ static const int kMaxPushedAtomId = 1500;
// Atom id that is the start of the pulled atoms.
static const int kPullAtomStartTag = 10000;
@@ -272,7 +342,48 @@
*
* The report may be requested via StatsManager API, or through adb cmd.
*/
- void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes);
+ void noteMetricsReportSent(const ConfigKey& key, const size_t numBytes,
+ const int32_t reportNumber);
+
+ /**
+ * Report failure in creating the device info metadata table for restricted configs.
+ */
+ void noteDeviceInfoTableCreationFailed(const ConfigKey& key);
+
+ /**
+ * Report db corruption for restricted configs.
+ */
+ void noteDbCorrupted(const ConfigKey& key);
+
+ /**
+ * Report db exceeded the size limit for restricted configs.
+ */
+ void noteDbSizeExceeded(const ConfigKey& key);
+
+ /**
+ * Report db size check with stat for restricted configs failed.
+ */
+ void noteDbStatFailed(const ConfigKey& key);
+
+ /**
+ * Report restricted config is invalid.
+ */
+ void noteDbConfigInvalid(const ConfigKey& key);
+
+ /**
+ * Report db is too old for restricted configs.
+ */
+ void noteDbTooOld(const ConfigKey& key);
+
+ /**
+ * Report db was deleted due to config removal.
+ */
+ void noteDbDeletionConfigRemoved(const ConfigKey& key);
+
+ /**
+ * Report db was deleted due to config update.
+ */
+ void noteDbDeletionConfigUpdated(const ConfigKey& key);
/**
* Report the size of output tuple of a condition.
@@ -284,7 +395,7 @@
* [id]: The id of the condition.
* [size]: The output tuple size.
*/
- void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
+ void noteConditionDimensionSize(const ConfigKey& key, int64_t id, int size);
/**
* Report the size of output tuple of a metric.
@@ -296,7 +407,7 @@
* [id]: The id of the metric.
* [size]: The output tuple size.
*/
- void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
+ void noteMetricDimensionSize(const ConfigKey& key, int64_t id, int size);
/**
* Report the max size of output tuple of dimension in condition across dimensions in what.
@@ -308,7 +419,7 @@
* [id]: The id of the metric.
* [size]: The output tuple size.
*/
- void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
+ void noteMetricDimensionInConditionSize(const ConfigKey& key, int64_t id, int size);
/**
* Report a matcher has been matched.
@@ -316,7 +427,7 @@
* [key]: The config key that this matcher belongs to.
* [id]: The id of the matcher.
*/
- void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
+ void noteMatcherMatched(const ConfigKey& key, int64_t id);
/**
* Report that an anomaly detection alert has been declared.
@@ -324,7 +435,7 @@
* [key]: The config key that this alert belongs to.
* [id]: The id of the alert.
*/
- void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
+ void noteAnomalyDeclared(const ConfigKey& key, int64_t id);
/**
* Report an atom event has been logged.
@@ -503,6 +614,9 @@
* in the queue */
void noteEventQueueOverflow(int64_t oldestEventTimestampNs, int32_t atomId, bool isSkipped);
+ /* Notes queue max size seen so far and associated timestamp */
+ void noteEventQueueSize(int32_t size, int64_t eventTimestampNs);
+
/**
* Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
* should have been sent, but instead was skipped due to hitting the guardrail.
@@ -519,6 +633,51 @@
*/
void noteAtomError(int atomTag, bool pull = false);
+ /** Report query of restricted metric succeed **/
+ void noteQueryRestrictedMetricSucceed(const int64_t configId, const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid, int64_t queryLatencyNs);
+
+ /** Report query of restricted metric failed **/
+ void noteQueryRestrictedMetricFailed(const int64_t configId, const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid, const InvalidQueryReason reason);
+
+ /** Report query of restricted metric failed along with an error string **/
+ void noteQueryRestrictedMetricFailed(const int64_t configId, const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid, const InvalidQueryReason reason,
+ const string& error);
+
+ // Reports that a restricted metric fails to be inserted to database.
+ void noteRestrictedMetricInsertError(const ConfigKey& configKey, int64_t metricId);
+
+ // Reports that a restricted metric fails to create table in database.
+ void noteRestrictedMetricTableCreationError(const ConfigKey& configKey, int64_t metricId);
+
+ // Reports that a restricted metric fails to delete table in database.
+ void noteRestrictedMetricTableDeletionError(const ConfigKey& configKey, int64_t metricId);
+
+ // Reports the time it takes for a restricted metric to flush the data to the database.
+ void noteRestrictedMetricFlushLatency(const ConfigKey& configKey, int64_t metricId,
+ const int64_t flushLatencyNs);
+
+ // Reports that a restricted metric had a category change.
+ void noteRestrictedMetricCategoryChanged(const ConfigKey& configKey, int64_t metricId);
+
+ // Reports the time is takes to flush a restricted config to the database.
+ void noteRestrictedConfigFlushLatency(const ConfigKey& configKey,
+ const int64_t totalFlushLatencyNs);
+
+ // Reports the size of the internal sqlite db.
+ void noteRestrictedConfigDbSize(const ConfigKey& configKey, int64_t elapsedTimeNs,
+ const int64_t dbSize);
+
+ /**
+ * Records libstatssocket was not able to write into socket.
+ */
+ void noteAtomSocketLoss(const SocketLossInfo& lossInfo);
+
/**
* Report a new subscription has started and report the static stats about the subscription
* config.
@@ -574,7 +733,20 @@
/**
* Return soft and hard atom key dimension size limits as an std::pair.
*/
- static std::pair<size_t, size_t> getAtomDimensionKeySizeLimits(const int atomId = -1);
+ static std::pair<size_t, size_t> getAtomDimensionKeySizeLimits(int atomId,
+ size_t defaultHardLimit);
+
+ inline static int clampDimensionKeySizeLimit(int dimLimit) {
+ return std::clamp(dimLimit, kDimensionKeySizeHardLimitMin, kDimensionKeySizeHardLimitMax);
+ }
+
+ /**
+ * Return the unique identifier for the statsd stats report. This id is
+ * reset on boot.
+ */
+ inline int32_t getStatsdStatsId() const {
+ return mStatsdStatsId;
+ }
/**
* Returns true if there is recorded event queue overflow
@@ -641,6 +813,11 @@
int32_t mStartTimeSec;
+ // Random id set using rand() during the initialization. Used to uniquely
+ // identify a session. This is more reliable than mStartTimeSec due to the
+ // unreliable nature of wall clock times.
+ const int32_t mStatsdStatsId;
+
// Track the number of dropped entries used by the uid map.
UidMapStats mUidMapStats;
@@ -650,7 +827,7 @@
// Stores the stats for the configs that are no longer in use.
// The size of the vector is capped by kMaxIceBoxSize.
- std::list<const std::shared_ptr<ConfigStats>> mIceBox;
+ std::list<std::shared_ptr<ConfigStats>> mIceBox;
// Stores the number of times a pushed atom is logged and skipped (if skipped).
// The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
@@ -680,6 +857,37 @@
// The max size of this map is kMaxPushedAtomErrorStatsSize.
std::map<int, int> mPushedAtomErrorStats;
+ // Stores the number of times a pushed atom was lost due to socket error.
+ // Represents counter per uid per tag per error with indication when the loss event was observed
+ // first & last time.
+ struct SocketLossStats {
+ SocketLossStats(int32_t uid, int64_t firstLossTsNanos, int64_t lastLossTsNanos)
+ : mUid(uid), mFirstLossTsNanos(firstLossTsNanos), mLastLossTsNanos(lastLossTsNanos) {
+ }
+
+ int32_t mUid;
+ int64_t mFirstLossTsNanos;
+ int64_t mLastLossTsNanos;
+ // atom loss count per error, atom id
+ struct AtomLossInfo {
+ AtomLossInfo(int32_t atomId, int32_t error, int32_t count)
+ : mAtomId(atomId), mError(error), mCount(count) {
+ }
+ int mAtomId;
+ int mError;
+ int mCount;
+ };
+ std::vector<AtomLossInfo> mLossCountPerErrorAtomId;
+ };
+ // The max size of this list is kMaxSocketLossStatsSize.
+ std::list<SocketLossStats> mSocketLossStats;
+
+ // Stores the number of times a pushed atom loss info was dropped from the stats
+ // on libstatssocket side due to guardrail hit.
+ // Represents counter per uid.
+ // The max size of this map is kMaxSocketLossStatsSize.
+ std::map<int32_t, int32_t> mSocketLossStatsOverflowCounters;
+
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
@@ -717,11 +925,51 @@
// Total number of events that are lost due to queue overflow.
int32_t mOverflowCount = 0;
+ // Max number of events stored into the queue seen so far.
+ int32_t mEventQueueMaxSizeObserved = 0;
+
+ // Event timestamp for associated max size hit.
+ int64_t mEventQueueMaxSizeObservedElapsedNanos = 0;
+
// Timestamps when we detect log loss, and the number of logs lost.
std::list<LogLossStats> mLogLossStats;
std::list<int32_t> mSystemServerRestartSec;
+ struct RestrictedMetricQueryStats {
+ RestrictedMetricQueryStats(int32_t callingUid, int64_t configId,
+ const string& configPackage, std::optional<int32_t> configUid,
+ int64_t queryTimeNs,
+ std::optional<InvalidQueryReason> invalidQueryReason,
+ const string& error, std::optional<int64_t> queryLatencyNs)
+ : mCallingUid(callingUid),
+ mConfigId(configId),
+ mConfigPackage(configPackage),
+ mConfigUid(configUid),
+ mQueryWallTimeNs(queryTimeNs),
+ mInvalidQueryReason(invalidQueryReason),
+ mError(error),
+ mQueryLatencyNs(queryLatencyNs) {
+ mHasError = invalidQueryReason.has_value();
+ }
+ int32_t mCallingUid;
+ int64_t mConfigId;
+ string mConfigPackage;
+ std::optional<int32_t> mConfigUid;
+ int64_t mQueryWallTimeNs;
+ std::optional<InvalidQueryReason> mInvalidQueryReason;
+ bool mHasError;
+ string mError;
+ std::optional<int64_t> mQueryLatencyNs;
+ };
+ std::list<RestrictedMetricQueryStats> mRestrictedMetricQueryStats;
+
+ void noteQueryRestrictedMetricFailedLocked(const int64_t configId, const string& configPackage,
+ const std::optional<int32_t> configUid,
+ const int32_t callingUid,
+ const InvalidQueryReason reason,
+ const string& error);
+
int32_t mSubscriptionPullThreadWakeupCount = 0;
// Maps Subscription ID to the corresponding SubscriptionStats struct object.
@@ -747,7 +995,8 @@
void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec);
- void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
+ void noteMetricsReportSent(const ConfigKey& key, const size_t numBytes, int32_t timeSec,
+ const int32_t reportNumber);
void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
@@ -761,42 +1010,50 @@
int getPushedAtomDropsLocked(int atomId) const;
+ bool hasRestrictedConfigErrors(const std::shared_ptr<ConfigStats>& configStats) const;
+
/**
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
*/
StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
- FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
+ FRIEND_TEST(LogEventQueue_test, TestQueueMaxSize);
+ FRIEND_TEST(SocketParseMessageTest, TestProcessMessage);
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
+ FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
+ FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
+ FRIEND_TEST(StatsdStatsTest, TestAtomDroppedStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomLog);
+ FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedAndSkippedStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomSkippedStats);
+ FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
+ FRIEND_TEST(StatsdStatsTest, TestHasHitDimensionGuardrail);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigMissingMetricId);
FRIEND_TEST(StatsdStatsTest, TestInvalidConfigOnlyMetricId);
- FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
- FRIEND_TEST(StatsdStatsTest, TestSubStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomLog);
FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
- FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
- FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
- FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
- FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
- FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomSkippedStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomDroppedStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedAndSkippedStats);
+ FRIEND_TEST(StatsdStatsTest, TestQueueStats);
+ FRIEND_TEST(StatsdStatsTest, TestRestrictedMetricsQueryStats);
+ FRIEND_TEST(StatsdStatsTest, TestRestrictedMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestShardOffsetProvider);
- FRIEND_TEST(StatsdStatsTest, TestHasHitDimensionGuardrail);
- FRIEND_TEST(StatsdStatsTest, TestSubscriptionStarted);
- FRIEND_TEST(StatsdStatsTest, TestSubscriptionFlushed);
- FRIEND_TEST(StatsdStatsTest, TestSubscriptionEnded);
+ FRIEND_TEST(StatsdStatsTest, TestSocketLossStats);
+ FRIEND_TEST(StatsdStatsTest, TestSocketLossStatsOverflowCounter);
+ FRIEND_TEST(StatsdStatsTest, TestSubStats);
FRIEND_TEST(StatsdStatsTest, TestSubscriptionAtomPulled);
+ FRIEND_TEST(StatsdStatsTest, TestSubscriptionEnded);
+ FRIEND_TEST(StatsdStatsTest, TestSubscriptionFlushed);
FRIEND_TEST(StatsdStatsTest, TestSubscriptionPullThreadWakeup);
+ FRIEND_TEST(StatsdStatsTest, TestSubscriptionStarted);
FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedMaxActiveSubscriptions);
FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedRemoveFinishedSubscription);
-
- FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
+ FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
+ FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
+ FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
};
InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason,
@@ -831,10 +1088,10 @@
const int64_t subscriptionId);
InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlarm(
- const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alarmId);
+ const InvalidConfigReasonEnum reason, int64_t subscriptionId, int64_t alarmId);
InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlert(
- const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alertId);
+ const InvalidConfigReasonEnum reason, int64_t subscriptionId, int64_t alertId);
} // namespace statsd
} // namespace os
diff --git a/statsd/src/guardrail/stats_log_enums.proto b/statsd/src/guardrail/stats_log_enums.proto
index b4172d3..c468d52 100644
--- a/statsd/src/guardrail/stats_log_enums.proto
+++ b/statsd/src/guardrail/stats_log_enums.proto
@@ -139,4 +139,30 @@
INVALID_CONFIG_REASON_METRIC_DIMENSIONAL_SAMPLING_INFO_MISSING_SAMPLED_FIELD = 81;
INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELD_INCORRECT_SIZE = 82;
INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT = 83;
+ INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED = 84;
+ INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED = 85;
+ INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE = 86;
+ INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING = 87;
+ INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER = 88;
+ INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL = 89;
+ INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE = 90;
+ INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE = 91;
+ INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY = 92;
+ INVALID_CONFIG_REASON_METRIC_INCORRECT_PULL_PROBABILITY = 93;
+ INVALID_CONFIG_REASON_GAUGE_METRIC_PUSHED_WITH_PULL_PROBABILITY = 94;
+ INVALID_CONFIG_REASON_GAUGE_METRIC_RANDOM_ONE_SAMPLE_WITH_PULL_PROBABILITY = 95;
+ INVALID_CONFIG_REASON_VALUE_METRIC_DEFINES_SINGLE_AND_MULTIPLE_AGG_TYPES = 96;
+ INVALID_CONFIG_REASON_VALUE_METRIC_AGG_TYPES_DNE_VALUE_FIELDS_SIZE = 97;
+};
+
+enum InvalidQueryReason {
+ UNKNOWN_REASON = 0;
+ FLAG_DISABLED = 1;
+ UNSUPPORTED_SQLITE_VERSION = 2;
+ AMBIGUOUS_CONFIG_KEY = 3;
+ CONFIG_KEY_NOT_FOUND = 4;
+ CONFIG_KEY_WITH_UNMATCHED_DELEGATE = 5;
+ QUERY_FAILURE = 6;
+ INCONSISTENT_ROW_SIZE = 7;
+ NULL_CALLBACK = 8;
};
diff --git a/statsd/src/logd/LogEvent.cpp b/statsd/src/logd/LogEvent.cpp
index bee279d..94fe5cf 100644
--- a/statsd/src/logd/LogEvent.cpp
+++ b/statsd/src/logd/LogEvent.cpp
@@ -23,7 +23,8 @@
#include <android/binder_ibinder.h>
#include <private/android_filesystem_config.h>
-#include "annotations.h"
+#include "flags/FlagProvider.h"
+#include "stats_annotations.h"
#include "stats_log_util.h"
#include "statslog_statsd.h"
@@ -53,7 +54,7 @@
} // namespace
LogEvent::LogEvent(int32_t uid, int32_t pid)
- : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
+ : mLogdTimestampNs(getWallClockNs()), mLogUid(uid), mLogPid(pid) {
}
LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
@@ -345,7 +346,7 @@
void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements) {
- // Allowed types: INT
+ // Allowed types: BOOL
if (mValues.empty() || annotationType != BOOL_TYPE || !checkPreviousValueType(INT) ||
numElements) {
VLOG("Atom ID %d error while parseExclusiveStateAnnotation()", mTagId);
@@ -373,7 +374,7 @@
void LogEvent::parseStateNestedAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements) {
- // Allowed types: INT
+ // Allowed types: BOOL
if (mValues.empty() || annotationType != BOOL_TYPE || !checkPreviousValueType(INT) ||
numElements) {
VLOG("Atom ID %d error while parseStateNestedAnnotation()", mTagId);
@@ -385,6 +386,40 @@
mValues[mValues.size() - 1].mAnnotations.setNested(nested);
}
+void LogEvent::parseRestrictionCategoryAnnotation(uint8_t annotationType) {
+ // Allowed types: INT, field value should be empty since this is atom-level annotation.
+ if (!mValues.empty() || annotationType != INT32_TYPE) {
+ mValid = false;
+ return;
+ }
+ int value = readNextValue<int32_t>();
+ // should be one of predefined category in StatsLog.java
+ switch (value) {
+ case ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC:
+ case ASTATSLOG_RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE:
+ case ASTATSLOG_RESTRICTION_CATEGORY_AUTHENTICATION:
+ case ASTATSLOG_RESTRICTION_CATEGORY_FRAUD_AND_ABUSE:
+ break;
+ default:
+ mValid = false;
+ return;
+ }
+ mRestrictionCategory = static_cast<StatsdRestrictionCategory>(value);
+ return;
+}
+
+void LogEvent::parseFieldRestrictionAnnotation(uint8_t annotationType) {
+ // Allowed types: BOOL
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+ // Read the value so that the rest of the event is correctly parsed
+ // TODO: store the field annotations once the metrics need to parse them.
+ readNextValue<uint8_t>();
+ return;
+}
+
// firstUidInChainIndex is a default parameter that is only needed when parsing
// annotations for attribution chains.
// numElements is a default param that is only needed when parsing annotations for repeated fields
@@ -395,27 +430,50 @@
uint8_t annotationType = readNextValue<uint8_t>();
switch (annotationId) {
- case ANNOTATION_ID_IS_UID:
+ case ASTATSLOG_ANNOTATION_ID_IS_UID:
parseIsUidAnnotation(annotationType, numElements);
break;
- case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
+ case ASTATSLOG_ANNOTATION_ID_TRUNCATE_TIMESTAMP:
parseTruncateTimestampAnnotation(annotationType);
break;
- case ANNOTATION_ID_PRIMARY_FIELD:
+ case ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD:
parsePrimaryFieldAnnotation(annotationType, numElements, firstUidInChainIndex);
break;
- case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
+ case ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
break;
- case ANNOTATION_ID_EXCLUSIVE_STATE:
+ case ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE:
parseExclusiveStateAnnotation(annotationType, numElements);
break;
- case ANNOTATION_ID_TRIGGER_STATE_RESET:
+ case ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET:
parseTriggerStateResetAnnotation(annotationType, numElements);
break;
- case ANNOTATION_ID_STATE_NESTED:
+ case ASTATSLOG_ANNOTATION_ID_STATE_NESTED:
parseStateNestedAnnotation(annotationType, numElements);
break;
+ case ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY:
+ if (isAtLeastU()) {
+ parseRestrictionCategoryAnnotation(annotationType);
+ } else {
+ mValid = false;
+ }
+ break;
+ // Currently field restrictions are ignored, so we parse but do not store them.
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING:
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION:
+ if (isAtLeastU()) {
+ parseFieldRestrictionAnnotation(annotationType);
+ } else {
+ mValid = false;
+ }
+ break;
default:
VLOG("Atom ID %d error while parseAnnotations() - wrong annotationId(%d)", mTagId,
annotationId);
diff --git a/statsd/src/logd/LogEvent.h b/statsd/src/logd/LogEvent.h
index 3c416bb..c34d34e 100644
--- a/statsd/src/logd/LogEvent.h
+++ b/statsd/src/logd/LogEvent.h
@@ -24,6 +24,7 @@
#include <vector>
#include "FieldValue.h"
+#include "utils/RestrictedPolicyManager.h"
namespace android {
namespace os {
@@ -215,7 +216,7 @@
//
// If the index within the atom definition is desired, do the following:
// const std::optional<size_t>& vectorIndex = LogEvent.getExclusiveStateFieldIndex();
- // if (!vectorIndex) {
+ // if (vectorIndex) {
// FieldValue& v = LogEvent.getValues()[vectorIndex.value()];
// int atomIndex = v.mField.getPosAtDepth(0);
// }
@@ -262,6 +263,14 @@
*/
LogEvent(const LogEvent&) = default;
+ inline StatsdRestrictionCategory getRestrictionCategory() const {
+ return mRestrictionCategory;
+ }
+
+ inline bool isRestricted() const {
+ return mRestrictionCategory != CATEGORY_NO_RESTRICTION;
+ }
+
private:
void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
@@ -285,7 +294,10 @@
void parseTriggerStateResetAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements);
void parseStateNestedAnnotation(uint8_t annotationType, std::optional<uint8_t> numElements);
+ void parseRestrictionCategoryAnnotation(uint8_t annotationType);
+ void parseFieldRestrictionAnnotation(uint8_t annotationType);
bool checkPreviousValueType(Type expected);
+ bool getRestrictedMetricsFlag();
/**
* The below two variables are only valid during the execution of
@@ -362,6 +374,7 @@
// Annotations
bool mTruncateTimestamp = false;
int mResetState = -1;
+ StatsdRestrictionCategory mRestrictionCategory = CATEGORY_NO_RESTRICTION;
size_t mNumUidFields = 0;
diff --git a/statsd/src/logd/LogEventQueue.cpp b/statsd/src/logd/LogEventQueue.cpp
index 96b7f01..a83fcf8 100644
--- a/statsd/src/logd/LogEventQueue.cpp
+++ b/statsd/src/logd/LogEventQueue.cpp
@@ -39,22 +39,23 @@
return item;
}
-bool LogEventQueue::push(unique_ptr<LogEvent> item, int64_t* oldestTimestampNs) {
- bool success;
+LogEventQueue::Result LogEventQueue::push(unique_ptr<LogEvent> item) {
+ Result result;
{
std::unique_lock<std::mutex> lock(mMutex);
if (mQueue.size() < mQueueLimit) {
mQueue.push(std::move(item));
- success = true;
+ result.success = true;
} else {
// safe operation as queue must not be empty.
- *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
- success = false;
+ result.oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
+ result.success = false;
}
+ result.size = mQueue.size();
}
mCondition.notify_one();
- return success;
+ return result;
}
} // namespace statsd
diff --git a/statsd/src/logd/LogEventQueue.h b/statsd/src/logd/LogEventQueue.h
index e0e2f4e..d01f3a9 100644
--- a/statsd/src/logd/LogEventQueue.h
+++ b/statsd/src/logd/LogEventQueue.h
@@ -40,12 +40,18 @@
*/
std::unique_ptr<LogEvent> waitPop();
+ struct Result {
+ bool success = false;
+ int64_t oldestTimestampNs = 0;
+ int32_t size = 0;
+ };
+
/**
* Puts a LogEvent ptr to the end of the queue.
* Returns false on failure when the queue is full, and output the oldest event timestamp
- * in the queue.
+ * in the queue. Returns true on success and new queue size.
*/
- bool push(std::unique_ptr<LogEvent> event, int64_t* oldestTimestampNs);
+ Result push(std::unique_ptr<LogEvent> event);
private:
const size_t mQueueLimit;
@@ -55,10 +61,8 @@
friend class SocketParseMessageTest;
- FRIEND_TEST(SocketParseMessageTestNoFiltering, TestProcessMessageNoFiltering);
- FRIEND_TEST(SocketParseMessageTestNoFiltering,
- TestProcessMessageNoFilteringWithEmptySetExplicitSet);
- FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterEmptySet);
+ FRIEND_TEST(SocketParseMessageTest, TestProcessMessage);
+ FRIEND_TEST(SocketParseMessageTest, TestProcessMessageEmptySetExplicitSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterCompleteSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterPartialSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterToggle);
diff --git a/statsd/src/logd/logevent_util.cpp b/statsd/src/logd/logevent_util.cpp
new file mode 100644
index 0000000..7700545
--- /dev/null
+++ b/statsd/src/logd/logevent_util.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logd/logevent_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+std::optional<SocketLossInfo> toSocketLossInfo(const LogEvent& event) {
+ const std::vector<FieldValue>& logEventValues = event.getValues();
+
+ // check that logEvent contains the minimum required number of values to represent
+ // SocketLossInfo atom data
+ if (logEventValues.size() < 7) {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ SocketLossInfo result;
+
+ result.uid = event.GetUid();
+ if (logEventValues[1].mField.getPosAtDepth(0) == 2 &&
+ logEventValues[1].mValue.getType() == LONG) {
+ result.firstLossTsNanos = logEventValues[1].mValue.long_value;
+ } else {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ if (logEventValues[2].mField.getPosAtDepth(0) == 3 &&
+ logEventValues[2].mValue.getType() == LONG) {
+ result.lastLossTsNanos = logEventValues[2].mValue.long_value;
+ } else {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ if (logEventValues[3].mField.getPosAtDepth(0) == 4 &&
+ logEventValues[3].mValue.getType() == INT) {
+ result.overflowCounter = logEventValues[3].mValue.int_value;
+ } else {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ // skipping uid + first & last timestamps + overflowCounter
+ const int arraysOffset = 4;
+
+ // expected to have 3 arrays of equal size
+ const size_t expectedEntriesCount = (logEventValues.size() - arraysOffset) / 3;
+
+ // first array holds errors, then come tags & the 3rd array holds counts
+ result.errors.reserve(expectedEntriesCount);
+ result.atomIds.reserve(expectedEntriesCount);
+ result.counts.reserve(expectedEntriesCount);
+
+ // scan over errors entries
+ std::vector<FieldValue>::const_iterator valuesIt = logEventValues.begin() + arraysOffset;
+ while (valuesIt != logEventValues.end() && valuesIt->mField.getPosAtDepth(0) == 5 &&
+ valuesIt->mValue.getType() == INT) {
+ result.errors.push_back(valuesIt->mValue.int_value);
+ valuesIt++;
+ }
+ if (result.errors.size() != expectedEntriesCount) {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ while (valuesIt != logEventValues.end() && valuesIt->mField.getPosAtDepth(0) == 6 &&
+ valuesIt->mValue.getType() == INT) {
+ result.atomIds.push_back(valuesIt->mValue.int_value);
+ valuesIt++;
+ }
+ if (result.atomIds.size() != expectedEntriesCount) {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ while (valuesIt != logEventValues.end() && valuesIt->mField.getPosAtDepth(0) == 7 &&
+ valuesIt->mValue.getType() == INT) {
+ result.counts.push_back(valuesIt->mValue.int_value);
+ valuesIt++;
+ }
+ if (result.counts.size() != expectedEntriesCount) {
+ // atom content is invalid
+ return std::nullopt;
+ }
+
+ if (valuesIt != logEventValues.end()) {
+ // atom content is invalid, some extra values are present
+ return std::nullopt;
+ }
+
+ return result;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/logd/logevent_util.h b/statsd/src/logd/logevent_util.h
new file mode 100644
index 0000000..d03e071
--- /dev/null
+++ b/statsd/src/logd/logevent_util.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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 <optional>
+
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+struct SocketLossInfo {
+ int32_t uid;
+ int64_t firstLossTsNanos;
+ int64_t lastLossTsNanos;
+ int32_t overflowCounter;
+
+ std::vector<int32_t> errors;
+ std::vector<int32_t> atomIds;
+ std::vector<int32_t> counts;
+};
+
+// helper API to parse LogEvent into SocketLossInfo;
+std::optional<SocketLossInfo> toSocketLossInfo(const LogEvent& event);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/main.cpp b/statsd/src/main.cpp
index d8d0856..5a1f6a1 100644
--- a/statsd/src/main.cpp
+++ b/statsd/src/main.cpp
@@ -23,6 +23,7 @@
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <stdio.h>
+#include <sys/random.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -66,6 +67,18 @@
sigaction(SIGTERM, &sa, nullptr);
}
+void initSeedRandom() {
+ unsigned int seed = 0;
+ // getrandom() reads bytes from urandom source into buf. If getrandom()
+ // is unable to read from urandom source, then it returns -1 and we set
+ // out seed to be time(nullptr) as a fallback.
+ if (TEMP_FAILURE_RETRY(
+ getrandom(static_cast<void*>(&seed), sizeof(unsigned int), GRND_NONBLOCK)) < 0) {
+ seed = time(nullptr);
+ }
+ srand(seed);
+}
+
int main(int /*argc*/, char** /*argv*/) {
// Set up the looper
sp<Looper> looper(Looper::prepare(0 /* opts */));
@@ -88,6 +101,7 @@
STATSD_INIT_COMPLETED_NO_DELAY_FLAG, FLAG_FALSE)
? 0
: StatsService::kStatsdInitDelaySecs;
+ initSeedRandom();
// Create the service
gStatsService =
SharedRefBase::make<StatsService>(uidMap, eventQueue, logEventFilter, initEventDelay);
diff --git a/statsd/src/matchers/AtomMatchingTracker.h b/statsd/src/matchers/AtomMatchingTracker.h
index 3e35d9b..49c35b2 100644
--- a/statsd/src/matchers/AtomMatchingTracker.h
+++ b/statsd/src/matchers/AtomMatchingTracker.h
@@ -32,14 +32,20 @@
namespace os {
namespace statsd {
+struct MatcherInitResult {
+ optional<InvalidConfigReason> invalidConfigReason;
+ bool hasStringTransformation;
+};
+
class AtomMatchingTracker : public virtual RefBase {
public:
- AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash)
- : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){};
+ AtomMatchingTracker(const int64_t id, const uint64_t protoHash)
+ : mId(id), mInitialized(false), mProtoHash(protoHash){};
virtual ~AtomMatchingTracker(){};
// Initialize this AtomMatchingTracker.
+ // matcherIndex: index of this AtomMatchingTracker in allAtomMatchingTrackers.
// allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't
// store the proto object in memory. We only need it during initilization.
// allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one
@@ -48,30 +54,32 @@
// CombinationAtomMatchingTrackers using DFS.
// stack: a bit map to record which matcher has been visited on the stack. This is for detecting
// circle dependency.
- virtual optional<InvalidConfigReason> init(
- const std::vector<AtomMatcher>& allAtomMatchers,
+ virtual MatcherInitResult init(
+ int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) = 0;
+ const std::unordered_map<int64_t, int>& matcherMap, std::vector<uint8_t>& stack) = 0;
// Update appropriate state on config updates. Primarily, all indices need to be updated.
// This matcher and all of its children are guaranteed to be preserved across the update.
// matcher: the AtomMatcher proto from the config.
- // index: the index of this matcher in mAllAtomMatchingTrackers.
// atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
virtual optional<InvalidConfigReason> onConfigUpdated(
- const AtomMatcher& matcher, const int index,
+ const AtomMatcher& matcher,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
// Called when a log event comes.
// event: the log event.
+ // matcherIndex: index of this AtomMatchingTracker in allAtomMatchingTrackers.
// allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
// processing is done recursively.
// matcherResults: The cached results for all matchers for this event. Parent matchers can
// directly access the children's matching results if they have been evaluated.
// Otherwise, call children matchers' onLogEvent.
- virtual void onLogEvent(const LogEvent& event,
+ // matcherTransformations: the cached transformations for all matchers for this event.
+ virtual void onLogEvent(const LogEvent& event, int matcherIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) = 0;
+ std::vector<MatchingState>& matcherResults,
+ std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) = 0;
// Get the tagIds that this matcher cares about. The combined collection is stored
// in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
@@ -96,9 +104,6 @@
// Name of this matching. We don't really need the name, but it makes log message easy to debug.
const int64_t mId;
- // Index of this AtomMatchingTracker in MetricsManager's container.
- int mIndex;
-
// Whether this AtomMatchingTracker has been properly initialized.
bool mInitialized;
diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
index 846a530..e97188f 100644
--- a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
+++ b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -25,52 +25,58 @@
namespace statsd {
using std::set;
+using std::shared_ptr;
using std::unordered_map;
using std::vector;
-CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index,
+CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t id,
const uint64_t protoHash)
- : AtomMatchingTracker(id, index, protoHash) {
+ : AtomMatchingTracker(id, protoHash) {
}
CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
}
-optional<InvalidConfigReason> CombinationAtomMatchingTracker::init(
- const vector<AtomMatcher>& allAtomMatchers,
+MatcherInitResult CombinationAtomMatchingTracker::init(
+ int matcherIndex, const vector<AtomMatcher>& allAtomMatchers,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) {
+ const unordered_map<int64_t, int>& matcherMap, vector<uint8_t>& stack) {
+ MatcherInitResult result{nullopt /* invalidConfigReason */,
+ false /* hasStringTransformation */};
if (mInitialized) {
- return nullopt;
+ // CombinationMatchers do not support string transformations so if mInitialized = true,
+ // we know that there is no string transformation and we do not need to check for it again.
+ return result;
}
// mark this node as visited in the recursion stack.
- stack[mIndex] = true;
+ stack[matcherIndex] = true;
- AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination();
+ AtomMatcher_Combination matcher = allAtomMatchers[matcherIndex].combination();
// LogicalOperation is missing in the config
if (!matcher.has_operation()) {
- return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_NO_OPERATION,
- mId);
+ result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+ INVALID_CONFIG_REASON_MATCHER_NO_OPERATION, mId);
+ return result;
}
mLogicalOperation = matcher.operation();
if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
- return createInvalidConfigReasonWithMatcher(
+ result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY, mId);
+ return result;
}
for (const auto& child : matcher.matcher()) {
auto pair = matcherMap.find(child);
if (pair == matcherMap.end()) {
ALOGW("Matcher %lld not found in the config", (long long)child);
- optional<InvalidConfigReason> invalidConfigReason =
- createInvalidConfigReasonWithMatcher(
- INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId);
- invalidConfigReason->matcherIds.push_back(child);
- return invalidConfigReason;
+ result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+ INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId);
+ result.invalidConfigReason->matcherIds.push_back(child);
+ return result;
}
int childIndex = pair->second;
@@ -78,18 +84,27 @@
// if the child is a visited node in the recursion -> circle detected.
if (stack[childIndex]) {
ALOGE("Circle detected in matcher config");
- optional<InvalidConfigReason> invalidConfigReason =
+ result.invalidConfigReason =
createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CYCLE, mId);
- invalidConfigReason->matcherIds.push_back(child);
- return invalidConfigReason;
+ result.invalidConfigReason->matcherIds.push_back(child);
+ return result;
}
- optional<InvalidConfigReason> invalidConfigReason =
- allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers,
- matcherMap, stack);
+ auto [invalidConfigReason, hasStringTransformation] =
+ allAtomMatchingTrackers[childIndex]->init(
+ childIndex, allAtomMatchers, allAtomMatchingTrackers, matcherMap, stack);
+ if (hasStringTransformation) {
+ ALOGE("String transformation detected in CombinationMatcher");
+ result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+ INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE, mId);
+ result.hasStringTransformation = true;
+ return result;
+ }
+
if (invalidConfigReason.has_value()) {
ALOGW("child matcher init failed %lld", (long long)child);
invalidConfigReason->matcherIds.push_back(mId);
- return invalidConfigReason;
+ result.invalidConfigReason = invalidConfigReason;
+ return result;
}
mChildren.push_back(childIndex);
@@ -100,16 +115,14 @@
mInitialized = true;
// unmark this node in the recursion stack.
- stack[mIndex] = false;
- return nullopt;
+ stack[matcherIndex] = false;
+ return result;
}
optional<InvalidConfigReason> CombinationAtomMatchingTracker::onConfigUpdated(
- const AtomMatcher& matcher, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- mIndex = index;
+ const AtomMatcher& matcher, const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
mChildren.clear();
- AtomMatcher_Combination combinationMatcher = matcher.combination();
+ const AtomMatcher_Combination& combinationMatcher = matcher.combination();
for (const int64_t child : combinationMatcher.matcher()) {
const auto& pair = atomMatchingTrackerMap.find(child);
if (pair == atomMatchingTrackerMap.end()) {
@@ -126,15 +139,17 @@
}
void CombinationAtomMatchingTracker::onLogEvent(
- const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- vector<MatchingState>& matcherResults) {
+ const LogEvent& event, int matcherIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ vector<MatchingState>& matcherResults,
+ vector<shared_ptr<LogEvent>>& matcherTransformations) {
// this event has been processed.
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ if (matcherResults[matcherIndex] != MatchingState::kNotComputed) {
return;
}
if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
+ matcherResults[matcherIndex] = MatchingState::kNotMatched;
return;
}
@@ -142,12 +157,13 @@
for (const int childIndex : mChildren) {
if (matcherResults[childIndex] == MatchingState::kNotComputed) {
const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
- child->onLogEvent(event, allAtomMatchingTrackers, matcherResults);
+ child->onLogEvent(event, childIndex, allAtomMatchingTrackers, matcherResults,
+ matcherTransformations);
}
}
bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+ matcherResults[matcherIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
}
} // namespace statsd
diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.h b/statsd/src/matchers/CombinationAtomMatchingTracker.h
index edcbb49..9e06533 100644
--- a/statsd/src/matchers/CombinationAtomMatchingTracker.h
+++ b/statsd/src/matchers/CombinationAtomMatchingTracker.h
@@ -29,22 +29,23 @@
// Represents a AtomMatcher_Combination in the StatsdConfig.
class CombinationAtomMatchingTracker : public AtomMatchingTracker {
public:
- CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
+ CombinationAtomMatchingTracker(const int64_t id, const uint64_t protoHash);
- optional<InvalidConfigReason> init(
- const std::vector<AtomMatcher>& allAtomMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
+ MatcherInitResult init(int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& matcherMap,
+ std::vector<uint8_t>& stack);
optional<InvalidConfigReason> onConfigUpdated(
- const AtomMatcher& matcher, const int index,
+ const AtomMatcher& matcher,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
~CombinationAtomMatchingTracker();
- void onLogEvent(const LogEvent& event,
+ void onLogEvent(const LogEvent& event, int matcherIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) override;
+ std::vector<MatchingState>& matcherResults,
+ std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) override;
private:
LogicalOperation mLogicalOperation;
diff --git a/statsd/src/matchers/EventMatcherWizard.cpp b/statsd/src/matchers/EventMatcherWizard.cpp
index 30a40a3..07f6f4c 100644
--- a/statsd/src/matchers/EventMatcherWizard.cpp
+++ b/statsd/src/matchers/EventMatcherWizard.cpp
@@ -19,17 +19,18 @@
namespace os {
namespace statsd {
-using std::vector;
-
-MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) {
- if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) {
- return MatchingState::kNotComputed;
+MatchLogEventResult EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcherIndex) {
+ if (matcherIndex < 0 || matcherIndex >= (int)mAllEventMatchers.size()) {
+ return {MatchingState::kNotComputed, nullptr};
}
std::fill(mMatcherCache.begin(), mMatcherCache.end(), MatchingState::kNotComputed);
- mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, mMatcherCache);
- return mMatcherCache[matcher_index];
+ std::fill(mMatcherTransformations.begin(), mMatcherTransformations.end(), nullptr);
+ mAllEventMatchers[matcherIndex]->onLogEvent(event, matcherIndex, mAllEventMatchers,
+ mMatcherCache, mMatcherTransformations);
+
+ return {mMatcherCache[matcherIndex], mMatcherTransformations[matcherIndex]};
}
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/statsd/src/matchers/EventMatcherWizard.h b/statsd/src/matchers/EventMatcherWizard.h
index 7bd4529..b2ef311 100644
--- a/statsd/src/matchers/EventMatcherWizard.h
+++ b/statsd/src/matchers/EventMatcherWizard.h
@@ -22,20 +22,27 @@
namespace os {
namespace statsd {
+struct MatchLogEventResult {
+ MatchingState matchingState;
+ std::shared_ptr<LogEvent> transformedEvent;
+};
+
class EventMatcherWizard : public virtual RefBase {
public:
EventMatcherWizard(){}; // for testing
EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers)
: mAllEventMatchers(eventTrackers),
- mMatcherCache(eventTrackers.size(), MatchingState::kNotComputed){};
+ mMatcherCache(eventTrackers.size(), MatchingState::kNotComputed),
+ mMatcherTransformations(eventTrackers.size(), nullptr){};
virtual ~EventMatcherWizard(){};
- MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
+ MatchLogEventResult matchLogEvent(const LogEvent& event, int matcherIndex);
private:
std::vector<sp<AtomMatchingTracker>> mAllEventMatchers;
std::vector<MatchingState> mMatcherCache;
+ std::vector<std::shared_ptr<LogEvent>> mMatcherTransformations;
};
} // namespace statsd
diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
index a0e30b2..db45560 100644
--- a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
+++ b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -23,14 +23,14 @@
namespace os {
namespace statsd {
+using std::shared_ptr;
using std::unordered_map;
using std::vector;
-SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index,
- const uint64_t protoHash,
+SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t id, const uint64_t protoHash,
const SimpleAtomMatcher& matcher,
const sp<UidMap>& uidMap)
- : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) {
+ : AtomMatchingTracker(id, protoHash), mMatcher(matcher), mUidMap(uidMap) {
if (!matcher.has_atom_id()) {
mInitialized = false;
} else {
@@ -42,22 +42,31 @@
SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() {
}
-optional<InvalidConfigReason> SimpleAtomMatchingTracker::init(
- const vector<AtomMatcher>& allAtomMatchers,
+MatcherInitResult SimpleAtomMatchingTracker::init(
+ int matcherIndex, const vector<AtomMatcher>& allAtomMatchers,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) {
+ const unordered_map<int64_t, int>& matcherMap, vector<uint8_t>& stack) {
+ MatcherInitResult result{nullopt /* invalidConfigReason */,
+ false /* hasStringTransformation */};
// no need to do anything.
if (!mInitialized) {
- return createInvalidConfigReasonWithMatcher(
+ result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
INVALID_CONFIG_REASON_MATCHER_TRACKER_NOT_INITIALIZED, mId);
+ return result;
}
- return nullopt;
+
+ for (const FieldValueMatcher& fvm : mMatcher.field_value_matcher()) {
+ if (fvm.has_replace_string()) {
+ result.hasStringTransformation = true;
+ break;
+ }
+ }
+
+ return result;
}
optional<InvalidConfigReason> SimpleAtomMatchingTracker::onConfigUpdated(
- const AtomMatcher& matcher, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- mIndex = index;
+ const AtomMatcher& matcher, const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
// Do not need to update mMatcher since the matcher must be identical across the update.
if (!mInitialized) {
return createInvalidConfigReasonWithMatcher(
@@ -67,21 +76,27 @@
}
void SimpleAtomMatchingTracker::onLogEvent(
- const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- vector<MatchingState>& matcherResults) {
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ const LogEvent& event, int matcherIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ vector<MatchingState>& matcherResults,
+ vector<shared_ptr<LogEvent>>& matcherTransformations) {
+ if (matcherResults[matcherIndex] != MatchingState::kNotComputed) {
VLOG("Matcher %lld already evaluated ", (long long)mId);
return;
}
if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
+ matcherResults[matcherIndex] = MatchingState::kNotMatched;
return;
}
- bool matched = matchesSimple(mUidMap, mMatcher, event);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+ auto [matched, transformedEvent] = matchesSimple(mUidMap, mMatcher, event);
+ matcherResults[matcherIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched);
+
+ if (matched && transformedEvent != nullptr) {
+ matcherTransformations[matcherIndex] = std::move(transformedEvent);
+ }
}
} // namespace statsd
diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.h b/statsd/src/matchers/SimpleAtomMatchingTracker.h
index 70585f9..641e9d8 100644
--- a/statsd/src/matchers/SimpleAtomMatchingTracker.h
+++ b/statsd/src/matchers/SimpleAtomMatchingTracker.h
@@ -30,23 +30,24 @@
class SimpleAtomMatchingTracker : public AtomMatchingTracker {
public:
- SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
+ SimpleAtomMatchingTracker(const int64_t id, const uint64_t protoHash,
const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
~SimpleAtomMatchingTracker();
- optional<InvalidConfigReason> init(
- const std::vector<AtomMatcher>& allAtomMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack) override;
+ MatcherInitResult init(int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& matcherMap,
+ std::vector<uint8_t>& stack) override;
optional<InvalidConfigReason> onConfigUpdated(
- const AtomMatcher& matcher, const int index,
+ const AtomMatcher& matcher,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
- void onLogEvent(const LogEvent& event,
+ void onLogEvent(const LogEvent& event, int matcherIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) override;
+ std::vector<MatchingState>& matcherResults,
+ std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) override;
private:
const SimpleAtomMatcher mMatcher;
diff --git a/statsd/src/matchers/matcher_util.cpp b/statsd/src/matchers/matcher_util.cpp
index 20450dd..2f7ec81 100644
--- a/statsd/src/matchers/matcher_util.cpp
+++ b/statsd/src/matchers/matcher_util.cpp
@@ -23,9 +23,11 @@
#include "matchers/AtomMatchingTracker.h"
#include "src/statsd_config.pb.h"
#include "stats_util.h"
+#include "utils/Regex.h"
using std::set;
using std::string;
+using std::unique_ptr;
using std::vector;
namespace android {
@@ -84,24 +86,23 @@
return matched;
}
-bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
- const string& str_match) {
+static bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+ const string& str_match) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
- std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
- return packageNames.find(str_match) != packageNames.end();
+ return uidMap->hasApp(uid, str_match);
} else if (fieldValue.mValue.getType() == STRING) {
return fieldValue.mValue.str_value == str_match;
}
return false;
}
-bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
- const string& wildcardPattern) {
+static bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+ const string& wildcardPattern) {
if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
int uid = fieldValue.mValue.int_value;
// TODO(b/236886985): replace aid/uid mapping with efficient bidirectional container
@@ -115,7 +116,7 @@
}
}
}
- std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
+ std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, false /* normalize*/);
for (const auto& packageName : packageNames) {
if (fnmatch(wildcardPattern.c_str(), packageName.c_str(), 0) == 0) {
return true;
@@ -127,17 +128,42 @@
return false;
}
-bool matchesSimple(const sp<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;
+static unique_ptr<LogEvent> getTransformedEvent(const FieldValueMatcher& matcher,
+ const LogEvent& event, int start, int end) {
+ if (!matcher.has_replace_string()) {
+ return nullptr;
}
- if (start >= end) {
- return false;
+ unique_ptr<Regex> re = Regex::create(matcher.replace_string().regex());
+
+ if (re == nullptr) {
+ return nullptr;
}
+ const string& replacement = matcher.replace_string().replacement();
+ unique_ptr<LogEvent> transformedEvent = nullptr;
+ for (int i = start; i < end; i++) {
+ const LogEvent& eventRef = transformedEvent == nullptr ? event : *transformedEvent;
+ const FieldValue& fieldValue = eventRef.getValues()[i];
+ if (fieldValue.mValue.getType() != STRING) {
+ continue;
+ }
+ string str = fieldValue.mValue.str_value;
+ if (!re->replace(str, replacement) || str == fieldValue.mValue.str_value) {
+ continue;
+ }
+
+ // String transformation occurred, update the FieldValue in transformedEvent.
+ if (transformedEvent == nullptr) {
+ transformedEvent = std::make_unique<LogEvent>(event);
+ }
+ (*transformedEvent->getMutableValues())[i].mValue.str_value = str;
+ }
+ return transformedEvent;
+}
+
+static pair<int, int> getStartEndAtDepth(int targetField, int start, int end, int depth,
+ const vector<FieldValue>& values) {
// Filter by entry field first
int newStart = -1;
int newEnd = end;
@@ -145,31 +171,45 @@
// 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 (pos == targetField) {
if (newStart == -1) {
newStart = i;
}
newEnd = i + 1;
- } else if (pos > matcher.field()) {
+ } else if (pos > targetField) {
break;
}
}
+ return {newStart, newEnd};
+}
+
+/*
+ * Returns pairs of start-end indices in vector<FieldValue> that pariticipate in matching.
+ * The returned vector is empty if an error was encountered.
+ * If Position is ANY and value_matcher is matches_tuple, the vector contains a start/end pair
+ * corresponding for each child FieldValueMatcher in matches_tuple. For all other cases, the
+ * returned vector is of size 1.
+ *
+ * Also updates the depth reference parameter if matcher has Position specified.
+ */
+static vector<pair<int, int>> computeRanges(const FieldValueMatcher& matcher,
+ const vector<FieldValue>& values, int start, int end,
+ int& depth) {
// Now we have zoomed in to a new range
- start = newStart;
- end = newEnd;
+ std::tie(start, end) = getStartEndAtDepth(matcher.field(), start, end, depth, values);
if (start == -1) {
// No such field found.
- return false;
+ return {};
}
- vector<pair<int, int>> ranges; // the ranges are for matching ANY position
+ vector<pair<int, int>> ranges;
if (matcher.has_position()) {
// Repeated fields position is stored as a node in the path.
depth++;
if (depth > 2) {
- return false;
+ return ranges;
}
switch (matcher.position()) {
case Position::FIRST: {
@@ -196,27 +236,42 @@
ranges.push_back(std::make_pair(start, end));
break;
}
+ case Position::ALL:
+ // ALL is only supported for string transformation. If a value_matcher other than
+ // matches_tuple is present, the matcher is invalid. This is enforced when
+ // the AtomMatchingTracker is initialized.
+
+ // fallthrough
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;
+ // For string transformation, this case is treated the same as Position:ALL.
+ // Given a matcher on attribution_node[ANY].tag with a matches_tuple containing a
+ // child FieldValueMatcher with eq_string: "foo" and regex_replace: "[\d]+$" --> "",
+ // an event with attribution tags: ["bar123", "foo12", "abc230"] will transform to
+ // have attribution tags ["bar", "foo", "abc"] and will be a successful match.
+
+ // Note that if value_matcher is matches_tuple, there should be no string
+ // transformation on this matcher. However, child FieldValueMatchers in
+ // matches_tuple can have string transformations. This is enforced when
+ // AtomMatchingTracker is initialized.
+
+ if (matcher.value_matcher_case() == FieldValueMatcher::kMatchesTuple) {
+ // For ANY with matches_tuple, if all the children matchers match in any of the
+ // sub trees, it's a match.
+ // 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(start, i));
+ start = i;
+ currentPos = newPos;
+ }
}
}
- ranges.push_back(std::make_pair(newStart, end));
+ ranges.push_back(std::make_pair(start, end));
break;
}
- case Position::ALL:
- ALOGE("Not supported: field matcher with ALL position.");
- break;
case Position::POSITION_UNKNOWN:
break;
}
@@ -224,23 +279,60 @@
// No position
ranges.push_back(std::make_pair(start, end));
}
- // start and end are still pointing to the matched range.
+
+ return ranges;
+}
+
+static MatchResult matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
+ const LogEvent& event, int start, int end, int depth) {
+ if (depth > 2) {
+ ALOGE("Depth >= 3 not supported");
+ return {false, nullptr};
+ }
+
+ if (start >= end) {
+ return {false, nullptr};
+ }
+
+ const vector<pair<int, int>> ranges =
+ computeRanges(matcher, event.getValues(), start, end, depth);
+
+ if (ranges.empty()) {
+ // No such field found.
+ return {false, nullptr};
+ }
+
+ // ranges should have exactly one start/end pair at this point unless position is ANY and
+ // value_matcher is matches_tuple.
+ std::tie(start, end) = ranges[0];
+
+ unique_ptr<LogEvent> transformedEvent = getTransformedEvent(matcher, event, start, end);
+
+ const vector<FieldValue>& values =
+ transformedEvent == nullptr ? event.getValues() : transformedEvent->getValues();
+
switch (matcher.value_matcher_case()) {
case FieldValueMatcher::kMatchesTuple: {
++depth;
// If any range matches all matchers, good.
- for (const auto& range : ranges) {
+ bool matchResult = false;
+ for (const auto& [rangeStart, rangeEnd] : ranges) {
bool matched = true;
for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
- if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
- depth)) {
+ const LogEvent& eventRef =
+ transformedEvent == nullptr ? event : *transformedEvent;
+ auto [hasMatched, newTransformedEvent] = matchesSimple(
+ uidMap, subMatcher, eventRef, rangeStart, rangeEnd, depth);
+ if (newTransformedEvent != nullptr) {
+ transformedEvent = std::move(newTransformedEvent);
+ }
+ if (!hasMatched) {
matched = false;
- break;
}
}
- if (matched) return true;
+ matchResult = matchResult || matched;
}
- return false;
+ return {matchResult, std::move(transformedEvent)};
}
// Finally, we get to the point of real value matching.
// If the field matcher ends with ANY, then we have [start, end) range > 1.
@@ -251,18 +343,18 @@
(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 {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
const auto& str_list = matcher.neq_any_string();
@@ -275,40 +367,40 @@
}
}
if (notEqAll) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
if (tryMatchString(uidMap, values[i], str)) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kEqWildcardString: {
for (int i = start; i < end; i++) {
if (tryMatchWildcardString(uidMap, values[i], matcher.eq_wildcard_string())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyWildcardString: {
const auto& str_list = matcher.eq_any_wildcard_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
if (tryMatchWildcardString(uidMap, values[i], str)) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyWildcardString: {
const auto& str_list = matcher.neq_any_wildcard_string();
@@ -321,24 +413,24 @@
}
}
if (notEqAll) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
// eq_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(matcher.eq_int() == values[i].mValue.long_value)) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kEqAnyInt: {
const auto& int_list = matcher.eq_any_int();
@@ -346,16 +438,16 @@
for (const int int_value : int_list.int_value()) {
if (values[i].mValue.getType() == INT &&
(int_value == values[i].mValue.int_value)) {
- return true;
+ return {true, std::move(transformedEvent)};
}
// eq_any_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(int_value == values[i].mValue.long_value)) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
case FieldValueMatcher::ValueMatcherCase::kNeqAnyInt: {
const auto& int_list = matcher.neq_any_int();
@@ -375,102 +467,113 @@
}
}
if (notEqAll) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
// lt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value < matcher.lt_int())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
// gt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value > matcher.gt_int())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
// lte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value <= matcher.lte_int())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
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 {true, std::move(transformedEvent)};
}
// gte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value >= matcher.gte_int())) {
- return true;
+ return {true, std::move(transformedEvent)};
}
}
- return false;
+ return {false, std::move(transformedEvent)};
}
default:
- return false;
+ // This only happens if the matcher has a string transformation and no value_matcher. So
+ // the default match result is true. If there is no string transformation either then
+ // this matcher is invalid, which is enforced when the AtomMatchingTracker is
+ // initialized.
+ return {true, std::move(transformedEvent)};
}
}
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
- const LogEvent& event) {
+MatchResult matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+ const LogEvent& event) {
if (event.GetTagId() != simpleMatcher.atom_id()) {
- return false;
+ return {false, nullptr};
}
+ unique_ptr<LogEvent> transformedEvent = nullptr;
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
- if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
- return false;
+ const LogEvent& inputEvent = transformedEvent == nullptr ? event : *transformedEvent;
+ auto [hasMatched, newTransformedEvent] =
+ matchesSimple(uidMap, matcher, inputEvent, 0, inputEvent.getValues().size(), 0);
+ if (newTransformedEvent != nullptr) {
+ transformedEvent = std::move(newTransformedEvent);
+ }
+ if (!hasMatched) {
+ return {false, std::move(transformedEvent)};
}
}
- return true;
+ return {true, std::move(transformedEvent)};
}
} // namespace statsd
diff --git a/statsd/src/matchers/matcher_util.h b/statsd/src/matchers/matcher_util.h
index 597b74f..5fb7c42 100644
--- a/statsd/src/matchers/matcher_util.h
+++ b/statsd/src/matchers/matcher_util.h
@@ -33,11 +33,16 @@
kMatched = 1,
};
+struct MatchResult {
+ bool matched;
+ std::unique_ptr<LogEvent> transformedEvent;
+};
+
bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
const std::vector<MatchingState>& matcherResults);
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
- const LogEvent& wrapper);
+MatchResult matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+ const LogEvent& wrapper);
} // namespace statsd
} // namespace os
diff --git a/statsd/src/metrics/CountMetricProducer.cpp b/statsd/src/metrics/CountMetricProducer.cpp
index d0e2fd0..fb5f488 100644
--- a/statsd/src/metrics/CountMetricProducer.cpp
+++ b/statsd/src/metrics/CountMetricProducer.cpp
@@ -54,6 +54,7 @@
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
const int FIELD_ID_DIMENSION_GUARDRAIL_HIT = 17;
+const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -73,14 +74,17 @@
const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap, getAppUpgradeBucketSplit(metric)),
- mDimensionGuardrailHit(false) {
+ stateGroupMap, getAppUpgradeBucketSplit(metric), configMetadataProvider),
+ mDimensionGuardrailHit(false),
+ mDimensionHardLimit(
+ StatsdStats::clampDimensionKeySizeLimit(metric.max_dimensions_per_bucket())) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -175,6 +179,7 @@
return invalidConfigReason;
}
}
+
return nullopt;
}
@@ -236,6 +241,8 @@
mDimensionGuardrailHit);
}
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES,
+ (long long)byteSizeLocked());
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
@@ -338,11 +345,11 @@
}
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mCurrentSlicedCounter->size() >= StatsdStats::kDimensionKeySizeSoftLimit) {
size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+ if (newTupleCount > mDimensionHardLimit) {
if (!mHasHitGuardrail) {
ALOGE("CountMetric %lld dropping data for dimension key %s", (long long)mMetricId,
newKey.toString().c_str());
@@ -397,7 +404,7 @@
// When a new matched event comes in, we check if event falls into the current
// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void CountMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
return;
@@ -413,7 +420,7 @@
(long long)mCurrentBucketStartTimeNs);
}
-bool CountMetricProducer::countPassesThreshold(const int64_t& count) {
+bool CountMetricProducer::countPassesThreshold(const int64_t count) {
if (mUploadThreshold == nullopt) {
return true;
}
@@ -433,8 +440,8 @@
}
}
-void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
+void CountMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+ const int64_t nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
diff --git a/statsd/src/metrics/CountMetricProducer.h b/statsd/src/metrics/CountMetricProducer.h
index 85ca814..757dad2 100644
--- a/statsd/src/metrics/CountMetricProducer.h
+++ b/statsd/src/metrics/CountMetricProducer.h
@@ -44,9 +44,10 @@
class CountMetricProducer : public MetricProducer {
public:
CountMetricProducer(
- const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+ const ConfigKey& key, const CountMetric& countMetric, int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const uint64_t protoHash, int64_t timeBaseNs, int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
@@ -81,10 +82,10 @@
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
// Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
@@ -94,15 +95,14 @@
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& newEventTime) override;
+ void flushIfNeededLocked(int64_t newEventTime) override;
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
+ void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -130,11 +130,13 @@
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
- bool countPassesThreshold(const int64_t& count);
+ bool countPassesThreshold(int64_t count);
// Tracks if the dimension guardrail has been hit in the current report.
bool mDimensionGuardrailHit;
+ const size_t mDimensionHardLimit;
+
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
@@ -145,6 +147,10 @@
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
+
+ FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit);
+
+ FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit);
};
} // namespace statsd
diff --git a/statsd/src/metrics/DurationMetricProducer.cpp b/statsd/src/metrics/DurationMetricProducer.cpp
index 745087f..e18f83c 100644
--- a/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/statsd/src/metrics/DurationMetricProducer.cpp
@@ -53,6 +53,7 @@
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
const int FIELD_ID_DIMENSION_GUARDRAIL_HIT = 17;
+const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
@@ -73,19 +74,22 @@
const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard, const uint64_t protoHash,
const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap, getAppUpgradeBucketSplit(metric)),
+ stateGroupMap, getAppUpgradeBucketSplit(metric), configMetadataProvider),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
mNested(nesting),
- mContainANYPositionInInternalDimensions(false) {
+ mContainANYPositionInInternalDimensions(false),
+ mDimensionHardLimit(
+ StatsdStats::clampDimensionKeySizeLimit(metric.max_dimensions_per_bucket())) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -316,6 +320,7 @@
const HashableDimensionKey& primaryKey,
const FieldValue& oldState,
const FieldValue& newState) {
+ std::lock_guard<std::mutex> lock(mMutex);
// Check if this metric has a StateMap. If so, map the new state value to
// the correct state group id.
FieldValue newStateCopy = newState;
@@ -514,6 +519,8 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES,
+ (long long)byteSizeLocked());
if (mPastBuckets.empty()) {
VLOG(" Duration metric, empty return");
@@ -601,7 +608,7 @@
}
}
-void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void DurationMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (currentBucketEndTimeNs > eventTimeNs) {
@@ -615,8 +622,8 @@
mCurrentBucketNum += numBucketsForward;
}
-void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
+void DurationMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+ const int64_t nextBucketStartTimeNs) {
const auto [globalConditionTrueNs, globalConditionCorrectionNs] =
mConditionTimer.newBucketStart(eventTimeNs, nextBucketStartTimeNs);
@@ -652,16 +659,16 @@
}
}
-bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) const {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mCurrentSlicedDurationTrackerMap.size() >= StatsdStats::kDimensionKeySizeSoftLimit) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+ if (newTupleCount > mDimensionHardLimit) {
if (!mHasHitGuardrail) {
ALOGE("DurationMetric %lld dropping data for what dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
@@ -690,16 +697,18 @@
auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
- it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
+ it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys, mDimensionHardLimit);
return;
}
if (mInternalDimensions.empty()) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys,
+ mDimensionHardLimit);
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
filterValues(mInternalDimensions, eventValues, &dimensionKey);
- it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
+ it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys,
+ mDimensionHardLimit);
}
}
diff --git a/statsd/src/metrics/DurationMetricProducer.h b/statsd/src/metrics/DurationMetricProducer.h
index b880bd1..b7bc29a 100644
--- a/statsd/src/metrics/DurationMetricProducer.h
+++ b/statsd/src/metrics/DurationMetricProducer.h
@@ -39,12 +39,12 @@
class DurationMetricProducer : public MetricProducer {
public:
DurationMetricProducer(
- const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const int whatIndex,
- const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+ const ConfigKey& key, const DurationMetric& durationMetric, int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, int whatIndex,
+ const int startIndex, int stopIndex, int stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard, const uint64_t protoHash,
- const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
- const int64_t startTimeNs,
+ const FieldMatcher& internalDimensions, int64_t timeBaseNs, const int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
const vector<int>& slicedStateAtoms = {},
@@ -57,7 +57,7 @@
const UpdateStatus& updateStatus,
const int64_t updateTimeNs) override;
- void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, const int64_t updateTimeNs) override;
+ void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, int64_t updateTimeNs) override;
void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
const HashableDimensionKey& primaryKey, const FieldValue& oldState,
@@ -77,13 +77,13 @@
private:
// Initializes true dimensions of the 'what' predicate. Only to be called during initialization.
- void initTrueDimensions(const int whatIndex, const int64_t startTimeNs);
+ void initTrueDimensions(const int whatIndex, int64_t startTimeNs);
void handleMatchedLogEventValuesLocked(const size_t matcherIndex,
const std::vector<FieldValue>& values,
const int64_t eventTimeNs);
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
- bool condition, const int64_t eventTimeNs,
+ bool condition, int64_t eventTimeNs,
const vector<FieldValue>& eventValues);
void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -96,13 +96,13 @@
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
// Internal interface to handle active state change.
void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
// Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
void onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs);
@@ -116,13 +116,12 @@
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& eventTime);
+ void flushIfNeededLocked(int64_t eventTime);
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
+ void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -138,7 +137,7 @@
std::vector<int>& metricsWithActivation) override;
void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker,
- const UpdateStatus& updateStatus, const int64_t updateTimeNs);
+ const UpdateStatus& updateStatus, int64_t updateTimeNs);
const DurationMetric_AggregationType mAggregationType;
@@ -172,19 +171,20 @@
std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDurationTrackerMap;
+ const size_t mDimensionHardLimit;
+
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
const MetricDimensionKey& eventKey) const;
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey) const;
static const size_t kBucketSize = sizeof(DurationBucket{});
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
FRIEND_TEST(DurationMetricProducerTest, TestSumDurationAppUpgradeSplitDisabled);
@@ -197,6 +197,10 @@
FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
+
+ FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit);
+
+ FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit);
};
} // namespace statsd
diff --git a/statsd/src/metrics/EventMetricProducer.cpp b/statsd/src/metrics/EventMetricProducer.cpp
index dd67919..a8f4539 100644
--- a/statsd/src/metrics/EventMetricProducer.cpp
+++ b/statsd/src/metrics/EventMetricProducer.cpp
@@ -48,6 +48,7 @@
const int FIELD_ID_ID = 1;
const int FIELD_ID_EVENT_METRICS = 4;
const int FIELD_ID_IS_ACTIVE = 14;
+const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18;
// for EventMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for EventMetricData
@@ -60,13 +61,15 @@
const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
const uint64_t protoHash, const int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap, /*splitBucketForAppUpgrade=*/nullopt) {
+ stateGroupMap, /*splitBucketForAppUpgrade=*/nullopt, configMetadataProvider),
+ mSamplingPercentage(metric.sampling_percentage()) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -77,6 +80,7 @@
}
mConditionSliced = true;
}
+
mTotalSize = 0;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)mMetricId,
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
@@ -171,6 +175,9 @@
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES,
+ (long long)byteSizeLocked());
+
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS);
for (const auto& [atomDimensionKey, elapsedTimestampsNs] : mAggregatedAtoms) {
uint64_t wrapperToken =
@@ -211,6 +218,10 @@
return;
}
+ if (mSamplingPercentage < 100 && !shouldKeepRandomSample(mSamplingPercentage)) {
+ return;
+ }
+
const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
AtomDimensionKey key(event.GetTagId(), HashableDimensionKey(event.getValues()));
diff --git a/statsd/src/metrics/EventMetricProducer.h b/statsd/src/metrics/EventMetricProducer.h
index f29becd..aed4358 100644
--- a/statsd/src/metrics/EventMetricProducer.h
+++ b/statsd/src/metrics/EventMetricProducer.h
@@ -35,9 +35,10 @@
class EventMetricProducer : public MetricProducer {
public:
EventMetricProducer(
- const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ const ConfigKey& key, const EventMetric& eventMetric, int conditionIndex,
const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, const int64_t startTimeNs,
+ const uint64_t protoHash, int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
@@ -50,6 +51,9 @@
return METRIC_TYPE_EVENT;
}
+protected:
+ size_t mTotalSize;
+
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -65,13 +69,13 @@
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
// Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -96,7 +100,7 @@
// Maps the field/value pairs of an atom to a list of timestamps used to deduplicate atoms.
std::unordered_map<AtomDimensionKey, std::vector<int64_t>> mAggregatedAtoms;
- size_t mTotalSize;
+ const int mSamplingPercentage;
};
} // namespace statsd
diff --git a/statsd/src/metrics/GaugeMetricProducer.cpp b/statsd/src/metrics/GaugeMetricProducer.cpp
index 75b2d52..f82f04e 100644
--- a/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -50,6 +50,7 @@
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
const int FIELD_ID_DIMENSION_GUARDRAIL_HIT = 17;
+const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
@@ -80,12 +81,14 @@
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
const size_t dimensionSoftLimit, const size_t dimensionHardLimit)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
- /*stateGroupMap=*/{}, getAppUpgradeBucketSplit(metric)),
+ /*stateGroupMap=*/{}, getAppUpgradeBucketSplit(metric),
+ configMetadataProvider),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -100,7 +103,9 @@
mDimensionSoftLimit(dimensionSoftLimit),
mDimensionHardLimit(dimensionHardLimit),
mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()),
- mDimensionGuardrailHit(false) {
+ mDimensionGuardrailHit(false),
+ mSamplingPercentage(metric.sampling_percentage()),
+ mPullProbability(metric.pull_probability()) {
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
int64_t bucketSizeMills = 0;
@@ -256,6 +261,8 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES,
+ (long long)byteSizeLocked());
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
@@ -393,7 +400,7 @@
default:
break;
}
- if (!triggerPuller) {
+ if (!triggerPuller || !shouldKeepRandomSample(mPullProbability)) {
return;
}
vector<std::shared_ptr<LogEvent>> allData;
@@ -409,10 +416,11 @@
return;
}
for (const auto& data : allData) {
- LogEvent localCopy = *data;
- localCopy.setElapsedTimestampNs(timestampNs);
- if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
- MatchingState::kMatched) {
+ const auto [matchResult, transformedEvent] =
+ mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+ if (matchResult == MatchingState::kMatched) {
+ LogEvent localCopy = transformedEvent == nullptr ? *data : *transformedEvent;
+ localCopy.setElapsedTimestampNs(timestampNs);
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
@@ -501,9 +509,11 @@
return;
}
for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(
- *data, mWhatMatcherIndex) == MatchingState::kMatched) {
- onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ const auto [matchResult, transformedEvent] =
+ mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+ if (matchResult == MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex,
+ transformedEvent == nullptr ? *data : *transformedEvent);
}
}
}
@@ -513,7 +523,7 @@
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) {
+ if (mCurrentSlicedBucket->size() >= mDimensionSoftLimit) {
size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
@@ -539,6 +549,12 @@
if (condition == false) {
return;
}
+
+ if (mPullTagId == -1 && mSamplingPercentage < 100 &&
+ !shouldKeepRandomSample(mSamplingPercentage)) {
+ return;
+ }
+
int64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -617,7 +633,7 @@
// bucket.
// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
// the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void GaugeMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
@@ -636,8 +652,8 @@
(long long)mCurrentBucketStartTimeNs);
}
-void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
+void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+ const int64_t nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
diff --git a/statsd/src/metrics/GaugeMetricProducer.h b/statsd/src/metrics/GaugeMetricProducer.h
index f607388..18e0b43 100644
--- a/statsd/src/metrics/GaugeMetricProducer.h
+++ b/statsd/src/metrics/GaugeMetricProducer.h
@@ -34,7 +34,7 @@
namespace statsd {
struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
+ GaugeAtom(const std::shared_ptr<vector<FieldValue>>& fields, int64_t elapsedTimeNs)
: mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
@@ -60,13 +60,13 @@
class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
public:
GaugeMetricProducer(
- const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+ const ConfigKey& key, const GaugeMetric& gaugeMetric, int conditionIndex,
const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int triggerAtomId, const int atomId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager,
+ const int pullTagId, int triggerAtomId, int atomId, const int64_t timeBaseNs,
+ int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap = {},
@@ -82,11 +82,12 @@
// Determine if metric needs to pull
bool isPullNeeded() const override {
std::lock_guard<std::mutex> lock(mMutex);
- return mIsActive && (mCondition == ConditionState::kTrue);
+ return mIsActive && (mCondition == ConditionState::kTrue) &&
+ shouldKeepRandomSample(mPullProbability);
};
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void notifyAppUpgradeInternalLocked(const int64_t eventTimeNs) override {
+ void notifyAppUpgradeInternalLocked(int64_t eventTimeNs) override {
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
@@ -94,9 +95,9 @@
};
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
+ void onStatsdInitCompleted(int64_t eventTimeNs) override {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
-
flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) {
pullAndMatchEventsLocked(eventTimeNs);
@@ -123,13 +124,13 @@
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
// Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
// Internal interface to handle active state change.
void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
// Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
@@ -139,10 +140,9 @@
void dropDataLocked(const int64_t dropTimeNs) override;
// Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& eventTime) override;
+ void flushIfNeededLocked(int64_t eventTime) override;
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
+ void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
void prepareFirstBucketLocked() override;
@@ -150,7 +150,7 @@
void pullAndMatchEventsLocked(const int64_t timestampNs);
optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -227,6 +227,10 @@
// Tracks if the dimension guardrail has been hit in the current report.
bool mDimensionGuardrailHit;
+ const int mSamplingPercentage;
+
+ const int mPullProbability;
+
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
@@ -242,6 +246,10 @@
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
+
+ FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit);
+
+ FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit);
};
} // namespace statsd
diff --git a/statsd/src/metrics/KllMetricProducer.cpp b/statsd/src/metrics/KllMetricProducer.cpp
index 91f22f4..26aafa4 100644
--- a/statsd/src/metrics/KllMetricProducer.cpp
+++ b/statsd/src/metrics/KllMetricProducer.cpp
@@ -31,12 +31,9 @@
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
-using std::map;
using std::nullopt;
using std::optional;
-using std::shared_ptr;
using std::string;
-using std::unordered_map;
using zetasketch::android::AggregatorStateProto;
namespace android {
@@ -61,9 +58,11 @@
const ConditionOptions& conditionOptions,
const StateOptions& stateOptions,
const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions)
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider)
: ValueMetricProducer(metric.id(), key, protoHash, pullOptions, bucketOptions, whatOptions,
- conditionOptions, stateOptions, activationOptions, guardrailOptions) {
+ conditionOptions, stateOptions, activationOptions, guardrailOptions,
+ configMetadataProvider) {
}
KllMetricProducer::DumpProtoFields KllMetricProducer::getDumpProtoFields() const {
diff --git a/statsd/src/metrics/KllMetricProducer.h b/statsd/src/metrics/KllMetricProducer.h
index c142113..16c1e2a 100644
--- a/statsd/src/metrics/KllMetricProducer.h
+++ b/statsd/src/metrics/KllMetricProducer.h
@@ -48,7 +48,8 @@
const PullOptions& pullOptions, const BucketOptions& bucketOptions,
const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
const StateOptions& stateOptions, const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions);
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
inline MetricType getMetricType() const override {
return METRIC_TYPE_KLL;
@@ -121,6 +122,10 @@
FRIEND_TEST(KllMetricProducerTest_PartialBucket, TestPushedEventsMultipleBuckets);
FRIEND_TEST(ConfigUpdateTest, TestUpdateKllMetrics);
+
+ FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit);
+
+ FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit);
};
} // namespace statsd
diff --git a/statsd/src/metrics/MetricProducer.cpp b/statsd/src/metrics/MetricProducer.cpp
index bd69798..14c07a8 100644
--- a/statsd/src/metrics/MetricProducer.cpp
+++ b/statsd/src/metrics/MetricProducer.cpp
@@ -45,15 +45,16 @@
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
MetricProducer::MetricProducer(
- const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
- const int conditionIndex, const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+ int64_t metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const uint64_t protoHash,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap,
- const optional<bool> splitBucketForAppUpgrade)
+ const optional<bool> splitBucketForAppUpgrade,
+ const wp<ConfigMetadataProvider> configMetadataProvider)
: mMetricId(metricId),
mProtoHash(protoHash),
mConfigKey(key),
@@ -78,7 +79,8 @@
mSplitBucketForAppUpgrade(splitBucketForAppUpgrade),
mHasHitGuardrail(false),
mSampledWhatFields({}),
- mShardCount(0) {
+ mShardCount(0),
+ mConfigMetadataProvider(configMetadataProvider) {
}
optional<InvalidConfigReason> MetricProducer::onConfigUpdatedLocked(
@@ -243,7 +245,7 @@
if (it == mEventDeactivationMap.end()) {
return;
}
- for (auto activationToCancelIt : it->second) {
+ for (auto& activationToCancelIt : it->second) {
activationToCancelIt->state = ActivationState::kNotActive;
}
}
@@ -319,7 +321,7 @@
}
}
-void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+void MetricProducer::queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
FieldValue* value) {
if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
value->mValue = Value(StateTracker::kStateUnknown);
@@ -329,7 +331,7 @@
}
}
-void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
+void MetricProducer::mapStateValue(int32_t atomId, FieldValue* value) {
// check if there is a state map for this atom
auto atomIt = mStateGroupMap.find(atomId);
if (atomIt == mStateGroupMap.end()) {
diff --git a/statsd/src/metrics/MetricProducer.h b/statsd/src/metrics/MetricProducer.h
index c4871e8..8bf3998 100644
--- a/statsd/src/metrics/MetricProducer.h
+++ b/statsd/src/metrics/MetricProducer.h
@@ -27,13 +27,17 @@
#include "condition/ConditionTimer.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
+#include "config/ConfigMetadataProvider.h"
#include "guardrail/StatsdStats.h"
#include "matchers/EventMatcherWizard.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
+#include "src/statsd_metadata.pb.h" // MetricMetadata
#include "state/StateListener.h"
#include "state/StateManager.h"
+#include "utils/DbUtils.h"
#include "utils/ShardOffsetProvider.h"
+#include "utils/api_tracing.h"
namespace android {
namespace os {
@@ -71,11 +75,12 @@
};
struct Activation {
- Activation(const ActivationType& activationType, const int64_t ttlNs)
+ Activation(const ActivationType& activationType, int64_t ttlNs)
: ttl_ns(ttlNs),
start_ns(0),
state(ActivationState::kNotActive),
- activationType(activationType) {}
+ activationType(activationType) {
+ }
const int64_t ttl_ns;
int64_t start_ns;
@@ -125,7 +130,7 @@
// be a no-op.
class MetricProducer : public virtual RefBase, public virtual StateListener {
public:
- MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
+ MetricProducer(int64_t metricId, const ConfigKey& key, int64_t timeBaseNs,
const int conditionIndex, const vector<ConditionState>& initialConditionCache,
const sp<ConditionWizard>& wizard, const uint64_t protoHash,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
@@ -133,7 +138,8 @@
eventDeactivationMap,
const vector<int>& slicedStateAtoms,
const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap,
- const optional<bool> splitBucketForAppUpgrade);
+ const optional<bool> splitBucketForAppUpgrade,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
virtual ~MetricProducer(){};
@@ -147,7 +153,7 @@
// This function also updates several maps used by metricsManager.
// This function clears all anomaly trackers. All anomaly trackers need to be added again.
optional<InvalidConfigReason> onConfigUpdated(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -173,7 +179,7 @@
/**
* Force a partial bucket split on app upgrade
*/
- void notifyAppUpgrade(const int64_t& eventTimeNs) {
+ void notifyAppUpgrade(int64_t eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
const bool splitBucket =
mSplitBucketForAppUpgrade ? mSplitBucketForAppUpgrade.value() : false;
@@ -183,7 +189,7 @@
notifyAppUpgradeInternalLocked(eventTimeNs);
};
- void notifyAppRemoved(const int64_t& eventTimeNs) {
+ void notifyAppRemoved(int64_t eventTimeNs) {
// Force buckets to split on removal also.
notifyAppUpgrade(eventTimeNs);
};
@@ -191,22 +197,24 @@
/**
* Force a partial bucket split on boot complete.
*/
- virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
+ virtual void onStatsdInitCompleted(int64_t eventTimeNs) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
flushLocked(eventTimeNs);
}
+
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
onMatchedLogEventLocked(matcherIndex, event);
}
- void onConditionChanged(const bool condition, const int64_t eventTime) {
+ void onConditionChanged(const bool condition, int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
onConditionChangedLocked(condition, eventTime);
}
- void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
+ void onSlicedConditionMayChange(bool overallCondition, int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
onSlicedConditionMayChangeLocked(overallCondition, eventTime);
}
@@ -234,7 +242,7 @@
}
virtual optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -305,6 +313,21 @@
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+ virtual void enforceRestrictedDataTtl(sqlite3* db, int64_t wallClockNs){};
+
+ virtual bool writeMetricMetadataToProto(metadata::MetricMetadata* metricMetadata) {
+ return false;
+ }
+
+ virtual void loadMetricMetadataFromProto(const metadata::MetricMetadata& metricMetadata){};
+
+ /* Called when the metric is to about to be removed from config. */
+ virtual void onMetricRemove() {
+ }
+
+ virtual void flushRestrictedData() {
+ }
+
// Start: getters/setters
inline int64_t getMetricId() const {
return mMetricId;
@@ -347,7 +370,7 @@
}
/* Adds an AnomalyTracker that has already been created */
- virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, const int64_t updateTimeNs) {
+ virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, int64_t updateTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
mAnomalyTrackers.push_back(anomalyTracker);
}
@@ -362,7 +385,7 @@
/**
* Flushes the current bucket if the eventTime is after the current bucket's end time.
*/
- virtual void flushIfNeededLocked(const int64_t& eventTime){};
+ virtual void flushIfNeededLocked(int64_t eventTime){};
/**
* For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
@@ -374,13 +397,12 @@
* flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the
* bucket timestamp and bucket number as needed.
*/
- virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {};
+ virtual void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs){};
/**
* Flushes all the data including the current partial bucket.
*/
- void flushLocked(const int64_t& eventTimeNs) {
+ void flushLocked(int64_t eventTimeNs) {
flushIfNeededLocked(eventTimeNs);
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
@@ -411,7 +433,7 @@
// Consume the parsed stats log entry that already matched the "what" of the metric.
virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
- virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
+ virtual void onConditionChangedLocked(const bool condition, int64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) = 0;
virtual void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -453,13 +475,12 @@
// Query StateManager for original state value using the queryKey.
// The field and value are output.
- void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
- FieldValue* value);
+ void queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey, FieldValue* value);
// If a state map exists for the given atom, replace the original state
// value with the group id mapped to the value.
// If no state map exists, keep the original state value.
- void mapStateValue(const int32_t atomId, FieldValue* value);
+ void mapStateValue(int32_t atomId, FieldValue* value);
// Returns a HashableDimensionKey with unknown state value for each state
// atom.
@@ -554,13 +575,19 @@
std::vector<SkippedBucket> mSkippedBuckets;
// If hard dimension guardrail is hit, do not spam logcat. This is a per bucket tracker.
- bool mHasHitGuardrail;
+ mutable bool mHasHitGuardrail;
// Matchers for sampled fields. Currently only one sampled dimension is supported.
std::vector<Matcher> mSampledWhatFields;
int mShardCount;
+ inline wp<ConfigMetadataProvider> getConfigMetadataProvider() const {
+ return mConfigMetadataProvider;
+ }
+
+ wp<ConfigMetadataProvider> mConfigMetadataProvider;
+
FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
diff --git a/statsd/src/metrics/MetricsManager.cpp b/statsd/src/metrics/MetricsManager.cpp
index 4db21e9..9b12640 100644
--- a/statsd/src/metrics/MetricsManager.cpp
+++ b/statsd/src/metrics/MetricsManager.cpp
@@ -33,6 +33,8 @@
#include "stats_log_util.h"
#include "stats_util.h"
#include "statslog_statsd.h"
+#include "utils/DbUtils.h"
+#include "utils/api_tracing.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
@@ -43,6 +45,7 @@
using std::set;
using std::string;
+using std::unique_ptr;
using std::vector;
namespace android {
@@ -76,13 +79,21 @@
mPullerManager(pullerManager),
mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
config.whitelisted_atom_ids().end()),
- mShouldPersistHistory(config.persist_locally()) {
+ mShouldPersistHistory(config.persist_locally()),
+ mUseV2SoftMemoryCalculation(config.statsd_config_options().use_v2_soft_memory_limit()) {
+ if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
+ mInvalidConfigReason =
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
+ return;
+ }
+ if (config.has_restricted_metrics_delegate_package_name()) {
+ mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
+ }
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
-
mInvalidConfigReason = initStatsdConfig(
key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, mTagIdsToMatchersMap, mAllAtomMatchingTrackers,
+ timeBaseNs, currentTimeNs, this, mTagIdsToMatchersMap, mAllAtomMatchingTrackers,
mAtomMatchingTrackerMap, mAllConditionTrackers, mConditionTrackerMap,
mAllMetricProducers, mMetricProducerMap, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
@@ -95,6 +106,7 @@
createAllLogSourcesFromConfig(config);
setMaxMetricsBytesFromConfig(config);
+ setTriggerGetDataBytesFromConfig(config);
mPullerManager->RegisterPullUidProvider(mConfigKey, this);
// Store the sub-configs used.
@@ -120,6 +132,16 @@
const int64_t currentTimeNs,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor) {
+ if (!isAtLeastU() && config.has_restricted_metrics_delegate_package_name()) {
+ mInvalidConfigReason =
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
+ return false;
+ }
+ if (config.has_restricted_metrics_delegate_package_name()) {
+ mRestrictedMetricsDelegatePackageName = config.restricted_metrics_delegate_package_name();
+ } else {
+ mRestrictedMetricsDelegatePackageName = nullopt;
+ }
vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
unordered_map<int64_t, int> newAtomMatchingTrackerMap;
vector<sp<ConditionTracker>> newConditionTrackers;
@@ -142,7 +164,7 @@
mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
- mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIdsToMatchersMap,
+ mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, this, mTagIdsToMatchersMap,
newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
@@ -171,6 +193,7 @@
config.whitelisted_atom_ids().end());
mShouldPersistHistory = config.persist_locally();
mPackageCertificateHashSizeBytes = config.package_certificate_hash_size_bytes();
+ mUseV2SoftMemoryCalculation = config.statsd_config_options().use_v2_soft_memory_limit();
// Store the sub-configs used.
mAnnotations.clear();
@@ -185,6 +208,7 @@
mPullAtomPackages.clear();
createAllLogSourcesFromConfig(config);
setMaxMetricsBytesFromConfig(config);
+ setTriggerGetDataBytesFromConfig(config);
verifyGuardrailsAndUpdateStatsdStats();
initializeConfigActiveStatus();
@@ -193,29 +217,22 @@
void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
// Init allowed pushed atom uids.
- if (config.allowed_log_source_size() == 0) {
- ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at "
- "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
- mInvalidConfigReason =
- InvalidConfigReason(INVALID_CONFIG_REASON_LOG_SOURCE_ALLOWLIST_EMPTY);
- } else {
- for (const auto& source : config.allowed_log_source()) {
- auto it = UidMap::sAidToUidMapping.find(source);
- if (it != UidMap::sAidToUidMapping.end()) {
- mAllowedUid.push_back(it->second);
- } else {
- mAllowedPkg.push_back(source);
- }
- }
-
- if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
- ALOGE("Too many log sources. This is likely to be an error in the config.");
- mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES);
+ for (const auto& source : config.allowed_log_source()) {
+ auto it = UidMap::sAidToUidMapping.find(source);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ mAllowedUid.push_back(it->second);
} else {
- initAllowedLogSources();
+ mAllowedPkg.push_back(source);
}
}
+ if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
+ ALOGE("Too many log sources. This is likely to be an error in the config.");
+ mInvalidConfigReason = InvalidConfigReason(INVALID_CONFIG_REASON_TOO_MANY_LOG_SOURCES);
+ } else {
+ initAllowedLogSources();
+ }
+
// Init default allowed pull atom uids.
int numPullPackages = 0;
for (const string& pullSource : config.default_pull_packages()) {
@@ -253,6 +270,10 @@
}
void MetricsManager::setMaxMetricsBytesFromConfig(const StatsdConfig& config) {
+ if (!config.has_max_metrics_memory_kb()) {
+ mMaxMetricsBytes = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
+ return;
+ }
if (config.max_metrics_memory_kb() <= 0 ||
static_cast<size_t>(config.max_metrics_memory_kb() * 1024) >
StatsdStats::kHardMaxMetricsBytesPerConfig) {
@@ -263,6 +284,21 @@
}
}
+void MetricsManager::setTriggerGetDataBytesFromConfig(const StatsdConfig& config) {
+ if (!config.has_soft_metrics_memory_kb()) {
+ mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
+ return;
+ }
+ if (config.soft_metrics_memory_kb() <= 0 ||
+ static_cast<size_t>(config.soft_metrics_memory_kb() * 1024) >
+ StatsdStats::kHardMaxTriggerGetDataBytes) {
+ ALOGW("Memory limit ust be between 0KB and 10MB. Setting to default value (192KB).");
+ mTriggerGetDataBytes = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
+ } else {
+ mTriggerGetDataBytes = config.soft_metrics_memory_kb() * 1024;
+ }
+}
+
void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
// Guardrail. Reject the config if it's too big.
if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig) {
@@ -332,7 +368,7 @@
return !mInvalidConfigReason.has_value();
}
-void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+void MetricsManager::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk, const int uid,
const int64_t version) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
@@ -353,8 +389,7 @@
}
}
-void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
- const int uid) {
+void MetricsManager::notifyAppRemoved(const int64_t eventTimeNs, const string& apk, const int uid) {
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->notifyAppRemoved(eventTimeNs);
@@ -374,7 +409,7 @@
}
}
-void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
+void MetricsManager::onUidMapReceived(const int64_t eventTimeNs) {
// Purposefully don't inform metric producers on a new snapshot
// because we don't need to flush partial buckets.
// This occurs if a new user is added/removed or statsd crashes.
@@ -386,7 +421,8 @@
initAllowedLogSources();
}
-void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
+void MetricsManager::onStatsdInitCompleted(const int64_t eventTimeNs) {
+ ATRACE_CALL();
// Inform all metric producers.
for (const auto& it : mAllMetricProducers) {
it->onStatsdInitCompleted(eventTimeNs);
@@ -410,6 +446,10 @@
return uids;
}
+bool MetricsManager::useV2SoftMemoryCalculation() {
+ return mUseV2SoftMemoryCalculation;
+}
+
void MetricsManager::dumpStates(int out, bool verbose) {
dprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
{
@@ -434,6 +474,11 @@
const bool include_current_partial_bucket, const bool erase_data,
const DumpLatency dumpLatency, std::set<string>* str_set,
ProtoOutputStream* protoOutput) {
+ if (hasRestrictedMetricsDelegate()) {
+ // TODO(b/268150038): report error to statsdstats
+ VLOG("Unexpected call to onDumpReport in restricted metricsmanager.");
+ return;
+ }
VLOG("=========================Metric Reports Start==========================");
// one StatsLogReport per MetricProduer
for (const auto& producer : mAllMetricProducers) {
@@ -474,6 +519,13 @@
if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
+
+ if (event.GetUid() == AID_ROOT ||
+ (event.GetUid() >= AID_SYSTEM && event.GetUid() < AID_SHELL)) {
+ // enable atoms logged from pre-installed Android system services
+ return true;
+ }
+
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
VLOG("log source %d not on the whitelist", event.GetUid());
@@ -482,68 +534,6 @@
return true;
}
-bool MetricsManager::eventSanityCheck(const LogEvent& event) {
- if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
- // Check that app breadcrumb reported fields are valid.
- status_t err = NO_ERROR;
-
- // Uid is 3rd from last field and must match the caller's uid,
- // unless that caller is statsd itself (statsd is allowed to spoof uids).
- long appHookUid = event.GetLong(event.size()-2, &err);
- if (err != NO_ERROR) {
- VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
- return false;
- }
-
- // Because the uid within the LogEvent may have been mapped from
- // isolated to host, map the loggerUid similarly before comparing.
- int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
- if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
- VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
- appHookUid, loggerUid);
- return false;
- }
-
- // The state must be from 0,3. This part of code must be manually updated.
- long appHookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR) {
- VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
- return false;
- } else if (appHookState < 0 || appHookState > 3) {
- VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
- return false;
- }
- } else if (event.GetTagId() == util::DAVEY_OCCURRED) {
- // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
- // Check that the davey duration is reasonable. Max length check is for privacy.
- status_t err = NO_ERROR;
-
- // Uid is the first field provided.
- long jankUid = event.GetLong(1, &err);
- if (err != NO_ERROR) {
- VLOG("Davey occurred had error when parsing the uid");
- return false;
- }
- int32_t loggerUid = event.GetUid();
- if (loggerUid != jankUid && loggerUid != AID_STATSD) {
- VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
- loggerUid);
- return false;
- }
-
- long duration = event.GetLong(event.size(), &err);
- if (err != NO_ERROR) {
- VLOG("Davey occurred had error when parsing the duration");
- return false;
- } else if (duration > 100000) {
- VLOG("Davey duration is unreasonably long: %ld", duration);
- return false;
- }
- }
-
- return true;
-}
-
// Consume the stats log if it's interesting to this metric.
void MetricsManager::onLogEvent(const LogEvent& event) {
if (!isConfigValid()) {
@@ -554,11 +544,6 @@
return;
}
- // TODO(b/212755214): this check could be done once on the StatsLogProcessor level
- if (!eventSanityCheck(event)) {
- return;
- }
-
const int tagId = event.GetTagId();
const int64_t eventTimeNs = event.GetElapsedTimestampNs();
@@ -598,10 +583,12 @@
vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
MatchingState::kNotComputed);
+ vector<shared_ptr<LogEvent>> matcherTransformations(matcherCache.size(), nullptr);
for (const auto& matcherIndex : matchersIt->second) {
- mAllAtomMatchingTrackers[matcherIndex]->onLogEvent(event, mAllAtomMatchingTrackers,
- matcherCache);
+ mAllAtomMatchingTrackers[matcherIndex]->onLogEvent(event, matcherIndex,
+ mAllAtomMatchingTrackers, matcherCache,
+ matcherTransformations);
}
// Set of metrics that received an activation cancellation.
@@ -642,13 +629,16 @@
mIsActive = isActive;
// A bitmap to see which ConditionTracker needs to be re-evaluated.
- vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
+ vector<uint8_t> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
+ vector<shared_ptr<LogEvent>> conditionToTransformedLogEvents(mAllConditionTrackers.size(),
+ nullptr);
- for (const auto& pair : mTrackerToConditionMap) {
- if (matcherCache[pair.first] == MatchingState::kMatched) {
- const auto& conditionList = pair.second;
+ for (const auto& [matcherIndex, conditionList] : mTrackerToConditionMap) {
+ if (matcherCache[matcherIndex] == MatchingState::kMatched) {
for (const int conditionIndex : conditionList) {
conditionToBeEvaluated[conditionIndex] = true;
+ conditionToTransformedLogEvents[conditionIndex] =
+ matcherTransformations[matcherIndex];
}
}
}
@@ -656,59 +646,65 @@
vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
ConditionState::kNotEvaluated);
// A bitmap to track if a condition has changed value.
- vector<bool> changedCache(mAllConditionTrackers.size(), false);
+ vector<uint8_t> changedCache(mAllConditionTrackers.size(), false);
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (conditionToBeEvaluated[i] == false) {
+ if (!conditionToBeEvaluated[i]) {
continue;
}
sp<ConditionTracker>& condition = mAllConditionTrackers[i];
- condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
- changedCache);
+ const LogEvent& conditionEvent = conditionToTransformedLogEvents[i] == nullptr
+ ? event
+ : *conditionToTransformedLogEvents[i];
+ condition->evaluateCondition(conditionEvent, matcherCache, mAllConditionTrackers,
+ conditionCache, changedCache);
}
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (changedCache[i] == false) {
+ if (!changedCache[i]) {
continue;
}
- auto pair = mConditionToMetricMap.find(i);
- if (pair != mConditionToMetricMap.end()) {
- auto& metricList = pair->second;
- for (auto metricIndex : metricList) {
- // Metric cares about non sliced condition, and it's changed.
- // Push the new condition to it directly.
- if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
- mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
- eventTimeNs);
- // Metric cares about sliced conditions, and it may have changed. Send
- // notification, and the metric can query the sliced conditions that are
- // interesting to it.
- } else {
- mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
- eventTimeNs);
- }
+ auto it = mConditionToMetricMap.find(i);
+ if (it == mConditionToMetricMap.end()) {
+ continue;
+ }
+ auto& metricList = it->second;
+ for (auto metricIndex : metricList) {
+ // Metric cares about non sliced condition, and it's changed.
+ // Push the new condition to it directly.
+ if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
+ mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
+ eventTimeNs);
+ // Metric cares about sliced conditions, and it may have changed. Send
+ // notification, and the metric can query the sliced conditions that are
+ // interesting to it.
+ } else {
+ mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
+ eventTimeNs);
}
}
}
-
// For matched AtomMatchers, tell relevant metrics that a matched event has come.
for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
if (matcherCache[i] == MatchingState::kMatched) {
StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
mAllAtomMatchingTrackers[i]->getId());
- auto pair = mTrackerToMetricMap.find(i);
- if (pair != mTrackerToMetricMap.end()) {
- auto& metricList = pair->second;
- for (const int metricIndex : metricList) {
- // pushed metrics are never scheduled pulls
- mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
- }
+ auto it = mTrackerToMetricMap.find(i);
+ if (it == mTrackerToMetricMap.end()) {
+ continue;
+ }
+ auto& metricList = it->second;
+ const LogEvent& metricEvent =
+ matcherTransformations[i] == nullptr ? event : *matcherTransformations[i];
+ for (const int metricIndex : metricList) {
+ // pushed metrics are never scheduled pulls
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, metricEvent);
}
}
}
}
void MetricsManager::onAnomalyAlarmFired(
- const int64_t& timestampNs,
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mAllAnomalyTrackers) {
itr->informAlarmsFired(timestampNs, alarmSet);
@@ -716,7 +712,7 @@
}
void MetricsManager::onPeriodicAlarmFired(
- const int64_t& timestampNs,
+ const int64_t timestampNs,
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
for (const auto& itr : mAllPeriodicAlarmTrackers) {
itr->informAlarmsFired(timestampNs, alarmSet);
@@ -784,6 +780,15 @@
}
metadataWritten |= alertWritten;
}
+
+ for (const auto& metricProducer : mAllMetricProducers) {
+ metadata::MetricMetadata* metricMetadata = statsMetadata->add_metric_metadata();
+ bool metricWritten = metricProducer->writeMetricMetadataToProto(metricMetadata);
+ if (!metricWritten) {
+ statsMetadata->mutable_metric_metadata()->RemoveLast();
+ }
+ metadataWritten |= metricWritten;
+ }
return metadataWritten;
}
@@ -792,7 +797,7 @@
int64_t systemElapsedTimeNs) {
for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
int64_t alertId = alertMetadata.alert_id();
- auto it = mAlertTrackerMap.find(alertId);
+ const auto& it = mAlertTrackerMap.find(alertId);
if (it == mAlertTrackerMap.end()) {
ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId);
continue;
@@ -801,6 +806,61 @@
currentWallClockTimeNs,
systemElapsedTimeNs);
}
+ for (const metadata::MetricMetadata& metricMetadata : metadata.metric_metadata()) {
+ int64_t metricId = metricMetadata.metric_id();
+ const auto& it = mMetricProducerMap.find(metricId);
+ if (it == mMetricProducerMap.end()) {
+ ALOGE("No metricProducer found for metricId %lld", (long long)metricId);
+ }
+ mAllMetricProducers[it->second]->loadMetricMetadataFromProto(metricMetadata);
+ }
+}
+
+void MetricsManager::enforceRestrictedDataTtls(const int64_t wallClockNs) {
+ if (!hasRestrictedMetricsDelegate()) {
+ return;
+ }
+ sqlite3* db = dbutils::getDb(mConfigKey);
+ if (db == nullptr) {
+ ALOGE("Failed to open sqlite db");
+ dbutils::closeDb(db);
+ return;
+ }
+ for (const auto& producer : mAllMetricProducers) {
+ producer->enforceRestrictedDataTtl(db, wallClockNs);
+ }
+ dbutils::closeDb(db);
+}
+
+bool MetricsManager::validateRestrictedMetricsDelegate(const int32_t callingUid) {
+ if (!hasRestrictedMetricsDelegate()) {
+ return false;
+ }
+
+ set<int32_t> possibleUids = mUidMap->getAppUid(mRestrictedMetricsDelegatePackageName.value());
+
+ return possibleUids.find(callingUid) != possibleUids.end();
+}
+
+void MetricsManager::flushRestrictedData() {
+ if (!hasRestrictedMetricsDelegate()) {
+ return;
+ }
+ int64_t flushStartNs = getElapsedRealtimeNs();
+ for (const auto& producer : mAllMetricProducers) {
+ producer->flushRestrictedData();
+ }
+ StatsdStats::getInstance().noteRestrictedConfigFlushLatency(
+ mConfigKey, getElapsedRealtimeNs() - flushStartNs);
+}
+
+vector<int64_t> MetricsManager::getAllMetricIds() const {
+ vector<int64_t> metricIds;
+ metricIds.reserve(mMetricProducerMap.size());
+ for (const auto& [metricId, _] : mMetricProducerMap) {
+ metricIds.push_back(metricId);
+ }
+ return metricIds;
}
void MetricsManager::addAllAtomIds(LogEventFilter::AtomIdSet& allIds) const {
diff --git a/statsd/src/metrics/MetricsManager.h b/statsd/src/metrics/MetricsManager.h
index f07ee64..3b28b6a 100644
--- a/statsd/src/metrics/MetricsManager.h
+++ b/statsd/src/metrics/MetricsManager.h
@@ -23,6 +23,7 @@
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionTracker.h"
#include "config/ConfigKey.h"
+#include "config/ConfigMetadataProvider.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
@@ -37,9 +38,11 @@
namespace statsd {
// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public virtual RefBase, public virtual PullUidProvider {
+class MetricsManager : public virtual RefBase,
+ public virtual PullUidProvider,
+ public virtual ConfigMetadataProvider {
public:
- MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
+ MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -47,8 +50,8 @@
virtual ~MetricsManager();
- bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+ bool updateConfig(const StatsdConfig& config, int64_t timeBaseNs, const int64_t currentTimeNs,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor);
// Return whether the configuration is valid.
@@ -56,31 +59,30 @@
bool checkLogCredentials(const LogEvent& event);
- bool eventSanityCheck(const LogEvent& event);
-
- void onLogEvent(const LogEvent& event);
+ virtual void onLogEvent(const LogEvent& event);
void onAnomalyAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
+ int64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
void onPeriodicAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
+ int64_t timestampNs,
+ unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version);
+ void notifyAppUpgrade(int64_t eventTimeNs, const string& apk, int uid, int64_t version);
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid);
+ void notifyAppRemoved(int64_t eventTimeNs, const string& apk, int uid);
- void onUidMapReceived(const int64_t& eventTimeNs);
+ void onUidMapReceived(int64_t eventTimeNs);
- void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+ void onStatsdInitCompleted(int64_t elapsedTimeNs);
void init();
vector<int32_t> getPullAtomUids(int32_t atomId) override;
+ bool useV2SoftMemoryCalculation() override;
+
bool shouldWriteToDisk() const {
return mNoReportMetricIds.size() != mAllMetricProducers.size();
}
@@ -133,7 +135,7 @@
virtual void dropData(const int64_t dropTimeNs);
- virtual void onDumpReport(const int64_t dumpTimeNs, const int64_t wallClockNs,
+ virtual void onDumpReport(const int64_t dumpTimeNs, int64_t wallClockNs,
const bool include_current_partial_bucket, const bool erase_data,
const DumpLatency dumpLatency, std::set<string>* str_set,
android::util::ProtoOutputStream* protoOutput);
@@ -162,6 +164,27 @@
int64_t currentWallClockTimeNs,
int64_t systemElapsedTimeNs);
+ inline bool hasRestrictedMetricsDelegate() const {
+ return mRestrictedMetricsDelegatePackageName.has_value();
+ }
+
+ inline string getRestrictedMetricsDelegate() const {
+ return hasRestrictedMetricsDelegate() ? mRestrictedMetricsDelegatePackageName.value() : "";
+ }
+
+ inline ConfigKey getConfigKey() const {
+ return mConfigKey;
+ }
+
+ void enforceRestrictedDataTtls(const int64_t wallClockNs);
+
+ bool validateRestrictedMetricsDelegate(int32_t callingUid);
+
+ virtual void flushRestrictedData();
+
+ // Slow, should not be called in a hotpath.
+ vector<int64_t> getAllMetricIds() const;
+
// Adds all atom ids referenced by matchers in the MetricsManager's config
void addAllAtomIds(LogEventFilter::AtomIdSet& allIds) const;
@@ -170,6 +193,10 @@
return mMaxMetricsBytes;
}
+ inline size_t getTriggerGetDataBytes() const {
+ return mTriggerGetDataBytes;
+ }
+
private:
// For test only.
inline int64_t getTtlEndNs() const { return mTtlEndNs; }
@@ -225,6 +252,7 @@
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
bool mShouldPersistHistory;
+ bool mUseV2SoftMemoryCalculation;
// All event tags that are interesting to config metrics matchers.
std::unordered_map<int, std::vector<int>> mTagIdsToMatchersMap;
@@ -326,16 +354,28 @@
// Hashes of the States used in this config, keyed by the state id, used in config updates.
std::map<int64_t, uint64_t> mStateProtoHashes;
+ // Optional package name of the delegate that processes restricted metrics
+ // If set, restricted metrics are only uploaded to the delegate.
+ optional<string> mRestrictedMetricsDelegatePackageName = nullopt;
+
// Only called on config creation/update. Sets the memory limit in bytes for storing metrics.
void setMaxMetricsBytesFromConfig(const StatsdConfig& config);
+ // Only called on config creation/update. Sets the soft memory limit in bytes for storing
+ // metrics.
+ void setTriggerGetDataBytesFromConfig(const StatsdConfig& config);
+
// The memory limit in bytes for storing metrics
size_t mMaxMetricsBytes;
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+ // The memory limit in bytes for triggering get data.
+ size_t mTriggerGetDataBytes;
+
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
+
+ FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling);
FRIEND_TEST(GaugeMetricE2ePulledTest, TestFirstNSamplesPulledNoTrigger);
FRIEND_TEST(GaugeMetricE2ePulledTest, TestFirstNSamplesPulledNoTriggerWithActivation);
FRIEND_TEST(GaugeMetricE2ePushedTest, TestMultipleFieldsForPushedEvent);
@@ -367,6 +407,8 @@
FRIEND_TEST(MetricsManagerTest, TestLogSources);
FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
+ FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfig);
+ FRIEND_TEST(MetricsManagerTest_SPlus, TestRestrictedMetricsConfigUpdate);
FRIEND_TEST(MetricsManagerUtilTest, TestSampledMetrics);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
@@ -391,7 +433,6 @@
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
FRIEND_TEST(DurationMetricE2eTest, TestUploadThreshold);
@@ -402,7 +443,8 @@
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
- FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithMultipleAggTypes);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithDefaultAggType);
};
} // namespace statsd
diff --git a/statsd/src/metrics/NumericValueMetricProducer.cpp b/statsd/src/metrics/NumericValueMetricProducer.cpp
index 12c8c04..53a1901 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.cpp
+++ b/statsd/src/metrics/NumericValueMetricProducer.cpp
@@ -22,6 +22,7 @@
#include <limits.h>
#include <stdlib.h>
+#include "FieldValue.h"
#include "guardrail/StatsdStats.h"
#include "metrics/parsing_utils/metrics_manager_util.h"
#include "stats_log_util.h"
@@ -34,7 +35,6 @@
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
-using std::map;
using std::optional;
using std::shared_ptr;
using std::string;
@@ -67,11 +67,13 @@
const PullOptions& pullOptions, const BucketOptions& bucketOptions,
const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
const StateOptions& stateOptions, const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions)
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider)
: ValueMetricProducer(metric.id(), key, protoHash, pullOptions, bucketOptions, whatOptions,
- conditionOptions, stateOptions, activationOptions, guardrailOptions),
+ conditionOptions, stateOptions, activationOptions, guardrailOptions,
+ configMetadataProvider),
mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
- mAggregationType(metric.aggregation_type()),
+ mAggregationTypes(whatOptions.aggregationTypes),
mIncludeSampleSize(metric.has_include_sample_size()
? metric.include_sample_size()
: metric.aggregation_type() == ValueMetric_AggregationType_AVG),
@@ -81,7 +83,8 @@
mUseZeroDefaultBase(metric.use_zero_default_base()),
mHasGlobalBase(false),
mMaxPullDelayNs(metric.has_max_pull_delay_sec() ? metric.max_pull_delay_sec() * NS_PER_SEC
- : StatsdStats::kPullMaxDelayNs) {
+ : StatsdStats::kPullMaxDelayNs),
+ mDedupedFieldMatchers(dedupFieldMatchers(whatOptions.fieldMatchers)) {
// TODO(b/186677791): Use initializer list to initialize mUploadThreshold.
if (metric.has_threshold()) {
mUploadThreshold = metric.threshold();
@@ -267,15 +270,17 @@
// before calculating the diff between sums of consecutive pulls.
std::unordered_map<HashableDimensionKey, pair<LogEvent, vector<int>>> aggregateEvents;
for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) !=
- MatchingState::kMatched) {
+ const auto [matchResult, transformedEvent] =
+ mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+ if (matchResult != MatchingState::kMatched) {
continue;
}
// Get dimensions_in_what key and value indices.
HashableDimensionKey dimensionsInWhat;
- vector<int> valueIndices(mFieldMatchers.size(), -1);
- if (!filterValues(mDimensionsInWhat, mFieldMatchers, data->getValues(),
+ vector<int> valueIndices(mDedupedFieldMatchers.size(), -1);
+ const LogEvent& eventRef = transformedEvent == nullptr ? *data : *transformedEvent;
+ if (!filterValues(mDimensionsInWhat, mDedupedFieldMatchers, eventRef.getValues(),
dimensionsInWhat, valueIndices)) {
StatsdStats::getInstance().noteBadValueType(mMetricId);
}
@@ -285,9 +290,9 @@
if (it == aggregateEvents.end()) {
aggregateEvents.emplace(std::piecewise_construct,
std::forward_as_tuple(dimensionsInWhat),
- std::forward_as_tuple(*data, valueIndices));
+ std::forward_as_tuple(eventRef, valueIndices));
} else {
- combineValueFields(it->second, *data, valueIndices);
+ combineValueFields(it->second, eventRef, valueIndices);
}
}
@@ -297,9 +302,10 @@
}
} else {
for (const auto& data : allData) {
- LogEvent localCopy = *data;
- if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
- MatchingState::kMatched) {
+ const auto [matchResult, transformedEvent] =
+ mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+ if (matchResult == MatchingState::kMatched) {
+ LogEvent localCopy = transformedEvent == nullptr ? *data : *transformedEvent;
localCopy.setElapsedTimestampNs(eventElapsedTimeNs);
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
@@ -476,7 +482,7 @@
}
if (interval.hasValue()) {
- switch (mAggregationType) {
+ switch (getAggregationTypeLocked(i)) {
case ValueMetric::SUM:
// for AVG, we add up and take average when flushing the bucket
case ValueMetric::AVG:
@@ -662,7 +668,7 @@
}
Value NumericValueMetricProducer::getFinalValue(const Interval& interval) const {
- if (mAggregationType != ValueMetric::AVG) {
+ if (getAggregationTypeLocked(interval.aggIndex) != ValueMetric::AVG) {
return interval.aggregate;
} else {
double sum = interval.aggregate.type == LONG ? (double)interval.aggregate.long_value
diff --git a/statsd/src/metrics/NumericValueMetricProducer.h b/statsd/src/metrics/NumericValueMetricProducer.h
index 78c414f..103f404 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.h
+++ b/statsd/src/metrics/NumericValueMetricProducer.h
@@ -36,7 +36,8 @@
const ConditionOptions& conditionOptions,
const StateOptions& stateOptions,
const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions);
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Process data pulled on bucket boundary.
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, PullResult pullResult,
@@ -118,7 +119,7 @@
bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
inline bool canSkipLogEventLocked(
- const MetricDimensionKey& eventKey, const bool condition, const int64_t eventTimeNs,
+ const MetricDimensionKey& eventKey, const bool condition, int64_t eventTimeNs,
const map<int, HashableDimensionKey>& statePrimaryKeys) const override {
// For pushed metrics, can only skip if condition is false.
// For pulled metrics, can only skip if metric is not diffed and condition is false or
@@ -142,12 +143,16 @@
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
- void combineValueFields(pair<LogEvent, vector<int>>& eventValues, const LogEvent& newEvent,
- const vector<int>& newValueIndices) const;
+ void combineValueFields(pair<LogEvent, std::vector<int>>& eventValues, const LogEvent& newEvent,
+ const std::vector<int>& newValueIndices) const;
+
+ ValueMetric::AggregationType getAggregationTypeLocked(int index) const {
+ return mAggregationTypes.size() == 1 ? mAggregationTypes[0] : mAggregationTypes[index];
+ }
const bool mUseAbsoluteValueOnReset;
- const ValueMetric::AggregationType mAggregationType;
+ const std::vector<ValueMetric::AggregationType> mAggregationTypes;
const bool mIncludeSampleSize;
@@ -171,6 +176,9 @@
const int64_t mMaxPullDelayNs;
+ // Deduped value fields for matching.
+ const std::vector<Matcher> mDedupedFieldMatchers;
+
// For anomaly detection.
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
@@ -232,6 +240,8 @@
FRIEND_TEST(NumericValueMetricProducerTest,
TestSlicedStateWithMultipleDimensionsMissingDataInPull);
FRIEND_TEST(NumericValueMetricProducerTest, TestUploadThreshold);
+ FRIEND_TEST(NumericValueMetricProducerTest, TestMultipleAggTypesPulled);
+ FRIEND_TEST(NumericValueMetricProducerTest, TestMultipleAggTypesPushed);
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
@@ -279,6 +289,10 @@
FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
+ FRIEND_TEST(MetricsManagerUtilDimLimitTest, TestDimLimit);
+
+ FRIEND_TEST(ConfigUpdateDimLimitTest, TestDimLimit);
+
friend class NumericValueMetricProducerTestHelper;
};
diff --git a/statsd/src/metrics/RestrictedEventMetricProducer.cpp b/statsd/src/metrics/RestrictedEventMetricProducer.cpp
new file mode 100644
index 0000000..83454fb
--- /dev/null
+++ b/statsd/src/metrics/RestrictedEventMetricProducer.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2023, 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 STATSD_DEBUG true
+#include "Log.h"
+
+#include "RestrictedEventMetricProducer.h"
+
+#include "stats_annotations.h"
+#include "stats_log_util.h"
+#include "utils/DbUtils.h"
+
+using std::lock_guard;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#define NS_PER_DAY (24 * 3600 * NS_PER_SEC)
+
+RestrictedEventMetricProducer::RestrictedEventMetricProducer(
+ const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const uint64_t protoHash, const int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, protoHash,
+ startTimeNs, configMetadataProvider, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap),
+ mRestrictedDataCategory(CATEGORY_UNKNOWN) {
+}
+
+void RestrictedEventMetricProducer::onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) {
+ if (!condition) {
+ return;
+ }
+ if (mRestrictedDataCategory != CATEGORY_UNKNOWN &&
+ mRestrictedDataCategory != event.getRestrictionCategory()) {
+ StatsdStats::getInstance().noteRestrictedMetricCategoryChanged(mConfigKey, mMetricId);
+ deleteMetricTable();
+ mLogEvents.clear();
+ mTotalSize = 0;
+ }
+ mRestrictedDataCategory = event.getRestrictionCategory();
+ mLogEvents.push_back(event);
+ mTotalSize += getSize(event.getValues()) + sizeof(event);
+}
+
+void RestrictedEventMetricProducer::onDumpReportLocked(
+ const int64_t dumpTimeNs, const bool include_current_partial_bucket, const bool erase_data,
+ const DumpLatency dumpLatency, std::set<string>* str_set,
+ android::util::ProtoOutputStream* protoOutput) {
+ VLOG("Unexpected call to onDumpReportLocked() in RestrictedEventMetricProducer");
+}
+
+void RestrictedEventMetricProducer::onMetricRemove() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsMetricTableCreated) {
+ return;
+ }
+ deleteMetricTable();
+}
+
+void RestrictedEventMetricProducer::enforceRestrictedDataTtl(sqlite3* db,
+ const int64_t wallClockNs) {
+ int32_t ttlInDays = RestrictedPolicyManager::getInstance().getRestrictedCategoryTtl(
+ mRestrictedDataCategory);
+ int64_t ttlTime = wallClockNs - ttlInDays * NS_PER_DAY;
+ dbutils::flushTtl(db, mMetricId, ttlTime);
+}
+
+void RestrictedEventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
+ VLOG("Unexpected call to clearPastBucketsLocked in RestrictedEventMetricProducer");
+}
+
+void RestrictedEventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
+ mLogEvents.clear();
+ mTotalSize = 0;
+ StatsdStats::getInstance().noteBucketDropped(mMetricId);
+}
+
+void RestrictedEventMetricProducer::flushRestrictedData() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mLogEvents.empty()) {
+ return;
+ }
+ int64_t flushStartNs = getElapsedRealtimeNs();
+ if (!mIsMetricTableCreated) {
+ if (!dbutils::isEventCompatible(mConfigKey, mMetricId, mLogEvents[0])) {
+ // Delete old data if schema changes
+ // TODO(b/268150038): report error to statsdstats
+ ALOGD("Detected schema change for metric %lld", (long long)mMetricId);
+ deleteMetricTable();
+ }
+ // TODO(b/271481944): add retry.
+ if (!dbutils::createTableIfNeeded(mConfigKey, mMetricId, mLogEvents[0])) {
+ ALOGE("Failed to create table for metric %lld", (long long)mMetricId);
+ StatsdStats::getInstance().noteRestrictedMetricTableCreationError(mConfigKey,
+ mMetricId);
+ return;
+ }
+ mIsMetricTableCreated = true;
+ }
+ string err;
+ if (!dbutils::insert(mConfigKey, mMetricId, mLogEvents, err)) {
+ ALOGE("Failed to insert logEvent to table for metric %lld. err=%s", (long long)mMetricId,
+ err.c_str());
+ StatsdStats::getInstance().noteRestrictedMetricInsertError(mConfigKey, mMetricId);
+ } else {
+ StatsdStats::getInstance().noteRestrictedMetricFlushLatency(
+ mConfigKey, mMetricId, getElapsedRealtimeNs() - flushStartNs);
+ }
+ mLogEvents.clear();
+ mTotalSize = 0;
+}
+
+bool RestrictedEventMetricProducer::writeMetricMetadataToProto(
+ metadata::MetricMetadata* metricMetadata) {
+ metricMetadata->set_metric_id(mMetricId);
+ metricMetadata->set_restricted_category(mRestrictedDataCategory);
+ return true;
+}
+
+void RestrictedEventMetricProducer::loadMetricMetadataFromProto(
+ const metadata::MetricMetadata& metricMetadata) {
+ mRestrictedDataCategory =
+ static_cast<StatsdRestrictionCategory>(metricMetadata.restricted_category());
+}
+
+void RestrictedEventMetricProducer::deleteMetricTable() {
+ if (!dbutils::deleteTable(mConfigKey, mMetricId)) {
+ StatsdStats::getInstance().noteRestrictedMetricTableDeletionError(mConfigKey, mMetricId);
+ VLOG("Failed to delete table for metric %lld", (long long)mMetricId);
+ }
+ mIsMetricTableCreated = false;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/metrics/RestrictedEventMetricProducer.h b/statsd/src/metrics/RestrictedEventMetricProducer.h
new file mode 100644
index 0000000..7f9a5eb
--- /dev/null
+++ b/statsd/src/metrics/RestrictedEventMetricProducer.h
@@ -0,0 +1,68 @@
+#ifndef RESTRICTED_EVENT_METRIC_PRODUCER_H
+#define RESTRICTED_EVENT_METRIC_PRODUCER_H
+
+#include <gtest/gtest_prod.h>
+
+#include "EventMetricProducer.h"
+#include "utils/RestrictedPolicyManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class RestrictedEventMetricProducer : public EventMetricProducer {
+public:
+ RestrictedEventMetricProducer(
+ const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const uint64_t protoHash, int64_t startTimeNs,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
+
+ void onMetricRemove() override;
+
+ void enforceRestrictedDataTtl(sqlite3* db, int64_t wallClockNs);
+
+ void flushRestrictedData() override;
+
+ bool writeMetricMetadataToProto(metadata::MetricMetadata* metricMetadata) override;
+
+ void loadMetricMetadataFromProto(const metadata::MetricMetadata& metricMetadata) override;
+
+ inline StatsdRestrictionCategory getRestrictionCategory() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mRestrictedDataCategory;
+ }
+
+private:
+ void onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
+
+ void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket,
+ const bool erase_data, const DumpLatency dumpLatency,
+ std::set<string>* str_set,
+ android::util::ProtoOutputStream* protoOutput) override;
+
+ void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+
+ void dropDataLocked(const int64_t dropTimeNs) override;
+
+ void deleteMetricTable();
+
+ bool mIsMetricTableCreated = false;
+
+ StatsdRestrictionCategory mRestrictedDataCategory;
+
+ vector<LogEvent> mLogEvents;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // RESTRICTED_EVENT_METRIC_PRODUCER_H
diff --git a/statsd/src/metrics/ValueMetricProducer.cpp b/statsd/src/metrics/ValueMetricProducer.cpp
index 833f55f..f4620dd 100644
--- a/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/statsd/src/metrics/ValueMetricProducer.cpp
@@ -54,6 +54,7 @@
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
const int FIELD_ID_IS_ACTIVE = 14;
const int FIELD_ID_DIMENSION_GUARDRAIL_HIT = 17;
+const int FIELD_ID_ESTIMATED_MEMORY_BYTES = 18;
// for *MetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
@@ -72,16 +73,18 @@
template <typename AggregatedValue, typename DimExtras>
ValueMetricProducer<AggregatedValue, DimExtras>::ValueMetricProducer(
- const int64_t& metricId, const ConfigKey& key, const uint64_t protoHash,
+ const int64_t metricId, const ConfigKey& key, const uint64_t protoHash,
const PullOptions& pullOptions, const BucketOptions& bucketOptions,
const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
const StateOptions& stateOptions, const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions)
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider)
: MetricProducer(metricId, key, bucketOptions.timeBaseNs, conditionOptions.conditionIndex,
conditionOptions.initialConditionCache, conditionOptions.conditionWizard,
protoHash, activationOptions.eventActivationMap,
activationOptions.eventDeactivationMap, stateOptions.slicedStateAtoms,
- stateOptions.stateGroupMap, bucketOptions.splitBucketForAppUpgrade),
+ stateOptions.stateGroupMap, bucketOptions.splitBucketForAppUpgrade,
+ configMetadataProvider),
mWhatMatcherIndex(whatOptions.whatMatcherIndex),
mEventMatcherWizard(whatOptions.matcherWizard),
mPullerManager(pullOptions.pullerManager),
@@ -154,7 +157,8 @@
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::onStatsdInitCompleted(
- const int64_t& eventTimeNs) {
+ const int64_t eventTimeNs) {
+ ATRACE_CALL();
lock_guard<mutex> lock(mMutex);
if (isPulled() && mCondition == ConditionState::kTrue && mIsActive) {
@@ -225,7 +229,7 @@
void ValueMetricProducer<AggregatedValue, DimExtras>::onStateChanged(
int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
- // TODO(b/189353769): Acquire lock.
+ std::lock_guard<std::mutex> lock(mMutex);
VLOG("ValueMetricProducer %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
(long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
oldState.mValue.int_value, newState.mValue.int_value);
@@ -320,6 +324,8 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ESTIMATED_MEMORY_BYTES,
+ (long long)byteSizeLocked());
if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
}
@@ -616,7 +622,7 @@
template <typename AggregatedValue, typename DimExtras>
bool ValueMetricProducer<AggregatedValue, DimExtras>::hitGuardRailLocked(
- const MetricDimensionKey& newKey) {
+ const MetricDimensionKey& newKey) const {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -717,7 +723,7 @@
// if mCondition and mIsActive are true!
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::flushIfNeededLocked(
- const int64_t& eventTimeNs) {
+ const int64_t eventTimeNs) {
const int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs,
@@ -742,7 +748,7 @@
template <typename AggregatedValue, typename DimExtras>
void ValueMetricProducer<AggregatedValue, DimExtras>::flushCurrentBucketLocked(
- const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) {
+ const int64_t eventTimeNs, const int64_t nextBucketStartTimeNs) {
if (mCondition == ConditionState::kUnknown) {
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
diff --git a/statsd/src/metrics/ValueMetricProducer.h b/statsd/src/metrics/ValueMetricProducer.h
index 6592b3e..bfde6a0 100644
--- a/statsd/src/metrics/ValueMetricProducer.h
+++ b/statsd/src/metrics/ValueMetricProducer.h
@@ -88,6 +88,7 @@
const sp<EventMatcherWizard>& matcherWizard;
const FieldMatcher& dimensionsInWhat;
const vector<Matcher>& fieldMatchers;
+ const vector<ValueMetric::AggregationType> aggregationTypes;
};
struct ConditionOptions {
@@ -127,18 +128,19 @@
}
// ValueMetric needs special logic if it's a pulled atom.
- void onStatsdInitCompleted(const int64_t& eventTimeNs) override;
+ void onStatsdInitCompleted(int64_t eventTimeNs) override;
void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) override;
protected:
- ValueMetricProducer(const int64_t& metricId, const ConfigKey& key, const uint64_t protoHash,
+ ValueMetricProducer(int64_t metricId, const ConfigKey& key, uint64_t protoHash,
const PullOptions& pullOptions, const BucketOptions& bucketOptions,
const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
const StateOptions& stateOptions,
const ActivationOptions& activationOptions,
- const GuardrailOptions& guardrailOptions);
+ const GuardrailOptions& guardrailOptions,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -178,7 +180,7 @@
}
// ValueMetricProducer internal interface to handle condition change.
- void onConditionChangedLocked(const bool condition, const int64_t eventTimeNs) override;
+ void onConditionChangedLocked(const bool condition, int64_t eventTimeNs) override;
// Only called when mIsActive, the event is NOT too late, and after pulling.
virtual void onConditionChangedInternalLocked(const ConditionState oldCondition,
@@ -187,7 +189,7 @@
}
// Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
void dumpStatesLocked(int out, bool verbose) const override;
@@ -195,12 +197,11 @@
// For pulled metrics, this method should only be called if a pull has been done. Else we will
// not have complete data for the bucket.
- void flushIfNeededLocked(const int64_t& eventTime) override;
+ void flushIfNeededLocked(int64_t eventTime) override;
// For pulled metrics, this method should only be called if a pulled has been done. Else we will
// not have complete data for the bucket.
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
+ void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
void dropDataLocked(const int64_t dropTimeNs) override;
@@ -216,7 +217,7 @@
void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
optional<InvalidConfigReason> onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const StatsdConfig& config, int configIndex, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -320,7 +321,7 @@
const int64_t mMinBucketSizeNs;
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey) const;
bool hasReachedGuardRailLimit() const;
@@ -332,7 +333,7 @@
virtual PastBucket<AggregatedValue> buildPartialBucket(int64_t bucketEndTime,
std::vector<Interval>& intervals) = 0;
- virtual void closeCurrentBucket(const int64_t eventTimeNs, const int64_t nextBucketStartTimeNs);
+ virtual void closeCurrentBucket(const int64_t eventTimeNs, int64_t nextBucketStartTimeNs);
virtual void initNextSlicedBucket(int64_t nextBucketStartTimeNs);
diff --git a/statsd/src/metrics/duration_helper/DurationTracker.h b/statsd/src/metrics/duration_helper/DurationTracker.h
index 0aa5081..a6cbfff 100644
--- a/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -73,8 +73,8 @@
class DurationTracker {
public:
- DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ DurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+ const sp<ConditionWizard>& wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
@@ -95,21 +95,21 @@
virtual ~DurationTracker(){};
- void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+ void onConfigUpdated(const sp<ConditionWizard>& wizard, int conditionTrackerIndex) {
sp<ConditionWizard> tmpWizard = mWizard;
mWizard = wizard;
mConditionTrackerIndex = conditionTrackerIndex;
mAnomalyTrackers.clear();
};
- virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) = 0;
- virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
+ virtual void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
+ const ConditionKey& conditionKey, size_t dimensionHardLimit) = 0;
+ virtual void noteStop(const HashableDimensionKey& key, int64_t eventTime,
const bool stopAll) = 0;
virtual void noteStopAll(const int64_t eventTime) = 0;
virtual void onSlicedConditionMayChange(const int64_t timestamp) = 0;
- virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
+ virtual void onConditionChanged(bool condition, int64_t timestamp) = 0;
virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
const FieldValue& newState) = 0;
@@ -123,7 +123,7 @@
// Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
// an app upgrade, we assume that we're trying to form a partial bucket.
virtual bool flushCurrentBucket(
- const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+ int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
const int64_t globalConditionTrueNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
@@ -138,7 +138,7 @@
virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
// Replace old value with new value for the given state atom.
- virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
+ virtual void updateCurrentStateKey(int32_t atomId, const FieldValue& newState) = 0;
virtual bool hasAccumulatedDuration() const = 0;
@@ -162,6 +162,8 @@
protected:
virtual bool hasStartedDuration() const = 0;
+ // Convenience to compute the current bucket's end time, which is always aligned with the
+ // start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
@@ -188,8 +190,8 @@
}
}
- void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
- const int64_t& bucketValue, const int64_t& bucketNum) {
+ void addPastBucketToAnomalyTrackers(const MetricDimensionKey& eventKey, int64_t bucketValue,
+ int64_t bucketNum) {
for (auto& anomalyTracker : mAnomalyTrackers) {
if (anomalyTracker != nullptr) {
anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
@@ -197,8 +199,8 @@
}
}
- void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum,
- const int64_t& currentBucketValue) {
+ void detectAndDeclareAnomaly(int64_t timestamp, int64_t currBucketNum,
+ int64_t currentBucketValue) {
for (auto& anomalyTracker : mAnomalyTrackers) {
if (anomalyTracker != nullptr) {
anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
@@ -207,12 +209,6 @@
}
}
- // Convenience to compute the current bucket's end time, which is always aligned with the
- // start time of the metric.
- int64_t getCurrentBucketEndTimeNs() {
- return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
- }
-
void setEventKey(const MetricDimensionKey& eventKey) {
mEventKey = eventKey;
}
@@ -272,12 +268,16 @@
std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
- bool mHasHitGuardrail;
+ mutable bool mHasHitGuardrail;
FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
+ FRIEND_TEST(OringDurationTrackerTest_DimLimit, TestDimLimit);
+
+ FRIEND_TEST(MaxDurationTrackerTest_DimLimit, TestDimLimit);
+
FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
};
diff --git a/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index ea99ae8..8a253e2 100644
--- a/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -24,12 +24,12 @@
namespace os {
namespace statsd {
-MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
+MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t id,
const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs,
- bool conditionSliced, bool fullLink,
+ const sp<ConditionWizard>& wizard, int conditionIndex,
+ bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs,
+ int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const vector<sp<AnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
@@ -37,18 +37,19 @@
mDuration = 0;
}
-bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey,
+ size_t dimensionHardLimit) const {
// ===========GuardRail==============
if (mInfos.find(newKey) != mInfos.end()) {
// if the key existed, we are good!
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mInfos.size() >= StatsdStats::kDimensionKeySizeSoftLimit) {
size_t newTupleCount = mInfos.size() + 1;
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) {
+ if (newTupleCount > dimensionHardLimit) {
if (!mHasHitGuardrail) {
ALOGE("MaxDurTracker %lld dropping data for dimension key %s",
(long long)mTrackerId, newKey.toString().c_str());
@@ -62,9 +63,10 @@
}
void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) {
+ const int64_t eventTime, const ConditionKey& conditionKey,
+ size_t dimensionHardLimit) {
// this will construct a new DurationInfo if this key didn't exist.
- if (hitGuardRail(key)) {
+ if (hitGuardRail(key, dimensionHardLimit)) {
return;
}
@@ -173,7 +175,7 @@
}
bool MaxDurationTracker::flushCurrentBucket(
- const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+ const int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
const int64_t globalConditionTrueNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
VLOG("MaxDurationTracker flushing.....");
diff --git a/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 8c18e64..2769232 100644
--- a/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,30 +28,29 @@
// they stop or bucket expires.
class MaxDurationTracker : public DurationTracker {
public:
- MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ MaxDurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+ const sp<ConditionWizard>& wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
MaxDurationTracker(const MaxDurationTracker& tracker) = default;
- void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- const bool stopAll) override;
+ void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
+ const ConditionKey& conditionKey, size_t dimensionHardLimit) override;
+ void noteStop(const HashableDimensionKey& key, int64_t eventTime, const bool stopAll) override;
void noteStopAll(const int64_t eventTime) override;
bool flushIfNeeded(
int64_t timestampNs, const optional<UploadThreshold>& uploadThreshold,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
bool flushCurrentBucket(
- const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+ int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
const int64_t globalConditionTrueNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override;
void onSlicedConditionMayChange(const int64_t timestamp) override;
- void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onConditionChanged(bool condition, int64_t timestamp) override;
void onStateChanged(const int64_t timestamp, const int32_t atomId,
const FieldValue& newState) override;
@@ -64,7 +63,7 @@
int64_t getCurrentStateKeyFullBucketDuration() const override;
- void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+ void updateCurrentStateKey(int32_t atomId, const FieldValue& newState);
bool hasAccumulatedDuration() const override;
@@ -81,7 +80,7 @@
const int64_t timestamp);
// return true if we should not allow newKey to be tracked because we are above the threshold
- bool hitGuardRail(const HashableDimensionKey& newKey);
+ bool hitGuardRail(const HashableDimensionKey& newKey, size_t dimensionHardLimit) const;
FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
diff --git a/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 9437dbe..3fab0e8 100644
--- a/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -24,11 +24,14 @@
using std::pair;
-OringDurationTracker::OringDurationTracker(
- const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
- int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink, const vector<sp<AnomalyTracker>>& anomalyTrackers)
+OringDurationTracker::OringDurationTracker(const ConfigKey& key, const int64_t id,
+ const MetricDimensionKey& eventKey,
+ const sp<ConditionWizard>& wizard, int conditionIndex,
+ bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs,
+ int64_t bucketSizeNs, bool conditionSliced,
+ bool fullLink,
+ const vector<sp<AnomalyTracker>>& anomalyTrackers)
: DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
anomalyTrackers),
@@ -37,17 +40,18 @@
mLastStartTime = 0;
}
-bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey,
+ size_t dimensionHardLimit) const {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) {
return false;
}
- if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ if (mConditionKeyMap.size() >= StatsdStats::kDimensionKeySizeSoftLimit) {
size_t newTupleCount = mConditionKeyMap.size() + 1;
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) {
+ if (newTupleCount > dimensionHardLimit) {
if (!mHasHitGuardrail) {
ALOGE("OringDurTracker %lld dropping data for dimension key %s",
(long long)mTrackerId, newKey.toString().c_str());
@@ -61,8 +65,9 @@
}
void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) {
- if (hitGuardRail(key)) {
+ const int64_t eventTime, const ConditionKey& conditionKey,
+ size_t dimensionHardLimit) {
+ if (hitGuardRail(key, dimensionHardLimit)) {
return;
}
if (condition) {
@@ -136,7 +141,7 @@
}
bool OringDurationTracker::flushCurrentBucket(
- const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+ const int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
const int64_t globalConditionTrueNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
VLOG("OringDurationTracker Flushing.............");
diff --git a/statsd/src/metrics/duration_helper/OringDurationTracker.h b/statsd/src/metrics/duration_helper/OringDurationTracker.h
index c6fa425..a50862b 100644
--- a/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -26,29 +26,27 @@
// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
class OringDurationTracker : public DurationTracker {
public:
- OringDurationTracker(const ConfigKey& key, const int64_t& id,
- const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, int64_t currentBucketStartNs,
- int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
- bool conditionSliced, bool fullLink,
- const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
+ OringDurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+ const sp<ConditionWizard>& wizard, int conditionIndex, bool nesting,
+ int64_t currentBucketStartNs, int64_t currentBucketNum,
+ int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
+ bool fullLink, const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
OringDurationTracker(const OringDurationTracker& tracker) = default;
- void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- const bool stopAll) override;
+ void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
+ const ConditionKey& conditionKey, size_t dimensionHardLimit) override;
+ void noteStop(const HashableDimensionKey& key, int64_t eventTime, const bool stopAll) override;
void noteStopAll(const int64_t eventTime) override;
void onSlicedConditionMayChange(const int64_t timestamp) override;
- void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onConditionChanged(bool condition, int64_t timestamp) override;
void onStateChanged(const int64_t timestamp, const int32_t atomId,
const FieldValue& newState) override;
bool flushCurrentBucket(
- const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+ int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
const int64_t globalConditionTrueNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
bool flushIfNeeded(
@@ -63,7 +61,7 @@
int64_t getCurrentStateKeyFullBucketDuration() const override;
- void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+ void updateCurrentStateKey(int32_t atomId, const FieldValue& newState);
bool hasAccumulatedDuration() const override;
@@ -83,7 +81,7 @@
std::unordered_map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
// return true if we should not allow newKey to be tracked because we are above the threshold
- bool hitGuardRail(const HashableDimensionKey& newKey);
+ bool hitGuardRail(const HashableDimensionKey& newKey, size_t dimensionHardLimit) const;
FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index c3972d9..a296dad 100644
--- a/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -37,7 +37,7 @@
const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- vector<UpdateStatus>& matchersToUpdate, vector<bool>& cycleTracker) {
+ vector<UpdateStatus>& matchersToUpdate, vector<uint8_t>& cycleTracker) {
// Have already examined this matcher.
if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) {
return nullopt;
@@ -145,7 +145,7 @@
// For combination matchers, we need to determine if any children need to be updated.
vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(atomMatcherCount, false);
+ vector<uint8_t> cycleTracker(atomMatcherCount, false);
for (int i = 0; i < atomMatcherCount; i++) {
invalidConfigReason = determineMatcherUpdateStatus(
config, i, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
@@ -171,7 +171,7 @@
const sp<AtomMatchingTracker>& tracker =
oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
invalidConfigReason =
- tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap);
+ tracker->onConfigUpdated(matcherProtos[i], newAtomMatchingTrackerMap);
if (invalidConfigReason.has_value()) {
ALOGW("Config update failed for matcher %lld", (long long)id);
return invalidConfigReason;
@@ -184,7 +184,7 @@
[[fallthrough]]; // Intentionally fallthrough to create the new matcher.
case UPDATE_NEW: {
sp<AtomMatchingTracker> tracker =
- createAtomMatchingTracker(matcher, i, uidMap, invalidConfigReason);
+ createAtomMatchingTracker(matcher, uidMap, invalidConfigReason);
if (tracker == nullptr) {
return invalidConfigReason;
}
@@ -203,8 +203,9 @@
std::fill(cycleTracker.begin(), cycleTracker.end(), false);
for (size_t matcherIndex = 0; matcherIndex < newAtomMatchingTrackers.size(); matcherIndex++) {
auto& matcher = newAtomMatchingTrackers[matcherIndex];
- invalidConfigReason = matcher->init(matcherProtos, newAtomMatchingTrackers,
- newAtomMatchingTrackerMap, cycleTracker);
+ const auto [invalidConfigReason, _] =
+ matcher->init(matcherIndex, matcherProtos, newAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, cycleTracker);
if (invalidConfigReason.has_value()) {
return invalidConfigReason;
}
@@ -238,7 +239,7 @@
const vector<sp<ConditionTracker>>& oldConditionTrackers,
const unordered_map<int64_t, int>& newConditionTrackerMap,
const set<int64_t>& replacedMatchers, vector<UpdateStatus>& conditionsToUpdate,
- vector<bool>& cycleTracker) {
+ vector<uint8_t>& cycleTracker) {
// Have already examined this condition.
if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
return nullopt;
@@ -369,7 +370,7 @@
}
vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(conditionTrackerCount, false);
+ vector<uint8_t> cycleTracker(conditionTrackerCount, false);
for (int i = 0; i < conditionTrackerCount; i++) {
invalidConfigReason = determineConditionUpdateStatus(
config, i, oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
@@ -519,7 +520,7 @@
if (invalidConfigReason.has_value()) {
return invalidConfigReason;
}
- const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
+ const sp<MetricProducer>& oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
if (oldMetricProducer->getMetricType() != metricType ||
oldMetricProducer->getProtoHash() != metricHash) {
updateStatus = UPDATE_REPLACE;
@@ -729,6 +730,7 @@
const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
const set<int64_t>& replacedStates, const unordered_map<int64_t, int>& oldMetricProducerMap,
const vector<sp<MetricProducer>>& oldMetricProducers,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
unordered_map<int64_t, int>& newMetricProducerMap,
vector<sp<MetricProducer>>& newMetricProducers,
unordered_map<int, vector<int>>& conditionToMetricMap,
@@ -744,6 +746,12 @@
newMetricProducers.reserve(allMetricsCount);
optional<InvalidConfigReason> invalidConfigReason;
+ if (config.has_restricted_metrics_delegate_package_name() &&
+ allMetricsCount != config.event_metric_size()) {
+ ALOGE("Restricted metrics only support event metric");
+ return InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED);
+ }
+
// Construct map from metric id to metric activation index. The map will be used to determine
// the metric activation corresponding to a metric.
unordered_map<int64_t, int> metricToActivationMap;
@@ -795,7 +803,7 @@
allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
- invalidConfigReason);
+ invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -837,7 +845,7 @@
allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
- invalidConfigReason);
+ invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -878,7 +886,7 @@
initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
- invalidConfigReason);
+ invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -921,7 +929,7 @@
stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
- invalidConfigReason);
+ invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -963,7 +971,7 @@
conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -1007,7 +1015,7 @@
stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
- invalidConfigReason);
+ invalidConfigReason, configMetadataProvider);
break;
}
default: {
@@ -1058,6 +1066,15 @@
newMetricProducers[i]->prepareFirstBucket();
}
}
+
+ for (const sp<MetricProducer>& oldMetricProducer : oldMetricProducers) {
+ const auto& it = newMetricProducerMap.find(oldMetricProducer->getMetricId());
+ // Consider metric removed if it's not present in newMetricProducerMap or it's replaced.
+ if (it == newMetricProducerMap.end() ||
+ replacedMetrics.find(oldMetricProducer->getMetricId()) != replacedMetrics.end()) {
+ oldMetricProducer->onMetricRemove();
+ }
+ }
return nullopt;
}
@@ -1191,6 +1208,7 @@
const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
const unordered_map<int64_t, int>& oldAlertTrackerMap,
const map<int64_t, uint64_t>& oldStateProtoHashes,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -1252,9 +1270,10 @@
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers, conditionCache,
stateAtomIdMap, allStateGroupMaps, replacedStates, oldMetricProducerMap,
- oldMetricProducers, newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
- deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics);
+ oldMetricProducers, configMetadataProvider, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationTrackerToMetricMap, deactivationTrackerToMetricMap, metricsWithActivation,
+ replacedMetrics);
if (invalidConfigReason.has_value()) {
ALOGE("updateMetrics failed");
return invalidConfigReason;
diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.h b/statsd/src/metrics/parsing_utils/config_update_utils.h
index 3bd1e80..a8915c2 100644
--- a/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -21,6 +21,7 @@
#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "condition/ConditionTracker.h"
+#include "config/ConfigMetadataProvider.h"
#include "external/StatsPullerManager.h"
#include "matchers/AtomMatchingTracker.h"
#include "metrics/MetricProducer.h"
@@ -46,11 +47,11 @@
// [cycleTracker]: intermediate param used during recursion.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> determineMatcherUpdateStatus(
- const StatsdConfig& config, const int matcherIdx,
+ const StatsdConfig& config, int matcherIdx,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
+ std::vector<UpdateStatus>& matchersToUpdate, std::vector<uint8_t>& cycleTracker);
// Updates the AtomMatchingTrackers.
// input:
@@ -86,12 +87,12 @@
// [cycleTracker]: intermediate param used during recursion.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> determineConditionUpdateStatus(
- const StatsdConfig& config, const int conditionIdx,
+ const StatsdConfig& config, int conditionIdx,
const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
const std::unordered_map<int64_t, int>& newConditionTrackerMap,
const std::set<int64_t>& replacedMatchers, std::vector<UpdateStatus>& conditionsToUpdate,
- std::vector<bool>& cycleTracker);
+ std::vector<uint8_t>& cycleTracker);
// Updates ConditionTrackers
// input:
@@ -164,7 +165,7 @@
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> updateMetrics(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -179,6 +180,7 @@
const std::set<int64_t>& replacedStates,
const std::unordered_map<int64_t, int>& oldMetricProducerMap,
const std::vector<sp<MetricProducer>>& oldMetricProducers,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
std::unordered_map<int64_t, int>& newMetricProducerMap,
std::vector<sp<MetricProducer>>& newMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -219,7 +221,7 @@
// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> updateAlerts(
- const StatsdConfig& config, const int64_t currentTimeNs,
+ const StatsdConfig& config, int64_t currentTimeNs,
const std::unordered_map<int64_t, int>& metricProducerMap,
const std::set<int64_t>& replacedMetrics,
const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
@@ -234,7 +236,7 @@
optional<InvalidConfigReason> updateStatsdConfig(
const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, int64_t timeBaseNs,
const int64_t currentTimeNs,
const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
@@ -245,6 +247,7 @@
const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+ const wp<ConfigMetadataProvider> configMetadataProvider,
std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index be41c90..43744d3 100644
--- a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -37,6 +37,7 @@
#include "metrics/KllMetricProducer.h"
#include "metrics/MetricProducer.h"
#include "metrics/NumericValueMetricProducer.h"
+#include "metrics/RestrictedEventMetricProducer.h"
#include "state/StateManager.h"
#include "stats_util.h"
@@ -63,10 +64,75 @@
return true;
}
+// DFS for ensuring there is no
+// 1. value matching in the FieldValueMatcher tree with Position::ALL.
+// 2. string replacement in the FieldValueMatcher tree without a value matcher with Position::ANY.
+// Using vector to keep track of visited FieldValueMatchers since we expect number of
+// FieldValueMatchers to be low.
+optional<InvalidConfigReasonEnum> validateFvmPositionAllAndAny(
+ const FieldValueMatcher& fvm, bool inPositionAll, bool inPositionAny,
+ vector<FieldValueMatcher const*>& visited) {
+ visited.push_back(&fvm);
+ inPositionAll = inPositionAll || fvm.position() == Position::ALL;
+ inPositionAny = inPositionAny || fvm.position() == Position::ANY;
+ if (fvm.value_matcher_case() == FieldValueMatcher::kMatchesTuple) {
+ for (const FieldValueMatcher& childFvm : fvm.matches_tuple().field_value_matcher()) {
+ if (std::find(visited.cbegin(), visited.cend(), &childFvm) != visited.cend()) {
+ continue;
+ }
+ const optional<InvalidConfigReasonEnum> reasonEnum =
+ validateFvmPositionAllAndAny(childFvm, inPositionAll, inPositionAny, visited);
+ if (reasonEnum != nullopt) {
+ return reasonEnum;
+ }
+ }
+ return nullopt;
+ }
+ if (inPositionAll && fvm.value_matcher_case() != FieldValueMatcher::VALUE_MATCHER_NOT_SET) {
+ // value_matcher is set to something other than matches_tuple with Position::ALL
+ return INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL;
+ }
+ if (inPositionAny && fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET &&
+ fvm.has_replace_string()) {
+ // value_matcher is not set and there is a string replacement with Position::ANY
+ return INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY;
+ }
+ return nullopt;
+}
+
+optional<InvalidConfigReason> validateSimpleAtomMatcher(int64_t matcherId,
+ const SimpleAtomMatcher& simpleMatcher) {
+ for (const FieldValueMatcher& fvm : simpleMatcher.field_value_matcher()) {
+ if (fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET &&
+ !fvm.has_replace_string()) {
+ return createInvalidConfigReasonWithMatcher(
+ INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER, matcherId);
+ } else if (fvm.has_replace_string() &&
+ !(fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET ||
+ fvm.value_matcher_case() == FieldValueMatcher::kEqString ||
+ fvm.value_matcher_case() == FieldValueMatcher::kEqAnyString ||
+ fvm.value_matcher_case() == FieldValueMatcher::kNeqAnyString ||
+ fvm.value_matcher_case() == FieldValueMatcher::kEqWildcardString ||
+ fvm.value_matcher_case() == FieldValueMatcher::kEqAnyWildcardString ||
+ fvm.value_matcher_case() == FieldValueMatcher::kNeqAnyWildcardString)) {
+ return createInvalidConfigReasonWithMatcher(
+ INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE,
+ matcherId);
+ }
+ vector<FieldValueMatcher const*> visited;
+ const optional<InvalidConfigReasonEnum> reasonEnum = validateFvmPositionAllAndAny(
+ fvm, false /* inPositionAll */, false /* inPositionAny */, visited);
+ if (reasonEnum != nullopt) {
+ return createInvalidConfigReasonWithMatcher(*reasonEnum, matcherId);
+ }
+ }
+ return nullopt;
+}
+
} // namespace
sp<AtomMatchingTracker> createAtomMatchingTracker(
- const AtomMatcher& logMatcher, const int index, const sp<UidMap>& uidMap,
+ const AtomMatcher& logMatcher, const sp<UidMap>& uidMap,
optional<InvalidConfigReason>& invalidConfigReason) {
string serializedMatcher;
if (!logMatcher.SerializeToString(&serializedMatcher)) {
@@ -78,12 +144,18 @@
uint64_t protoHash = Hash64(serializedMatcher);
switch (logMatcher.contents_case()) {
case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
+ invalidConfigReason =
+ validateSimpleAtomMatcher(logMatcher.id(), logMatcher.simple_atom_matcher());
+ if (invalidConfigReason != nullopt) {
+ ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
+ return nullptr;
+ }
sp<AtomMatchingTracker> simpleAtomMatcher = new SimpleAtomMatchingTracker(
- logMatcher.id(), index, protoHash, logMatcher.simple_atom_matcher(), uidMap);
+ logMatcher.id(), protoHash, logMatcher.simple_atom_matcher(), uidMap);
return simpleAtomMatcher;
}
case AtomMatcher::ContentsCase::kCombination:
- return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
+ return new CombinationAtomMatchingTracker(logMatcher.id(), protoHash);
default:
ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
invalidConfigReason = createInvalidConfigReasonWithMatcher(
@@ -248,7 +320,7 @@
return nullopt;
}
-optional<InvalidConfigReason> handleMetricWithSampling(
+optional<InvalidConfigReason> handleMetricWithDimensionalSampling(
const int64_t metricId, const DimensionalSamplingInfo& dimSamplingInfo,
const vector<Matcher>& dimensionsInWhat, SamplingInfo& samplingInfo) {
if (!dimSamplingInfo.has_sampled_what_field()) {
@@ -437,7 +509,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
invalidConfigReason =
@@ -525,14 +598,14 @@
return nullopt;
}
- sp<MetricProducer> metricProducer =
- new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+ sp<MetricProducer> metricProducer = new CountMetricProducer(
+ key, metric, conditionIndex, initialConditionCache, wizard, metricHash, timeBaseNs,
+ currentTimeNs, configMetadataProvider, eventActivationMap, eventDeactivationMap,
+ slicedStateAtoms, stateGroupMap);
SamplingInfo samplingInfo;
if (metric.has_dimensional_sampling_info()) {
- invalidConfigReason = handleMetricWithSampling(
+ invalidConfigReason = handleMetricWithDimensionalSampling(
metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo);
if (invalidConfigReason.has_value()) {
return nullopt;
@@ -558,7 +631,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
(long long)metric.id());
@@ -619,7 +693,7 @@
}
}
- FieldMatcher internalDimensions = simplePredicate.dimensions();
+ const FieldMatcher& internalDimensions = simplePredicate.dimensions();
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -706,8 +780,8 @@
sp<MetricProducer> metricProducer = new DurationMetricProducer(
key, metric, conditionIndex, initialConditionCache, whatIndex, startIndex, stopIndex,
stopAllIndex, nesting, wizard, metricHash, internalDimensions, timeBaseNs,
- currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap);
+ currentTimeNs, configMetadataProvider, eventActivationMap, eventDeactivationMap,
+ slicedStateAtoms, stateGroupMap);
if (!metricProducer->isValid()) {
// TODO: Remove once invalidConfigReason is added to the DurationMetricProducer constructor
invalidConfigReason = InvalidConfigReason(
@@ -717,7 +791,7 @@
SamplingInfo samplingInfo;
if (metric.has_dimensional_sampling_info()) {
- invalidConfigReason = handleMetricWithSampling(
+ invalidConfigReason = handleMetricWithDimensionalSampling(
metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo);
if (invalidConfigReason.has_value()) {
return nullopt;
@@ -741,7 +815,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find the metric name or what in config");
invalidConfigReason =
@@ -773,6 +848,12 @@
}
}
+ if (metric.sampling_percentage() < 1 || metric.sampling_percentage() > 100) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE, metric.id());
+ return nullopt;
+ }
+
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
invalidConfigReason = handleMetricActivation(
@@ -788,9 +869,14 @@
return nullopt;
}
+ if (config.has_restricted_metrics_delegate_package_name()) {
+ return {new RestrictedEventMetricProducer(
+ key, metric, conditionIndex, initialConditionCache, wizard, metricHash, timeBaseNs,
+ configMetadataProvider, eventActivationMap, eventDeactivationMap)};
+ }
return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, eventActivationMap,
- eventDeactivationMap)};
+ metricHash, timeBaseNs, configMetadataProvider,
+ eventActivationMap, eventDeactivationMap)};
}
optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata(
@@ -810,7 +896,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
invalidConfigReason =
@@ -839,6 +926,27 @@
return nullopt;
}
+ std::vector<ValueMetric::AggregationType> aggregationTypes;
+ if (metric.aggregation_types_size() != 0) {
+ if (metric.has_aggregation_type()) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_VALUE_METRIC_DEFINES_SINGLE_AND_MULTIPLE_AGG_TYPES,
+ metric.id());
+ return nullopt;
+ }
+ if (metric.aggregation_types_size() != (int)fieldMatchers.size()) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_VALUE_METRIC_AGG_TYPES_DNE_VALUE_FIELDS_SIZE,
+ metric.id());
+ return nullopt;
+ }
+ for (int i = 0; i < metric.aggregation_types_size(); i++) {
+ aggregationTypes.push_back(metric.aggregation_types(i));
+ }
+ } else { // aggregation_type() is set or default is used.
+ aggregationTypes.push_back(metric.aggregation_type());
+ }
+
int trackerIndex;
invalidConfigReason = handleMetricWithAtomMatchingTrackers(
metric.what(), metric.id(), metricIndex,
@@ -848,7 +956,7 @@
return nullopt;
}
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
int atomTagId = *(atomMatcher->getAtomIds().begin());
int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
@@ -921,7 +1029,9 @@
const bool shouldUseNestedDimensions = ShouldUseNestedDimensions(metric.dimensions_in_what());
const auto [dimensionSoftLimit, dimensionHardLimit] =
- StatsdStats::getAtomDimensionKeySizeLimits(pullTagId);
+ StatsdStats::getAtomDimensionKeySizeLimits(
+ pullTagId,
+ StatsdStats::clampDimensionKeySizeLimit(metric.max_dimensions_per_bucket()));
// get the condition_correction_threshold_nanos value
const optional<int64_t> conditionCorrectionThresholdNs =
@@ -934,14 +1044,15 @@
{timeBaseNs, currentTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
conditionCorrectionThresholdNs, getAppUpgradeBucketSplit(metric)},
{containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions, trackerIndex,
- matcherWizard, metric.dimensions_in_what(), fieldMatchers},
+ matcherWizard, metric.dimensions_in_what(), fieldMatchers, aggregationTypes},
{conditionIndex, metric.links(), initialConditionCache, wizard},
{metric.state_link(), slicedStateAtoms, stateGroupMap},
- {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit});
+ {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit},
+ configMetadataProvider);
SamplingInfo samplingInfo;
if (metric.has_dimensional_sampling_info()) {
- invalidConfigReason = handleMetricWithSampling(
+ invalidConfigReason = handleMetricWithDimensionalSampling(
metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo);
if (invalidConfigReason.has_value()) {
return nullopt;
@@ -969,7 +1080,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find metric id or \"what\" in KllMetric \"%lld\"", (long long)metric.id());
invalidConfigReason =
@@ -1075,24 +1187,32 @@
const bool containsAnyPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
const bool shouldUseNestedDimensions = ShouldUseNestedDimensions(metric.dimensions_in_what());
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
const int atomTagId = *(atomMatcher->getAtomIds().begin());
const auto [dimensionSoftLimit, dimensionHardLimit] =
- StatsdStats::getAtomDimensionKeySizeLimits(atomTagId);
+ StatsdStats::getAtomDimensionKeySizeLimits(
+ atomTagId,
+ StatsdStats::clampDimensionKeySizeLimit(metric.max_dimensions_per_bucket()));
sp<MetricProducer> metricProducer = new KllMetricProducer(
key, metric, metricHash, {/*pullTagId=*/-1, pullerManager},
{timeBaseNs, currentTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
/*conditionCorrectionThresholdNs=*/nullopt, getAppUpgradeBucketSplit(metric)},
- {containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions, trackerIndex,
- matcherWizard, metric.dimensions_in_what(), fieldMatchers},
+ {containsAnyPositionInDimensionsInWhat,
+ shouldUseNestedDimensions,
+ trackerIndex,
+ matcherWizard,
+ metric.dimensions_in_what(),
+ fieldMatchers,
+ {}},
{conditionIndex, metric.links(), initialConditionCache, wizard},
{metric.state_link(), slicedStateAtoms, stateGroupMap},
- {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit});
+ {eventActivationMap, eventDeactivationMap}, {dimensionSoftLimit, dimensionHardLimit},
+ configMetadataProvider);
SamplingInfo samplingInfo;
if (metric.has_dimensional_sampling_info()) {
- invalidConfigReason = handleMetricWithSampling(
+ invalidConfigReason = handleMetricWithDimensionalSampling(
metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo);
if (invalidConfigReason.has_value()) {
return nullopt;
@@ -1118,7 +1238,8 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason) {
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
if (!metric.has_id() || !metric.has_what()) {
ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
invalidConfigReason =
@@ -1151,7 +1272,7 @@
return nullopt;
}
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
int atomTagId = *(atomMatcher->getAtomIds().begin());
int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
@@ -1178,7 +1299,7 @@
if (invalidConfigReason.has_value()) {
return nullopt;
}
- sp<AtomMatchingTracker> triggerAtomMatcher =
+ const sp<AtomMatchingTracker>& triggerAtomMatcher =
allAtomMatchingTrackers.at(triggerTrackerIndex);
triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
}
@@ -1200,6 +1321,38 @@
}
}
+ if (pullTagId != -1 && metric.sampling_percentage() != 100) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING, metric.id());
+ return nullopt;
+ }
+
+ if (metric.sampling_percentage() < 1 || metric.sampling_percentage() > 100) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE, metric.id());
+ return nullopt;
+ }
+
+ if (metric.pull_probability() < 1 || metric.pull_probability() > 100) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_METRIC_INCORRECT_PULL_PROBABILITY, metric.id());
+ return nullopt;
+ }
+
+ if (metric.pull_probability() != 100) {
+ if (pullTagId == -1) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_GAUGE_METRIC_PUSHED_WITH_PULL_PROBABILITY, metric.id());
+ return nullopt;
+ }
+ if (metric.sampling_type() == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ invalidConfigReason = InvalidConfigReason(
+ INVALID_CONFIG_REASON_GAUGE_METRIC_RANDOM_ONE_SAMPLE_WITH_PULL_PROBABILITY,
+ metric.id());
+ return nullopt;
+ }
+ }
+
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
invalidConfigReason = handleMetricActivation(
@@ -1218,19 +1371,21 @@
}
const auto [dimensionSoftLimit, dimensionHardLimit] =
- StatsdStats::getAtomDimensionKeySizeLimits(pullTagId);
+ StatsdStats::getAtomDimensionKeySizeLimits(
+ pullTagId,
+ StatsdStats::clampDimensionKeySizeLimit(metric.max_dimensions_per_bucket()));
sp<MetricProducer> metricProducer = new GaugeMetricProducer(
key, metric, conditionIndex, initialConditionCache, wizard, metricHash, trackerIndex,
matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
- pullerManager, eventActivationMap, eventDeactivationMap, dimensionSoftLimit,
- dimensionHardLimit);
+ pullerManager, configMetadataProvider, eventActivationMap, eventDeactivationMap,
+ dimensionSoftLimit, dimensionHardLimit);
SamplingInfo samplingInfo;
std::vector<Matcher> dimensionsInWhat;
translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
if (metric.has_dimensional_sampling_info()) {
- invalidConfigReason = handleMetricWithSampling(
+ invalidConfigReason = handleMetricWithDimensionalSampling(
metric.id(), metric.dimensional_sampling_info(), dimensionsInWhat, samplingInfo);
if (invalidConfigReason.has_value()) {
return nullopt;
@@ -1295,7 +1450,7 @@
for (int i = 0; i < atomMatcherCount; i++) {
const AtomMatcher& logMatcher = config.atom_matcher(i);
sp<AtomMatchingTracker> tracker =
- createAtomMatchingTracker(logMatcher, i, uidMap, invalidConfigReason);
+ createAtomMatchingTracker(logMatcher, uidMap, invalidConfigReason);
if (tracker == nullptr) {
return invalidConfigReason;
}
@@ -1309,11 +1464,12 @@
matcherConfigs.push_back(logMatcher);
}
- vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false);
+ vector<uint8_t> stackTracker2(allAtomMatchingTrackers.size(), false);
for (size_t matcherIndex = 0; matcherIndex < allAtomMatchingTrackers.size(); matcherIndex++) {
auto& matcher = allAtomMatchingTrackers[matcherIndex];
- invalidConfigReason = matcher->init(matcherConfigs, allAtomMatchingTrackers,
- atomMatchingTrackerMap, stackTracker2);
+ const auto [invalidConfigReason, _] =
+ matcher->init(matcherIndex, matcherConfigs, allAtomMatchingTrackers,
+ atomMatchingTrackerMap, stackTracker2);
if (invalidConfigReason.has_value()) {
return invalidConfigReason;
}
@@ -1370,7 +1526,7 @@
conditionConfigs.push_back(condition);
}
- vector<bool> stackTracker(allConditionTrackers.size(), false);
+ vector<uint8_t> stackTracker(allConditionTrackers.size(), false);
for (size_t i = 0; i < allConditionTrackers.size(); i++) {
auto& conditionTracker = allConditionTrackers[i];
invalidConfigReason =
@@ -1405,8 +1561,8 @@
stateProtoHashes[stateId] = Hash64(serializedState);
const StateMap& stateMap = state.map();
- for (auto group : stateMap.group()) {
- for (auto value : group.value()) {
+ for (const auto& group : stateMap.group()) {
+ for (const auto& value : group.value()) {
allStateGroupMaps[stateId][value] = group.group_id();
}
}
@@ -1431,7 +1587,8 @@
std::set<int64_t>& noReportMetricIds,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
+ vector<int>& metricsWithActivation,
+ const wp<ConfigMetadataProvider> configMetadataProvider) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
@@ -1440,6 +1597,12 @@
allMetricProducers.reserve(allMetricsCount);
optional<InvalidConfigReason> invalidConfigReason;
+ if (config.has_restricted_metrics_delegate_package_name() &&
+ allMetricsCount != config.event_metric_size()) {
+ ALOGE("Restricted metrics only support event metric");
+ return InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED);
+ }
+
// Construct map from metric id to metric activation index. The map will be used to determine
// the metric activation corresponding to a metric.
unordered_map<int64_t, int> metricToActivationMap;
@@ -1466,7 +1629,7 @@
conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1485,7 +1648,7 @@
conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1502,7 +1665,8 @@
atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, invalidConfigReason);
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, invalidConfigReason,
+ configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1520,7 +1684,7 @@
conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1538,7 +1702,7 @@
conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1556,7 +1720,7 @@
conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, invalidConfigReason);
+ metricsWithActivation, invalidConfigReason, configMetadataProvider);
if (!producer) {
return invalidConfigReason;
}
@@ -1644,7 +1808,7 @@
const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor,
const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs,
+ const int64_t currentTimeNs, const wp<ConfigMetadataProvider> configMetadataProvider,
std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
unordered_map<int64_t, int>& atomMatchingTrackerMap,
@@ -1700,7 +1864,7 @@
allConditionTrackers, initialConditionCache, allMetricProducers, conditionToMetricMap,
trackerToMetricMap, metricProducerMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
+ metricsWithActivation, configMetadataProvider);
if (invalidConfigReason.has_value()) {
ALOGE("initMetricProducers failed");
return invalidConfigReason;
diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index c4e231a..8a73ff0 100644
--- a/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -22,6 +22,7 @@
#include "anomaly/AlarmTracker.h"
#include "condition/ConditionTracker.h"
+#include "config/ConfigMetadataProvider.h"
#include "external/StatsPullerManager.h"
#include "matchers/AtomMatchingTracker.h"
#include "metrics/MetricProducer.h"
@@ -36,12 +37,11 @@
// Create a AtomMatchingTracker.
// input:
// [logMatcher]: the input AtomMatcher from the StatsdConfig
-// [index]: the index of the matcher
// [invalidConfigReason]: logging ids if config is invalid
// output:
// new AtomMatchingTracker, or null if the tracker is unable to be created
sp<AtomMatchingTracker> createAtomMatchingTracker(
- const AtomMatcher& logMatcher, const int index, const sp<UidMap>& uidMap,
+ const AtomMatcher& logMatcher, const sp<UidMap>& uidMap,
optional<InvalidConfigReason>& invalidConfigReason);
// Create a ConditionTracker.
@@ -53,21 +53,20 @@
// output:
// new ConditionTracker, or null if the tracker is unable to be created
sp<ConditionTracker> createConditionTracker(
- const ConfigKey& key, const Predicate& predicate, const int index,
+ const ConfigKey& key, const Predicate& predicate, int index,
const unordered_map<int64_t, int>& atomMatchingTrackerMap,
optional<InvalidConfigReason>& invalidConfigReason);
// Get the hash of a metric, combining the activation if the metric has one.
optional<InvalidConfigReason> getMetricProtoHash(
- const StatsdConfig& config, const google::protobuf::MessageLite& metric, const int64_t id,
+ const StatsdConfig& config, const google::protobuf::MessageLite& metric, int64_t id,
const std::unordered_map<int64_t, int>& metricToActivationMap, uint64_t& metricHash);
// 1. Validates matcher existence
// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom
// 3. Gets matcher index and updates tracker to metric map
optional<InvalidConfigReason> handleMetricWithAtomMatchingTrackers(
- const int64_t matcherId, const int64_t metricId, const int metricIndex,
- const bool enforceOneAtom,
+ const int64_t matcherId, int64_t metricId, int metricIndex, const bool enforceOneAtom,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex);
@@ -75,7 +74,7 @@
// 1. Validates condition existence, including those in links
// 2. Gets condition index and updates condition to metric map
optional<InvalidConfigReason> handleMetricWithConditions(
- const int64_t condition, const int64_t metricId, const int metricIndex,
+ const int64_t condition, int64_t metricId, int metricIndex,
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& links,
const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
@@ -85,7 +84,7 @@
// Fills the new event activation/deactivation maps, preserving the existing activations.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> handleMetricActivationOnConfigUpdate(
- const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const StatsdConfig& config, int64_t metricId, int metricIndex,
const std::unordered_map<int64_t, int>& metricToActivationMap,
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -99,8 +98,8 @@
// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+ const int64_t currentTimeNs, const CountMetric& metric, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -113,14 +112,14 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- optional<InvalidConfigReason>& invalidConfigReason);
+ std::vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+ const int64_t currentTimeNs, const DurationMetric& metric, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -133,14 +132,14 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- optional<InvalidConfigReason>& invalidConfigReason);
+ std::vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const EventMetric& metric, const int metricIndex,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+ const EventMetric& metric, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -151,15 +150,15 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- optional<InvalidConfigReason>& invalidConfigReason);
+ std::vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates a NumericValueMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createNumericValueMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const ValueMetric& metric, const int metricIndex,
+ const ValueMetric& metric, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -173,15 +172,15 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- optional<InvalidConfigReason>& invalidConfigReason);
+ std::vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const GaugeMetric& metric, const int metricIndex,
+ const GaugeMetric& metric, int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -193,15 +192,15 @@
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- optional<InvalidConfigReason>& invalidConfigReason);
+ std::vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates a KllMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createKllMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const KllMetric& metric, const int metricIndex,
+ const KllMetric& metric, int metricIndex,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const unordered_map<int64_t, int>& atomMatchingTrackerMap,
vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -215,13 +214,14 @@
unordered_map<int, vector<int>>& conditionToMetricMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason);
+ vector<int>& metricsWithActivation, optional<InvalidConfigReason>& invalidConfigReason,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Creates an AnomalyTracker and adds it to the appropriate metric.
// Returns an sp to the AnomalyTracker, or nullopt if there was an error.
optional<sp<AnomalyTracker>> createAnomalyTracker(
const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const UpdateStatus& updateStatus, const int64_t currentTimeNs,
+ const UpdateStatus& updateStatus, int64_t currentTimeNs,
const std::unordered_map<int64_t, int>& metricProducerMap,
std::vector<sp<MetricProducer>>& allMetricProducers,
optional<InvalidConfigReason>& invalidConfigReason);
@@ -340,7 +340,7 @@
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
// Returns nullopt if successful and InvalidConfigReason if not.
optional<InvalidConfigReason> initMetrics(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+ const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseTimeNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
const std::unordered_map<int64_t, int>& conditionTrackerMap,
@@ -355,13 +355,14 @@
std::set<int64_t>& noReportMetricIds,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
+ std::vector<int>& metricsWithActivation,
+ const wp<ConfigMetadataProvider> configMetadataProvider);
// Initialize alarms
// Is called both on initialize new configs and config updates since alarms do not have any state.
optional<InvalidConfigReason> initAlarms(const StatsdConfig& config, const ConfigKey& key,
const sp<AlarmMonitor>& periodicAlarmMonitor,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
+ const int64_t timeBaseNs, int64_t currentTimeNs,
std::vector<sp<AlarmTracker>>& allAlarmTrackers);
// Initialize MetricsManager from StatsdConfig.
@@ -369,8 +370,8 @@
optional<InvalidConfigReason> initStatsdConfig(
const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs,
+ const sp<AlarmMonitor>& periodicAlarmMonitor, int64_t timeBaseNs,
+ const int64_t currentTimeNs, const wp<ConfigMetadataProvider> configMetadataProvider,
std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
diff --git a/statsd/src/packages/PackageInfoListener.h b/statsd/src/packages/PackageInfoListener.h
index 485f9fd..d796774 100644
--- a/statsd/src/packages/PackageInfoListener.h
+++ b/statsd/src/packages/PackageInfoListener.h
@@ -29,15 +29,14 @@
public:
// Uid map will notify this listener that the app with apk name and uid has been upgraded to
// the specified version.
- virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const std::string& apk,
- const int uid, const int64_t version) = 0;
+ virtual void notifyAppUpgrade(int64_t eventTimeNs, const std::string& apk, const int uid,
+ int64_t version) = 0;
// Notify interested listeners that the given apk and uid combination no longer exits.
- virtual void notifyAppRemoved(const int64_t& eventTimeNs, const std::string& apk,
- const int uid) = 0;
+ virtual void notifyAppRemoved(int64_t eventTimeNs, const std::string& apk, const int uid) = 0;
// Notify the listener that the UidMap snapshot is available.
- virtual void onUidMapReceived(const int64_t& eventTimeNs) = 0;
+ virtual void onUidMapReceived(int64_t eventTimeNs) = 0;
};
} // namespace statsd
diff --git a/statsd/src/packages/UidMap.cpp b/statsd/src/packages/UidMap.cpp
index aa43e8d..79e8534 100644
--- a/statsd/src/packages/UidMap.cpp
+++ b/statsd/src/packages/UidMap.cpp
@@ -25,7 +25,6 @@
using namespace android;
-using android::base::StringPrintf;
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_BYTES;
@@ -94,12 +93,12 @@
return normalizedName;
}
-std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const {
+std::set<string> UidMap::getAppNamesFromUid(const int32_t uid, bool returnNormalized) const {
lock_guard<mutex> lock(mMutex);
return getAppNamesFromUidLocked(uid,returnNormalized);
}
-std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
+std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t uid, bool returnNormalized) const {
std::set<string> names;
for (const auto& kv : mMap) {
if (kv.first.first == uid && !kv.second.deleted) {
@@ -119,7 +118,7 @@
return it->second.versionCode;
}
-void UidMap::updateMap(const int64_t& timestamp, const UidData& uidData) {
+void UidMap::updateMap(const int64_t timestamp, const UidData& uidData) {
wp<PackageInfoListener> broadcast = NULL;
{
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
@@ -157,13 +156,13 @@
// itself before we call it. It's then the listener's job to handle it (expect the callback to
// be called after listener is removed, and the listener should properly ignore it).
auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
+ if (strongPtr != nullptr) {
strongPtr->onUidMapReceived(timestamp);
}
}
-void UidMap::updateApp(const int64_t& timestamp, const string& appName, const int32_t& uid,
- const int64_t& versionCode, const string& versionString,
+void UidMap::updateApp(const int64_t timestamp, const string& appName, const int32_t uid,
+ const int64_t versionCode, const string& versionString,
const string& installer, const vector<uint8_t>& certificateHash) {
wp<PackageInfoListener> broadcast = NULL;
@@ -202,7 +201,7 @@
}
auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
+ if (strongPtr != nullptr) {
strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
}
}
@@ -224,7 +223,7 @@
}
}
-void UidMap::removeApp(const int64_t& timestamp, const string& app, const int32_t& uid) {
+void UidMap::removeApp(const int64_t timestamp, const string& app, const int32_t uid) {
wp<PackageInfoListener> broadcast = NULL;
{
lock_guard<mutex> lock(mMutex);
@@ -255,12 +254,12 @@
}
auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
+ if (strongPtr != nullptr) {
strongPtr->notifyAppRemoved(timestamp, app, uid);
}
}
-void UidMap::setListener(wp<PackageInfoListener> listener) {
+void UidMap::setListener(const wp<PackageInfoListener>& listener) {
lock_guard<mutex> lock(mMutex); // Lock for updates
mSubscriber = listener;
}
@@ -409,7 +408,7 @@
}
}
-void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+void UidMap::appendUidMap(const int64_t timestamp, const ConfigKey& key,
const bool includeVersionStrings, const bool includeInstaller,
const uint8_t truncatedCertificateHashSize, std::set<string>* str_set,
ProtoOutputStream* proto) {
@@ -639,6 +638,7 @@
{"AID_SDK_SANDBOX", 1090},
{"AID_SECURITY_LOG_WRITER", 1091},
{"AID_PRNG_SEEDER", 1092},
+ {"AID_UPROBESTATS", 1093},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002},
diff --git a/statsd/src/packages/UidMap.h b/statsd/src/packages/UidMap.h
index 72330f5..45429cb 100644
--- a/statsd/src/packages/UidMap.h
+++ b/statsd/src/packages/UidMap.h
@@ -73,9 +73,9 @@
const string versionString;
const string prevVersionString;
- ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
- const int32_t uid, const int64_t version, const string versionString,
- const int64_t prevVersion, const string prevVersionString)
+ ChangeRecord(const bool isDeletion, int64_t timestampNs, const string& package,
+ const int32_t uid, int64_t version, const string& versionString,
+ const int64_t prevVersion, const string& prevVersionString)
: deletion(isDeletion),
timestampNs(timestampNs),
package(package),
@@ -99,18 +99,18 @@
static sp<UidMap> getInstance();
- void updateMap(const int64_t& timestamp, const UidData& uidData);
+ void updateMap(const int64_t timestamp, const UidData& uidData);
- void updateApp(const int64_t& timestamp, const string& appName, const int32_t& uid,
- const int64_t& versionCode, const string& versionString, const string& installer,
+ void updateApp(const int64_t timestamp, const string& appName, const int32_t uid,
+ const int64_t versionCode, const string& versionString, const string& installer,
const vector<uint8_t>& certificateHash);
- void removeApp(const int64_t& timestamp, const string& app, const int32_t& uid);
+ void removeApp(const int64_t timestamp, const string& app, const int32_t uid);
// Returns true if the given uid contains the specified app (eg. com.google.android.gms).
bool hasApp(int uid, const string& packageName) const;
// Returns the app names from uid.
- std::set<string> getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const;
+ std::set<string> getAppNamesFromUid(int32_t uid, bool returnNormalized) const;
int64_t getAppVersion(int uid, const string& packageName) const;
@@ -121,7 +121,7 @@
// Command for indicating to the map that StatsLogProcessor should be notified if an app is
// updated. This allows metric producers and managers to distinguish when the same uid or app
// represents a different version of an app.
- void setListener(wp<PackageInfoListener> listener);
+ void setListener(const wp<PackageInfoListener>& listener);
// Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
void OnConfigUpdated(const ConfigKey& key);
@@ -138,10 +138,9 @@
// Gets all snapshots and changes that have occurred since the last output.
// If every config key has received a change or snapshot record, then this
// record is deleted.
- void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
- const bool includeVersionStrings, const bool includeInstaller,
- const uint8_t truncatedCertificateHashSize, std::set<string>* str_set,
- ProtoOutputStream* proto);
+ void appendUidMap(int64_t timestamp, const ConfigKey& key, const bool includeVersionStrings,
+ const bool includeInstaller, const uint8_t truncatedCertificateHashSize,
+ std::set<string>* str_set, ProtoOutputStream* proto);
// Forces the output to be cleared. We still generate a snapshot based on the current state.
// This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -165,7 +164,7 @@
ProtoOutputStream* proto) const;
private:
- std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
+ std::set<string> getAppNamesFromUidLocked(int32_t uid, bool returnNormalized) const;
string normalizeAppName(const string& appName) const;
void writeUidMapSnapshotLocked(const int64_t timestamp, const bool includeVersionStrings,
@@ -179,7 +178,7 @@
mutable mutex mIsolatedMutex;
struct PairHash {
- size_t operator()(std::pair<int, string> p) const noexcept {
+ size_t operator()(const std::pair<int, string>& p) const noexcept {
std::hash<std::string> hash_fn;
return hash_fn(std::to_string(p.first) + p.second);
}
@@ -221,6 +220,9 @@
size_t mBytesUsed;
// Allows unit-test to access private methods.
+ FRIEND_TEST(RestrictedEventMetricE2eTest, TestRestrictedConfigUpdateDoesNotUpdateUidMap);
+ FRIEND_TEST(RestrictedEventMetricE2eTest,
+ TestRestrictedConfigUpdateAddsDelegateRemovesUidMapEntry);
FRIEND_TEST(UidMapTest, TestClearingOutput);
FRIEND_TEST(UidMapTest, TestRemovedAppRetained);
FRIEND_TEST(UidMapTest, TestRemovedAppOverGuardrail);
diff --git a/statsd/src/shell/ShellSubscriber.cpp b/statsd/src/shell/ShellSubscriber.cpp
index 0a21c9a..56c3ccd 100644
--- a/statsd/src/shell/ShellSubscriber.cpp
+++ b/statsd/src/shell/ShellSubscriber.cpp
@@ -24,6 +24,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_log_util.h"
+#include "utils/api_tracing.h"
using aidl::android::os::IStatsSubscriptionCallback;
@@ -126,10 +127,15 @@
}
void ShellSubscriber::onLogEvent(const LogEvent& event) {
+ ATRACE_CALL();
// Skip if event is skipped
if (event.isParsedHeaderOnly()) {
return;
}
+ // Skip RestrictedLogEvents
+ if (event.isRestricted()) {
+ return;
+ }
std::unique_lock<std::mutex> lock(mMutex);
for (auto clientIt = mClientSet.begin(); clientIt != mClientSet.end();) {
(*clientIt)->onLogEvent(event);
@@ -193,9 +199,6 @@
void ShellSubscriber::updateLogEventFilterLocked() const {
VLOG("ShellSubscriber: Updating allAtomIds");
- if (!mLogEventFilter) {
- return;
- }
LogEventFilter::AtomIdSet allAtomIds;
for (const auto& client : mClientSet) {
client->addAllAtomIds(allAtomIds);
diff --git a/statsd/src/shell/ShellSubscriber.h b/statsd/src/shell/ShellSubscriber.h
index 4ff8861..5e54fbf 100644
--- a/statsd/src/shell/ShellSubscriber.h
+++ b/statsd/src/shell/ShellSubscriber.h
@@ -56,7 +56,7 @@
*/
class ShellSubscriber : public virtual RefBase {
public:
- ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr,
+ ShellSubscriber(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerMgr,
const std::shared_ptr<LogEventFilter>& logEventFilter)
: mUidMap(uidMap), mPullerMgr(pullerMgr), mLogEventFilter(logEventFilter){};
diff --git a/statsd/src/shell/ShellSubscriberClient.cpp b/statsd/src/shell/ShellSubscriberClient.cpp
index aaa4126..67f7eb1 100644
--- a/statsd/src/shell/ShellSubscriberClient.cpp
+++ b/statsd/src/shell/ShellSubscriberClient.cpp
@@ -24,7 +24,6 @@
#include "stats_log_util.h"
using android::base::unique_fd;
-using android::util::ProtoOutputStream;
using Status = ::ndk::ScopedAStatus;
namespace android {
@@ -177,23 +176,25 @@
bool ShellSubscriberClient::writeEventToProtoIfMatched(const LogEvent& event,
const SimpleAtomMatcher& matcher,
const sp<UidMap>& uidMap) {
- if (!matchesSimple(uidMap, matcher, event)) {
+ auto [matched, transformedEvent] = matchesSimple(mUidMap, matcher, event);
+ if (!matched) {
return false;
}
+ const LogEvent& eventRef = transformedEvent == nullptr ? event : *transformedEvent;
// Cache atom event in mProtoOut.
uint64_t atomToken = mProtoOut.start(util::FIELD_TYPE_MESSAGE | util::FIELD_COUNT_REPEATED |
FIELD_ID_SHELL_DATA__ATOM);
- event.ToProto(mProtoOut);
+ eventRef.ToProto(mProtoOut);
mProtoOut.end(atomToken);
- const int64_t timestampNs = truncateTimestampIfNecessary(event);
+ const int64_t timestampNs = truncateTimestampIfNecessary(eventRef);
mProtoOut.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED |
FIELD_ID_SHELL_DATA__ELAPSED_TIMESTAMP_NANOS,
static_cast<long long>(timestampNs));
// Update byte size of cached data.
- mCacheSize += getSize(event.getValues()) + sizeof(timestampNs);
+ mCacheSize += getSize(eventRef.getValues()) + sizeof(timestampNs);
return true;
}
diff --git a/statsd/src/shell/ShellSubscriberClient.h b/statsd/src/shell/ShellSubscriberClient.h
index 2aab6eb..9d1724a 100644
--- a/statsd/src/shell/ShellSubscriberClient.h
+++ b/statsd/src/shell/ShellSubscriberClient.h
@@ -94,14 +94,13 @@
return kMaxSizeKb;
}
+ void addAllAtomIds(LogEventFilter::AtomIdSet& allAtomIds) const;
+
// Minimum pull interval for callback subscriptions.
static constexpr int64_t kMinCallbackPullIntervalMs = 60'000; // 60 seconds.
// Minimum sleep for the pull thread for callback subscriptions.
static constexpr int64_t kMinCallbackSleepIntervalMs = 2000; // 2 seconds.
-
- void addAllAtomIds(LogEventFilter::AtomIdSet& allAtomIds) const;
-
private:
int64_t pullIfNeeded(int64_t nowSecs, int64_t nowMillis, int64_t nowNanos);
diff --git a/statsd/src/socket/LogEventFilter.h b/statsd/src/socket/LogEventFilter.h
index aec91ef..941e359 100644
--- a/statsd/src/socket/LogEventFilter.h
+++ b/statsd/src/socket/LogEventFilter.h
@@ -114,15 +114,12 @@
FRIEND_TEST(LogEventFilterTest, TestEmptyFilter);
FRIEND_TEST(LogEventFilterTest, TestRemoveNonExistingEmptyFilter);
FRIEND_TEST(LogEventFilterTest, TestEmptyFilterDisabled);
- FRIEND_TEST(LogEventFilterTest, TestEmptyFilterDisabledSetter);
FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterFullOverlap);
FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterPartialOverlap);
FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterDisabled);
FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterDisabledPartialOverlap);
FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerOverlapIds);
- FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerNonOverlapIds);
FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerOverlapIdsRemoved);
- FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerNonOverlapIdsRemoved);
FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerEmptyFilter);
};
diff --git a/statsd/src/socket/StatsSocketListener.cpp b/statsd/src/socket/StatsSocketListener.cpp
index 5873df3..7f0eb43 100644
--- a/statsd/src/socket/StatsSocketListener.cpp
+++ b/statsd/src/socket/StatsSocketListener.cpp
@@ -16,7 +16,10 @@
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
+#include "StatsSocketListener.h"
+
#include <ctype.h>
+#include <cutils/sockets.h>
#include <limits.h>
#include <stdio.h>
#include <sys/cdefs.h>
@@ -26,24 +29,25 @@
#include <sys/un.h>
#include <unistd.h>
-#include <cutils/sockets.h>
-
-#include "StatsSocketListener.h"
#include "guardrail/StatsdStats.h"
+#include "logd/logevent_util.h"
#include "stats_log_util.h"
+#include "statslog_statsd.h"
+#include "utils/api_tracing.h"
namespace android {
namespace os {
namespace statsd {
-StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue,
+StatsSocketListener::StatsSocketListener(const std::shared_ptr<LogEventQueue>& queue,
const std::shared_ptr<LogEventFilter>& logEventFilter)
: SocketListener(getLogSocket(), false /*start listen*/),
- mQueue(std::move(queue)),
+ mQueue(queue),
mLogEventFilter(logEventFilter) {
}
bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
+ ATRACE_CALL();
static bool name_set;
if (!name_set) {
prctl(PR_SET_NAME, "statsd.writer");
@@ -59,7 +63,7 @@
NULL, 0, &iov, 1, control, sizeof(control), 0,
};
- int socket = cli->getSocket();
+ const int socket = cli->getSocket();
// To clear the entire buffer is secure/safe, but this contributes to 1.68%
// overhead under logging load. We are safe because we check counts, but
@@ -90,8 +94,26 @@
cred->uid = DEFAULT_OVERFLOWUID;
}
- uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
- n -= sizeof(android_log_header_t);
+ const uint32_t uid = cred->uid;
+ const uint32_t pid = cred->pid;
+
+ processSocketMessage(buffer, n, uid, pid, *mQueue, *mLogEventFilter);
+
+ return true;
+}
+
+void StatsSocketListener::processSocketMessage(const char* buffer, const uint32_t len, uint32_t uid,
+ uint32_t pid, LogEventQueue& queue,
+ const LogEventFilter& filter) {
+ ATRACE_CALL();
+ static const uint32_t kStatsEventTag = 1937006964;
+
+ if (len <= (ssize_t)(sizeof(android_log_header_t)) + sizeof(uint32_t)) {
+ return;
+ }
+
+ const uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
+ uint32_t bufferLen = len - sizeof(android_log_header_t);
// When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would
// be sent to statsd when the socket communication becomes available again.
@@ -100,8 +122,9 @@
// Note that all normal stats logs are in the format of event_list, so there won't be confusion.
//
// TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config.
- if (n == sizeof(android_log_event_long_t)) {
- android_log_event_long_t* long_event = reinterpret_cast<android_log_event_long_t*>(ptr);
+ if (bufferLen == sizeof(android_log_event_long_t)) {
+ const android_log_event_long_t* long_event =
+ reinterpret_cast<const android_log_event_long_t*>(ptr);
if (long_event->payload.type == EVENT_TYPE_LONG) {
int64_t composed_long = long_event->payload.data;
@@ -111,33 +134,35 @@
int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32);
ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count,
- long_event->header.tag, last_atom_tag, cred->uid);
+ long_event->header.tag, last_atom_tag, uid);
StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count,
- long_event->header.tag, last_atom_tag, cred->uid,
- cred->pid);
- return true;
+ long_event->header.tag, last_atom_tag, uid, pid);
+ return;
}
}
+ // test that received valid StatsEvent buffer
+ const uint32_t statsEventTag = *reinterpret_cast<const uint32_t*>(ptr);
+ if (statsEventTag != kStatsEventTag) {
+ return;
+ }
+
// move past the 4-byte StatsEventTag
const uint8_t* msg = ptr + sizeof(uint32_t);
- const uint32_t len = n - sizeof(uint32_t);
- const uint32_t uid = cred->uid;
- const uint32_t pid = cred->pid;
+ bufferLen -= sizeof(uint32_t);
- processMessage(msg, len, uid, pid, mQueue, mLogEventFilter);
-
- return true;
+ processStatsEventBuffer(msg, bufferLen, uid, pid, queue, filter);
}
-void StatsSocketListener::processMessage(const uint8_t* msg, uint32_t len, uint32_t uid,
- uint32_t pid, const std::shared_ptr<LogEventQueue>& queue,
- const std::shared_ptr<LogEventFilter>& filter) {
+void StatsSocketListener::processStatsEventBuffer(const uint8_t* msg, const uint32_t len,
+ uint32_t uid, uint32_t pid, LogEventQueue& queue,
+ const LogEventFilter& filter) {
+ ATRACE_CALL();
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
- if (filter && filter->getFilteringEnabled()) {
+ if (filter.getFilteringEnabled()) {
const LogEvent::BodyBufferInfo bodyInfo = logEvent->parseHeader(msg, len);
- if (filter->isAtomInUse(logEvent->GetTagId())) {
+ if (filter.isAtomInUse(logEvent->GetTagId())) {
logEvent->parseBody(bodyInfo);
}
} else {
@@ -146,8 +171,27 @@
const int32_t atomId = logEvent->GetTagId();
const bool isAtomSkipped = logEvent->isParsedHeaderOnly();
- int64_t oldestTimestamp;
- if (!queue->push(std::move(logEvent), &oldestTimestamp)) {
+ const int64_t atomTimestamp = logEvent->GetElapsedTimestampNs();
+
+ if (atomId == util::STATS_SOCKET_LOSS_REPORTED) {
+ if (isAtomSkipped) {
+ ALOGW("Atom STATS_SOCKET_LOSS_REPORTED should not be skipped");
+ }
+
+ // handling socket loss info reported atom
+ // processing it here to not lose info due to queue overflow
+ const std::optional<SocketLossInfo>& lossInfo = toSocketLossInfo(*logEvent);
+ if (lossInfo) {
+ StatsdStats::getInstance().noteAtomSocketLoss(*lossInfo);
+ } else {
+ ALOGW("Atom STATS_SOCKET_LOSS_REPORTED content is invalid");
+ }
+ }
+
+ const auto [success, oldestTimestamp, queueSize] = queue.push(std::move(logEvent));
+ if (success) {
+ StatsdStats::getInstance().noteEventQueueSize(queueSize, atomTimestamp);
+ } else {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp, atomId, isAtomSkipped);
}
}
diff --git a/statsd/src/socket/StatsSocketListener.h b/statsd/src/socket/StatsSocketListener.h
index 74a338e..2142e9e 100644
--- a/statsd/src/socket/StatsSocketListener.h
+++ b/statsd/src/socket/StatsSocketListener.h
@@ -38,7 +38,7 @@
class StatsSocketListener : public SocketListener, public virtual RefBase {
public:
- explicit StatsSocketListener(std::shared_ptr<LogEventQueue> queue,
+ explicit StatsSocketListener(const std::shared_ptr<LogEventQueue>& queue,
const std::shared_ptr<LogEventFilter>& logEventFilter);
virtual ~StatsSocketListener() = default;
@@ -50,6 +50,21 @@
static int getLogSocket();
/**
+ * @brief Helper API to parse raw socket data buffer, make the LogEvent & submit it into the
+ * queue. Performs preliminary data validation.
+ * Created as a separate API to be easily tested without StatsSocketListener instance
+ *
+ * @param buffer buffer to parse
+ * @param len size of buffer in bytes
+ * @param uid arguments for LogEvent constructor
+ * @param pid arguments for LogEvent constructor
+ * @param queue queue to submit the event
+ * @param filter to be used for event evaluation
+ */
+ static void processSocketMessage(const char* buffer, uint32_t len, uint32_t uid, uint32_t pid,
+ LogEventQueue& queue, const LogEventFilter& filter);
+
+ /**
* @brief Helper API to parse buffer, make the LogEvent & submit it into the queue
* Created as a separate API to be easily tested without StatsSocketListener instance
*
@@ -60,9 +75,9 @@
* @param queue queue to submit the event
* @param filter to be used for event evaluation
*/
- static void processMessage(const uint8_t* msg, uint32_t len, uint32_t uid, uint32_t pid,
- const std::shared_ptr<LogEventQueue>& queue,
- const std::shared_ptr<LogEventFilter>& filter);
+ static void processStatsEventBuffer(const uint8_t* msg, uint32_t len, uint32_t uid,
+ uint32_t pid, LogEventQueue& queue,
+ const LogEventFilter& filter);
/**
* Who is going to get the events when they're read.
@@ -71,18 +86,18 @@
std::shared_ptr<LogEventFilter> mLogEventFilter;
- friend class SocketParseMessageTest;
- friend void generateAtomLogging(const std::shared_ptr<LogEventQueue>& queue,
- const std::shared_ptr<LogEventFilter>& filter, int eventCount,
- int startAtomId);
+ friend void fuzzSocket(const uint8_t* data, size_t size);
- FRIEND_TEST(SocketParseMessageTestNoFiltering, TestProcessMessageNoFiltering);
- FRIEND_TEST(SocketParseMessageTestNoFiltering,
- TestProcessMessageNoFilteringWithEmptySetExplicitSet);
- FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterEmptySet);
+ friend class SocketParseMessageTest;
+ friend void generateAtomLogging(LogEventQueue& queue, const LogEventFilter& filter,
+ int eventCount, int startAtomId);
+
+ FRIEND_TEST(SocketParseMessageTest, TestProcessMessage);
+ FRIEND_TEST(SocketParseMessageTest, TestProcessMessageEmptySetExplicitSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterCompleteSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterPartialSet);
FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterToggle);
+ FRIEND_TEST(LogEventQueue_test, TestQueueMaxSize);
};
} // namespace statsd
diff --git a/statsd/src/state/StateManager.cpp b/statsd/src/state/StateManager.cpp
index e75bd97..1a32495 100644
--- a/statsd/src/state/StateManager.cpp
+++ b/statsd/src/state/StateManager.cpp
@@ -43,8 +43,9 @@
void StateManager::onLogEvent(const LogEvent& event) {
// Only process state events from uids in AID_* and packages that are whitelisted in
// mAllowedPkg.
- // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000)
- if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) ||
+ // Allowlisted AIDs are AID_ROOT and all AIDs in [1000, 2000) which is [AID_SYSTEM, AID_SHELL)
+ if (event.GetUid() == AID_ROOT ||
+ (event.GetUid() >= AID_SYSTEM && event.GetUid() < AID_SHELL) ||
mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) {
if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
mStateTrackers[event.GetTagId()]->onLogEvent(event);
@@ -52,7 +53,7 @@
}
}
-void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::registerListener(const int32_t atomId, const wp<StateListener>& listener) {
// Check if state tracker already exists.
if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
mStateTrackers[atomId] = new StateTracker(atomId);
@@ -60,7 +61,7 @@
mStateTrackers[atomId]->registerListener(listener);
}
-void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(const int32_t atomId, const wp<StateListener>& listener) {
std::unique_lock<std::mutex> lock(mMutex);
// Hold the sp<> until the lock is released so that ~StateTracker() is
diff --git a/statsd/src/state/StateManager.h b/statsd/src/state/StateManager.h
index 68b5c90..2db206e 100644
--- a/statsd/src/state/StateManager.h
+++ b/statsd/src/state/StateManager.h
@@ -55,17 +55,17 @@
// If the correct StateTracker does not exist, a new StateTracker is created.
// Note: StateTrackers can be created for non-state atoms. They are essentially empty and
// do not perform any actions.
- void registerListener(const int32_t atomId, wp<StateListener> listener);
+ void registerListener(const int32_t atomId, const wp<StateListener>& listener);
// Notifies the correct StateTracker to unregister a listener
// and removes the tracker if it no longer has any listeners.
- void unregisterListener(const int32_t atomId, wp<StateListener> listener);
+ void unregisterListener(const int32_t atomId, const wp<StateListener>& listener);
// Returns true if the StateTracker exists and queries for the
// original state value mapped to the given query key. The state value is
// stored and output in a FieldValue class.
// Returns false if the StateTracker doesn't exist.
- bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ bool getStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
FieldValue* output) const;
// Updates mAllowedLogSources with the latest uids for the packages that are allowed to log.
@@ -77,7 +77,7 @@
return mStateTrackers.size();
}
- inline int getListenersCount(const int32_t atomId) const {
+ inline int getListenersCount(int32_t atomId) const {
auto it = mStateTrackers.find(atomId);
if (it != mStateTrackers.end()) {
return it->second->getListenersCount();
diff --git a/statsd/src/state/StateTracker.cpp b/statsd/src/state/StateTracker.cpp
index 0feba42..d037e73 100644
--- a/statsd/src/state/StateTracker.cpp
+++ b/statsd/src/state/StateTracker.cpp
@@ -25,7 +25,7 @@
namespace os {
namespace statsd {
-StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
+StateTracker::StateTracker(int32_t atomId) : mField(atomId, 0) {
}
void StateTracker::onLogEvent(const LogEvent& event) {
@@ -59,15 +59,14 @@
}
const bool nested = newState.mAnnotations.isNested();
- StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
- updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, mStateMap[primaryKey]);
}
-void StateTracker::registerListener(wp<StateListener> listener) {
+void StateTracker::registerListener(const wp<StateListener>& listener) {
mListeners.insert(listener);
}
-void StateTracker::unregisterListener(wp<StateListener> listener) {
+void StateTracker::unregisterListener(const wp<StateListener>& listener) {
mListeners.erase(listener);
}
@@ -90,7 +89,7 @@
for (auto& [primaryKey, stateValueInfo] : mStateMap) {
updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
false /* nested; treat this state change as not nested */,
- &stateValueInfo);
+ stateValueInfo);
}
}
@@ -106,36 +105,28 @@
if (it != mStateMap.end()) {
updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
false /* nested; treat this state change as not nested */,
- &it->second);
+ it->second);
}
}
void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
const HashableDimensionKey& primaryKey,
const FieldValue& newState, const bool nested,
- StateValueInfo* stateValueInfo) {
+ StateValueInfo& stateValueInfo) {
FieldValue oldState;
oldState.mField = mField;
- oldState.mValue.setInt(stateValueInfo->state);
- const int32_t oldStateValue = stateValueInfo->state;
+ oldState.mValue.setInt(stateValueInfo.state);
+ const int32_t oldStateValue = stateValueInfo.state;
const int32_t newStateValue = newState.mValue.int_value;
- if (kStateUnknown == newStateValue) {
- mStateMap.erase(primaryKey);
- }
-
- // Update state map for non-nested counting case.
+ // Update state map and notify listeners if state has changed.
// Every state event triggers a state overwrite.
if (!nested) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
-
- // Notify listeners if state has changed.
- if (oldStateValue != newStateValue) {
+ if (newStateValue != oldStateValue) {
+ stateValueInfo.state = newStateValue;
+ stateValueInfo.count = 1;
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- return;
- }
// Update state map for nested counting case.
//
@@ -147,27 +138,34 @@
// In atoms.proto, a state atom with nested counting enabled
// must only have 2 states. There is no enforcemnt here of this requirement.
// The atom must be logged correctly.
- if (kStateUnknown == newStateValue) {
- if (kStateUnknown != oldStateValue) {
+ } else if (newStateValue == kStateUnknown) {
+ if (oldStateValue != kStateUnknown) {
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
} else if (oldStateValue == kStateUnknown) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
+ stateValueInfo.state = newStateValue;
+ stateValueInfo.count = 1;
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
} else if (oldStateValue == newStateValue) {
- stateValueInfo->count++;
- } else if (--stateValueInfo->count == 0) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
+ stateValueInfo.count++;
+ } else if (--stateValueInfo.count == 0) {
+ stateValueInfo.state = newStateValue;
+ stateValueInfo.count = 1;
notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
+
+ // Clear primary key entry from state map if state is now unknown.
+ // stateValueInfo points to a value in mStateMap and should not be accessed after erasing the
+ // entry
+ if (newStateValue == kStateUnknown) {
+ mStateMap.erase(primaryKey);
+ }
}
void StateTracker::notifyListeners(const int64_t eventTimeNs,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
- for (auto l : mListeners) {
+ for (const auto& l : mListeners) {
auto sl = l.promote();
if (sl != nullptr) {
sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
diff --git a/statsd/src/state/StateTracker.h b/statsd/src/state/StateTracker.h
index abd579e..8e8f27f 100644
--- a/statsd/src/state/StateTracker.h
+++ b/statsd/src/state/StateTracker.h
@@ -16,20 +16,21 @@
#pragma once
#include <utils/RefBase.h>
+
+#include <set>
+#include <unordered_map>
+
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
-
#include "state/StateListener.h"
-#include <unordered_map>
-
namespace android {
namespace os {
namespace statsd {
class StateTracker : public virtual RefBase {
public:
- StateTracker(const int32_t atomId);
+ StateTracker(int32_t atomId);
virtual ~StateTracker(){};
@@ -40,9 +41,9 @@
// Adds new listeners to set of StateListeners. If a listener is already
// registered, it is ignored.
- void registerListener(wp<StateListener> listener);
+ void registerListener(const wp<StateListener>& listener);
- void unregisterListener(wp<StateListener> listener);
+ void unregisterListener(const wp<StateListener>& listener);
// The output is a FieldValue object that has mStateField as the field and
// the original state value (found using the given query key) as the value.
@@ -80,7 +81,7 @@
// Update the StateMap based on the received state value.
void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
const FieldValue& newState, const bool nested,
- StateValueInfo* stateValueInfo);
+ StateValueInfo& stateValueInfo);
// Notify registered state listeners of state change.
void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto
index af21fe2..09ba6a8 100644
--- a/statsd/src/stats_log.proto
+++ b/statsd/src/stats_log.proto
@@ -318,6 +318,8 @@
optional bool dimension_guardrail_hit = 17;
+ optional int64 estimated_data_bytes = 18;
+
// Do not use.
reserved 13, 15;
}
@@ -400,7 +402,11 @@
repeated string strings = 9;
- repeated DataCorruptedReason data_corrupted_reason = 10;
+ reserved 10;
+
+ repeated DataCorruptedReason data_corrupted_reason = 11;
+
+ optional int64 estimated_data_bytes = 12;
}
message ConfigMetricsReportList {
@@ -412,6 +418,10 @@
repeated ConfigMetricsReport reports = 2;
+ optional int32 report_number = 3;
+
+ optional int32 statsd_stats_id = 4;
+
reserved 10 to 13, 101;
}
@@ -480,6 +490,19 @@
repeated Annotation annotation = 18;
repeated int32 activation_time_sec = 22;
repeated int32 deactivation_time_sec = 23;
+ repeated RestrictedMetricStats restricted_metric_stats = 25;
+ optional bool device_info_table_creation_failed = 26;
+ optional int32 restricted_db_corrupted_count = 27;
+ repeated int64 restricted_flush_latency = 28;
+ repeated int64 restricted_db_size_time_sec = 29;
+ repeated int64 restricted_db_size_bytes = 30;
+ repeated int32 dump_report_number = 31;
+ optional int32 db_deletion_stat_failed = 32;
+ optional int32 db_deletion_size_exceeded_limit = 33;
+ optional int32 db_deletion_config_invalid = 34;
+ optional int32 db_deletion_too_old = 35;
+ optional int32 db_deletion_config_removed = 36;
+ optional int32 db_deletion_config_updated = 37;
}
repeated ConfigStats config_stats = 3;
@@ -601,7 +624,29 @@
repeated ActivationBroadcastGuardrail activation_guardrail_stats = 19;
+ message RestrictedMetricStats {
+ optional int64 restricted_metric_id = 1;
+ optional int64 insert_error = 2;
+ optional int64 table_creation_error = 3;
+ optional int64 table_deletion_error = 4;
+ repeated int64 flush_latency_ns = 5;
+ optional int64 category_changed_count = 6;
+ }
+
+ message RestrictedMetricQueryStats {
+ optional int32 calling_uid = 1;
+ optional int64 config_id = 2;
+ optional int32 config_uid = 3;
+ optional string config_package = 4;
+ optional InvalidQueryReason invalid_query_reason = 5;
+ optional int64 query_wall_time_ns = 6;
+ optional bool has_error = 7;
+ optional string query_error = 8;
+ optional int64 query_latency_ns = 9;
+ }
+ repeated RestrictedMetricQueryStats restricted_metric_query_stats = 20;
optional uint32 shard_offset = 21;
+ optional int32 statsd_stats_id = 22;
message SubscriptionStats {
message PerSubscriptionStats {
@@ -617,6 +662,39 @@
}
optional SubscriptionStats subscription_stats = 23;
+
+ message SocketLossStats {
+ message LossStatsPerUid {
+ message AtomIdLossStats {
+ optional int32 atom_id = 1;
+ optional int32 error = 2;
+ optional int32 count = 3;
+ }
+ optional int32 uid = 1;
+ optional int64 first_timestamp_nanos = 2;
+ optional int64 last_timestamp_nanos = 3;
+ repeated AtomIdLossStats atom_id_loss_stats = 4;
+ }
+
+ repeated LossStatsPerUid loss_stats_per_uid = 1;
+
+ // tracks overflow of stats container on the libstatssocket side per logging application
+ message LossStatsOverflowCounters {
+ optional int32 uid = 1;
+ optional int32 count = 2;
+ }
+
+ repeated LossStatsOverflowCounters loss_stats_overflow_counters = 2;
+ }
+
+ optional SocketLossStats socket_loss_stats = 24;
+
+ message EventQueueStats {
+ optional int32 max_size_observed = 1;
+ optional int64 max_size_observed_elapsed_nanos = 2;
+ }
+
+ optional EventQueueStats event_queue_stats = 25;
}
message AlertTriggerDetails {
diff --git a/statsd/src/stats_log_util.cpp b/statsd/src/stats_log_util.cpp
index 1d9a43b..6d47344 100644
--- a/statsd/src/stats_log_util.cpp
+++ b/statsd/src/stats_log_util.cpp
@@ -598,6 +598,10 @@
return nano / 1000000;
}
+int64_t NanoToSeconds(const int64_t nano) {
+ return nano / NS_PER_SEC;
+}
+
int64_t MillisToNano(const int64_t millis) {
return millis * 1000000;
}
@@ -617,7 +621,7 @@
return success;
}
-void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap> uidMap, LogEvent& event) {
+void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap>& uidMap, LogEvent& event) {
uint8_t remainingUidCount = event.getNumUidFields();
vector<FieldValue>* fieldValues = event.getMutableValues();
auto it = fieldValues->begin();
diff --git a/statsd/src/stats_log_util.h b/statsd/src/stats_log_util.h
index 68155e4..16cdb35 100644
--- a/statsd/src/stats_log_util.h
+++ b/statsd/src/stats_log_util.h
@@ -76,10 +76,12 @@
int64_t NanoToMillis(const int64_t nano);
+int64_t NanoToSeconds(const int64_t nano);
+
int64_t MillisToNano(const int64_t millis);
// Helper function to write a stats field to ProtoOutputStream if it's a non-zero value.
-void writeNonZeroStatToStream(const uint64_t fieldId, const int64_t value,
+void writeNonZeroStatToStream(const uint64_t fieldId, int64_t value,
ProtoOutputStream* protoOutput);
// Helper function to write PulledAtomStats to ProtoOutputStream
@@ -117,7 +119,7 @@
return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
}
-void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap> uidMap, LogEvent& event);
+void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap>& uidMap, LogEvent& event);
std::string toHexString(const string& bytes);
diff --git a/statsd/src/stats_policy_config.proto b/statsd/src/stats_policy_config.proto
new file mode 100644
index 0000000..c1ea446
--- /dev/null
+++ b/statsd/src/stats_policy_config.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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";
+
+package android.os.statsd;
+
+option java_package = "com.android.internal.os";
+option java_outer_classname = "StatsPolicyConfig";
+
+message StatsPolicyConfig {
+ optional int32 minimumClientsInAggregateResult = 1;
+}
\ No newline at end of file
diff --git a/statsd/src/stats_util.h b/statsd/src/stats_util.h
index fde1825..fabb7e3 100644
--- a/statsd/src/stats_util.h
+++ b/statsd/src/stats_util.h
@@ -59,6 +59,10 @@
return isAtLeastU;
}
+inline bool shouldKeepRandomSample(int samplingPercentage) {
+ return (rand() % (100) + 1) <= samplingPercentage;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/statsd_config.proto b/statsd/src/statsd_config.proto
index adb7235..8d48df0 100644
--- a/statsd/src/statsd_config.proto
+++ b/statsd/src/statsd_config.proto
@@ -56,6 +56,14 @@
repeated FieldMatcher child = 3;
}
+message StringReplacer {
+ // Regex for matching the string.
+ optional string regex = 1;
+
+ // String with which to replace the matched string.
+ optional string replacement = 2;
+}
+
message FieldValueMatcher {
optional int32 field = 1;
@@ -85,6 +93,11 @@
StringListMatcher eq_any_wildcard_string = 18;
StringListMatcher neq_any_wildcard_string = 19;
}
+
+ // Can only be present if either:
+ // 1. value_matcher is not set.
+ // 2. value_matcher is set to one that is applicable to string fields.
+ optional StringReplacer replace_string = 20;
}
message MessageMatcher {
@@ -229,6 +242,8 @@
repeated MetricConditionLink links = 4;
+ optional int32 sampling_percentage = 5 [default = 100];
+
reserved 100;
reserved 101;
}
@@ -258,6 +273,8 @@
optional DimensionalSamplingInfo dimensional_sampling_info = 12;
+ optional int32 max_dimensions_per_bucket = 13;
+
reserved 100;
reserved 101;
}
@@ -294,6 +311,8 @@
optional DimensionalSamplingInfo dimensional_sampling_info = 13;
+ optional int32 max_dimensions_per_bucket = 14;
+
reserved 100;
reserved 101;
}
@@ -335,6 +354,12 @@
optional DimensionalSamplingInfo dimensional_sampling_info = 15;
+ optional int32 max_dimensions_per_bucket = 16;
+
+ optional int32 sampling_percentage = 17 [default = 100];
+
+ optional int32 pull_probability = 18 [default = 100];
+
reserved 100;
reserved 101;
}
@@ -370,6 +395,8 @@
}
optional AggregationType aggregation_type = 8 [default = SUM];
+ repeated AggregationType aggregation_types = 25;
+
optional bool include_sample_size = 22;
optional int64 min_bucket_size_nanos = 10;
@@ -398,37 +425,41 @@
optional DimensionalSamplingInfo dimensional_sampling_info = 23;
+ optional int32 max_dimensions_per_bucket = 24;
+
reserved 100;
reserved 101;
}
message KllMetric {
- optional int64 id = 1;
+ optional int64 id = 1;
- optional int64 what = 2;
+ optional int64 what = 2;
- optional FieldMatcher kll_field = 3;
+ optional FieldMatcher kll_field = 3;
- optional int64 condition = 4;
+ optional int64 condition = 4;
- optional FieldMatcher dimensions_in_what = 5;
+ optional FieldMatcher dimensions_in_what = 5;
- optional TimeUnit bucket = 6;
+ optional TimeUnit bucket = 6;
- repeated MetricConditionLink links = 7;
+ repeated MetricConditionLink links = 7;
- optional int64 min_bucket_size_nanos = 8;
+ optional int64 min_bucket_size_nanos = 8;
- optional bool split_bucket_for_app_upgrade = 9;
+ optional bool split_bucket_for_app_upgrade = 9;
- repeated int64 slice_by_state = 10;
+ repeated int64 slice_by_state = 10;
- repeated MetricStateLink state_link = 11;
+ repeated MetricStateLink state_link = 11;
- optional DimensionalSamplingInfo dimensional_sampling_info = 12;
+ optional DimensionalSamplingInfo dimensional_sampling_info = 12;
- reserved 100;
- reserved 101;
+ optional int32 max_dimensions_per_bucket = 13;
+
+ reserved 100;
+ reserved 101;
}
message Alert {
@@ -441,6 +472,8 @@
optional int32 refractory_period_secs = 4;
optional double trigger_if_sum_gt = 5;
+
+ optional float probability_of_informing = 6 [default = 1.1];
}
message Alarm {
@@ -449,6 +482,8 @@
optional int64 offset_millis = 2;
optional int64 period_millis = 3;
+
+ optional float probability_of_informing = 4 [default = 1.1];
}
message IncidentdDetails {
@@ -478,6 +513,15 @@
optional bytes trace_config = 1;
}
+message UprobestatsDetails {
+ // The |config| field is a proto-encoded message of type
+ // uprobestats.protos.UprobestatsConfig defined in
+ // //packages/modules/UprobeStats/src/config.proto. On device,
+ // statsd doesn't need to deserialize the message as it's just
+ // passed binary-encoded to the Uprobestats API.
+ optional bytes config = 1;
+}
+
message BroadcastSubscriberDetails {
optional int64 subscriber_id = 1;
repeated string cookie = 2;
@@ -499,6 +543,7 @@
IncidentdDetails incidentd_details = 4;
PerfettoDetails perfetto_details = 5;
BroadcastSubscriberDetails broadcast_subscriber_details = 6;
+ UprobestatsDetails uprobestats_details = 9;
}
optional float probability_of_informing = 7 [default = 1.1];
@@ -591,8 +636,18 @@
optional uint32 package_certificate_hash_size_bytes = 26;
+ optional string restricted_metrics_delegate_package_name = 27;
+
optional int32 max_metrics_memory_kb = 28;
+ optional int32 soft_metrics_memory_kb = 29;
+
+ message StatsdConfigOptions {
+ optional bool use_v2_soft_memory_limit = 1;
+ }
+
+ optional StatsdConfigOptions statsd_config_options = 30;
+
// Do not use.
reserved 1000, 1001;
}
diff --git a/statsd/src/statsd_metadata.proto b/statsd/src/statsd_metadata.proto
index 200b392..7d18894 100644
--- a/statsd/src/statsd_metadata.proto
+++ b/statsd/src/statsd_metadata.proto
@@ -56,12 +56,18 @@
repeated AlertDimensionKeyedData alert_dim_keyed_data = 2;
}
+message MetricMetadata {
+ optional int64 metric_id = 1;
+ optional int32 restricted_category = 2;
+}
+
// All metadata for a config in statsd
message StatsMetadata {
optional ConfigKey config_key = 1;
repeated AlertMetadata alert_metadata = 2;
+ repeated MetricMetadata metric_metadata = 3;
}
message StatsMetadataList {
repeated StatsMetadata stats_metadata = 1;
-}
\ No newline at end of file
+}
diff --git a/statsd/src/storage/StorageManager.cpp b/statsd/src/storage/StorageManager.cpp
index 1c669eb..7ca88fe 100644
--- a/statsd/src/storage/StorageManager.cpp
+++ b/statsd/src/storage/StorageManager.cpp
@@ -17,15 +17,19 @@
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "android-base/stringprintf.h"
-#include "guardrail/StatsdStats.h"
#include "storage/StorageManager.h"
-#include "stats_log_util.h"
#include <android-base/file.h>
#include <private/android_filesystem_config.h>
+#include <sys/stat.h>
+
#include <fstream>
+#include "android-base/stringprintf.h"
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "utils/DbUtils.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -118,6 +122,16 @@
output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
}
+// Returns array of int64_t which contains a sqlite db's uid and configId
+static ConfigKey parseDbName(char* name) {
+ char* uid = strtok(name, "_");
+ char* configId = strtok(nullptr, ".");
+ if (uid == nullptr || configId == nullptr) {
+ return ConfigKey(-1, -1);
+ }
+ return ConfigKey(StrToInt64(uid), StrToInt64(configId));
+}
+
void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
@@ -807,6 +821,55 @@
totalFileSize);
}
+void StorageManager::enforceDbGuardrails(const char* path, const int64_t currWallClockSec,
+ const int64_t maxBytes) {
+ if (!isAtLeastU()) {
+ return;
+ }
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
+ if (dir == NULL) {
+ VLOG("Path %s does not exist", path);
+ return;
+ }
+
+ dirent* de;
+ int64_t deleteThresholdSec = currWallClockSec - StatsdStats::kMaxAgeSecond;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.' || de->d_type == DT_DIR) continue;
+ string fullPathName = StringPrintf("%s/%s", path, name);
+ struct stat fileInfo;
+ const ConfigKey key = parseDbName(name);
+ if (stat(fullPathName.c_str(), &fileInfo) != 0) {
+ StatsdStats::getInstance().noteDbStatFailed(key);
+ // Remove file if stat fails.
+ remove(fullPathName.c_str());
+ continue;
+ }
+ StatsdStats::getInstance().noteRestrictedConfigDbSize(key, currWallClockSec,
+ fileInfo.st_size);
+ if (fileInfo.st_mtime <= deleteThresholdSec) {
+ StatsdStats::getInstance().noteDbTooOld(key);
+ remove(fullPathName.c_str());
+ }
+ if (fileInfo.st_size >= maxBytes) {
+ StatsdStats::getInstance().noteDbSizeExceeded(key);
+ remove(fullPathName.c_str());
+ }
+ if (hasFile(dbutils::getDbName(key).c_str())) {
+ dbutils::verifyIntegrityAndDeleteIfNecessary(key);
+ } else {
+ // Remove file if the file name fails to parse.
+ remove(fullPathName.c_str());
+ }
+ }
+}
+
+bool StorageManager::hasFile(const char* file) {
+ struct stat fileInfo;
+ return stat(file, &fileInfo) == 0;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/storage/StorageManager.h b/statsd/src/storage/StorageManager.h
index b0840ae..0a459a8 100644
--- a/statsd/src/storage/StorageManager.h
+++ b/statsd/src/storage/StorageManager.h
@@ -42,7 +42,7 @@
class StorageManager : public virtual RefBase {
public:
struct FileInfo {
- FileInfo(std::string name, bool isHistory, int fileSize, long fileAge)
+ FileInfo(const std::string& name, bool isHistory, int fileSize, long fileAge)
: mFileName(name),
mIsHistory(isHistory),
mFileSizeBytes(fileSize),
@@ -162,6 +162,10 @@
static void sortFiles(vector<FileInfo>* fileNames);
+ static void enforceDbGuardrails(const char* path, int64_t wallClockSec, int64_t maxBytes);
+
+ static bool hasFile(const char* file);
+
private:
/**
* Prints disk usage statistics about a directory related to statsd.
diff --git a/statsd/src/utils/DbUtils.cpp b/statsd/src/utils/DbUtils.cpp
new file mode 100644
index 0000000..35ad150
--- /dev/null
+++ b/statsd/src/utils/DbUtils.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2023 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 STATSD_DEBUG false // STOPSHIP if true
+
+#include "Log.h"
+
+#include "utils/DbUtils.h"
+
+#include <android/api-level.h>
+
+#include "FieldValue.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
+#include "stats_log_util.h"
+#include "storage/StorageManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+namespace dbutils {
+
+using ::android::os::statsd::FLOAT;
+using ::android::os::statsd::INT;
+using ::android::os::statsd::LONG;
+using ::android::os::statsd::StorageManager;
+using ::android::os::statsd::STRING;
+using base::GetProperty;
+using base::StringPrintf;
+
+const string TABLE_NAME_PREFIX = "metric_";
+const string COLUMN_NAME_ATOM_TAG = "atomId";
+const string COLUMN_NAME_EVENT_ELAPSED_CLOCK_NS = "elapsedTimestampNs";
+const string COLUMN_NAME_EVENT_WALL_CLOCK_NS = "wallTimestampNs";
+
+const string COLUMN_NAME_SDK_VERSION = "sdkVersion";
+const string COLUMN_NAME_MODEL = "model";
+const string COLUMN_NAME_PRODUCT = "product";
+const string COLUMN_NAME_HARDWARE = "hardware";
+const string COLUMN_NAME_DEVICE = "device";
+const string COLUMN_NAME_BUILD = "osBuild";
+const string COLUMN_NAME_FINGERPRINT = "fingerprint";
+const string COLUMN_NAME_BRAND = "brand";
+const string COLUMN_NAME_MANUFACTURER = "manufacturer";
+const string COLUMN_NAME_BOARD = "board";
+
+static std::vector<std::string> getExpectedTableSchema(const LogEvent& logEvent) {
+ vector<std::string> result;
+ for (const FieldValue& fieldValue : logEvent.getValues()) {
+ if (fieldValue.mField.getDepth() > 0) {
+ // Repeated fields are not supported.
+ continue;
+ }
+ switch (fieldValue.mValue.getType()) {
+ case INT:
+ case LONG:
+ result.push_back("INTEGER");
+ break;
+ case STRING:
+ result.push_back("TEXT");
+ break;
+ case FLOAT:
+ result.push_back("REAL");
+ break;
+ default:
+ // Byte array fields are not supported.
+ break;
+ }
+ }
+ return result;
+}
+
+static int integrityCheckCallback(void*, int colCount, char** queryResults, char**) {
+ if (colCount == 0 || strcmp(queryResults[0], "ok") != 0) {
+ // Returning 1 is an error code that causes exec to stop and error.
+ return 1;
+ }
+ return 0;
+}
+
+string getDbName(const ConfigKey& key) {
+ return StringPrintf("%s/%d_%lld.db", STATS_RESTRICTED_DATA_DIR, key.GetUid(),
+ (long long)key.GetId());
+}
+
+static string getCreateSqlString(const int64_t metricId, const LogEvent& event) {
+ string result = StringPrintf("CREATE TABLE IF NOT EXISTS %s%s", TABLE_NAME_PREFIX.c_str(),
+ reformatMetricId(metricId).c_str());
+ result += StringPrintf("(%s INTEGER,%s INTEGER,%s INTEGER,", COLUMN_NAME_ATOM_TAG.c_str(),
+ COLUMN_NAME_EVENT_ELAPSED_CLOCK_NS.c_str(),
+ COLUMN_NAME_EVENT_WALL_CLOCK_NS.c_str());
+ for (size_t fieldId = 1; fieldId <= event.getValues().size(); ++fieldId) {
+ const FieldValue& fieldValue = event.getValues()[fieldId - 1];
+ if (fieldValue.mField.getDepth() > 0) {
+ // Repeated fields are not supported.
+ continue;
+ }
+ switch (fieldValue.mValue.getType()) {
+ case INT:
+ case LONG:
+ result += StringPrintf("field_%d INTEGER,", fieldValue.mField.getPosAtDepth(0));
+ break;
+ case STRING:
+ result += StringPrintf("field_%d TEXT,", fieldValue.mField.getPosAtDepth(0));
+ break;
+ case FLOAT:
+ result += StringPrintf("field_%d REAL,", fieldValue.mField.getPosAtDepth(0));
+ break;
+ default:
+ // Byte array fields are not supported.
+ break;
+ }
+ }
+ result.pop_back();
+ result += ") STRICT;";
+ return result;
+}
+
+string reformatMetricId(const int64_t metricId) {
+ return metricId < 0 ? StringPrintf("n%lld", (long long)metricId * -1)
+ : StringPrintf("%lld", (long long)metricId);
+}
+
+bool createTableIfNeeded(const ConfigKey& key, const int64_t metricId, const LogEvent& event) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ sqlite3_close(db);
+ return false;
+ }
+
+ char* error = nullptr;
+ string zSql = getCreateSqlString(metricId, event);
+ sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
+ sqlite3_close(db);
+ if (error) {
+ ALOGW("Failed to create table to db: %s", error);
+ return false;
+ }
+ return true;
+}
+
+bool isEventCompatible(const ConfigKey& key, const int64_t metricId, const LogEvent& event) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ sqlite3_close(db);
+ return false;
+ }
+ string zSql = StringPrintf("PRAGMA table_info(metric_%s);", reformatMetricId(metricId).c_str());
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ if (!query(key, zSql, rows, columnTypes, columnNames, err)) {
+ ALOGE("Failed to check table schema for metric %lld: %s", (long long)metricId, err.c_str());
+ sqlite3_close(db);
+ return false;
+ }
+ // Sample query result
+ // cid name type notnull dflt_value pk
+ // --- ----------------- ------- ------- ---------- --
+ // 0 atomId INTEGER 0 (null) 0
+ // 1 elapsedTimestampNs INTEGER 0 (null) 0
+ // 2 wallTimestampNs INTEGER 0 (null) 0
+ // 3 field_1 INTEGER 0 (null) 0
+ // 4 field_2 TEXT 0 (null) 0
+ std::vector<string> tableSchema;
+ for (size_t i = 3; i < rows.size(); ++i) { // Atom fields start at the third row
+ tableSchema.push_back(rows[i][2]); // The third column stores the data type for the column
+ }
+ sqlite3_close(db);
+ // An empty rows vector implies the table has not yet been created.
+ return rows.size() == 0 || getExpectedTableSchema(event) == tableSchema;
+}
+
+bool deleteTable(const ConfigKey& key, const int64_t metricId) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ sqlite3_close(db);
+ return false;
+ }
+ string zSql = StringPrintf("DROP TABLE metric_%s", reformatMetricId(metricId).c_str());
+ char* error = nullptr;
+ sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
+ sqlite3_close(db);
+ if (error) {
+ ALOGW("Failed to drop table from db: %s", error);
+ return false;
+ }
+ return true;
+}
+
+void deleteDb(const ConfigKey& key) {
+ const string dbName = getDbName(key);
+ StorageManager::deleteFile(dbName.c_str());
+}
+
+sqlite3* getDb(const ConfigKey& key) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) == SQLITE_OK) {
+ return db;
+ }
+ return nullptr;
+}
+
+void closeDb(sqlite3* db) {
+ sqlite3_close(db);
+}
+
+static bool getInsertSqlStmt(sqlite3* db, sqlite3_stmt** stmt, const int64_t metricId,
+ const vector<LogEvent>& events, string& err) {
+ string result =
+ StringPrintf("INSERT INTO metric_%s VALUES", reformatMetricId(metricId).c_str());
+ for (auto& logEvent : events) {
+ result += StringPrintf("(%d, %lld, %lld,", logEvent.GetTagId(),
+ (long long)logEvent.GetElapsedTimestampNs(),
+ (long long)logEvent.GetLogdTimestampNs());
+ for (auto& fieldValue : logEvent.getValues()) {
+ if (fieldValue.mField.getDepth() > 0 || fieldValue.mValue.getType() == STORAGE) {
+ // Repeated fields and byte fields are not supported.
+ continue;
+ }
+ result += "?,";
+ }
+ result.pop_back();
+ result += "),";
+ }
+ result.pop_back();
+ result += ";";
+ if (sqlite3_prepare_v2(db, result.c_str(), -1, stmt, nullptr) != SQLITE_OK) {
+ err = sqlite3_errmsg(db);
+ return false;
+ }
+ // ? parameters start with an index of 1 from start of query string to the
+ // end.
+ int32_t index = 1;
+ for (auto& logEvent : events) {
+ for (auto& fieldValue : logEvent.getValues()) {
+ if (fieldValue.mField.getDepth() > 0 || fieldValue.mValue.getType() == STORAGE) {
+ // Repeated fields and byte fields are not supported.
+ continue;
+ }
+ switch (fieldValue.mValue.getType()) {
+ case INT:
+ sqlite3_bind_int(*stmt, index, fieldValue.mValue.int_value);
+ break;
+ case LONG:
+ sqlite3_bind_int64(*stmt, index, fieldValue.mValue.long_value);
+ break;
+ case STRING:
+ sqlite3_bind_text(*stmt, index, fieldValue.mValue.str_value.c_str(), -1,
+ SQLITE_STATIC);
+ break;
+ case FLOAT:
+ sqlite3_bind_double(*stmt, index, fieldValue.mValue.float_value);
+ break;
+ default:
+ // Byte array fields are not supported.
+ break;
+ }
+ ++index;
+ }
+ }
+ return true;
+}
+
+bool insert(const ConfigKey& key, const int64_t metricId, const vector<LogEvent>& events,
+ string& error) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ error = sqlite3_errmsg(db);
+ sqlite3_close(db);
+ return false;
+ }
+ bool success = insert(db, metricId, events, error);
+ sqlite3_close(db);
+ return success;
+}
+
+bool insert(sqlite3* db, const int64_t metricId, const vector<LogEvent>& events, string& error) {
+ sqlite3_stmt* stmt = nullptr;
+ if (!getInsertSqlStmt(db, &stmt, metricId, events, error)) {
+ ALOGW("Failed to generate prepared sql insert query %s", error.c_str());
+ sqlite3_finalize(stmt);
+ return false;
+ }
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ error = sqlite3_errmsg(db);
+ ALOGW("Failed to insert data to db: %s", error.c_str());
+ sqlite3_finalize(stmt);
+ return false;
+ }
+ sqlite3_finalize(stmt);
+ return true;
+}
+
+bool query(const ConfigKey& key, const string& zSql, vector<vector<string>>& rows,
+ vector<int32_t>& columnTypes, vector<string>& columnNames, string& err) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open_v2(dbName.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) {
+ err = sqlite3_errmsg(db);
+ sqlite3_close(db);
+ return false;
+ }
+ sqlite3_stmt* stmt;
+ if (sqlite3_prepare_v2(db, zSql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
+ err = sqlite3_errmsg(db);
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return false;
+ }
+ int result = sqlite3_step(stmt);
+ bool firstIter = true;
+ while (result == SQLITE_ROW) {
+ int colCount = sqlite3_column_count(stmt);
+ vector<string> rowData(colCount);
+ for (int i = 0; i < colCount; ++i) {
+ if (firstIter) {
+ int32_t columnType = sqlite3_column_type(stmt, i);
+ // Needed to convert to java compatible cursor types. See AbstractCursor#getType()
+ if (columnType == 5) {
+ columnType = 0; // Remap 5 (null type) to 0 for java cursor
+ }
+ columnTypes.push_back(columnType);
+ columnNames.push_back(reinterpret_cast<const char*>(sqlite3_column_name(stmt, i)));
+ }
+ const unsigned char* textResult = sqlite3_column_text(stmt, i);
+ string colData =
+ textResult != nullptr ? string(reinterpret_cast<const char*>(textResult)) : "";
+ rowData[i] = std::move(colData);
+ }
+ rows.push_back(std::move(rowData));
+ firstIter = false;
+ result = sqlite3_step(stmt);
+ }
+ sqlite3_finalize(stmt);
+ if (result != SQLITE_DONE) {
+ err = sqlite3_errmsg(db);
+ sqlite3_close(db);
+ return false;
+ }
+ sqlite3_close(db);
+ return true;
+}
+
+bool flushTtl(sqlite3* db, const int64_t metricId, const int64_t ttlWallClockNs) {
+ string zSql = StringPrintf("DELETE FROM %s%s WHERE %s <= %lld", TABLE_NAME_PREFIX.c_str(),
+ reformatMetricId(metricId).c_str(),
+ COLUMN_NAME_EVENT_WALL_CLOCK_NS.c_str(), (long long)ttlWallClockNs);
+
+ char* error = nullptr;
+ sqlite3_exec(db, zSql.c_str(), nullptr, nullptr, &error);
+ if (error) {
+ ALOGW("Failed to enforce ttl: %s", error);
+ return false;
+ }
+ return true;
+}
+
+void verifyIntegrityAndDeleteIfNecessary(const ConfigKey& configKey) {
+ const string dbName = getDbName(configKey);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ sqlite3_close(db);
+ return;
+ }
+ string zSql = "PRAGMA integrity_check";
+
+ char* error = nullptr;
+ sqlite3_exec(db, zSql.c_str(), integrityCheckCallback, nullptr, &error);
+ if (error) {
+ StatsdStats::getInstance().noteDbCorrupted(configKey);
+ ALOGW("Integrity Check failed %s", error);
+ sqlite3_close(db);
+ deleteDb(configKey);
+ return;
+ }
+ sqlite3_close(db);
+}
+
+static bool getDeviceInfoInsertStmt(sqlite3* db, sqlite3_stmt** stmt, string error) {
+ string insertSql = StringPrintf("INSERT INTO device_info VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ if (sqlite3_prepare_v2(db, insertSql.c_str(), -1, stmt, nullptr) != SQLITE_OK) {
+ error = sqlite3_errmsg(db);
+ return false;
+ }
+
+ // ? parameters start with an index of 1 from start of query string to the end.
+ int32_t index = 1;
+
+ int32_t sdkVersion = android_get_device_api_level();
+ sqlite3_bind_int(*stmt, index, sdkVersion);
+ ++index;
+
+ string model = GetProperty("ro.product.model", "(unknown)");
+ sqlite3_bind_text(*stmt, index, model.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string product = GetProperty("ro.product.name", "(unknown)");
+ sqlite3_bind_text(*stmt, index, product.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string hardware = GetProperty("ro.hardware", "(unknown)");
+ sqlite3_bind_text(*stmt, index, hardware.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string device = GetProperty("ro.product.device", "(unknown)");
+ sqlite3_bind_text(*stmt, index, device.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string osBuild = GetProperty("ro.build.id", "(unknown)");
+ sqlite3_bind_text(*stmt, index, osBuild.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string fingerprint = GetProperty("ro.build.fingerprint", "(unknown)");
+ sqlite3_bind_text(*stmt, index, fingerprint.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string brand = GetProperty("ro.product.brand", "(unknown)");
+ sqlite3_bind_text(*stmt, index, brand.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string manufacturer = GetProperty("ro.product.manufacturer", "(unknown)");
+ sqlite3_bind_text(*stmt, index, manufacturer.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ string board = GetProperty("ro.product.board", "(unknown)");
+ sqlite3_bind_text(*stmt, index, board.c_str(), -1, SQLITE_TRANSIENT);
+ ++index;
+
+ return true;
+}
+
+bool updateDeviceInfoTable(const ConfigKey& key, string& error) {
+ const string dbName = getDbName(key);
+ sqlite3* db;
+ if (sqlite3_open(dbName.c_str(), &db) != SQLITE_OK) {
+ error = sqlite3_errmsg(db);
+ sqlite3_close(db);
+ return false;
+ }
+
+ string dropTableSql = "DROP TABLE device_info";
+ // Ignore possible error result code if table has not yet been created.
+ sqlite3_exec(db, dropTableSql.c_str(), nullptr, nullptr, nullptr);
+
+ string createTableSql = StringPrintf(
+ "CREATE TABLE device_info(%s INTEGER, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s "
+ "TEXT, %s TEXT, %s TEXT, %s TEXT) "
+ "STRICT",
+ COLUMN_NAME_SDK_VERSION.c_str(), COLUMN_NAME_MODEL.c_str(), COLUMN_NAME_PRODUCT.c_str(),
+ COLUMN_NAME_HARDWARE.c_str(), COLUMN_NAME_DEVICE.c_str(), COLUMN_NAME_BUILD.c_str(),
+ COLUMN_NAME_FINGERPRINT.c_str(), COLUMN_NAME_BRAND.c_str(),
+ COLUMN_NAME_MANUFACTURER.c_str(), COLUMN_NAME_BOARD.c_str());
+ if (sqlite3_exec(db, createTableSql.c_str(), nullptr, nullptr, nullptr) != SQLITE_OK) {
+ error = sqlite3_errmsg(db);
+ ALOGW("Failed to create device info table %s", error.c_str());
+ sqlite3_close(db);
+ return false;
+ }
+
+ sqlite3_stmt* stmt = nullptr;
+ if (!getDeviceInfoInsertStmt(db, &stmt, error)) {
+ ALOGW("Failed to generate device info prepared sql insert query %s", error.c_str());
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return false;
+ }
+
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ error = sqlite3_errmsg(db);
+ ALOGW("Failed to insert data to device info table: %s", error.c_str());
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return false;
+ }
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return true;
+}
+} // namespace dbutils
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/DbUtils.h b/statsd/src/utils/DbUtils.h
new file mode 100644
index 0000000..06d1292
--- /dev/null
+++ b/statsd/src/utils/DbUtils.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 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 <sqlite3.h>
+
+#include "config/ConfigKey.h"
+#include "logd/LogEvent.h"
+
+using std::string;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+namespace dbutils {
+
+#define STATS_RESTRICTED_DATA_DIR "/data/misc/stats-data/restricted-data"
+
+inline int32_t getDbVersion() {
+ return SQLITE_VERSION_NUMBER;
+};
+
+string getDbName(const ConfigKey& key);
+
+string reformatMetricId(const int64_t metricId);
+
+/* Creates a new data table for a specified metric if one does not yet exist. */
+bool createTableIfNeeded(const ConfigKey& key, int64_t metricId, const LogEvent& event);
+
+/* Checks whether the table schema for the given metric matches the event.
+ * Returns true if the table has not yet been created.
+ */
+bool isEventCompatible(const ConfigKey& key, int64_t metricId, const LogEvent& event);
+
+/* Deletes a data table for the specified metric. */
+bool deleteTable(const ConfigKey& key, int64_t metricId);
+
+/* Deletes the SQLite db data file. */
+void deleteDb(const ConfigKey& key);
+
+/* Gets a handle to the sqlite db. You must call closeDb to free the allocated memory.
+ * Returns a nullptr if an error occurs.
+ */
+sqlite3* getDb(const ConfigKey& key);
+
+/* Closes the handle to the sqlite db. */
+void closeDb(sqlite3* db);
+
+/* Inserts new data into the specified metric data table.
+ * A temp sqlite handle is created using the ConfigKey.
+ */
+bool insert(const ConfigKey& key, int64_t metricId, const vector<LogEvent>& events, string& error);
+
+/* Inserts new data into the specified sqlite db handle. */
+bool insert(sqlite3* db, int64_t metricId, const vector<LogEvent>& events, string& error);
+
+/* Executes a sql query on the specified SQLite db.
+ * A temp sqlite handle is created using the ConfigKey.
+ */
+bool query(const ConfigKey& key, const string& zSql, vector<vector<string>>& rows,
+ vector<int32_t>& columnTypes, vector<string>& columnNames, string& err);
+
+bool flushTtl(sqlite3* db, int64_t metricId, int64_t ttlWallClockNs);
+
+/* Checks for database corruption and deletes the db if it is corrupted. */
+void verifyIntegrityAndDeleteIfNecessary(const ConfigKey& key);
+
+/* Creates and updates the device info table for the given configKey. */
+bool updateDeviceInfoTable(const ConfigKey& key, string& error);
+
+} // namespace dbutils
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/MultiConditionTrigger.cpp b/statsd/src/utils/MultiConditionTrigger.cpp
index 5ef50ee..d9ad25b 100644
--- a/statsd/src/utils/MultiConditionTrigger.cpp
+++ b/statsd/src/utils/MultiConditionTrigger.cpp
@@ -19,19 +19,21 @@
#include <thread>
-using namespace std;
-
namespace android {
namespace os {
namespace statsd {
+using std::function;
+using std::set;
+using std::string;
+
MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
function<void()> trigger)
: mRemainingConditionNames(conditionNames),
- mTrigger(trigger),
+ mTrigger(std::move(trigger)),
mCompleted(mRemainingConditionNames.empty()) {
if (mCompleted) {
- thread executorThread([this] { mTrigger(); });
+ std::thread executorThread([this] { mTrigger(); });
executorThread.detach();
}
}
@@ -39,7 +41,7 @@
void MultiConditionTrigger::markComplete(const string& conditionName) {
bool doTrigger = false;
{
- lock_guard<mutex> lg(mMutex);
+ std::lock_guard<std::mutex> lg(mMutex);
if (mCompleted) {
return;
}
diff --git a/statsd/src/utils/MultiConditionTrigger.h b/statsd/src/utils/MultiConditionTrigger.h
index 51f6029..7a09119 100644
--- a/statsd/src/utils/MultiConditionTrigger.h
+++ b/statsd/src/utils/MultiConditionTrigger.h
@@ -40,16 +40,15 @@
// Mark a specific condition as true. If this condition has called markComplete already or if
// the event was not specified in the constructor, the function is a no-op.
- void markComplete(const std::string& eventName);
+ void markComplete(const std::string& conditionName);
private:
mutable std::mutex mMutex;
std::set<std::string> mRemainingConditionNames;
std::function<void()> mTrigger;
bool mCompleted;
-
- FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName);
};
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/src/utils/Regex.cpp b/statsd/src/utils/Regex.cpp
new file mode 100644
index 0000000..7c24dc8
--- /dev/null
+++ b/statsd/src/utils/Regex.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 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 "Regex.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::string;
+using std::unique_ptr;
+
+Regex::Regex(regex_t impl) : mImpl(std::move(impl)) {
+}
+
+Regex::~Regex() {
+ regfree(&mImpl);
+}
+
+unique_ptr<Regex> Regex::create(const string& pattern) {
+ regex_t impl;
+ int status = regcomp(&impl, pattern.c_str(), REG_EXTENDED);
+
+ if (status != 0) { // Invalid regex pattern.
+ // Calculate size of string needed to store error description.
+ size_t errBufSize = regerror(status, &impl, nullptr, 0);
+
+ // Get the error description.
+ char errBuf[errBufSize];
+ regerror(status, &impl, errBuf, errBufSize);
+
+ ALOGE("regex_error: %s, pattern: %s", errBuf, pattern.c_str());
+ regfree(&impl);
+ return nullptr;
+ } else if (impl.re_nsub > 0) {
+ ALOGE("regex_error: subexpressions are not allowed, pattern: %s", pattern.c_str());
+ regfree(&impl);
+ return nullptr;
+ } else {
+ return std::make_unique<Regex>(impl);
+ }
+}
+
+bool Regex::replace(string& str, const string& replacement) {
+ regmatch_t match;
+ int status = regexec(&mImpl, str.c_str(), 1 /* nmatch */, &match /* pmatch */, 0 /* flags */);
+
+ if (status != 0 || match.rm_so == -1) { // No match.
+ return false;
+ }
+ str.replace(match.rm_so, match.rm_eo - match.rm_so, replacement);
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/Regex.h b/statsd/src/utils/Regex.h
new file mode 100644
index 0000000..f1931fa
--- /dev/null
+++ b/statsd/src/utils/Regex.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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 <regex.h>
+
+#include <memory>
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class Regex {
+public:
+ Regex(regex_t impl); // Do not use. It is public for std::make_unique. Use Regex::create.
+ ~Regex();
+ Regex& operator=(const Regex&) = delete;
+ Regex(const Regex&) = delete;
+
+ // Returns nullptr if pattern is not valid POSIX regex.
+ static std::unique_ptr<Regex> create(const std::string& pattern);
+
+ // Looks for a regex match in str and replaces the matched portion with replacement in-place.
+ // Returns true if there was a match, false otherwise.
+ bool replace(std::string& str, const std::string& replacement);
+
+private:
+ regex_t mImpl;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/RestrictedPolicyManager.cpp b/statsd/src/utils/RestrictedPolicyManager.cpp
new file mode 100644
index 0000000..0c18a13
--- /dev/null
+++ b/statsd/src/utils/RestrictedPolicyManager.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 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 "utils/RestrictedPolicyManager.h"
+
+#include "stats_annotations.h"
+
+#define STATSD_DEBUG false // STOPSHIP if true
+
+namespace android {
+namespace os {
+namespace statsd {
+
+RestrictedPolicyManager& RestrictedPolicyManager::getInstance() {
+ static RestrictedPolicyManager policyInstance;
+ return policyInstance;
+}
+
+int32_t RestrictedPolicyManager::getRestrictedCategoryTtl(
+ const StatsdRestrictionCategory categoryId) const {
+ return mRestrictionCategoryTtlInDaysMap.find(categoryId) !=
+ mRestrictionCategoryTtlInDaysMap.end()
+ ? mRestrictionCategoryTtlInDaysMap.at(categoryId)
+ : DEFAULT_RESTRICTED_CATEGORY_TTL_DAYS;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/RestrictedPolicyManager.h b/statsd/src/utils/RestrictedPolicyManager.h
new file mode 100644
index 0000000..9795640
--- /dev/null
+++ b/statsd/src/utils/RestrictedPolicyManager.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 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 <stdlib.h>
+
+#include <map>
+
+#include "stats_annotations.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#define DEFAULT_RESTRICTED_CATEGORY_TTL_DAYS 7
+
+// Restricted categories used internally by statsd.
+enum StatsdRestrictionCategory : int32_t {
+ CATEGORY_UNKNOWN = -1,
+ CATEGORY_NO_RESTRICTION = 0,
+ CATEGORY_DIAGNOSTIC = ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC,
+ CATEGORY_SYSTEM_INTELLIGENCE = ASTATSLOG_RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE,
+ CATEGORY_AUTHENTICATION = ASTATSLOG_RESTRICTION_CATEGORY_AUTHENTICATION,
+ CATEGORY_FRAUD_AND_ABUSE = ASTATSLOG_RESTRICTION_CATEGORY_FRAUD_AND_ABUSE,
+};
+
+// Single instance shared across the process.
+class RestrictedPolicyManager {
+ RestrictedPolicyManager() = default;
+
+public:
+ static RestrictedPolicyManager& getInstance();
+
+ // Gets the TTL in days for a particular restricted category. Returns the default for unknown
+ // categories.
+ int32_t getRestrictedCategoryTtl(StatsdRestrictionCategory categoryId) const;
+
+private:
+ std::map<StatsdRestrictionCategory, int32_t> mRestrictionCategoryTtlInDaysMap;
+};
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/src/utils/ShardOffsetProvider.cpp b/statsd/src/utils/ShardOffsetProvider.cpp
index 4a0df97..8894b4d 100644
--- a/statsd/src/utils/ShardOffsetProvider.cpp
+++ b/statsd/src/utils/ShardOffsetProvider.cpp
@@ -16,31 +16,15 @@
#include "ShardOffsetProvider.h"
-#include <errno.h>
-#include <sys/random.h>
-#include <unistd.h>
-
-#include <chrono>
-
namespace android {
namespace os {
namespace statsd {
-ShardOffsetProvider::ShardOffsetProvider() {
- unsigned int seed = 0;
- // getrandom() reads bytes from urandom source into buf. If getrandom()
- // is unable to read from urandom source, then it returns -1 and we set
- // our seed to be time(nullptr) as a fallback.
- if (TEMP_FAILURE_RETRY(
- getrandom(static_cast<void*>(&seed), sizeof(unsigned int), GRND_NONBLOCK)) < 0) {
- seed = time(nullptr);
- }
- srand(seed);
- mShardOffset = rand();
+ShardOffsetProvider::ShardOffsetProvider(const uint32_t shardOffset) : mShardOffset(shardOffset) {
}
ShardOffsetProvider& ShardOffsetProvider::getInstance() {
- static ShardOffsetProvider sShardOffsetProvider;
+ static ShardOffsetProvider sShardOffsetProvider(rand());
return sShardOffsetProvider;
}
diff --git a/statsd/src/utils/ShardOffsetProvider.h b/statsd/src/utils/ShardOffsetProvider.h
index dd6a5a7..506e230 100644
--- a/statsd/src/utils/ShardOffsetProvider.h
+++ b/statsd/src/utils/ShardOffsetProvider.h
@@ -39,7 +39,7 @@
static ShardOffsetProvider& getInstance();
private:
- ShardOffsetProvider();
+ ShardOffsetProvider(const uint32_t shardOffset);
// Only used for testing.
void setShardOffset(const uint32_t shardOffset) {
@@ -51,6 +51,7 @@
FRIEND_TEST(CountMetricE2eTest, TestDimensionalSampling);
FRIEND_TEST(DurationMetricE2eTest, TestDimensionalSampling);
FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling);
+ FRIEND_TEST(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSamplingWithDimensionalSampling);
FRIEND_TEST(GaugeMetricProducerTest, TestPullDimensionalSampling);
FRIEND_TEST(KllMetricE2eTest, TestDimensionalSampling);
FRIEND_TEST(NumericValueMetricProducerTest, TestDimensionalSampling);
diff --git a/statsd/src/utils/api_tracing.h b/statsd/src/utils/api_tracing.h
new file mode 100644
index 0000000..69edeae
--- /dev/null
+++ b/statsd/src/utils/api_tracing.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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
+
+#define ATRACE_TAG ATRACE_TAG_APP
+
+#include <utils/Trace.h>
diff --git a/statsd/tests/ConfigManager_test.cpp b/statsd/tests/ConfigManager_test.cpp
index 84c5774..e1c6d7d 100644
--- a/statsd/tests/ConfigManager_test.cpp
+++ b/statsd/tests/ConfigManager_test.cpp
@@ -161,3 +161,96 @@
manager->RemoveConfigs(1);
manager->RemoveConfigs(3);
}
+
+TEST(ConfigManagerTest, TestSendRestrictedMetricsChangedBroadcast) {
+ const string configPackage = "pkg";
+ const int64_t configId = 123;
+ const int32_t callingUid = 456;
+ const vector<int64_t> metricIds = {100, 200, 999};
+
+ shared_ptr<MockPendingIntentRef> pir = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir, sendRestrictedMetricsChangedBroadcast(metricIds))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ sp<ConfigManager> manager = new ConfigManager();
+ manager->SetRestrictedMetricsChangedReceiver(configPackage, configId, callingUid, pir);
+
+ manager->SendRestrictedMetricsBroadcast({configPackage}, configId, {callingUid}, metricIds);
+}
+
+TEST(ConfigManagerTest, TestSendRestrictedMetricsChangedBroadcastMultipleConfigs) {
+ const string package1 = "pkg1", package2 = "pkg2", package3 = "pkg3";
+ const int64_t configId = 123;
+ const int32_t callingUid1 = 456, callingUid2 = 789, callingUid3 = 1111;
+ const vector<int64_t> metricIds1 = {100, 200, 999}, metricIds2 = {101}, metricIds3 = {202, 101};
+
+ shared_ptr<MockPendingIntentRef> pir1 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir1, sendRestrictedMetricsChangedBroadcast(metricIds1))
+ .Times(3)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+ EXPECT_CALL(*pir1, sendRestrictedMetricsChangedBroadcast(metricIds2))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ shared_ptr<MockPendingIntentRef> pir2 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir2, sendRestrictedMetricsChangedBroadcast(metricIds1))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ shared_ptr<MockPendingIntentRef> pir3 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir3, sendRestrictedMetricsChangedBroadcast(metricIds1))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+ EXPECT_CALL(*pir3, sendRestrictedMetricsChangedBroadcast(metricIds2))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ shared_ptr<MockPendingIntentRef> pir4 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir4, sendRestrictedMetricsChangedBroadcast(metricIds1))
+ .Times(2)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ shared_ptr<MockPendingIntentRef> pir5 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir5, sendRestrictedMetricsChangedBroadcast(metricIds3))
+ .Times(1)
+ .WillRepeatedly(Invoke([](const vector<int64_t>&) { return Status::ok(); }));
+
+ sp<ConfigManager> manager = new ConfigManager();
+ manager->SetRestrictedMetricsChangedReceiver(package1, configId, callingUid1, pir1);
+ manager->SetRestrictedMetricsChangedReceiver(package2, configId, callingUid2, pir2);
+ manager->SetRestrictedMetricsChangedReceiver(package1, configId, callingUid2, pir3);
+ manager->SetRestrictedMetricsChangedReceiver(package2, configId, callingUid1, pir4);
+ manager->SetRestrictedMetricsChangedReceiver(package3, configId, callingUid3, pir5);
+
+ // Invoke pir 1, 2, 3, 4.
+ manager->SendRestrictedMetricsBroadcast({package1, package2}, configId,
+ {callingUid1, callingUid2}, metricIds1);
+ // Invoke pir 1 only
+ manager->SendRestrictedMetricsBroadcast({package1}, configId, {callingUid1}, metricIds1);
+ // Invoke pir 1, 4.
+ manager->SendRestrictedMetricsBroadcast({package1, package2}, configId, {callingUid1},
+ metricIds1);
+ // Invoke pir 1, 3
+ manager->SendRestrictedMetricsBroadcast({package1}, configId, {callingUid1, callingUid2},
+ metricIds2);
+ // Invoke pir 5
+ manager->SendRestrictedMetricsBroadcast({package3}, configId, {callingUid1, callingUid3},
+ metricIds3);
+}
+
+TEST(ConfigManagerTest, TestRemoveRestrictedMetricsChangedBroadcast) {
+ const string configPackage = "pkg";
+ const int64_t configId = 123;
+ const int32_t callingUid = 456;
+ const vector<int64_t> metricIds = {100, 200, 999};
+
+ shared_ptr<MockPendingIntentRef> pir = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir, sendRestrictedMetricsChangedBroadcast(metricIds)).Times(0);
+
+ sp<ConfigManager> manager = new ConfigManager();
+ manager->SetRestrictedMetricsChangedReceiver(configPackage, configId, callingUid, pir);
+ manager->RemoveRestrictedMetricsChangedReceiver(configPackage, configId, callingUid);
+
+ manager->SendRestrictedMetricsBroadcast({configPackage}, configId, {callingUid}, metricIds);
+}
\ No newline at end of file
diff --git a/statsd/tests/FieldValue_test.cpp b/statsd/tests/FieldValue_test.cpp
index 64be559..8e9f789 100644
--- a/statsd/tests/FieldValue_test.cpp
+++ b/statsd/tests/FieldValue_test.cpp
@@ -158,6 +158,40 @@
EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
}
+TEST(AtomMatcherTest, TestFilter_FIRST) {
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::FIRST);
+
+ child->add_child()->set_field(1);
+ child->add_child()->set_field(2);
+
+ child = matcher1.add_child();
+ child->set_field(2);
+
+ vector<Matcher> matchers;
+ translateFieldMatcher(matcher1, &matchers);
+
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
+ HashableDimensionKey output;
+
+ filterValues(matchers, event.getValues(), &output);
+
+ ASSERT_EQ((size_t)3, output.getValues().size());
+ EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
+ EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
+ EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
+ EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
+ EXPECT_EQ((int32_t)0x00020000, output.getValues()[2].mField.getField());
+ EXPECT_EQ("some value", output.getValues()[2].mValue.str_value);
+};
+
TEST(AtomMatcherTest, TestFilterRepeated_FIRST) {
FieldMatcher matcher;
matcher.set_field(123);
@@ -508,6 +542,124 @@
}
}
+TEST(AtomMatcherTest, TestDedupFieldMatchersAllDifferent) {
+ // Matchers: Fields 1, 2, 3
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(3);
+
+ vector<Matcher> fieldMatchers;
+ translateFieldMatcher(matcher1, &fieldMatchers);
+ ASSERT_EQ(3, fieldMatchers.size());
+
+ // Deduped Matchers: Fields 1, 2, 3
+ std::vector<Matcher> dedupedFieldMatchers = dedupFieldMatchers(fieldMatchers);
+ ASSERT_EQ((size_t)3, dedupedFieldMatchers.size());
+ EXPECT_EQ(fieldMatchers[0], dedupedFieldMatchers[0]);
+ EXPECT_EQ(fieldMatchers[1], dedupedFieldMatchers[1]);
+ EXPECT_EQ(fieldMatchers[2], dedupedFieldMatchers[2]);
+}
+
+TEST(AtomMatcherTest, TestDedupFieldMatchersAllSame) {
+ // Matcher: Fields 1, 1, 1
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(1);
+
+ vector<Matcher> fieldMatchers;
+ translateFieldMatcher(matcher1, &fieldMatchers);
+ ASSERT_EQ(3, fieldMatchers.size());
+
+ // Deduped Matchers: Fields 1, 1, 1
+ std::vector<Matcher> dedupedFieldMatchers = dedupFieldMatchers(fieldMatchers);
+ ASSERT_EQ((size_t)1, dedupedFieldMatchers.size());
+ EXPECT_EQ(fieldMatchers[0], dedupedFieldMatchers[0]);
+}
+
+TEST(AtomMatcherTest, TestDedupFieldMatcherMixOfFields) {
+ // Matcher: Fields 2, 2, 1, 3, 2, 1, 3
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(3);
+ child = matcher1.add_child();
+ child->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(3);
+
+ vector<Matcher> fieldMatchers;
+ translateFieldMatcher(matcher1, &fieldMatchers);
+ ASSERT_EQ(7, fieldMatchers.size());
+
+ // Deduped Matchers: Fields 2, 1, 3
+ std::vector<Matcher> dedupedFieldMatchers = dedupFieldMatchers(fieldMatchers);
+ ASSERT_EQ((size_t)3, dedupedFieldMatchers.size());
+ EXPECT_EQ(fieldMatchers[0], dedupedFieldMatchers[0]);
+ EXPECT_EQ(fieldMatchers[2], dedupedFieldMatchers[1]);
+ EXPECT_EQ(fieldMatchers[3], dedupedFieldMatchers[2]);
+}
+
+TEST(AtomMatcherTest, TestDedupFieldMatcherDifferentPositionSameFields) {
+ // Matcher: Fields 3, 1.1(FIRST), 1.2(FIRST), 1.1(FIRST), 1.1(LAST), 1.2(FIRST), 2
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(3);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::FIRST);
+ child->add_child()->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::FIRST);
+ child->add_child()->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::FIRST);
+ child->add_child()->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::LAST);
+ child->add_child()->set_field(1);
+ child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::FIRST);
+ child->add_child()->set_field(2);
+ child = matcher1.add_child();
+ child->set_field(2);
+
+ vector<Matcher> fieldMatchers;
+ translateFieldMatcher(matcher1, &fieldMatchers);
+ ASSERT_EQ(7, fieldMatchers.size());
+
+ // Deduped Matchers: Fields 3, 1.1(FIRST), 1.2(FIRST), 1.1(LAST) 2
+ std::vector<Matcher> dedupedFieldMatchers = dedupFieldMatchers(fieldMatchers);
+ ASSERT_EQ((size_t)5, dedupedFieldMatchers.size());
+ EXPECT_EQ(fieldMatchers[0], dedupedFieldMatchers[0]);
+ EXPECT_EQ(fieldMatchers[1], dedupedFieldMatchers[1]);
+ EXPECT_EQ(fieldMatchers[2], dedupedFieldMatchers[2]);
+ EXPECT_EQ(fieldMatchers[4], dedupedFieldMatchers[3]);
+ EXPECT_EQ(fieldMatchers[6], dedupedFieldMatchers[4]);
+}
+
void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel,
int32_t nodeDepthInAttributionChain,
int32_t uid, string tag) {
@@ -739,7 +891,7 @@
bool boolArray[boolArrayLength];
boolArray[0] = 1;
boolArray[1] = 0;
- vector<bool> boolArrayVector = {1, 0};
+ vector<uint8_t> boolArrayVector = {1, 0};
vector<int> enumArray = {TestAtomReported::ON, TestAtomReported::OFF};
unique_ptr<LogEvent> event = CreateTestAtomReportedEventVariableRepeatedFields(
@@ -904,6 +1056,138 @@
EXPECT_FALSE(subsetDimensions(matchers2, matchers1));
}
+/*
+ * Test multiple combinations of repeated field matchers with different positions.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions_RepeatedFields) {
+ // Initialize matchers with position ALL.
+ FieldMatcher matcherAll;
+ matcherAll.set_field(10);
+ FieldMatcher* child = matcherAll.add_child();
+ child->set_field(1);
+ child = matcherAll.add_child();
+ child->set_field(2);
+ child->set_position(Position::ALL);
+ FieldMatcher* attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersAll;
+ translateFieldMatcher(matcherAll, &matchersAll);
+
+ // Initialize matchers with position FIRST.
+ FieldMatcher matcherFirst;
+ matcherFirst.set_field(10);
+ child = matcherFirst.add_child();
+ child->set_field(1);
+ child = matcherFirst.add_child();
+ child->set_field(2);
+ child->set_position(Position::FIRST);
+ attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersFirst;
+ translateFieldMatcher(matcherFirst, &matchersFirst);
+
+ // Initialize matchers with position LAST.
+ FieldMatcher matcherLast;
+ matcherLast.set_field(10);
+ child = matcherLast.add_child();
+ child->set_field(1);
+ child = matcherLast.add_child();
+ child->set_field(2);
+ child->set_position(Position::LAST);
+ attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersLast;
+ translateFieldMatcher(matcherLast, &matchersLast);
+
+ // Initialize matchers with position ANY.
+ FieldMatcher matcherAny;
+ matcherAny.set_field(10);
+ child = matcherAny.add_child();
+ child->set_field(1);
+ child = matcherAny.add_child();
+ child->set_field(2);
+ child->set_position(Position::ANY);
+ attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersAny;
+ translateFieldMatcher(matcherAny, &matchersAny);
+
+ // Initialize matchers with position ALL, different field number.
+ FieldMatcher matcherAllDifferent;
+ matcherAllDifferent.set_field(10);
+ child = matcherAllDifferent.add_child();
+ child->set_field(1);
+ child = matcherAllDifferent.add_child();
+ child->set_field(2);
+ child->set_position(Position::ALL);
+ attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(2);
+
+ vector<Matcher> matchersAllDifferent;
+ translateFieldMatcher(matcherAllDifferent, &matchersAllDifferent);
+
+ // Positions ALL, FIRST, LAST are subsets of position ALL.
+ EXPECT_TRUE(subsetDimensions(matchersAll, matchersAll));
+ EXPECT_TRUE(subsetDimensions(matchersFirst, matchersAll));
+ EXPECT_TRUE(subsetDimensions(matchersLast, matchersAll));
+ EXPECT_FALSE(subsetDimensions(matchersAny, matchersAll));
+ EXPECT_FALSE(subsetDimensions(matchersAllDifferent, matchersAll));
+
+ // Position FIRST is a subset of position FIRST.
+ EXPECT_FALSE(subsetDimensions(matchersAll, matchersFirst));
+ EXPECT_TRUE(subsetDimensions(matchersFirst, matchersFirst));
+ EXPECT_FALSE(subsetDimensions(matchersLast, matchersFirst));
+ EXPECT_FALSE(subsetDimensions(matchersAny, matchersFirst));
+ EXPECT_FALSE(subsetDimensions(matchersAllDifferent, matchersFirst));
+
+ // Position LAST is a subset of position LAST.
+ EXPECT_FALSE(subsetDimensions(matchersAll, matchersLast));
+ EXPECT_FALSE(subsetDimensions(matchersFirst, matchersLast));
+ EXPECT_TRUE(subsetDimensions(matchersLast, matchersLast));
+ EXPECT_FALSE(subsetDimensions(matchersAny, matchersLast));
+ EXPECT_FALSE(subsetDimensions(matchersAllDifferent, matchersLast));
+
+ // Position ANY is a subset of position ANY.
+ EXPECT_FALSE(subsetDimensions(matchersAll, matchersAny));
+ EXPECT_FALSE(subsetDimensions(matchersFirst, matchersAny));
+ EXPECT_FALSE(subsetDimensions(matchersLast, matchersAny));
+ EXPECT_TRUE(subsetDimensions(matchersAny, matchersAny));
+ EXPECT_FALSE(subsetDimensions(matchersAllDifferent, matchersAny));
+}
+
+TEST(AtomMatcherTest, TestAllPositionMatcher) {
+ // Initialize matcher with position ALL.
+ FieldMatcher matcherAll;
+ matcherAll.set_field(10);
+ FieldMatcher* child = matcherAll.add_child();
+ child->set_field(2);
+ child->set_position(Position::ALL);
+ FieldMatcher* attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersAll;
+ translateFieldMatcher(matcherAll, &matchersAll);
+
+ // Initialize matcher with position ANY.
+ FieldMatcher matcherAny;
+ matcherAny.set_field(10);
+ child = matcherAny.add_child();
+ child->set_field(2);
+ child->set_position(Position::ANY);
+ attributionNodeChild = child->add_child();
+ attributionNodeChild->set_field(1);
+
+ vector<Matcher> matchersAny;
+ translateFieldMatcher(matcherAny, &matchersAny);
+
+ EXPECT_TRUE(matchersAll[0].hasAllPositionMatcher());
+ EXPECT_FALSE(matchersAny[0].hasAllPositionMatcher());
+}
+
TEST(AtomMatcherTest, TestIsPrimitiveRepeatedField) {
int pos1[] = {1, 1, 1}; // attribution uid
int pos2[] = {1, 1, 2}; // attribution tag
diff --git a/statsd/tests/LogEntryMatcher_test.cpp b/statsd/tests/LogEntryMatcher_test.cpp
index f625fc4..75ec3b5 100644
--- a/statsd/tests/LogEntryMatcher_test.cpp
+++ b/statsd/tests/LogEntryMatcher_test.cpp
@@ -15,15 +15,16 @@
#include <gtest/gtest.h>
#include <stdio.h>
-#include "annotations.h"
-#include "src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
+#include "src/statsd_config.pb.h"
+#include "stats_annotations.h"
#include "stats_event.h"
#include "stats_log_util.h"
#include "stats_util.h"
#include "statsd_test_util.h"
using namespace android::os::statsd;
+using std::shared_ptr;
using std::unordered_map;
using std::vector;
@@ -31,7 +32,7 @@
const int32_t TAG_ID_2 = 28; // hardcoded tag of atom with uid field
const int FIELD_ID_1 = 1;
const int FIELD_ID_2 = 2;
-const int FIELD_ID_3 = 2;
+const int FIELD_ID_3 = 3;
const int ATTRIBUTION_UID_FIELD_ID = 1;
const int ATTRIBUTION_TAG_FIELD_ID = 2;
@@ -120,7 +121,7 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_writeInt32Array(statsEvent, intArray.data(), intArray.size());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
parseStatsEventToLogEvent(statsEvent, logEvent);
}
@@ -151,11 +152,11 @@
makeIntLogEvent(&event, TAG_ID, 0, 11);
// Test
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Wrong tag id.
simpleMatcher->set_atom_id(TAG_ID + 1);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestAttributionMatcher) {
@@ -186,43 +187,43 @@
fieldMatcher->set_eq_string("some value");
// Tag not matched.
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last node.
attributionMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any node.
attributionMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location4");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Attribution match but primitive field not match.
attributionMatcher->set_position(Position::ANY);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"location2");
fieldMatcher->set_eq_string("wrong value");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldMatcher->set_eq_string("some value");
@@ -232,7 +233,7 @@
ATTRIBUTION_UID_FIELD_ID);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
UidData uidData;
*uidData.add_app_info() = createApplicationInfo(/*uid*/ 1111, /*version*/ 1, "v1", "pkg0");
@@ -243,47 +244,47 @@
uidMap->updateMap(1, uidData);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ "PkG3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ "Pkg2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::FIRST);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ "PkG3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ "Pkg2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::LAST);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ "PkG3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ "Pkg2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Uid + tag.
attributionMatcher->set_position(Position::ANY);
@@ -293,96 +294,96 @@
"pkg0");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
+ "Pkg2");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::FIRST);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
+ "Pkg2");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::LAST);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg0");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
"pkg1");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
+ "Pkg2");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
+ "PkG3");
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
"location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestUidFieldMatcher) {
@@ -407,20 +408,20 @@
// Make event without is_uid annotation.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event1, TAG_ID, 0, 1111);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// Make event with is_uid annotation.
LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true);
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ASTATSLOG_ANNOTATION_ID_IS_UID,
+ true);
// Event has is_uid annotation, so mapping from uid to package name occurs.
simpleMatcher->set_atom_id(TAG_ID_2);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
// Event has is_uid annotation, but uid maps to different package name.
- simpleMatcher->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2"); // package names are normalized
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
}
TEST(AtomMatcherTest, TestRepeatedUidFieldMatcher) {
@@ -449,35 +450,35 @@
fieldValueMatcher->set_position(Position::FIRST);
fieldValueMatcher->set_eq_string("pkg0");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
fieldValueMatcher->set_position(Position::LAST);
fieldValueMatcher->set_eq_string("pkg1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
fieldValueMatcher->set_position(Position::ANY);
- fieldValueMatcher->set_eq_string("pkg2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ fieldValueMatcher->set_eq_string("Pkg2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// is_uid annotation, mapping from uid to package name.
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeRepeatedUidLogEvent(&event2, TAG_ID, intArray);
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
fieldValueMatcher->set_eq_string("pkg0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
fieldValueMatcher->set_eq_string("pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
fieldValueMatcher->set_position(Position::ANY);
fieldValueMatcher->set_eq_string("pkg");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
- fieldValueMatcher->set_eq_string("pkg2"); // package names are normalized
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
+ fieldValueMatcher->set_eq_string("Pkg2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
}
TEST(AtomMatcherTest, TestNeqAnyStringMatcher_SingleString) {
@@ -497,17 +498,17 @@
// First string matched.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event1, TAG_ID, 0, "some value");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// Second string matched.
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event2, TAG_ID, 0, "another value");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
// No strings matched.
LogEvent event3(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event3, TAG_ID, 0, "foo");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
}
TEST(AtomMatcherTest, TestNeqAnyStringMatcher_AttributionUids) {
@@ -543,33 +544,33 @@
auto neqStringList = attributionMatcher->mutable_matches_tuple()
->mutable_field_value_matcher(0)
->mutable_neq_any_string();
- neqStringList->add_str_value("pkg2");
- neqStringList->add_str_value("pkg3");
+ neqStringList->add_str_value("Pkg2");
+ neqStringList->add_str_value("PkG3");
auto fieldMatcher = simpleMatcher->add_field_value_matcher();
fieldMatcher->set_field(FIELD_ID_2);
fieldMatcher->set_eq_string("some value");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->Clear();
neqStringList->add_str_value("pkg1");
- neqStringList->add_str_value("pkg3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ neqStringList->add_str_value("PkG3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::ANY);
neqStringList->Clear();
neqStringList->add_str_value("maps.com");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->Clear();
neqStringList->add_str_value("PkG3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::LAST);
neqStringList->Clear();
neqStringList->add_str_value("AID_STATSD");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
@@ -612,29 +613,29 @@
fieldMatcher->set_field(FIELD_ID_2);
fieldMatcher->set_eq_string("some value");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
attributionMatcher->set_position(Position::ANY);
eqStringList->Clear();
eqStringList->add_str_value("AID_STATSD");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->Clear();
eqStringList->add_str_value("pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
auto normalStringField = fieldMatcher->mutable_eq_any_string();
normalStringField->add_str_value("some value123");
normalStringField->add_str_value("some value");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
normalStringField->Clear();
normalStringField->add_str_value("AID_STATSD");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->Clear();
eqStringList->add_str_value("maps.com");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestBoolMatcher) {
@@ -655,19 +656,19 @@
// Test
keyValue1->set_eq_bool(true);
keyValue2->set_eq_bool(false);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue1->set_eq_bool(false);
keyValue2->set_eq_bool(false);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue1->set_eq_bool(false);
keyValue2->set_eq_bool(true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue1->set_eq_bool(true);
keyValue2->set_eq_bool(true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestStringMatcher) {
@@ -685,7 +686,7 @@
makeStringLogEvent(&event, TAG_ID, 0, "some value");
// Test
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestIntMatcher_EmptyRepeatedField) {
@@ -705,16 +706,16 @@
// Match first int.
fieldValueMatcher->set_position(Position::FIRST);
fieldValueMatcher->set_eq_int(9);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last int.
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any int.
fieldValueMatcher->set_position(Position::ANY);
fieldValueMatcher->set_eq_int(13);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestIntMatcher_RepeatedIntField) {
@@ -735,28 +736,28 @@
fieldValueMatcher->set_field(FIELD_ID_1);
fieldValueMatcher->set_position(Position::FIRST);
fieldValueMatcher->set_eq_int(9);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_int(21);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last int.
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_int(9);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any int.
fieldValueMatcher->set_position(Position::ANY);
fieldValueMatcher->set_eq_int(13);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_int(21);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_int(9);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestLtIntMatcher_RepeatedIntField) {
@@ -777,34 +778,34 @@
fieldValueMatcher->set_field(FIELD_ID_1);
fieldValueMatcher->set_position(Position::FIRST);
fieldValueMatcher->set_lt_int(9);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(21);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(23);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last int.
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(9);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(8);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any int.
fieldValueMatcher->set_position(Position::ANY);
fieldValueMatcher->set_lt_int(21);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(8);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_lt_int(23);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestStringMatcher_RepeatedStringField) {
@@ -825,31 +826,31 @@
fieldValueMatcher->set_field(FIELD_ID_1);
fieldValueMatcher->set_position(Position::FIRST);
fieldValueMatcher->set_eq_string("str2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_string("str1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last int.
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_string("str3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any int.
fieldValueMatcher->set_position(Position::ANY);
fieldValueMatcher->set_eq_string("str4");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_string("str1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_string("str2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_eq_string("str3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestEqAnyStringMatcher_RepeatedStringField) {
@@ -870,43 +871,43 @@
StringListMatcher* eqStringList = fieldValueMatcher->mutable_eq_any_string();
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->add_str_value("str4");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->add_str_value("str2");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->add_str_value("str3");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
eqStringList->add_str_value("str1");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestNeqAnyStringMatcher_RepeatedStringField) {
@@ -927,43 +928,43 @@
StringListMatcher* neqStringList = fieldValueMatcher->mutable_neq_any_string();
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->add_str_value("str4");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->add_str_value("str2");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->add_str_value("str3");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
neqStringList->add_str_value("str1");
fieldValueMatcher->set_position(Position::FIRST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
fieldValueMatcher->set_position(Position::ANY);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
@@ -984,15 +985,15 @@
// Test
keyValue1->set_eq_int(2);
keyValue2->set_eq_int(3);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue1->set_eq_int(2);
keyValue2->set_eq_int(4);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue1->set_eq_int(4);
keyValue2->set_eq_int(3);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestIntComparisonMatcher) {
@@ -1013,43 +1014,43 @@
// eq_int
keyValue->set_eq_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_eq_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_eq_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// lt_int
keyValue->set_lt_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_lt_int(11);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_lt_int(12);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// lte_int
keyValue->set_lte_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_lte_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_lte_int(12);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// gt_int
keyValue->set_gt_int(10);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_gt_int(11);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_gt_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// gte_int
keyValue->set_gte_int(10);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_gte_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
keyValue->set_gte_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
}
TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
@@ -1065,20 +1066,20 @@
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
keyValue->set_lt_float(10.0);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
LogEvent event3(/*uid=*/0, /*pid=*/0);
makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
keyValue->set_gt_float(10.0);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
LogEvent event4(/*uid=*/0, /*pid=*/0);
makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4).matched);
}
// Helper for the composite matchers.
@@ -1225,17 +1226,17 @@
// Event without is_uid annotation.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event1, TAG_ID, 0, 1111);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// Event where mapping from uid to package name occurs.
LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID, 1111, ANNOTATION_ID_IS_UID, true);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID, 1111, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
// Event where uid maps to package names that don't fit wildcard pattern.
LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event3, TAG_ID, 3333, ANNOTATION_ID_IS_UID, true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+ makeIntWithBoolAnnotationLogEvent(&event3, TAG_ID, 3333, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
// Update matcher to match one AID
simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string(
@@ -1243,30 +1244,30 @@
// Event where mapping from uid to aid doesn't fit wildcard pattern.
LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event4, TAG_ID, 1005, ANNOTATION_ID_IS_UID, true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+ makeIntWithBoolAnnotationLogEvent(&event4, TAG_ID, 1005, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4).matched);
// Event where mapping from uid to aid does fit wildcard pattern.
LogEvent event5(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event5, TAG_ID, 1000, ANNOTATION_ID_IS_UID, true);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event5));
+ makeIntWithBoolAnnotationLogEvent(&event5, TAG_ID, 1000, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event5).matched);
// Update matcher to match multiple AIDs
simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string("AID_SDCARD_*");
// Event where mapping from uid to aid doesn't fit wildcard pattern.
LogEvent event6(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event6, TAG_ID, 1036, ANNOTATION_ID_IS_UID, true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6));
+ makeIntWithBoolAnnotationLogEvent(&event6, TAG_ID, 1036, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6).matched);
// Event where mapping from uid to aid does fit wildcard pattern.
LogEvent event7(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event7, TAG_ID, 1034, ANNOTATION_ID_IS_UID, true);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+ makeIntWithBoolAnnotationLogEvent(&event7, TAG_ID, 1034, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7).matched);
LogEvent event8(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event8, TAG_ID, 1035, ANNOTATION_ID_IS_UID, true);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+ makeIntWithBoolAnnotationLogEvent(&event8, TAG_ID, 1035, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8).matched);
}
TEST(AtomMatcherTest, TestWildcardStringMatcher) {
@@ -1283,55 +1284,57 @@
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event1, TAG_ID, 0, "test.string:test_0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event2, TAG_ID, 0, "test.string:test_19");
- EXPECT_FALSE(
- matchesSimple(uidMap, *simpleMatcher, event2)); // extra character at end of string
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)
+ .matched); // extra character at end of string
LogEvent event3(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event3, TAG_ID, 0, "extra.test.string:test_1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
- event3)); // extra characters at beginning of string
+ event3)
+ .matched); // extra characters at beginning of string
LogEvent event4(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event4, TAG_ID, 0, "test.string:test_");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
- event4)); // missing character from 0-9 at end of string
+ event4)
+ .matched); // missing character from 0-9 at end of string
LogEvent event5(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event5, TAG_ID, 0, "est.string:test_1");
- EXPECT_FALSE(
- matchesSimple(uidMap, *simpleMatcher, event5)); // missing 't' at beginning of string
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event5)
+ .matched); // missing 't' at beginning of string
LogEvent event6(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event6, TAG_ID, 0, "test.string:test_1extra");
- EXPECT_FALSE(
- matchesSimple(uidMap, *simpleMatcher, event6)); // extra characters at end of string
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6)
+ .matched); // extra characters at end of string
// Matches any string that contains "test.string:test_" + any extra characters before or after
fieldValueMatcher->set_eq_wildcard_string("*test.string:test_*");
LogEvent event7(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event7, TAG_ID, 0, "test.string:test_");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7).matched);
LogEvent event8(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event8, TAG_ID, 0, "extra.test.string:test_");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8).matched);
LogEvent event9(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event9, TAG_ID, 0, "test.string:test_extra");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event9));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event9).matched);
LogEvent event10(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event10, TAG_ID, 0, "est.string:test_");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event10));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event10).matched);
LogEvent event11(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event11, TAG_ID, 0, "test.string:test");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event11));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event11).matched);
}
TEST(AtomMatcherTest, TestEqAnyWildcardStringMatcher) {
@@ -1351,17 +1354,17 @@
// First wildcard pattern matched.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event1, TAG_ID, 0, "first_string_1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// Second wildcard pattern matched.
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event2, TAG_ID, 0, "second_string_1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
// No wildcard patterns matched.
LogEvent event3(/*uid=*/0, /*pid=*/0);
makeStringLogEvent(&event3, TAG_ID, 0, "third_string_1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
}
TEST(AtomMatcherTest, TestNeqAnyWildcardStringMatcher) {
@@ -1388,27 +1391,27 @@
// First tag is not matched. neq string list {"tag"}
neqWildcardStrList->add_str_value("tag");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// First tag is matched. neq string list {"tag", "location_*"}
neqWildcardStrList->add_str_value("location_*");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last tag.
attributionMatcher->set_position(Position::LAST);
// Last tag is not matched. neq string list {"tag", "location_*"}
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Last tag is matched. neq string list {"tag", "location_*", "location*"}
neqWildcardStrList->add_str_value("location*");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any tag.
attributionMatcher->set_position(Position::ANY);
// All tags are matched. neq string list {"tag", "location_*", "location*"}
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Set up another log event.
std::vector<string> attributionTags2 = {"location_1", "location", "string"};
@@ -1416,7 +1419,7 @@
makeAttributionLogEvent(&event2, TAG_ID, 0, attributionUids, attributionTags2, "some value");
// Tag "string" is not matched. neq string list {"tag", "location_*", "location*"}
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
}
TEST(AtomMatcherTest, TestEqAnyIntMatcher) {
@@ -1436,17 +1439,17 @@
// First int matched.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event1, TAG_ID, 0, 3);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
// Second int matched.
LogEvent event2(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event2, TAG_ID, 0, 5);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
// No ints matched.
LogEvent event3(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event3, TAG_ID, 0, 4);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
}
TEST(AtomMatcherTest, TestNeqAnyIntMatcher) {
@@ -1472,31 +1475,593 @@
// First uid is not matched. neq int list {4444}
neqIntList->add_int_value(4444);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// First uid is matched. neq int list {4444, 1111}
neqIntList->add_int_value(1111);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match last uid.
attributionMatcher->set_position(Position::LAST);
// Last uid is not matched. neq int list {4444, 1111}
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Last uid is matched. neq int list {4444, 1111, 3333}
neqIntList->add_int_value(3333);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// Match any uid.
attributionMatcher->set_position(Position::ANY);
// Uid 2222 is not matched. neq int list {4444, 1111, 3333}
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
// All uids are matched. neq int list {4444, 1111, 3333, 2222}
neqIntList->add_int_value(2222);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRoot) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace second field.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(FIELD_ID_2);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "some value");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagFirst) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace first attribution tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::FIRST);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagLast) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace last attribution tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::LAST);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAll) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace all attribution tags.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::ALL);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceNestedAllWithMultipleNestedStringFields) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Manually change uid fields to string fields, as there is no direct way to create a
+ // LogEvent with multiple nested string fields.
+ (*event.getMutableValues())[0].mValue = android::os::statsd::Value("abc1");
+ (*event.getMutableValues())[2].mValue = android::os::statsd::Value("xyz2");
+ (*event.getMutableValues())[4].mValue = android::os::statsd::Value("abc3");
+
+ // Set up the matcher. Replace all attribution tags.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::ALL);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.str_value, "abc1");
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[2].mValue.str_value, "xyz2");
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[4].mValue.str_value, "abc3");
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRootOnMatchedField) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace second field and match on replaced field.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(FIELD_ID_2);
+ fvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+ "some value123");
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "bar");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagFirstOnMatchedField) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace first attribution tag and match on that tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::FIRST);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ attributionTagFvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+ "some value123");
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ attributionTags = {"bar1", "bar2", "bar3"};
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "bar");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "bar2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "bar3");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagLastOnMatchedField) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace last attribution tag and match on that tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::LAST);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ attributionTagFvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+ "some value123");
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ attributionTags = {"bar1", "bar2", "bar3"};
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "bar1");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "bar2");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "bar");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyOnMatchedField) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace all attribution tags but match on any tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::ANY);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ attributionTagFvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+ "some value123");
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ attributionTags = {"foo1", "bar2", "foo3"};
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyAndRootOnMatchedFields) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace all attribution tags but match on any tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::ANY);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ attributionTagFvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+ FieldValueMatcher* rootFvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ rootFvm->set_field(FIELD_ID_2);
+ rootFvm->set_eq_string("blah");
+ stringReplacer = rootFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+ {"location1", "location2", "location3"} /* tags */,
+ "some value123" /* name */);
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+ {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+ {"foo1", "bar2", "foo3"} /* tags */, "blah123" /* name */);
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "blah");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyWithAttributionUidValueMatcher) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the matcher. Replace all attribution tags but match on any uid and tag.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* attributionFvm =
+ matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ attributionFvm->set_field(FIELD_ID_1);
+ attributionFvm->set_position(Position::ANY);
+ FieldValueMatcher* attributionUidFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionUidFvm->set_field(ATTRIBUTION_UID_FIELD_ID);
+ attributionUidFvm->set_eq_int(2222);
+ FieldValueMatcher* attributionTagFvm =
+ attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+ attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ attributionTagFvm->set_eq_string("bar");
+ StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+ {"location1", "location2", "location3"} /* tags */,
+ "some value123" /* name */);
+
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 3223, 3333} /* uids */,
+ {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+ EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+ }
+
+ {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+ {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_NE(transformedEvent, nullptr);
+ const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+ ASSERT_EQ(fieldValues.size(), 7);
+ EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+ EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+ EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+ EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+ EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+ EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+ }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceBadRegex) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace second field.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(FIELD_ID_2);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(
+ R"(*[0-9]+$)"); // bad regex: asterisk not preceded by any expression.
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_EQ(transformedEvent, nullptr);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRegexWithSubgroup) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace second field.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(FIELD_ID_2);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(([a-z]+)[0-9]+$)"); // "([a-z]+)" is a subgroup.
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_EQ(transformedEvent, nullptr);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceNoop) {
+ sp<UidMap> uidMap = new UidMap();
+
+ // Set up the log event.
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+ // Set up the matcher. Replace second field.
+ AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(FIELD_ID_2);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(this_pattern_should_not_match)");
+ stringReplacer->set_replacement("");
+
+ const auto [hasMatched, transformedEvent] =
+ matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+ EXPECT_TRUE(hasMatched);
+ ASSERT_EQ(transformedEvent, nullptr);
}
#else
diff --git a/statsd/tests/LogEvent_test.cpp b/statsd/tests/LogEvent_test.cpp
index c9a90dd..f53e247 100644
--- a/statsd/tests/LogEvent_test.cpp
+++ b/statsd/tests/LogEvent_test.cpp
@@ -16,9 +16,11 @@
#include <gtest/gtest.h>
+#include "flags/FlagProvider.h"
#include "frameworks/proto_logging/stats/atoms.pb.h"
#include "frameworks/proto_logging/stats/enums/stats/launcher/launcher.pb.h"
#include "log/log_event_list.h"
+#include "stats_annotations.h"
#include "stats_event.h"
#include "statsd_test_util.h"
@@ -35,7 +37,8 @@
namespace {
-Field getField(int32_t tag, const vector<int32_t>& pos, int32_t depth, const vector<bool>& last) {
+Field getField(int32_t tag, const vector<int32_t>& pos, int32_t depth,
+ const vector<uint8_t>& last) {
Field f(tag, (int32_t*)pos.data(), depth);
// only decorate last position for depths with repeated fields (depth 1)
@@ -86,6 +89,50 @@
return logEvent->isValid();
}
+bool createAtomLevelIntAnnotationLogEvent(LogEvent* logEvent, uint8_t typeId, uint8_t annotationId,
+ int annotationValue, bool doHeaderPrefetch) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
+ AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue);
+ fillStatsEventWithSampleValue(statsEvent, typeId);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ const uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ if (doHeaderPrefetch) {
+ // Testing LogEvent header prefetch logic
+ const LogEvent::BodyBufferInfo bodyInfo = logEvent->parseHeader(buf, size);
+ logEvent->parseBody(bodyInfo);
+ } else {
+ logEvent->parseBuffer(buf, size);
+ }
+ AStatsEvent_release(statsEvent);
+
+ return logEvent->isValid();
+}
+
+bool createAtomLevelBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t typeId, uint8_t annotationId,
+ bool annotationValue, bool doHeaderPrefetch) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
+ AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
+ fillStatsEventWithSampleValue(statsEvent, typeId);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ const uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ if (doHeaderPrefetch) {
+ // Testing LogEvent header prefetch logic
+ const LogEvent::BodyBufferInfo bodyInfo = logEvent->parseHeader(buf, size);
+ logEvent->parseBody(bodyInfo);
+ } else {
+ logEvent->parseBuffer(buf, size);
+ }
+ AStatsEvent_release(statsEvent);
+
+ return logEvent->isValid();
+}
+
} // anonymous namespace
// Setup for parameterized tests.
@@ -657,8 +704,8 @@
TEST_P(LogEventTest, TestAnnotationIdIsUid) {
LogEvent event(/*uid=*/0, /*pid=*/0);
- EXPECT_TRUE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE, ANNOTATION_ID_IS_UID,
- true,
+ EXPECT_TRUE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE,
+ ASTATSLOG_ANNOTATION_ID_IS_UID, true,
/*doHeaderPrefetch=*/GetParam()));
ASSERT_EQ(event.getNumUidFields(), 1);
@@ -682,7 +729,7 @@
AStatsEvent_setAtomId(statsEvent, 100);
AStatsEvent_writeInt32(statsEvent, 5);
AStatsEvent_writeInt32Array(statsEvent, int32Array, numElements);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeStringArray(statsEvent, cStringArray, numElements);
AStatsEvent_build(statsEvent);
@@ -708,7 +755,7 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 100);
AStatsEvent_writeInt32Array(statsEvent, int32Array, numElements);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_build(statsEvent);
size_t size;
@@ -728,7 +775,7 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 100);
AStatsEvent_writeInt32Array(statsEvent, int32Array, /*numElements*/ 0);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, 5);
AStatsEvent_build(statsEvent);
@@ -748,7 +795,7 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
AStatsEvent_writeInt64Array(statsEvent, int64Array, /*numElements*/ 2);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_build(statsEvent);
size_t size;
@@ -772,7 +819,7 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
AStatsEvent_writeStringArray(statsEvent, cStringArray, /*numElements*/ 2);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_build(statsEvent);
size_t size;
@@ -790,21 +837,22 @@
if (std::get<0>(GetParam()) != INT32_TYPE && std::get<0>(GetParam()) != LIST_TYPE) {
EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_IS_UID, true,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_IS_UID, true,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
TEST_P(LogEventTest, TestAnnotationIdIsUid_NotIntAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
- EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE, ANNOTATION_ID_IS_UID, 10,
+ EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE,
+ ASTATSLOG_ANNOTATION_ID_IS_UID, 10,
/*doHeaderPrefetch=*/GetParam()));
}
TEST_P(LogEventTest, TestAnnotationIdStateNested) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_TRUE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_STATE_NESTED, true,
+ ASTATSLOG_ANNOTATION_ID_STATE_NESTED, true,
/*doHeaderPrefetch=*/GetParam()));
const vector<FieldValue>& values = event.getValues();
@@ -817,7 +865,7 @@
if (std::get<0>(GetParam()) != INT32_TYPE) {
EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_STATE_NESTED, true,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_STATE_NESTED, true,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
@@ -825,14 +873,14 @@
TEST_P(LogEventTest, TestAnnotationIdStateNested_NotIntAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_STATE_NESTED, 10,
+ ASTATSLOG_ANNOTATION_ID_STATE_NESTED, 10,
/*doHeaderPrefetch=*/GetParam()));
}
TEST_P(LogEventTest, TestPrimaryFieldAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_TRUE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_PRIMARY_FIELD, true,
+ ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true,
/*doHeaderPrefetch=*/GetParam()));
const vector<FieldValue>& values = event.getValues();
@@ -845,7 +893,7 @@
if (std::get<0>(GetParam()) == LIST_TYPE || std::get<0>(GetParam()) == ATTRIBUTION_CHAIN_TYPE) {
EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_PRIMARY_FIELD, true,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
@@ -853,14 +901,14 @@
TEST_P(LogEventTest, TestPrimaryFieldAnnotation_NotIntAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_PRIMARY_FIELD, 10,
+ ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, 10,
/*doHeaderPrefetch=*/GetParam()));
}
TEST_P(LogEventTest, TestExclusiveStateAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_TRUE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_EXCLUSIVE_STATE, true,
+ ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true,
/*doHeaderPrefetch=*/GetParam()));
const vector<FieldValue>& values = event.getValues();
@@ -873,7 +921,7 @@
if (std::get<0>(GetParam()) != INT32_TYPE) {
EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_EXCLUSIVE_STATE, true,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
@@ -881,7 +929,7 @@
TEST_P(LogEventTest, TestExclusiveStateAnnotation_NotIntAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_EXCLUSIVE_STATE, 10,
+ ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, 10,
/*doHeaderPrefetch=*/GetParam()));
}
@@ -901,7 +949,8 @@
AStatsEvent_writeInt32(statsEvent, 10);
}
AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
+ true);
AStatsEvent_build(statsEvent);
// Construct LogEvent
@@ -922,46 +971,83 @@
if (std::get<0>(GetParam()) != ATTRIBUTION_CHAIN_TYPE) {
EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
+ true,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
TEST_P(LogEventTest, TestPrimaryFieldFirstUidAnnotation_NotIntAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
- EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(&event, ATTRIBUTION_CHAIN_TYPE,
- ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, 10,
- /*doHeaderPrefetch=*/GetParam()));
+ EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(
+ &event, ATTRIBUTION_CHAIN_TYPE, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, 10,
+ /*doHeaderPrefetch=*/GetParam()));
}
TEST_P(LogEventTest, TestResetStateAnnotation) {
int32_t resetState = 10;
LogEvent event(/*uid=*/0, /*pid=*/0);
- EXPECT_TRUE(createFieldWithIntAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_TRIGGER_STATE_RESET, resetState,
- /*doHeaderPrefetch=*/GetParam()));
+ EXPECT_TRUE(createFieldWithIntAnnotationLogEvent(
+ &event, INT32_TYPE, ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET, resetState,
+ /*doHeaderPrefetch=*/GetParam()));
const vector<FieldValue>& values = event.getValues();
ASSERT_EQ(values.size(), 1);
EXPECT_EQ(event.getResetState(), resetState);
}
+TEST_P(LogEventTest, TestRestrictionCategoryAnnotation) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ int32_t restrictionCategory = ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC;
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_TRUE(createAtomLevelIntAnnotationLogEvent(
+ &event, INT32_TYPE, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, restrictionCategory,
+ /*doHeaderPrefetch=*/GetParam()));
+
+ ASSERT_EQ(event.getRestrictionCategory(), restrictionCategory);
+}
+
+TEST_P(LogEventTest, TestInvalidRestrictionCategoryAnnotation) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ int32_t restrictionCategory = 619; // unknown category
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_FALSE(createAtomLevelIntAnnotationLogEvent(
+ &event, INT32_TYPE, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, restrictionCategory,
+ /*doHeaderPrefetch=*/GetParam()));
+}
+
+TEST_P(LogEventTest, TestRestrictionCategoryAnnotationBelowUDevice) {
+ if (isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ int32_t restrictionCategory = ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC;
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_FALSE(createAtomLevelIntAnnotationLogEvent(
+ &event, INT32_TYPE, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY, restrictionCategory,
+ /*doHeaderPrefetch=*/GetParam()));
+}
+
TEST_P(LogEventTestBadAnnotationFieldTypes, TestResetStateAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
int32_t resetState = 10;
if (std::get<0>(GetParam()) != INT32_TYPE) {
EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(
- &event, std::get<0>(GetParam()), ANNOTATION_ID_TRIGGER_STATE_RESET, resetState,
+ &event, std::get<0>(GetParam()), ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET,
+ resetState,
/*doHeaderPrefetch=*/std::get<1>(GetParam())));
}
}
TEST_P(LogEventTest, TestResetStateAnnotation_NotBoolAnnotation) {
LogEvent event(/*uid=*/0, /*pid=*/0);
- EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE,
- ANNOTATION_ID_TRIGGER_STATE_RESET, true,
- /*doHeaderPrefetch=*/GetParam()));
+ EXPECT_FALSE(createFieldWithBoolAnnotationLogEvent(
+ &event, INT32_TYPE, ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET, true,
+ /*doHeaderPrefetch=*/GetParam()));
}
TEST_P(LogEventTest, TestUidAnnotationWithInt8MaxValues) {
@@ -977,7 +1063,7 @@
AStatsEvent_writeInt32Array(event, int32Array, numElements);
AStatsEvent_writeInt32(event, 10);
AStatsEvent_writeInt32(event, 11);
- AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(event, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_build(event);
size_t size;
@@ -997,7 +1083,7 @@
AStatsEvent_writeInt32(event, 10);
AStatsEvent_writeAttributionChain(event, uids, tags, 0);
- AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_addBoolAnnotation(event, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
AStatsEvent_build(event);
@@ -1010,6 +1096,103 @@
AStatsEvent_release(event);
}
+// Setup for parameterized tests.
+class LogEvent_FieldRestrictionTest : public testing::TestWithParam<std::tuple<int, bool>> {
+public:
+ static std::string ToString(testing::TestParamInfo<std::tuple<int, bool>> info) {
+ const std::string boolName = std::get<1>(info.param) ? "_prefetchTrue" : "_prefetchFalse";
+
+ switch (std::get<0>(info.param)) {
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO:
+ return "PeripheralDeviceInfo" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE:
+ return "AppUsage" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY:
+ return "AppActivity" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT:
+ return "HealthConnect" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY:
+ return "Accessibility" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH:
+ return "SystemSearch" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT:
+ return "UserEngagement" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING:
+ return "AmbientSensing" + boolName;
+ case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION:
+ return "DemographicClassification" + boolName;
+ default:
+ return "Unknown" + boolName;
+ }
+ }
+ void TearDown() override {
+ FlagProvider::getInstance().resetOverrides();
+ }
+};
+
+// TODO(b/222539899): Add BOOL_TYPE value once parseAnnotations is updated to check specific
+// typeIds. BOOL_TYPE should be a bad field type for is_uid, nested, and reset state annotations.
+INSTANTIATE_TEST_SUITE_P(
+ LogEvent_FieldRestrictionTest, LogEvent_FieldRestrictionTest,
+ testing::Combine(
+ testing::Values(
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING,
+ ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION),
+ testing::Bool()),
+ LogEvent_FieldRestrictionTest::ToString);
+
+TEST_P(LogEvent_FieldRestrictionTest, TestFieldRestrictionAnnotation) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_TRUE(
+ createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE, std::get<0>(GetParam()), true,
+ /*doHeaderPrefetch=*/std::get<1>(GetParam())));
+ // Some basic checks to make sure the event is parsed correctly.
+ EXPECT_EQ(event.GetTagId(), 100);
+ ASSERT_EQ(event.getValues().size(), 1);
+ EXPECT_EQ(event.getValues()[0].mValue.getType(), Type::INT);
+}
+
+TEST_P(LogEvent_FieldRestrictionTest, TestInvalidAnnotationIntType) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_FALSE(createFieldWithIntAnnotationLogEvent(
+ &event, STRING_TYPE, std::get<0>(GetParam()),
+ /*random int*/ 15, /*doHeaderPrefetch=*/std::get<1>(GetParam())));
+}
+
+TEST_P(LogEvent_FieldRestrictionTest, TestInvalidAnnotationAtomLevel) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_FALSE(createAtomLevelBoolAnnotationLogEvent(
+ &event, STRING_TYPE, std::get<0>(GetParam()), true,
+ /*doHeaderPrefetch=*/std::get<1>(GetParam())));
+}
+
+TEST_P(LogEvent_FieldRestrictionTest, TestRestrictionCategoryAnnotationBelowUDevice) {
+ if (isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ int32_t restrictionCategory = ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC;
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ EXPECT_FALSE(
+ createFieldWithBoolAnnotationLogEvent(&event, INT32_TYPE, std::get<0>(GetParam()), true,
+ /*doHeaderPrefetch=*/std::get<1>(GetParam())));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/MetricsManager_test.cpp b/statsd/tests/MetricsManager_test.cpp
index e8a7e07..d86e975 100644
--- a/statsd/tests/MetricsManager_test.cpp
+++ b/statsd/tests/MetricsManager_test.cpp
@@ -48,48 +48,24 @@
namespace statsd {
namespace {
-const ConfigKey kConfigKey(0, 12345);
+const int kConfigId = 12345;
+const ConfigKey kConfigKey(0, kConfigId);
const long timeBaseSec = 1000;
-StatsdConfig buildGoodConfig() {
+StatsdConfig buildGoodRestrictedConfig() {
StatsdConfig config;
- config.set_id(12345);
+ config.set_id(kConfigId);
+ config.set_restricted_metrics_delegate_package_name("delegate");
AtomMatcher* eventMatcher = config.add_atom_matcher();
eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- combination->add_matcher(StringToId("SCREEN_IS_OFF"));
-
- CountMetric* metric = config.add_count_metric();
+ EventMetric* metric = config.add_event_metric();
metric->set_id(3);
metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
return config;
}
@@ -175,11 +151,11 @@
TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) {
string app1 = "app1";
- set<int32_t> app1Uids = {1111, 11111};
+ set<int32_t> app1Uids = {11110, 11111};
string app2 = "app2";
- set<int32_t> app2Uids = {2222};
+ set<int32_t> app2Uids = {22220};
string app3 = "app3";
- set<int32_t> app3Uids = {3333, 1111};
+ set<int32_t> app3Uids = {33330, 11110};
map<string, set<int32_t>> pkgToUids;
pkgToUids[app1] = app1Uids;
@@ -205,7 +181,6 @@
sp<AlarmMonitor> periodicAlarmMonitor;
StatsdConfig config;
- config.add_allowed_log_source("AID_SYSTEM");
config.add_allowed_log_source(app1);
config.add_default_pull_packages("AID_SYSTEM");
config.add_default_pull_packages("AID_ROOT");
@@ -226,7 +201,6 @@
// Update with new allowed log sources.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
newConfig.add_allowed_log_source(app2);
newConfig.add_default_pull_packages("AID_SYSTEM");
newConfig.add_default_pull_packages("AID_STATSD");
@@ -245,10 +219,9 @@
periodicAlarmMonitor);
EXPECT_TRUE(metricsManager.isConfigValid());
- EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_ROOT));
EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app2));
EXPECT_THAT(metricsManager.mAllowedLogSources,
- ContainerEq(unionSet(vector<set<int32_t>>({app2Uids, {AID_ROOT}}))));
+ ContainerEq(unionSet(vector<set<int32_t>>({app2Uids}))));
const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_STATSD};
EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
@@ -264,6 +237,43 @@
UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}})));
}
+struct MetricsManagerServerFlagParam {
+ string flagValue;
+ string label;
+};
+
+class MetricsManagerTest_SPlus
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<MetricsManagerServerFlagParam> {
+protected:
+ void SetUp() override {
+ if (shouldSkipTest()) {
+ GTEST_SKIP() << skipReason();
+ }
+ }
+
+ bool shouldSkipTest() const {
+ return !isAtLeastS();
+ }
+
+ string skipReason() const {
+ return "Skipping MetricsManagerTest_SPlus because device is not S+";
+ }
+
+ std::optional<string> originalFlagValue;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ MetricsManagerTest_SPlus, MetricsManagerTest_SPlus,
+ testing::ValuesIn<MetricsManagerServerFlagParam>({
+ // Server flag values
+ {FLAG_TRUE, "ServerFlagTrue"},
+ {FLAG_FALSE, "ServerFlagFalse"},
+ }),
+ [](const testing::TestParamInfo<MetricsManagerTest_SPlus::ParamType>& info) {
+ return info.param.label;
+ });
+
TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
sp<UidMap> uidMap;
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -277,7 +287,8 @@
MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
- LogEvent event(0 /* uid */, 0 /* pid */);
+ const int32_t customAppUid = AID_APP_START + 1;
+ LogEvent event(customAppUid /* uid */, 0 /* pid */);
CreateNoValuesLogEvent(&event, 10 /* atom id */, 0 /* timestamp */);
EXPECT_FALSE(metricsManager.checkLogCredentials(event));
@@ -294,7 +305,7 @@
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config = buildGoodConfig(kConfigId);
config.add_allowed_log_source("AID_SYSTEM");
config.add_whitelisted_atom_ids(3);
config.add_whitelisted_atom_ids(4);
@@ -316,6 +327,54 @@
EXPECT_FALSE(metricsManager.isConfigValid());
}
+TEST_P(MetricsManagerTest_SPlus, TestRestrictedMetricsConfig) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config = buildGoodRestrictedConfig();
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.set_restricted_metrics_delegate_package_name("rm");
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ if (isAtLeastU()) {
+ EXPECT_TRUE(metricsManager.isConfigValid());
+ } else {
+ EXPECT_EQ(metricsManager.mInvalidConfigReason,
+ INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
+ ASSERT_FALSE(metricsManager.isConfigValid());
+ }
+}
+
+TEST_P(MetricsManagerTest_SPlus, TestRestrictedMetricsConfigUpdate) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config = buildGoodRestrictedConfig();
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.set_restricted_metrics_delegate_package_name("rm");
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ StatsdConfig config2 = buildGoodRestrictedConfig();
+ metricsManager.updateConfig(config, timeBaseSec, timeBaseSec, anomalyAlarmMonitor,
+ periodicAlarmMonitor);
+
+ if (isAtLeastU()) {
+ EXPECT_TRUE(metricsManager.isConfigValid());
+ } else {
+ EXPECT_EQ(metricsManager.mInvalidConfigReason,
+ INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED);
+ ASSERT_FALSE(metricsManager.isConfigValid());
+ }
+}
+
TEST(MetricsManagerTest, TestMaxMetricsMemoryKb) {
sp<UidMap> uidMap;
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -323,7 +382,7 @@
sp<AlarmMonitor> periodicAlarmMonitor;
size_t memoryLimitKb = 8 * 1024;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config = buildGoodConfig(kConfigId);
config.add_allowed_log_source("AID_SYSTEM");
config.set_max_metrics_memory_kb(memoryLimitKb);
@@ -341,7 +400,7 @@
sp<AlarmMonitor> periodicAlarmMonitor;
size_t memoryLimitKb = 8 * 1024;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config = buildGoodConfig(kConfigId);
config.add_allowed_log_source("AID_SYSTEM");
config.set_max_metrics_memory_kb(memoryLimitKb);
@@ -371,7 +430,7 @@
size_t memoryLimitKb = (StatsdStats::kHardMaxMetricsBytesPerConfig / 1024) + 1;
size_t defaultMemoryLimit = StatsdStats::kDefaultMaxMetricsBytesPerConfig;
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config = buildGoodConfig(kConfigId);
config.add_allowed_log_source("AID_SYSTEM");
config.set_max_metrics_memory_kb(memoryLimitKb);
@@ -383,6 +442,91 @@
EXPECT_TRUE(metricsManager.isConfigValid());
}
+TEST(MetricsManagerTest, TestGetTriggerMemoryKb) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ size_t memoryLimitKb = 8 * 1024;
+
+ StatsdConfig config = buildGoodConfig(kConfigId);
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.set_soft_metrics_memory_kb(memoryLimitKb);
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ EXPECT_EQ(memoryLimitKb, metricsManager.getTriggerGetDataBytes() / 1024);
+ EXPECT_TRUE(metricsManager.isConfigValid());
+}
+
+TEST(MetricsManagerTest, TestGetTriggerMemoryKbOnConfigUpdate) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ size_t memoryLimitKb = 8 * 1024;
+
+ StatsdConfig config = buildGoodConfig(kConfigId);
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.set_soft_metrics_memory_kb(memoryLimitKb);
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ EXPECT_EQ(memoryLimitKb, metricsManager.getTriggerGetDataBytes() / 1024);
+ EXPECT_TRUE(metricsManager.isConfigValid());
+
+ // Update with new memory limit
+ size_t newMemoryLimitKb = 9 * 1024;
+ StatsdConfig newConfig;
+ newConfig.add_allowed_log_source("AID_SYSTEM");
+ newConfig.set_soft_metrics_memory_kb(newMemoryLimitKb);
+
+ metricsManager.updateConfig(newConfig, timeBaseSec, timeBaseSec, anomalyAlarmMonitor,
+ periodicAlarmMonitor);
+ EXPECT_EQ(newMemoryLimitKb, metricsManager.getTriggerGetDataBytes() / 1024);
+ EXPECT_TRUE(metricsManager.isConfigValid());
+}
+
+TEST(MetricsManagerTest, TestGetTriggerMemoryKbInvalid) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ size_t memoryLimitKb = (StatsdStats::kHardMaxTriggerGetDataBytes / 1024) + 1;
+ size_t defaultMemoryLimit = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
+
+ StatsdConfig config = buildGoodConfig(kConfigId);
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.set_soft_metrics_memory_kb(memoryLimitKb);
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ // Since 10MB + 1B is invalid for the memory limit, we default back to 192KB
+ EXPECT_EQ(defaultMemoryLimit, metricsManager.getTriggerGetDataBytes());
+ EXPECT_TRUE(metricsManager.isConfigValid());
+}
+
+TEST(MetricsManagerTest, TestGetTriggerMemoryKbUnset) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+ size_t defaultMemoryLimit = StatsdStats::kDefaultBytesPerConfigTriggerGetData;
+
+ StatsdConfig config = buildGoodConfig(kConfigId);
+ config.add_allowed_log_source("AID_SYSTEM");
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ // Since the memory limit is unset, we default back to 192KB
+ EXPECT_EQ(defaultMemoryLimit, metricsManager.getTriggerGetDataBytes());
+ EXPECT_TRUE(metricsManager.isConfigValid());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/SocketListener_test.cpp b/statsd/tests/SocketListener_test.cpp
index 42c084b..cc5475e 100644
--- a/statsd/tests/SocketListener_test.cpp
+++ b/statsd/tests/SocketListener_test.cpp
@@ -55,26 +55,24 @@
} // namespace
-void generateAtomLogging(const std::shared_ptr<LogEventQueue>& queue,
- const std::shared_ptr<LogEventFilter>& filter, int eventCount,
+void generateAtomLogging(LogEventQueue& queue, const LogEventFilter& filter, int eventCount,
int startAtomId) {
// create number of AStatsEvent
for (int i = 0; i < eventCount; i++) {
AStatsEventWrapper event(startAtomId + i);
auto [buf, size] = event.getBuffer();
- StatsSocketListener::processMessage(buf, size, kTestUid, kTestPid, queue, filter);
+ StatsSocketListener::processStatsEventBuffer(buf, size, kTestUid, kTestPid, queue, filter);
}
}
-class SocketParseMessageTestNoFiltering : public testing::TestWithParam<bool> {
+class SocketParseMessageTest : public testing::TestWithParam<bool> {
protected:
- std::shared_ptr<LogEventQueue> mEventQueue;
- std::shared_ptr<LogEventFilter> mLogEventFilter;
+ LogEventQueue mEventQueue;
+ LogEventFilter mLogEventFilter;
public:
- SocketParseMessageTestNoFiltering()
- : mEventQueue(std::make_shared<LogEventQueue>(kEventCount /*buffer limit*/)),
- mLogEventFilter(GetParam() ? std::make_shared<LogEventFilter>() : nullptr) {
+ SocketParseMessageTest() : mEventQueue(kEventCount /*buffer limit*/) {
+ mLogEventFilter.setFilteringEnabled(GetParam());
}
static std::string ToString(testing::TestParamInfo<bool> info) {
@@ -82,100 +80,61 @@
}
};
-INSTANTIATE_TEST_SUITE_P(SocketParseMessageTestNoFiltering, SocketParseMessageTestNoFiltering,
- testing::Bool(), SocketParseMessageTestNoFiltering::ToString);
+INSTANTIATE_TEST_SUITE_P(SocketParseMessageTest, SocketParseMessageTest, testing::Bool(),
+ SocketParseMessageTest::ToString);
-TEST_P(SocketParseMessageTestNoFiltering, TestProcessMessageNoFiltering) {
- if (GetParam()) {
- mLogEventFilter->setFilteringEnabled(false);
- }
+TEST_P(SocketParseMessageTest, TestProcessMessage) {
+ StatsdStats::getInstance().reset();
generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
+ int64_t lastEventTs = 0;
// check content of the queue
- EXPECT_EQ(kEventCount, mEventQueue->mQueue.size());
+ EXPECT_EQ(kEventCount, mEventQueue.mQueue.size());
for (int i = 0; i < kEventCount; i++) {
- auto logEvent = mEventQueue->waitPop();
+ auto logEvent = mEventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
- EXPECT_FALSE(logEvent->isParsedHeaderOnly());
+ EXPECT_EQ(logEvent->isParsedHeaderOnly(), GetParam());
+ lastEventTs = logEvent->GetElapsedTimestampNs();
}
+
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, kEventCount);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, lastEventTs);
}
-TEST_P(SocketParseMessageTestNoFiltering, TestProcessMessageNoFilteringWithEmptySetExplicitSet) {
- if (GetParam()) {
- mLogEventFilter->setFilteringEnabled(false);
- LogEventFilter::AtomIdSet idsList;
- mLogEventFilter->setAtomIds(idsList, nullptr);
- }
-
- generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
-
- // check content of the queue
- EXPECT_EQ(kEventCount, mEventQueue->mQueue.size());
- for (int i = 0; i < kEventCount; i++) {
- auto logEvent = mEventQueue->waitPop();
- EXPECT_TRUE(logEvent->isValid());
- EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
- EXPECT_FALSE(logEvent->isParsedHeaderOnly());
- }
-}
-
-TEST(SocketParseMessageTest, TestProcessMessageFilterEmptySet) {
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(kEventCount /*buffer limit*/);
-
- std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
-
- generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
-
- // check content of the queue
- for (int i = 0; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
- EXPECT_TRUE(logEvent->isValid());
- EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
- EXPECT_TRUE(logEvent->isParsedHeaderOnly());
- }
-}
-
-TEST(SocketParseMessageTest, TestProcessMessageFilterEmptySetExplicitSet) {
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(kEventCount /*buffer limit*/);
-
- std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
-
+TEST_P(SocketParseMessageTest, TestProcessMessageEmptySetExplicitSet) {
LogEventFilter::AtomIdSet idsList;
- logEventFilter->setAtomIds(idsList, nullptr);
-
- generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
+ mLogEventFilter.setAtomIds(idsList, nullptr);
+ generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
// check content of the queue
+ EXPECT_EQ(kEventCount, mEventQueue.mQueue.size());
for (int i = 0; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = mEventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
- EXPECT_TRUE(logEvent->isParsedHeaderOnly());
+ EXPECT_EQ(logEvent->isParsedHeaderOnly(), GetParam());
}
}
TEST(SocketParseMessageTest, TestProcessMessageFilterCompleteSet) {
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(kEventCount /*buffer limit*/);
+ LogEventQueue eventQueue(kEventCount /*buffer limit*/);
- std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
+ LogEventFilter logEventFilter;
LogEventFilter::AtomIdSet idsList;
for (int i = 0; i < kEventCount; i++) {
idsList.insert(kAtomId + i);
}
- logEventFilter->setAtomIds(idsList, nullptr);
+ logEventFilter.setAtomIds(idsList, nullptr);
generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
// check content of the queue
- EXPECT_EQ(kEventCount, eventQueue->mQueue.size());
+ EXPECT_EQ(kEventCount, eventQueue.mQueue.size());
for (int i = 0; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
EXPECT_FALSE(logEvent->isParsedHeaderOnly());
@@ -183,30 +142,29 @@
}
TEST(SocketParseMessageTest, TestProcessMessageFilterPartialSet) {
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(kEventCount /*buffer limit*/);
+ LogEventQueue eventQueue(kEventCount /*buffer limit*/);
- std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
+ LogEventFilter logEventFilter;
LogEventFilter::AtomIdSet idsList;
for (int i = 0; i < kEventFilteredCount; i++) {
idsList.insert(kAtomId + i);
}
- logEventFilter->setAtomIds(idsList, nullptr);
+ logEventFilter.setAtomIds(idsList, nullptr);
generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
// check content of the queue
- EXPECT_EQ(kEventCount, eventQueue->mQueue.size());
+ EXPECT_EQ(kEventCount, eventQueue.mQueue.size());
for (int i = 0; i < kEventFilteredCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
EXPECT_FALSE(logEvent->isParsedHeaderOnly());
}
for (int i = kEventFilteredCount; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
EXPECT_TRUE(logEvent->isParsedHeaderOnly());
@@ -214,41 +172,40 @@
}
TEST(SocketParseMessageTest, TestProcessMessageFilterToggle) {
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(kEventCount * 3 /*buffer limit*/);
+ LogEventQueue eventQueue(kEventCount * 3 /*buffer limit*/);
- std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
+ LogEventFilter logEventFilter;
LogEventFilter::AtomIdSet idsList;
for (int i = 0; i < kEventFilteredCount; i++) {
idsList.insert(kAtomId + i);
}
// events with ids from kAtomId to kAtomId + kEventFilteredCount should not be skipped
- logEventFilter->setAtomIds(idsList, nullptr);
+ logEventFilter.setAtomIds(idsList, nullptr);
generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
- logEventFilter->setFilteringEnabled(false);
+ logEventFilter.setFilteringEnabled(false);
// since filtering is disabled - events with any ids should not be skipped
// will generate events with ids [kAtomId + kEventCount, kAtomId + kEventCount * 2]
generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId + kEventCount);
- logEventFilter->setFilteringEnabled(true);
+ logEventFilter.setFilteringEnabled(true);
LogEventFilter::AtomIdSet idsList2;
for (int i = kEventFilteredCount; i < kEventCount; i++) {
idsList2.insert(kAtomId + kEventCount * 2 + i);
}
// events with idsList2 ids should not be skipped
- logEventFilter->setAtomIds(idsList2, nullptr);
+ logEventFilter.setAtomIds(idsList2, nullptr);
// will generate events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId + kEventCount * 2);
// check content of the queue
- EXPECT_EQ(kEventCount * 3, eventQueue->mQueue.size());
+ EXPECT_EQ(kEventCount * 3, eventQueue.mQueue.size());
// events with ids from kAtomId to kAtomId + kEventFilteredCount should not be skipped
for (int i = 0; i < kEventFilteredCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
EXPECT_FALSE(logEvent->isParsedHeaderOnly());
@@ -256,7 +213,7 @@
// all events above kAtomId + kEventFilteredCount to kAtomId + kEventCount should be skipped
for (int i = kEventFilteredCount; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
EXPECT_TRUE(logEvent->isParsedHeaderOnly());
@@ -265,7 +222,7 @@
// events with ids [kAtomId + kEventCount, kAtomId + kEventCount * 2] should not be skipped
// since wiltering was disabled at that time
for (int i = 0; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + kEventCount + i, logEvent->GetTagId());
EXPECT_FALSE(logEvent->isParsedHeaderOnly());
@@ -274,7 +231,7 @@
// first half events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
// should be skipped
for (int i = 0; i < kEventFilteredCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + kEventCount * 2 + i, logEvent->GetTagId());
EXPECT_TRUE(logEvent->isParsedHeaderOnly());
@@ -283,7 +240,7 @@
// second half events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
// should be processed
for (int i = kEventFilteredCount; i < kEventCount; i++) {
- auto logEvent = eventQueue->waitPop();
+ auto logEvent = eventQueue.waitPop();
EXPECT_TRUE(logEvent->isValid());
EXPECT_EQ(kAtomId + kEventCount * 2 + i, logEvent->GetTagId());
EXPECT_FALSE(logEvent->isParsedHeaderOnly());
diff --git a/statsd/tests/StatsLogProcessor_test.cpp b/statsd/tests/StatsLogProcessor_test.cpp
index 27cef6b..61e861f 100644
--- a/statsd/tests/StatsLogProcessor_test.cpp
+++ b/statsd/tests/StatsLogProcessor_test.cpp
@@ -30,6 +30,7 @@
#include "statslog_statsdtest.h"
#include "storage/StorageManager.h"
#include "tests/statsd_test_util.h"
+#include "utils/DbUtils.h"
using namespace android;
using namespace testing;
@@ -53,20 +54,29 @@
*/
class MockMetricsManager : public MetricsManager {
public:
- MockMetricsManager()
- : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
+ MockMetricsManager(ConfigKey configKey = ConfigKey(1, 12345))
+ : MetricsManager(configKey, StatsdConfig(), 1000, 1000, new UidMap(),
new StatsPullerManager(),
- new AlarmMonitor(10,
- [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
- [](const shared_ptr<IStatsCompanionService>&) {}),
- new AlarmMonitor(10,
- [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
- [](const shared_ptr<IStatsCompanionService>&) {})) {
+ new AlarmMonitor(
+ 10, [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {}),
+ new AlarmMonitor(
+ 10, [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {})) {
}
MOCK_METHOD0(byteSize, size_t());
MOCK_METHOD1(dropData, void(const int64_t dropTimeNs));
+
+ MOCK_METHOD(void, onLogEvent, (const LogEvent& event), (override));
+
+ MOCK_METHOD(void, onDumpReport,
+ (const int64_t dumpTimeNs, const int64_t wallClockNs,
+ const bool include_current_partial_bucket, const bool erase_data,
+ const DumpLatency dumpLatency, std::set<string>* str_set,
+ android::util::ProtoOutputStream* protoOutput),
+ (override));
};
TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
@@ -78,7 +88,9 @@
StatsLogProcessor p(
m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
[](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, nullptr);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {},
+ std::make_shared<LogEventFilter>());
MockMetricsManager mockMetricsManager;
@@ -103,7 +115,9 @@
broadcastCount++;
return true;
},
- [](const int&, const vector<int64_t>&) { return true; }, nullptr);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {},
+ std::make_shared<LogEventFilter>());
MockMetricsManager mockMetricsManager;
@@ -136,7 +150,9 @@
broadcastCount++;
return true;
},
- [](const int&, const vector<int64_t>&) { return true; }, nullptr);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {},
+ std::make_shared<LogEventFilter>());
MockMetricsManager mockMetricsManager;
@@ -155,7 +171,6 @@
StatsdConfig MakeConfig(bool includeMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
if (includeMetric) {
auto appCrashMatcher = CreateProcessCrashAtomMatcher();
@@ -168,6 +183,44 @@
return config;
}
+StatsdConfig makeRestrictedConfig(bool includeMetric = false) {
+ StatsdConfig config;
+ config.set_restricted_metrics_delegate_package_name("delegate");
+
+ if (includeMetric) {
+ auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = appCrashMatcher;
+ auto eventMetric = config.add_event_metric();
+ eventMetric->set_id(StringToId("EventAppCrashes"));
+ eventMetric->set_what(appCrashMatcher.id());
+ }
+ return config;
+}
+
+class MockRestrictedMetricsManager : public MetricsManager {
+public:
+ MockRestrictedMetricsManager(ConfigKey configKey = ConfigKey(1, 12345))
+ : MetricsManager(configKey, makeRestrictedConfig(), 1000, 1000, new UidMap(),
+ new StatsPullerManager(),
+ new AlarmMonitor(
+ 10, [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {}),
+ new AlarmMonitor(
+ 10, [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {})) {
+ }
+
+ MOCK_METHOD(void, onLogEvent, (const LogEvent& event), (override));
+ MOCK_METHOD(void, onDumpReport,
+ (const int64_t dumpTimeNs, const int64_t wallClockNs,
+ const bool include_current_partial_bucket, const bool erase_data,
+ const DumpLatency dumpLatency, std::set<string>* str_set,
+ android::util::ProtoOutputStream* protoOutput),
+ (override));
+ MOCK_METHOD(size_t, byteSize, (), (override));
+ MOCK_METHOD(void, flushRestrictedData, (), (override));
+};
+
TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
ConfigKey key(3, 4);
StatsdConfig config = MakeConfig(true);
@@ -190,7 +243,8 @@
broadcastCount++;
return true;
},
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &p)).Times(1);
@@ -232,7 +286,8 @@
broadcastCount++;
return true;
},
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &p)).Times(1);
@@ -256,7 +311,6 @@
auto annotation = config.add_annotation();
annotation->set_field_int64(1);
annotation->set_field_int32(2);
- config.add_allowed_log_source("AID_ROOT");
sp<UidMap> m = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -272,7 +326,8 @@
broadcastCount++;
return true;
},
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &p)).Times(1);
@@ -295,7 +350,6 @@
TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
// Setup a simple config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
@@ -355,7 +409,8 @@
StatsLogProcessor p(
m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
[](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &p)).Times(3);
@@ -389,7 +444,8 @@
StatsLogProcessor p(
m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
[](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(CreateAtomIdSetDefault(), &p)).Times(1);
// atom used by matcher defined in MakeConfig() API
@@ -410,7 +466,8 @@
EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size());
StatsdConfig invalidConfig = MakeConfig(true);
- invalidConfig.clear_allowed_log_source();
+ auto invalidCountMetric = invalidConfig.add_count_metric();
+ invalidCountMetric->set_what(0);
p.OnConfigUpdated(0, key, invalidConfig);
EXPECT_EQ(0, p.mMetricsManagers.size());
// The current configs should not contain the invalid config.
@@ -429,7 +486,6 @@
StatsdConfig config1;
int64_t cfgId1 = 12341;
config1.set_id(cfgId1);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -451,7 +507,6 @@
StatsdConfig config2;
int64_t cfgId2 = 12342;
config2.set_id(cfgId2);
- config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config2.add_atom_matcher() = wakelockAcquireMatcher;
long metricId3 = 1234561;
@@ -480,7 +535,6 @@
StatsdConfig config3;
int64_t cfgId3 = 12343;
config3.set_id(cfgId3);
- config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config3.add_atom_matcher() = wakelockAcquireMatcher;
long metricId5 = 1234565;
@@ -534,7 +588,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
// config1,config2,config3 use the same atom
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config1);
@@ -780,7 +834,6 @@
StatsdConfig config1;
config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -904,7 +957,6 @@
// Metric 2: Always active
StatsdConfig config1;
config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -1305,7 +1357,6 @@
// Metric 2: Always active
StatsdConfig config1;
config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -1569,7 +1620,6 @@
// Metric 3: Always active
StatsdConfig config1;
config1.set_id(configId);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
@@ -1624,8 +1674,8 @@
// Send the config.
const sp<UidMap> uidMap = new UidMap();
- const shared_ptr<StatsService> service =
- SharedRefBase::make<StatsService>(uidMap, /* queue */ nullptr, nullptr);
+ const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
string serialized = config1.SerializeAsString();
service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
@@ -1816,7 +1866,8 @@
StatsLogProcessor p(
m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
[](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, mockLogEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
Expectation filterSetFalse =
EXPECT_CALL(*mockLogEventFilter, setFilteringEnabled(false)).Times(1);
@@ -2112,6 +2163,202 @@
EXPECT_EQ(output.reports(0).data_corrupted_reason(1), DATA_CORRUPTED_SOCKET_LOSS);
}
+class StatsLogProcessorTestRestricted : public Test {
+protected:
+ const ConfigKey mConfigKey = ConfigKey(1, 12345);
+ void SetUp() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ }
+ void TearDown() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ FlagProvider::getInstance().resetOverrides();
+ StorageManager::deleteAllFiles(STATS_DATA_DIR);
+ dbutils::deleteDb(mConfigKey);
+ }
+};
+
+TEST_F(StatsLogProcessorTestRestricted, TestInconsistentRestrictedMetricsConfigUpdate) {
+ ConfigKey key(3, 4);
+ StatsdConfig config = makeRestrictedConfig(true);
+ config.set_restricted_metrics_delegate_package_name("rm_package");
+
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ UidData uidData;
+ *uidData.add_app_info() = createApplicationInfo(/*uid*/ 1, /*version*/ 1, "v1", "p1");
+ *uidData.add_app_info() = createApplicationInfo(/*uid*/ 2, /*version*/ 2, "v2", "p2");
+ m->updateMap(1, uidData);
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ std::shared_ptr<MockLogEventFilter> mockLogEventFilter = std::make_shared<MockLogEventFilter>();
+ EXPECT_CALL(*mockLogEventFilter, setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
+ .Times(1);
+ StatsLogProcessor p(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; },
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
+
+ // new newConfig will be the same as config
+ const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
+ EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &p)).Times(2);
+
+ p.OnConfigUpdated(0, key, config);
+
+ EXPECT_EQ(1, p.mMetricsManagers.size());
+ EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end());
+ sp<MetricsManager> oldMetricsManager = p.mMetricsManagers.find(key)->second;
+
+ StatsdConfig newConfig = makeRestrictedConfig(true);
+ newConfig.clear_restricted_metrics_delegate_package_name();
+ p.OnConfigUpdated(/*timestampNs=*/0, key, newConfig);
+
+ ASSERT_NE(p.mMetricsManagers.find(key)->second, oldMetricsManager);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, TestRestrictedLogEventNotPassed) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, StatsdConfig(), mConfigKey);
+ ConfigKey key(3, 4);
+ sp<MockMetricsManager> metricsManager = new MockMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, onLogEvent).Times(0);
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_FALSE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ unique_ptr<LogEvent> event = CreateRestrictedLogEvent(123);
+ EXPECT_TRUE(event->isValid());
+ EXPECT_TRUE(event->isRestricted());
+ processor->OnLogEvent(event.get());
+}
+
+TEST_F(StatsLogProcessorTestRestricted, TestRestrictedLogEventPassed) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, StatsdConfig(), mConfigKey);
+ sp<MockRestrictedMetricsManager> metricsManager = new MockRestrictedMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, onLogEvent).Times(1);
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_TRUE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ unique_ptr<LogEvent> event = CreateRestrictedLogEvent(123);
+ EXPECT_TRUE(event->isValid());
+ EXPECT_TRUE(event->isRestricted());
+ processor->OnLogEvent(event.get());
+}
+
+TEST_F(StatsLogProcessorTestRestricted, RestrictedMetricsManagerOnDumpReportNotCalled) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, makeRestrictedConfig(/*includeMetric=*/true),
+ mConfigKey);
+ sp<MockRestrictedMetricsManager> metricsManager = new MockRestrictedMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, onDumpReport).Times(0);
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_TRUE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ vector<uint8_t> buffer;
+ processor->onConfigMetricsReportLocked(mConfigKey, /*dumpTimeStampNs=*/1, /*wallClockNs=*/0,
+ /*include_current_partial_bucket=*/true,
+ /*erase_data=*/true, GET_DATA_CALLED, FAST,
+ /*dataSavedToDisk=*/true, &buffer);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, RestrictedMetricFlushIfReachMemoryLimit) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, makeRestrictedConfig(/*includeMetric=*/true),
+ mConfigKey);
+ sp<MockRestrictedMetricsManager> metricsManager = new MockRestrictedMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, flushRestrictedData).Times(1);
+ EXPECT_CALL(*metricsManager, byteSize)
+ .Times(1)
+ .WillOnce(Return(StatsdStats::kBytesPerRestrictedConfigTriggerFlush + 1));
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_TRUE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ processor->flushIfNecessaryLocked(mConfigKey, *metricsManager);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, RestrictedMetricNotFlushIfNotReachMemoryLimit) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, makeRestrictedConfig(/*includeMetric=*/true),
+ mConfigKey);
+ sp<MockRestrictedMetricsManager> metricsManager = new MockRestrictedMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, flushRestrictedData).Times(0);
+ EXPECT_CALL(*metricsManager, byteSize)
+ .Times(1)
+ .WillOnce(Return(StatsdStats::kBytesPerRestrictedConfigTriggerFlush - 1));
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_TRUE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ processor->flushIfNecessaryLocked(mConfigKey, *metricsManager);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, NonRestrictedMetricsManagerOnDumpReportCalled) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, MakeConfig(/*includeMetric=*/true), mConfigKey);
+ sp<MockMetricsManager> metricsManager = new MockMetricsManager(mConfigKey);
+ EXPECT_CALL(*metricsManager, onDumpReport).Times(1);
+
+ processor->mMetricsManagers[mConfigKey] = metricsManager;
+ EXPECT_FALSE(processor->mMetricsManagers[mConfigKey]->hasRestrictedMetricsDelegate());
+
+ vector<uint8_t> buffer;
+ processor->onConfigMetricsReportLocked(mConfigKey, /*dumpTimeStampNs=*/1, /*wallClockNs=*/0,
+ /*include_current_partial_bucket=*/true,
+ /*erase_data=*/true, GET_DATA_CALLED, FAST,
+ /*dataSavedToDisk=*/true, &buffer);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, RestrictedMetricOnDumpReportEmpty) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, makeRestrictedConfig(/*includeMetric=*/true),
+ mConfigKey);
+ ProtoOutputStream proto;
+ processor->onDumpReport(mConfigKey, /*dumpTimeStampNs=*/1, /*wallClockNs=*/2,
+ /*include_current_partial_bucket=*/true, /*erase_data=*/true,
+ DEVICE_SHUTDOWN, FAST, &proto);
+ ASSERT_EQ(proto.size(), 0);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, NonRestrictedMetricOnDumpReportNotEmpty) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, MakeConfig(/*includeMetric=*/true), mConfigKey);
+
+ ProtoOutputStream proto;
+ processor->onDumpReport(mConfigKey, /*dumpTimeStampNs=*/1, /*wallClockNs=*/2,
+ /*include_current_partial_bucket=*/true, /*erase_data=*/true,
+ DEVICE_SHUTDOWN, FAST, &proto);
+ ASSERT_NE(proto.size(), 0);
+}
+
+TEST_F(StatsLogProcessorTestRestricted, RestrictedMetricNotWriteToDisk) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, makeRestrictedConfig(/*includeMetric=*/true),
+ mConfigKey);
+
+ processor->WriteDataToDiskLocked(mConfigKey, /*timestampNs=*/0, /*wallClockNs=*/0,
+ CONFIG_UPDATED, FAST);
+
+ ASSERT_FALSE(StorageManager::hasConfigMetricsReport(mConfigKey));
+}
+
+TEST_F(StatsLogProcessorTestRestricted, NonRestrictedMetricWriteToDisk) {
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ /*timeBaseNs=*/1, /*currentTimeNs=*/1, MakeConfig(true), mConfigKey);
+
+ processor->WriteDataToDiskLocked(mConfigKey, /*timestampNs=*/0, /*wallClockNs=*/0,
+ CONFIG_UPDATED, FAST);
+
+ ASSERT_TRUE(StorageManager::hasConfigMetricsReport(mConfigKey));
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/StatsService_test.cpp b/statsd/tests/StatsService_test.cpp
index 5bc2f74..ad85639 100644
--- a/statsd/tests/StatsService_test.cpp
+++ b/statsd/tests/StatsService_test.cpp
@@ -43,7 +43,6 @@
StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType samplingType) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
*config.add_atom_matcher() = atomMatcher;
@@ -68,7 +67,7 @@
TEST(StatsServiceTest, TestAddConfig_simple) {
const sp<UidMap> uidMap = new UidMap();
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
const int kConfigKey = 12345;
const int kCallingUid = 123;
StatsdConfig config;
@@ -87,7 +86,7 @@
TEST(StatsServiceTest, TestAddConfig_empty) {
const sp<UidMap> uidMap = new UidMap();
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
string serialized = "";
const int kConfigKey = 12345;
const int kCallingUid = 123;
@@ -103,7 +102,7 @@
TEST(StatsServiceTest, TestAddConfig_invalid) {
const sp<UidMap> uidMap = new UidMap();
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
string serialized = "Invalid config!";
EXPECT_FALSE(
@@ -122,7 +121,7 @@
const sp<UidMap> uidMap = new UidMap();
shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
service->mEngBuild = true;
// "-1"
@@ -162,7 +161,7 @@
shared_ptr<StatsService> createStatsService() override {
return SharedRefBase::make<StatsService>(new UidMap(), /*queue=*/nullptr,
- /*LogEventFilter=*/nullptr,
+ std::make_shared<LogEventFilter>(),
/*initEventDelaySecs=*/kInitDelaySec);
}
};
diff --git a/statsd/tests/UidMap_test.cpp b/statsd/tests/UidMap_test.cpp
index 2304c74..9e5d7f0 100644
--- a/statsd/tests/UidMap_test.cpp
+++ b/statsd/tests/UidMap_test.cpp
@@ -52,7 +52,7 @@
const vector<string> kApps{kApp1, kApp2, kApp3};
const vector<string> kInstallers{"", "", "com.android.vending"};
const vector<vector<uint8_t>> kCertificateHashes{{'a', 'z'}, {'b', 'c'}, {'d', 'e'}};
-const vector<bool> kDeleted(3, false);
+const vector<uint8_t> kDeleted(3, false);
void sendPackagesToStatsd(shared_ptr<StatsService> service, const vector<int32_t>& uids,
const vector<int64_t>& versions, const vector<string>& versionStrings,
@@ -108,7 +108,9 @@
StatsLogProcessor p(
m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
[](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, nullptr);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {},
+ std::make_shared<LogEventFilter>());
std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent(
1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/);
@@ -125,7 +127,7 @@
TEST(UidMapTest, TestUpdateMap) {
const sp<UidMap> uidMap = new UidMap();
const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
sendPackagesToStatsd(service, kUids, kVersions, kVersionStrings, kApps, kInstallers,
kCertificateHashes);
@@ -157,7 +159,7 @@
TEST(UidMapTest, TestUpdateMapMultiple) {
const sp<UidMap> uidMap = new UidMap();
const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
sendPackagesToStatsd(service, kUids, kVersions, kVersionStrings, kApps, kInstallers,
kCertificateHashes);
@@ -201,7 +203,7 @@
TEST(UidMapTest, TestRemoveApp) {
const sp<UidMap> uidMap = new UidMap();
const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
sendPackagesToStatsd(service, kUids, kVersions, kVersionStrings, kApps, kInstallers,
kCertificateHashes);
@@ -212,7 +214,7 @@
std::set<string> name_set = uidMap->getAppNamesFromUid(1000, true /* returnNormalized */);
EXPECT_THAT(name_set, UnorderedElementsAre(kApp2));
- vector<bool> deleted(kDeleted);
+ vector<uint8_t> deleted(kDeleted);
deleted[0] = true;
vector<PackageInfo> expectedPackageInfos =
buildPackageInfos(kApps, kUids, kVersions, kVersionStrings, kInstallers,
@@ -258,7 +260,7 @@
TEST(UidMapTest, TestUpdateApp) {
const sp<UidMap> uidMap = new UidMap();
const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ nullptr);
+ uidMap, /* queue */ nullptr, std::make_shared<LogEventFilter>());
sendPackagesToStatsd(service, kUids, kVersions, kVersionStrings, kApps, kInstallers,
kCertificateHashes);
@@ -297,7 +299,7 @@
versionStrings[0] = "v40";
vector<string> apps = concatenate(kApps, {"NeW_aPP1_NAmE", "NeW_aPP1_NAmE"});
vector<string> installers = concatenate(kInstallers, {"com.android.vending", "new_installer"});
- vector<bool> deleted = concatenate(kDeleted, {false, false});
+ vector<uint8_t> deleted = concatenate(kDeleted, {false, false});
vector<vector<uint8_t>> certHashes = concatenate(kCertificateHashes, {{'a'}, {'b'}});
vector<PackageInfo> expectedPackageInfos =
buildPackageInfos(apps, uids, versions, versionStrings, installers, certHashes, deleted,
@@ -530,7 +532,7 @@
: config1(1, StringToId("config1")),
uidMap(new UidMap()),
service(SharedRefBase::make<StatsService>(uidMap, /* queue */ nullptr,
- /* LogEventFilter */ nullptr)) {
+ std::make_shared<LogEventFilter>())) {
}
void SetUp() override {
diff --git a/statsd/tests/anomaly/AlarmTracker_test.cpp b/statsd/tests/anomaly/AlarmTracker_test.cpp
index 64ea219..9a4863b 100644
--- a/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -17,8 +17,12 @@
#include <gtest/gtest.h>
#include <log/log_time.h>
#include <stdio.h>
+
#include <vector>
+#include "src/subscriber/SubscriberReporter.h"
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
using std::set;
@@ -32,7 +36,11 @@
namespace os {
namespace statsd {
-const ConfigKey kConfigKey(0, 12345);
+namespace {
+const int kConfigUid = 0;
+const int kConfigId = 12345;
+const ConfigKey kConfigKey(kConfigUid, kConfigId);
+} // anonymous namespace
TEST(AlarmTrackerTest, TestTriggerTimestamp) {
sp<AlarmMonitor> subscriberAlarmMonitor =
@@ -86,6 +94,115 @@
EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
}
+TEST(AlarmTrackerTest, TestProbabilityOfInforming) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test
+ StatsdStats::getInstance();
+ srand(/*commonly used seed=*/0);
+ sp<AlarmMonitor> subscriberAlarmMonitor = new AlarmMonitor(
+ 100, [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {});
+ int broadcastSubRandId = 1, broadcastSubAlwaysId = 2, broadcastSubNeverId = 3;
+
+ int64_t startMillis = 100000000 * MS_PER_SEC;
+ uint64_t currentTimeSec = startMillis / MS_PER_SEC + 15 + 60 * 60;
+
+ // Alarm with probability of informing set to 0.5
+ Alarm alarmRand = createAlarm("alarmRand", /*offsetMillis=*/15 * MS_PER_SEC,
+ /*periodMillis=*/60 * 60 * MS_PER_SEC);
+ alarmRand.set_probability_of_informing(0.5);
+ AlarmTracker trackerRand(startMillis, startMillis, alarmRand, kConfigKey,
+ subscriberAlarmMonitor);
+ Subscription subRand = createSubscription("subRand", /*rule_type=*/Subscription::ALARM,
+ /*rule_id=*/alarmRand.id());
+ subRand.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubRandId);
+ trackerRand.addSubscription(subRand);
+
+ // Alarm with probability of informing set to 1.1 (always; set by default)
+ Alarm alarmAlways = createAlarm("alarmAlways", /*offsetMillis=*/15 * MS_PER_SEC,
+ /*periodMillis=*/60 * 60 * MS_PER_SEC);
+ AlarmTracker trackerAlways(startMillis, startMillis, alarmAlways, kConfigKey,
+ subscriberAlarmMonitor);
+ Subscription subAlways = createSubscription("subAlways", /*rule_type=*/Subscription::ALARM,
+ /*rule_id=*/alarmAlways.id());
+ subAlways.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubAlwaysId);
+ trackerAlways.addSubscription(subAlways);
+
+ // Alarm with probability of informing set to -0.1 (never)
+ Alarm alarmNever = createAlarm("alarmNever", /*offsetMillis=*/15 * MS_PER_SEC,
+ /*periodMillis=*/60 * 60 * MS_PER_SEC);
+ alarmNever.set_probability_of_informing(-0.1);
+ AlarmTracker trackerNever(startMillis, startMillis, alarmNever, kConfigKey,
+ subscriberAlarmMonitor);
+ Subscription subNever = createSubscription("subNever", /*rule_type=*/Subscription::ALARM,
+ /*rule_id=*/alarmNever.id());
+ subNever.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubNeverId);
+ trackerNever.addSubscription(subNever);
+
+ std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
+ subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ ASSERT_EQ(firedAlarmSet.size(), 3u);
+
+ int alarmRandCount = 0, alarmAlwaysCount = 0;
+ // The binder calls here will happen synchronously because they are in-process.
+ shared_ptr<MockPendingIntentRef> randBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*randBroadcast,
+ sendSubscriberBroadcast(kConfigUid, kConfigId, subRand.id(), alarmRand.id(), _, _))
+ .Times(3)
+ .WillRepeatedly([&alarmRandCount] {
+ alarmRandCount++;
+ return Status::ok();
+ });
+
+ shared_ptr<MockPendingIntentRef> alwaysBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*alwaysBroadcast, sendSubscriberBroadcast(kConfigUid, kConfigId, subAlways.id(),
+ alarmAlways.id(), _, _))
+ .Times(10)
+ .WillRepeatedly([&alarmAlwaysCount] {
+ alarmAlwaysCount++;
+ return Status::ok();
+ });
+
+ shared_ptr<MockPendingIntentRef> neverBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*neverBroadcast, sendSubscriberBroadcast(kConfigUid, kConfigId, subNever.id(),
+ alarmNever.id(), _, _))
+ .Times(0);
+
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubRandId,
+ randBroadcast);
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubAlwaysId,
+ alwaysBroadcast);
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubNeverId,
+ neverBroadcast);
+ // Trying to inform the subscription 10x.
+ // Deterministic sequence for trackerRand:
+ // 0.96, 0.95, 0.95, 0.94, 0.43, 0.92, 0.92, 0.41, 0.39, 0.88
+ for (size_t i = 0; i < 10; i++) {
+ trackerRand.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ if (i <= 3) {
+ EXPECT_EQ(alarmRandCount, 0);
+ } else if (i >= 4 && i <= 6) {
+ EXPECT_EQ(alarmRandCount, 1);
+ } else if (i == 7) {
+ EXPECT_EQ(alarmRandCount, 2);
+ } else {
+ EXPECT_EQ(alarmRandCount, 3);
+ }
+ trackerAlways.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+ EXPECT_EQ(alarmAlwaysCount, i + 1);
+ trackerNever.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+
+ currentTimeSec = startMillis / MS_PER_SEC + 15 + (i + 2) * 60 * 60;
+ firedAlarmSet =
+ subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+ }
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubRandId);
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubAlwaysId);
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubNeverId);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/anomaly/AnomalyTracker_test.cpp b/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 0cc8af1..827f8f5 100644
--- a/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -20,10 +20,12 @@
#include <vector>
+#include "src/subscriber/SubscriberReporter.h"
#include "tests/statsd_test_util.h"
using namespace testing;
using android::sp;
+using ::ndk::SharedRefBase;
using std::set;
using std::unordered_map;
using std::vector;
@@ -34,7 +36,11 @@
namespace os {
namespace statsd {
-const ConfigKey kConfigKey(0, 12345);
+namespace {
+const int kConfigUid = 0;
+const int kConfigId = 12345;
+const ConfigKey kConfigKey(kConfigUid, kConfigId);
+} // anonymous namespace
MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
int pos[] = {key, 0, 0};
@@ -68,8 +74,7 @@
}
// Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
-bool detectAnomaliesPass(AnomalyTracker& tracker,
- const int64_t& bucketNum,
+bool detectAnomaliesPass(AnomalyTracker& tracker, int64_t bucketNum,
const std::shared_ptr<DimToValMap>& currentBucket,
const std::set<const MetricDimensionKey>& trueList,
const std::set<const MetricDimensionKey>& falseList) {
@@ -87,10 +92,8 @@
}
// Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
-void detectAndDeclareAnomalies(AnomalyTracker& tracker,
- const int64_t& bucketNum,
- const std::shared_ptr<DimToValMap>& bucket,
- const int64_t& eventTimestamp) {
+void detectAndDeclareAnomalies(AnomalyTracker& tracker, int64_t bucketNum,
+ const std::shared_ptr<DimToValMap>& bucket, int64_t eventTimestamp) {
for (const auto& kv : *bucket) {
tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
kv.second);
@@ -101,9 +104,8 @@
// timestamp (in ns) + refractoryPeriodSec.
// If a timestamp value is negative, instead asserts that the refractory period is inapplicable
// (either non-existant or already past).
-void checkRefractoryTimes(AnomalyTracker& tracker,
- const int64_t& currTimestampNs,
- const int32_t& refractoryPeriodSec,
+void checkRefractoryTimes(AnomalyTracker& tracker, int64_t currTimestampNs,
+ int32_t refractoryPeriodSec,
const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
for (const auto& kv : timestamps) {
if (kv.second < 0) {
@@ -400,6 +402,115 @@
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
}
+TEST(AnomalyTrackerTest, TestProbabilityOfInforming) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test
+ StatsdStats::getInstance();
+ srand(/*commonly used seed=*/0);
+ const int64_t bucketSizeNs = 30 * NS_PER_SEC;
+ const int32_t refractoryPeriodSec = bucketSizeNs / NS_PER_SEC;
+ int broadcastSubRandId = 1, broadcastSubAlwaysId = 2, broadcastSubNeverId = 3;
+
+ // Alert with probability of informing set to 0.5
+ Alert alertRand = createAlert("alertRand", /*metric id=*/0, /*buckets=*/1, /*triggerSum=*/0);
+ alertRand.set_refractory_period_secs(refractoryPeriodSec);
+ alertRand.set_probability_of_informing(0.5);
+ AnomalyTracker anomalyTrackerRand(alertRand, kConfigKey);
+
+ Subscription subRand = createSubscription("subRand", /*rule_type=*/Subscription::ALERT,
+ /*rule_id=*/alertRand.id());
+ subRand.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubRandId);
+ anomalyTrackerRand.addSubscription(subRand);
+
+ // Alert with probability of informing set to 1.1 (always; set by default)
+ Alert alertAlways =
+ createAlert("alertAlways", /*metric id=*/0, /*buckets=*/1, /*triggerSum=*/0);
+ alertAlways.set_refractory_period_secs(refractoryPeriodSec);
+ AnomalyTracker anomalyTrackerAlways(alertAlways, kConfigKey);
+
+ Subscription subAlways = createSubscription("subAlways", /*rule_type=*/Subscription::ALERT,
+ /*rule_id=*/alertAlways.id());
+ subAlways.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubAlwaysId);
+ anomalyTrackerAlways.addSubscription(subAlways);
+
+ // Alert with probability of informing set to -0.1 (never)
+ Alert alertNever = createAlert("alertNever", /*metric id=*/0, /*buckets=*/1, /*triggerSum=*/0);
+ alertNever.set_refractory_period_secs(refractoryPeriodSec);
+ alertNever.set_probability_of_informing(-0.1);
+ AnomalyTracker anomalyTrackerNever(alertNever, kConfigKey);
+
+ Subscription subNever = createSubscription("subNever", /*rule_type=*/Subscription::ALERT,
+ /*rule_id=*/alertNever.id());
+ subNever.mutable_broadcast_subscriber_details()->set_subscriber_id(broadcastSubNeverId);
+ anomalyTrackerNever.addSubscription(subNever);
+
+ // Bucket value needs to be greater than 0 to detect and declare anomaly
+ int bucketValue = 1;
+
+ int alertRandCount = 0, alertAlwaysCount = 0;
+ // The binder calls here will happen synchronously because they are in-process.
+ shared_ptr<MockPendingIntentRef> randBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*randBroadcast,
+ sendSubscriberBroadcast(kConfigUid, kConfigId, subRand.id(), alertRand.id(), _, _))
+ .Times(3)
+ .WillRepeatedly([&alertRandCount] {
+ alertRandCount++;
+ return Status::ok();
+ });
+
+ shared_ptr<MockPendingIntentRef> alwaysBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*alwaysBroadcast, sendSubscriberBroadcast(kConfigUid, kConfigId, subAlways.id(),
+ alertAlways.id(), _, _))
+ .Times(10)
+ .WillRepeatedly([&alertAlwaysCount] {
+ alertAlwaysCount++;
+ return Status::ok();
+ });
+
+ shared_ptr<MockPendingIntentRef> neverBroadcast =
+ SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*neverBroadcast, sendSubscriberBroadcast(kConfigUid, kConfigId, subNever.id(),
+ alertNever.id(), _, _))
+ .Times(0);
+
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubRandId,
+ randBroadcast);
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubAlwaysId,
+ alwaysBroadcast);
+ SubscriberReporter::getInstance().setBroadcastSubscriber(kConfigKey, broadcastSubNeverId,
+ neverBroadcast);
+
+ // Trying to inform the subscription and start the refractory period countdown 10x.
+ // Deterministic sequence for anomalyTrackerRand:
+ // 0.96, 0.95, 0.95, 0.94, 0.43, 0.92, 0.92, 0.41, 0.39, 0.88
+ for (size_t i = 0; i < 10; i++) {
+ int64_t curEventTimestamp = bucketSizeNs * i;
+ anomalyTrackerRand.detectAndDeclareAnomaly(curEventTimestamp, /*bucketNum=*/i,
+ /*metric_id=*/0, DEFAULT_METRIC_DIMENSION_KEY,
+ bucketValue);
+ if (i <= 3) {
+ EXPECT_EQ(alertRandCount, 0);
+ } else if (i >= 4 && i <= 6) {
+ EXPECT_EQ(alertRandCount, 1);
+ } else if (i == 7) {
+ EXPECT_EQ(alertRandCount, 2);
+ } else {
+ EXPECT_EQ(alertRandCount, 3);
+ }
+ anomalyTrackerAlways.detectAndDeclareAnomaly(curEventTimestamp, /*bucketNum=*/i,
+ /*metric_id=*/0, DEFAULT_METRIC_DIMENSION_KEY,
+ bucketValue);
+ EXPECT_EQ(alertAlwaysCount, i + 1);
+ anomalyTrackerNever.detectAndDeclareAnomaly(curEventTimestamp, /*bucketNum=*/i,
+ /*metric_id=*/0, DEFAULT_METRIC_DIMENSION_KEY,
+ bucketValue);
+ }
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubRandId);
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubAlwaysId);
+ SubscriberReporter::getInstance().unsetBroadcastSubscriber(kConfigKey, broadcastSubNeverId);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/condition/SimpleConditionTracker_test.cpp b/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 1407cf4..b38ae4d 100644
--- a/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -146,7 +146,7 @@
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
vector<MatchingState> matcherState;
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
// Matched stop event.
// Check that condition is still false.
@@ -200,7 +200,7 @@
EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
vector<MatchingState> matcherState;
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
// Matched stop event.
// Check that condition is changed to false.
@@ -256,7 +256,7 @@
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
changedCache);
@@ -343,7 +343,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
changedCache);
@@ -419,7 +419,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
@@ -524,7 +524,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
@@ -616,7 +616,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
@@ -714,7 +714,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
changedCache);
@@ -761,7 +761,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
changedCache);
@@ -791,7 +791,7 @@
matcherState.push_back(MatchingState::kNotMatched);
vector<sp<ConditionTracker>> allPredicates;
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ vector<uint8_t> changedCache(1, false);
conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
changedCache);
diff --git a/statsd/tests/e2e/Alarm_e2e_test.cpp b/statsd/tests/e2e/Alarm_e2e_test.cpp
index 93b2783..1a1329d 100644
--- a/statsd/tests/e2e/Alarm_e2e_test.cpp
+++ b/statsd/tests/e2e/Alarm_e2e_test.cpp
@@ -30,7 +30,6 @@
StatsdConfig CreateStatsdConfig() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto alarm = config.add_alarm();
alarm->set_id(123456);
diff --git a/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
index eccaafe..9719888 100644
--- a/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ b/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -31,7 +31,6 @@
StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
diff --git a/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
index abc5a51..26dac91 100644
--- a/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ b/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -40,7 +40,6 @@
DurationMetric::AggregationType aggregationType,
bool nesting) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
diff --git a/statsd/tests/e2e/Attribution_e2e_test.cpp b/statsd/tests/e2e/Attribution_e2e_test.cpp
index e5f80b0..46b6539 100644
--- a/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -31,7 +31,6 @@
StatsdConfig CreateStatsdConfig(const Position position) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
auto attributionNodeMatcher =
wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher();
diff --git a/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index 0bce0ba..63c1e45 100644
--- a/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -30,7 +30,6 @@
StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
diff --git a/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp b/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
index a098482..aa944a9 100644
--- a/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
+++ b/statsd/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
@@ -248,9 +248,10 @@
ConfigKey cfgKey(0, 12345);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
- // Uses AID_ROOT, which isn't in allowed log sources.
+ const int32_t customAppUid = AID_APP_START + 1;
+ // override default uid (which is user running the test)
unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent(
- baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB, customAppUid);
processor->OnLogEvent(event.get());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -304,7 +305,6 @@
TEST_P(ConfigUpdateE2eAbTest, TestExistingGaugePullRandomOneSample) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
AtomMatcher subsystemSleepMatcher =
diff --git a/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp
index ead1c46..66ca283 100644
--- a/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp
+++ b/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp
@@ -50,16 +50,13 @@
sp<StatsLogProcessor> CreateStatsLogProcessor(
const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config,
const ConfigKey& key, const shared_ptr<MockLogEventFilter>& logEventFilter) {
- if (logEventFilter) {
- // call from StatsLogProcessor constructor
- Expectation initCall = EXPECT_CALL(*logEventFilter,
- setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
- .Times(1);
- EXPECT_CALL(*logEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
- .Times(1)
- .After(initCall);
- }
-
+ // call from StatsLogProcessor constructor
+ Expectation initCall =
+ EXPECT_CALL(*logEventFilter, setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
+ .Times(1);
+ EXPECT_CALL(*logEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
+ .Times(1)
+ .After(initCall);
return CreateStatsLogProcessor(timeBaseNs, currentTimeNs, config, key, nullptr, 0, new UidMap(),
logEventFilter);
}
@@ -67,26 +64,17 @@
} // Anonymous namespace.
// Setup for test fixture.
-class ConfigUpdateE2eTest : public testing::TestWithParam<bool> {
+class ConfigUpdateE2eTest : public testing::Test {
protected:
std::shared_ptr<MockLogEventFilter> mLogEventFilter;
void SetUp() override {
- mLogEventFilter = GetParam() ? std::make_shared<MockLogEventFilter>() : nullptr;
- }
-
-public:
- static std::string ToString(testing::TestParamInfo<bool> info) {
- return info.param ? "WithLogEventFilter" : "NoLogEventFilter";
+ mLogEventFilter = std::make_shared<MockLogEventFilter>();
}
};
-INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eTest, ConfigUpdateE2eTest, testing::Bool(),
- ConfigUpdateE2eTest::ToString);
-
-TEST_P(ConfigUpdateE2eTest, TestEventMetric) {
+TEST_F(ConfigUpdateE2eTest, TestEventMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -172,7 +160,6 @@
// Do update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = screenOnMatcher;
*newConfig.add_atom_matcher() = batteryPluggedUsbMatcher;
@@ -193,11 +180,8 @@
*newConfig.add_event_metric() = eventPersist;
int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Send events after the update.
@@ -311,9 +295,8 @@
EXPECT_EQ(data.atom().sync_state_changed().sync_name(), "sync3");
}
-TEST_P(ConfigUpdateE2eTest, TestCountMetric) {
+TEST_F(ConfigUpdateE2eTest, TestCountMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -416,7 +399,6 @@
// Do update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = screenOnMatcher;
*newConfig.add_atom_matcher() = screenOffMatcher;
@@ -436,11 +418,8 @@
*newConfig.add_count_metric() = countPersist;
int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Send events after the update. Counts reset to 0 since this is a new bucket.
@@ -569,9 +548,8 @@
ValidateCountBucket(data.bucket_info(0), updateTimeNs, bucketStartTimeNs + bucketSizeNs, 2);
}
-TEST_P(ConfigUpdateE2eTest, TestDurationMetric) {
+TEST_F(ConfigUpdateE2eTest, TestDurationMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -701,7 +679,6 @@
// Do update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = wakelockReleaseMatcher;
*newConfig.add_atom_matcher() = syncStopMatcher;
@@ -725,11 +702,8 @@
// At update, only uid 1 is syncing & holding a wakelock, duration=33. Max is paused for uid3.
// Before the update, only uid2 will report a duration for max, since others are started/paused.
int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Send events after the update.
@@ -927,9 +901,8 @@
ValidateDurationBucket(data.bucket_info(0), updateTimeNs, bucketEndTimeNs, 20 * NS_PER_SEC);
}
-TEST_P(ConfigUpdateE2eTest, TestGaugeMetric) {
+TEST_F(ConfigUpdateE2eTest, TestGaugeMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
AtomMatcher appStartMatcher = CreateSimpleAtomMatcher("AppStart", util::APP_START_OCCURRED);
@@ -988,15 +961,13 @@
ConfigKey key(123, 987);
uint64_t bucketStartTimeNs = getElapsedRealtimeNs(); // 0:10
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL;
- if (GetParam()) {
- // call from StatsLogProcessor constructor
- Expectation initCall = EXPECT_CALL(*mLogEventFilter,
- setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
- .Times(1);
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
- .Times(1)
- .After(initCall);
- }
+ // call from StatsLogProcessor constructor
+ Expectation initCall =
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
+ .Times(1);
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
+ .Times(1)
+ .After(initCall);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key,
SharedRefBase::make<FakeSubsystemSleepCallback>(),
@@ -1041,7 +1012,6 @@
// Do the update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
newConfig.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
*newConfig.add_atom_matcher() = screenOffMatcher;
@@ -1066,9 +1036,7 @@
int64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;
// Update pulls gaugePullPersist and gaugeNew.
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Verify puller manager is properly set.
@@ -1335,9 +1303,8 @@
EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 902);
}
-TEST_P(ConfigUpdateE2eTest, TestValueMetric) {
+TEST_F(ConfigUpdateE2eTest, TestValueMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
AtomMatcher brightnessMatcher = CreateScreenBrightnessChangedAtomMatcher();
@@ -1389,15 +1356,13 @@
ConfigKey key(123, 987);
uint64_t bucketStartTimeNs = getElapsedRealtimeNs();
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL;
- if (GetParam()) {
- // call from StatsLogProcessor constructor
- Expectation initCall = EXPECT_CALL(*mLogEventFilter,
- setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
- .Times(1);
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
- .Times(1)
- .After(initCall);
- }
+ // call from StatsLogProcessor constructor
+ Expectation initCall =
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
+ .Times(1);
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
+ .Times(1)
+ .After(initCall);
// Config creation triggers pull #1.
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key,
@@ -1425,7 +1390,6 @@
// Do the update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
newConfig.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
*newConfig.add_atom_matcher() = screenOffMatcher;
@@ -1450,9 +1414,7 @@
int64_t updateTimeNs = bucketStartTimeNs + 30 * NS_PER_SEC;
// Update pulls valuePullPersist and valueNew. Pull #3.
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Verify puller manager is properly set.
@@ -1675,9 +1637,8 @@
EXPECT_EQ(skipBucket.drop_event(0).drop_reason(), BucketDropReason::DUMP_REPORT_REQUESTED);
}
-TEST_P(ConfigUpdateE2eTest, TestKllMetric) {
+TEST_F(ConfigUpdateE2eTest, TestKllMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher brightnessMatcher = CreateScreenBrightnessChangedAtomMatcher();
*config.add_atom_matcher() = brightnessMatcher;
@@ -1731,7 +1692,6 @@
// Do the update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = screenOffMatcher;
*newConfig.add_atom_matcher() = unpluggedMatcher;
@@ -1757,11 +1717,8 @@
*newConfig.add_kll_metric() = kllPersist;
int64_t updateTimeNs = bucketStartTimeNs + 30 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Send events after the update. This is a new bucket.
@@ -1893,10 +1850,9 @@
EXPECT_EQ(kllPersistAfter.kll_metrics().skipped_size(), 0);
}
-TEST_P(ConfigUpdateE2eTest, TestMetricActivation) {
+TEST_F(ConfigUpdateE2eTest, TestMetricActivation) {
ALOGE("Start ConfigUpdateE2eTest#TestMetricActivation");
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
string immediateTag = "immediate", bootTag = "boot", childTag = "child";
@@ -2000,7 +1956,6 @@
// Do update. Add matchers/conditions in different order to force indices to change.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
newConfig.set_hash_strings_in_metric_report(false); // Modify metadata for fun.
// Change combination matcher, will mean combination metric isn't active after update.
@@ -2021,11 +1976,8 @@
*newConfig.add_metric_activation() = immediateMetricActivation;
int64_t updateTimeNs = bucketStartTimeNs + 40 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// The reboot will write to disk again, so sleep for 1 second to avoid this.
@@ -2149,9 +2101,8 @@
ALOGE("End ConfigUpdateE2eTest#TestMetricActivation");
}
-TEST_P(ConfigUpdateE2eTest, TestAnomalyCountMetric) {
+TEST_F(ConfigUpdateE2eTest, TestAnomalyCountMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -2285,7 +2236,6 @@
// Do config update.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = wakelockAcquireMatcher;
*newConfig.add_atom_matcher() = syncStartMatcher;
@@ -2325,11 +2275,8 @@
SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast);
int64_t updateTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Within refractory of AlertPreserve, but AlertNew should fire since the full bucket has 2.
@@ -2365,9 +2312,8 @@
SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId);
}
-TEST_P(ConfigUpdateE2eTest, TestAnomalyDurationMetric) {
+TEST_F(ConfigUpdateE2eTest, TestAnomalyDurationMetric) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
@@ -2469,17 +2415,15 @@
SubscriberReporter::getInstance().setBroadcastSubscriber(key, removeSubId, removeBroadcast);
const sp<UidMap> uidMap = new UidMap();
- if (GetParam()) {
- // call from StatsLogProcessor constructor
- Expectation initCall = EXPECT_CALL(*mLogEventFilter,
- setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
- .Times(1);
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
- .Times(1)
- .After(initCall);
- }
- const shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(
- uidMap, /* queue */ nullptr, /* LogEventFilter */ mLogEventFilter);
+ // call from StatsLogProcessor constructor
+ Expectation initCall =
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(StatsLogProcessor::getDefaultAtomIdSet(), _))
+ .Times(1);
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), _))
+ .Times(1)
+ .After(initCall);
+ const shared_ptr<StatsService> service =
+ SharedRefBase::make<StatsService>(uidMap, /* queue */ nullptr, mLogEventFilter);
sp<StatsLogProcessor> processor = service->mProcessor;
uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(TEN_MINUTES) * 1000000LL;
int64_t bucketStartTimeNs = processor->mTimeBaseNs;
@@ -2623,7 +2567,6 @@
// Do config update.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
*newConfig.add_atom_matcher() = wakelockAcquireMatcher;
*newConfig.add_atom_matcher() = screenOffMatcher;
*newConfig.add_atom_matcher() = wakelockReleaseMatcher;
@@ -2666,9 +2609,7 @@
SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast);
int64_t updateTimeNs = bucket2StartTimeNs + 50 * NS_PER_SEC;
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
processor->OnConfigUpdated(updateTimeNs, key, newConfig);
// Alert preserve will set alarm after the refractory period, but alert new will set it for
@@ -2728,9 +2669,8 @@
SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId);
}
-TEST_P(ConfigUpdateE2eTest, TestAlarms) {
+TEST_F(ConfigUpdateE2eTest, TestAlarms) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
Alarm alarmPreserve = createAlarm("AlarmPreserve", /*offset*/ 5 * MS_PER_SEC,
/*period*/ TimeUnitToBucketSizeInMillis(ONE_MINUTE));
Alarm alarmReplace = createAlarm("AlarmReplace", /*offset*/ 1,
@@ -2826,7 +2766,6 @@
// Do config update.
StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
// Change alarm replace's definition.
alarmReplace.set_period_millis(TimeUnitToBucketSizeInMillis(ONE_MINUTE));
@@ -2856,9 +2795,7 @@
return Status::ok();
});
SubscriberReporter::getInstance().setBroadcastSubscriber(key, newSubId, newBroadcast);
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(newConfig), _)).Times(1);
processor->OnConfigUpdated((startTimeSec + 90) * NS_PER_SEC, key, newConfig);
// After the update, the alarm time should remain unchanged since alarm replace now fires every
// minute with no offset.
@@ -2898,9 +2835,8 @@
SubscriberReporter::getInstance().unsetBroadcastSubscriber(key, newSubId);
}
-TEST_P(ConfigUpdateE2eTest, TestNewDurationExistingWhat) {
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhat) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -2930,11 +2866,8 @@
durationMetric->set_bucket(FIVE_MINUTES);
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, config);
event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1,
@@ -2965,9 +2898,8 @@
EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC);
}
-TEST_P(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) {
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
@@ -3025,11 +2957,8 @@
CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, config);
event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13
@@ -3069,9 +2998,8 @@
EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC);
}
-TEST_P(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) {
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -3144,11 +3072,8 @@
CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00
- if (GetParam()) {
- EXPECT_CALL(*mLogEventFilter,
- setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
- .Times(1);
- }
+ EXPECT_CALL(*mLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), processor.get()))
+ .Times(1);
processor->OnConfigUpdated(updateTimeNs, key, config);
event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2,
diff --git a/statsd/tests/e2e/CountMetric_e2e_test.cpp b/statsd/tests/e2e/CountMetric_e2e_test.cpp
index 10fdb8e..f7e5aea 100644
--- a/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -39,7 +39,6 @@
TEST(CountMetricE2eTest, TestInitialConditionChanges) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto syncStartMatcher = CreateSyncStartAtomMatcher();
@@ -116,7 +115,6 @@
TEST(CountMetricE2eTest, TestConditionTrueNanos) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
AtomMatcher syncStartMatcher = CreateSyncStartAtomMatcher();
@@ -253,6 +251,7 @@
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
StatsLogReport::CountMetricDataWrapper countMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
@@ -283,7 +282,6 @@
TEST(CountMetricE2eTest, TestSlicedState) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -432,7 +430,6 @@
TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto syncStartMatcher = CreateSyncStartAtomMatcher();
*config.add_atom_matcher() = syncStartMatcher;
@@ -611,7 +608,6 @@
TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
@@ -815,7 +811,6 @@
TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
@@ -1090,7 +1085,6 @@
TEST(CountMetricE2eTest, TestUploadThreshold) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto appCrashMatcher = CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
*config.add_atom_matcher() = appCrashMatcher;
@@ -1161,7 +1155,6 @@
TEST(CountMetricE2eTest, TestRepeatedFieldsAndEmptyArrays) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1229,7 +1222,6 @@
TEST(CountMetricE2eTest, TestMatchRepeatedFieldPositionAny) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedStateAnyOnAtomMatcher =
CreateTestAtomRepeatedStateAnyOnAtomMatcher();
@@ -1295,7 +1287,6 @@
TEST(CountMetricE2eTest, TestRepeatedFieldDimension_PositionFirst) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1383,7 +1374,6 @@
TEST(CountMetricE2eTest, TestRepeatedFieldDimension_PositionLast) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1462,7 +1452,6 @@
TEST(CountMetricE2eTest, TestRepeatedFieldDimension_PositionAll) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1568,7 +1557,6 @@
TEST(CountMetricE2eTest, TestMultipleRepeatedFieldDimensions_PositionFirst) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1683,7 +1671,6 @@
TEST(CountMetricE2eTest, TestMultipleRepeatedFieldDimensions_PositionAll) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -1813,7 +1800,6 @@
TEST(CountMetricE2eTest, TestConditionSlicedByRepeatedUidWithUidDimension) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher uidProcessStateChangedAtomMatcher = CreateUidProcessStateChangedAtomMatcher();
AtomMatcher repeatedStateFirstOffAtomMatcher = CreateTestAtomRepeatedStateFirstOffAtomMatcher();
@@ -1946,7 +1932,6 @@
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
@@ -2029,4 +2014,4 @@
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index be04f7f..308ef52 100644
--- a/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -29,7 +29,6 @@
TEST(DurationMetricE2eTest, TestOneBucket) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -95,6 +94,7 @@
backfillStartEndTimestamp(&reports);
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
@@ -112,7 +112,6 @@
TEST(DurationMetricE2eTest, TestTwoBuckets) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -198,7 +197,6 @@
TEST(DurationMetricE2eTest, TestWithActivation) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -254,7 +252,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(CreateAtomIdSetFromConfig(config), &processor))
.Times(1);
@@ -377,7 +375,6 @@
TEST(DurationMetricE2eTest, TestWithCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
@@ -470,8 +467,6 @@
TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
@@ -574,7 +569,6 @@
TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -741,7 +735,6 @@
TEST(DurationMetricE2eTest, TestWithSlicedState) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
@@ -884,7 +877,6 @@
TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
@@ -1046,7 +1038,6 @@
TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
@@ -1194,7 +1185,6 @@
TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -1238,7 +1228,6 @@
TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) {
// Initialize config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -1485,7 +1474,6 @@
TEST(DurationMetricE2eTest, TestUploadThreshold) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -1580,7 +1568,6 @@
TEST(DurationMetricE2eTest, TestConditionOnRepeatedEnumField) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher repeatedStateFirstOffAtomMatcher = CreateTestAtomRepeatedStateFirstOffAtomMatcher();
AtomMatcher repeatedStateFirstOnAtomMatcher = CreateTestAtomRepeatedStateFirstOnAtomMatcher();
@@ -1669,7 +1656,6 @@
ShardOffsetProvider::getInstance().setShardOffset(5);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
*config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
diff --git a/statsd/tests/e2e/EventMetric_e2e_test.cpp b/statsd/tests/e2e/EventMetric_e2e_test.cpp
index 81a6d91..6909dc2 100644
--- a/statsd/tests/e2e/EventMetric_e2e_test.cpp
+++ b/statsd/tests/e2e/EventMetric_e2e_test.cpp
@@ -41,7 +41,6 @@
TEST_F(EventMetricE2eTest, TestEventMetricDataAggregated) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
@@ -85,8 +84,10 @@
ASSERT_EQ(reports.reports_size(), 1);
ConfigMetricsReport report = reports.reports(0);
+ EXPECT_TRUE(report.has_estimated_data_bytes());
ASSERT_EQ(report.metrics_size(), 1);
StatsLogReport wakelockEventMetricReport = report.metrics(0);
+ EXPECT_TRUE(wakelockEventMetricReport.has_estimated_data_bytes());
EXPECT_EQ(wakelockEventMetricReport.metric_id(), wakelockEventMetric.id());
EXPECT_TRUE(wakelockEventMetricReport.has_event_metrics());
ASSERT_EQ(wakelockEventMetricReport.event_metrics().data_size(), 3);
@@ -103,7 +104,6 @@
TEST_F(EventMetricE2eTest, TestRepeatedFieldsAndEmptyArrays) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -129,7 +129,7 @@
bool boolArray[boolArrayLength];
boolArray[0] = 1;
boolArray[1] = 0;
- vector<bool> boolArrayVector = {1, 0};
+ vector<uint8_t> boolArrayVector = {1, 0};
vector<int> enumArray = {TestAtomReported::ON, TestAtomReported::OFF};
events.push_back(CreateTestAtomReportedEventVariableRepeatedFields(
@@ -195,7 +195,6 @@
TEST_F(EventMetricE2eTest, TestMatchRepeatedFieldPositionFirst) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedStateFirstOnAtomMatcher =
CreateTestAtomRepeatedStateFirstOnAtomMatcher();
@@ -257,6 +256,90 @@
EXPECT_THAT(atom.repeated_enum_field(), ElementsAreArray(enumArrayMatch));
}
+TEST_F(EventMetricE2eTest, TestDumpReportIncrementsReportNumber) {
+ StatsdConfig config;
+
+ AtomMatcher testAtomReportedStateFirstOnAtomMatcher =
+ CreateTestAtomRepeatedStateFirstOnAtomMatcher();
+ *config.add_atom_matcher() = testAtomReportedStateFirstOnAtomMatcher;
+
+ EventMetric testAtomReportedEventMetric = createEventMetric(
+ "EventTestAtomReported", testAtomReportedStateFirstOnAtomMatcher.id(), nullopt);
+ *config.add_event_metric() = testAtomReportedEventMetric;
+
+ ConfigKey key(123, 987);
+ uint64_t configUpdateTime = 10000000000; // 0:10
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(configUpdateTime, configUpdateTime, config, key);
+
+ uint64_t dumpTimeNs = configUpdateTime + 100 * NS_PER_SEC;
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 1);
+
+ EXPECT_EQ(reports.report_number(), 1);
+ EXPECT_EQ(reports.statsd_stats_id(), StatsdStats::getInstance().getStatsdStatsId());
+
+ buffer.clear();
+ processor->onDumpReport(key, dumpTimeNs + 100, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 1);
+
+ EXPECT_EQ(reports.report_number(), 2);
+ EXPECT_EQ(reports.statsd_stats_id(), StatsdStats::getInstance().getStatsdStatsId());
+}
+
+TEST_F(EventMetricE2eTest, TestEventMetricSampling) {
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ StatsdConfig config;
+
+ AtomMatcher batterySaverOnMatcher = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = batterySaverOnMatcher;
+
+ EventMetric batterySaverOnEventMetric =
+ createEventMetric("EventBatterySaverOn", batterySaverOnMatcher.id(), nullopt);
+ batterySaverOnEventMetric.set_sampling_percentage(50);
+ *config.add_event_metric() = batterySaverOnEventMetric;
+
+ ConfigKey key(123, 987);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+ // Initialize log events before update.
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ for (int i = 0; i < 100; i++) {
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + (10 + 10 * i) * NS_PER_SEC));
+ }
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ uint64_t dumpTimeNs = bucketStartTimeNs + 2000 * NS_PER_SEC;
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(reports.reports_size(), 1);
+
+ ConfigMetricsReport report = reports.reports(0);
+ ASSERT_EQ(report.metrics_size(), 1);
+ StatsLogReport metricReport = report.metrics(0);
+ EXPECT_EQ(metricReport.metric_id(), batterySaverOnEventMetric.id());
+ EXPECT_TRUE(metricReport.has_event_metrics());
+ ASSERT_EQ(metricReport.event_metrics().data_size(), 46);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 474cb7a..e9d5b0e 100644
--- a/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -37,7 +37,6 @@
StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
bool useCondition = true) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
*config.add_atom_matcher() = atomMatcher;
@@ -146,6 +145,7 @@
backfillAggregatedAtoms(&reports);
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
ASSERT_GT((int)gaugeMetrics.data_size(), 1);
@@ -956,6 +956,420 @@
EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
}
+TEST(GaugeMetricE2ePulledTest, TestGaugeMetricPullProbabilityWithTriggerEvent) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+ StatsdStats::getInstance();
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ auto config = CreateStatsdConfig(GaugeMetric::FIRST_N_SAMPLES, /*useCondition=*/false);
+ auto gaugeMetric = config.mutable_gauge_metric(0);
+ gaugeMetric->set_pull_probability(50);
+ auto triggerEventMatcher = CreateScreenTurnedOnAtomMatcher();
+ gaugeMetric->set_trigger_event(triggerEventMatcher.id());
+ gaugeMetric->set_max_num_gauge_atoms_per_bucket(200);
+ gaugeMetric->set_bucket(ONE_HOUR);
+
+ int64_t configAddedTimeNs = 60 * NS_PER_SEC;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor =
+ CreateStatsLogProcessor(configAddedTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ // First bucket events.
+ for (int i = 0; i < 30; i++) {
+ events.push_back(CreateScreenStateChangedEvent(configAddedTimeNs + (i * 10 * NS_PER_SEC),
+ android::view::DISPLAY_STATE_ON));
+ }
+ // Second bucket events.
+ for (int i = 0; i < 30; i++) {
+ events.push_back(CreateScreenStateChangedEvent(
+ configAddedTimeNs + bucketSizeNs + (i * 10 * NS_PER_SEC),
+ android::view::DISPLAY_STATE_ON));
+ }
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+ ADB_DUMP, FAST, &buffer);
+
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ((int)gaugeMetrics.data_size(), 2); // 2 sets of data for each pull.
+
+ // Data 1
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_1",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(2, data.bucket_info_size());
+
+ // Data 1, Bucket 1
+ ASSERT_EQ(13, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+ {(int64_t)60 * NS_PER_SEC, (int64_t)80 * NS_PER_SEC, (int64_t)90 * NS_PER_SEC,
+ (int64_t)130 * NS_PER_SEC, (int64_t)150 * NS_PER_SEC, (int64_t)170 * NS_PER_SEC,
+ (int64_t)190 * NS_PER_SEC, (int64_t)200 * NS_PER_SEC, (int64_t)240 * NS_PER_SEC,
+ (int64_t)250 * NS_PER_SEC, (int64_t)300 * NS_PER_SEC, (int64_t)330 * NS_PER_SEC,
+ (int64_t)340 * NS_PER_SEC});
+
+ // Data 1, Bucket 2
+ ASSERT_EQ(18, data.bucket_info(1).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(1), configAddedTimeNs + bucketSizeNs,
+ configAddedTimeNs + 2 * bucketSizeNs,
+ {(int64_t)3660 * NS_PER_SEC, (int64_t)3680 * NS_PER_SEC, (int64_t)3700 * NS_PER_SEC,
+ (int64_t)3710 * NS_PER_SEC, (int64_t)3720 * NS_PER_SEC, (int64_t)3740 * NS_PER_SEC,
+ (int64_t)3780 * NS_PER_SEC, (int64_t)3790 * NS_PER_SEC, (int64_t)3820 * NS_PER_SEC,
+ (int64_t)3850 * NS_PER_SEC, (int64_t)3860 * NS_PER_SEC, (int64_t)3870 * NS_PER_SEC,
+ (int64_t)3880 * NS_PER_SEC, (int64_t)3900 * NS_PER_SEC, (int64_t)3910 * NS_PER_SEC,
+ (int64_t)3920 * NS_PER_SEC, (int64_t)3930 * NS_PER_SEC, (int64_t)3940 * NS_PER_SEC});
+
+ // Data 2
+ data = gaugeMetrics.data(1);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_2",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(2, data.bucket_info_size());
+
+ // Data 2, Bucket 1
+ ASSERT_EQ(13, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+ {(int64_t)60 * NS_PER_SEC, (int64_t)80 * NS_PER_SEC, (int64_t)90 * NS_PER_SEC,
+ (int64_t)130 * NS_PER_SEC, (int64_t)150 * NS_PER_SEC, (int64_t)170 * NS_PER_SEC,
+ (int64_t)190 * NS_PER_SEC, (int64_t)200 * NS_PER_SEC, (int64_t)240 * NS_PER_SEC,
+ (int64_t)250 * NS_PER_SEC, (int64_t)300 * NS_PER_SEC, (int64_t)330 * NS_PER_SEC,
+ (int64_t)340 * NS_PER_SEC});
+
+ // Data 2, Bucket 2
+ ASSERT_EQ(18, data.bucket_info(1).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(1), configAddedTimeNs + bucketSizeNs,
+ configAddedTimeNs + 2 * bucketSizeNs,
+ {(int64_t)3660 * NS_PER_SEC, (int64_t)3680 * NS_PER_SEC, (int64_t)3700 * NS_PER_SEC,
+ (int64_t)3710 * NS_PER_SEC, (int64_t)3720 * NS_PER_SEC, (int64_t)3740 * NS_PER_SEC,
+ (int64_t)3780 * NS_PER_SEC, (int64_t)3790 * NS_PER_SEC, (int64_t)3820 * NS_PER_SEC,
+ (int64_t)3850 * NS_PER_SEC, (int64_t)3860 * NS_PER_SEC, (int64_t)3870 * NS_PER_SEC,
+ (int64_t)3880 * NS_PER_SEC, (int64_t)3900 * NS_PER_SEC, (int64_t)3910 * NS_PER_SEC,
+ (int64_t)3920 * NS_PER_SEC, (int64_t)3930 * NS_PER_SEC, (int64_t)3940 * NS_PER_SEC});
+}
+
+TEST(GaugeMetricE2ePulledTest, TestGaugeMetricPullProbabilityWithBucketBoundaryAlarm) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+ StatsdStats::getInstance();
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ auto config = CreateStatsdConfig(GaugeMetric::FIRST_N_SAMPLES, /*useCondition=*/false);
+ auto gaugeMetric = config.mutable_gauge_metric(0);
+ gaugeMetric->set_pull_probability(50);
+ gaugeMetric->set_max_num_gauge_atoms_per_bucket(200);
+
+ int64_t baseTimeNs = 5 * 60 * NS_PER_SEC;
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor =
+ CreateStatsLogProcessor(configAddedTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+
+ // Pulling alarm arrives on time and resets the sequential pulling alarm.
+ for (int i = 1; i < 31; i++) {
+ processor->informPullAlarmFired(configAddedTimeNs + i * bucketSizeNs);
+ }
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 32 * bucketSizeNs + 10, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ((int)gaugeMetrics.data_size(), 2);
+
+ // Data 1
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_1",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(14, data.bucket_info_size());
+
+ EXPECT_EQ(1, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs, {configAddedTimeNs});
+
+ EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(1), configAddedTimeNs + 2 * bucketSizeNs,
+ configAddedTimeNs + 3 * bucketSizeNs,
+ {configAddedTimeNs + 2 * bucketSizeNs}); // 1200000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(2).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(2), configAddedTimeNs + 3 * bucketSizeNs,
+ configAddedTimeNs + 4 * bucketSizeNs,
+ {(int64_t)configAddedTimeNs + 3 * bucketSizeNs}); // 1500000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(3).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(3), configAddedTimeNs + 7 * bucketSizeNs,
+ configAddedTimeNs + 8 * bucketSizeNs,
+ {configAddedTimeNs + 7 * bucketSizeNs}); // 2700000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(4).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(4), configAddedTimeNs + 9 * bucketSizeNs,
+ configAddedTimeNs + 10 * bucketSizeNs,
+ {configAddedTimeNs + 9 * bucketSizeNs}); // 3300000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(5).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(5), configAddedTimeNs + 11 * bucketSizeNs,
+ configAddedTimeNs + 12 * bucketSizeNs,
+ {configAddedTimeNs + 11 * bucketSizeNs}); // 3900000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(6).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(6), configAddedTimeNs + 13 * bucketSizeNs,
+ configAddedTimeNs + 14 * bucketSizeNs,
+ {configAddedTimeNs + 13 * bucketSizeNs}); // 4500000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(7).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(7), configAddedTimeNs + 14 * bucketSizeNs,
+ configAddedTimeNs + 15 * bucketSizeNs,
+ {configAddedTimeNs + 14 * bucketSizeNs}); // 4800000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(8).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(8), configAddedTimeNs + 18 * bucketSizeNs,
+ configAddedTimeNs + 19 * bucketSizeNs,
+ {configAddedTimeNs + 18 * bucketSizeNs}); // 6000000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(9).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(9), configAddedTimeNs + 19 * bucketSizeNs,
+ configAddedTimeNs + 20 * bucketSizeNs,
+ {configAddedTimeNs + 19 * bucketSizeNs}); // 6300000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(10).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(10), configAddedTimeNs + 24 * bucketSizeNs,
+ configAddedTimeNs + 25 * bucketSizeNs,
+ {configAddedTimeNs + 24 * bucketSizeNs}); // 7800000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(11).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(11), configAddedTimeNs + 27 * bucketSizeNs,
+ configAddedTimeNs + 28 * bucketSizeNs,
+ {configAddedTimeNs + 27 * bucketSizeNs}); // 8700000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(12).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(12), configAddedTimeNs + 28 * bucketSizeNs,
+ configAddedTimeNs + 29 * bucketSizeNs,
+ {configAddedTimeNs + 28 * bucketSizeNs}); // 9000000000000ns
+
+ EXPECT_EQ(1, data.bucket_info(13).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(13), configAddedTimeNs + 30 * bucketSizeNs,
+ configAddedTimeNs + 31 * bucketSizeNs,
+ {configAddedTimeNs + 30 * bucketSizeNs}); // 9600000000000ns
+
+ // Data 2
+ data = gaugeMetrics.data(1);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_2",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(14, data.bucket_info_size());
+
+ EXPECT_EQ(1, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs, {configAddedTimeNs});
+
+ EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(1), configAddedTimeNs + 2 * bucketSizeNs,
+ configAddedTimeNs + 3 * bucketSizeNs,
+ {configAddedTimeNs + 2 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(2).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(2), configAddedTimeNs + 3 * bucketSizeNs,
+ configAddedTimeNs + 4 * bucketSizeNs,
+ {(int64_t)configAddedTimeNs + 3 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(3).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(3), configAddedTimeNs + 7 * bucketSizeNs,
+ configAddedTimeNs + 8 * bucketSizeNs,
+ {configAddedTimeNs + 7 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(4).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(4), configAddedTimeNs + 9 * bucketSizeNs,
+ configAddedTimeNs + 10 * bucketSizeNs,
+ {configAddedTimeNs + 9 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(5).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(5), configAddedTimeNs + 11 * bucketSizeNs,
+ configAddedTimeNs + 12 * bucketSizeNs,
+ {configAddedTimeNs + 11 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(6).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(6), configAddedTimeNs + 13 * bucketSizeNs,
+ configAddedTimeNs + 14 * bucketSizeNs,
+ {configAddedTimeNs + 13 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(7).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(7), configAddedTimeNs + 14 * bucketSizeNs,
+ configAddedTimeNs + 15 * bucketSizeNs,
+ {configAddedTimeNs + 14 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(8).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(8), configAddedTimeNs + 18 * bucketSizeNs,
+ configAddedTimeNs + 19 * bucketSizeNs,
+ {configAddedTimeNs + 18 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(9).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(9), configAddedTimeNs + 19 * bucketSizeNs,
+ configAddedTimeNs + 20 * bucketSizeNs,
+ {configAddedTimeNs + 19 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(10).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(10), configAddedTimeNs + 24 * bucketSizeNs,
+ configAddedTimeNs + 25 * bucketSizeNs,
+ {configAddedTimeNs + 24 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(11).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(11), configAddedTimeNs + 27 * bucketSizeNs,
+ configAddedTimeNs + 28 * bucketSizeNs,
+ {configAddedTimeNs + 27 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(12).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(12), configAddedTimeNs + 28 * bucketSizeNs,
+ configAddedTimeNs + 29 * bucketSizeNs,
+ {configAddedTimeNs + 28 * bucketSizeNs});
+
+ EXPECT_EQ(1, data.bucket_info(13).atom_size());
+ ValidateGaugeBucketTimes(data.bucket_info(13), configAddedTimeNs + 30 * bucketSizeNs,
+ configAddedTimeNs + 31 * bucketSizeNs,
+ {configAddedTimeNs + 30 * bucketSizeNs});
+}
+
+TEST(GaugeMetricE2ePulledTest, TestGaugeMetricPullProbabilityWithCondition) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+ StatsdStats::getInstance();
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE, /*useCondition=*/true);
+ auto gaugeMetric = config.mutable_gauge_metric(0);
+ gaugeMetric->set_pull_probability(50);
+ gaugeMetric->set_max_num_gauge_atoms_per_bucket(200);
+ gaugeMetric->set_bucket(ONE_HOUR);
+
+ int64_t configAddedTimeNs = 60 * NS_PER_SEC;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor =
+ CreateStatsLogProcessor(configAddedTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ // First bucket events.
+ for (int i = 0; i < 30; i++) {
+ events.push_back(CreateScreenStateChangedEvent(configAddedTimeNs + (i * 10 * NS_PER_SEC),
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(configAddedTimeNs + (i * 11 * NS_PER_SEC),
+ android::view::DISPLAY_STATE_ON));
+ }
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs, false, true, ADB_DUMP,
+ FAST, &buffer);
+
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ((int)gaugeMetrics.data_size(), 2); // 2 sets of data for each pull.
+
+ // Data 1
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_1",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(1, data.bucket_info_size());
+
+ // Data 1, Bucket 1
+ ASSERT_EQ(13, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+ {(int64_t)60 * NS_PER_SEC, (int64_t)80 * NS_PER_SEC, (int64_t)90 * NS_PER_SEC,
+ (int64_t)130 * NS_PER_SEC, (int64_t)150 * NS_PER_SEC, (int64_t)170 * NS_PER_SEC,
+ (int64_t)190 * NS_PER_SEC, (int64_t)200 * NS_PER_SEC, (int64_t)240 * NS_PER_SEC,
+ (int64_t)250 * NS_PER_SEC, (int64_t)300 * NS_PER_SEC, (int64_t)330 * NS_PER_SEC,
+ (int64_t)340 * NS_PER_SEC});
+
+ // Data 2
+ data = gaugeMetrics.data(1);
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(1 /* subsystem name field */,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+ EXPECT_EQ("subsystem_name_2",
+ data.dimensions_in_what().value_tuple().dimensions_value(0).value_str());
+ ASSERT_EQ(1, data.bucket_info_size());
+
+ // Data 2, Bucket 1
+ ASSERT_EQ(13, data.bucket_info(0).atom_size());
+ ValidateGaugeBucketTimes(
+ data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+ {(int64_t)60 * NS_PER_SEC, (int64_t)80 * NS_PER_SEC, (int64_t)90 * NS_PER_SEC,
+ (int64_t)130 * NS_PER_SEC, (int64_t)150 * NS_PER_SEC, (int64_t)170 * NS_PER_SEC,
+ (int64_t)190 * NS_PER_SEC, (int64_t)200 * NS_PER_SEC, (int64_t)240 * NS_PER_SEC,
+ (int64_t)250 * NS_PER_SEC, (int64_t)300 * NS_PER_SEC, (int64_t)330 * NS_PER_SEC,
+ (int64_t)340 * NS_PER_SEC});
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 81192b2..1f35564 100644
--- a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -31,7 +31,6 @@
StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
*config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
@@ -72,7 +71,6 @@
StatsdConfig CreateStatsdConfigForRepeatedFieldsPushedEvent(
const GaugeMetric::SamplingType sampling_type) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedAtomMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -197,6 +195,7 @@
backfillAggregatedAtoms(&reports);
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
&gaugeMetrics);
@@ -343,7 +342,7 @@
bool boolArray[boolArrayLength];
boolArray[0] = 1;
boolArray[1] = 0;
- vector<bool> boolArrayVector = {1, 0};
+ vector<uint8_t> boolArrayVector = {1, 0};
vector<int> enumArray = {TestAtomReported::ON, TestAtomReported::OFF};
events.push_back(CreateTestAtomReportedEventVariableRepeatedFields(
@@ -418,7 +417,6 @@
ShardOffsetProvider::getInstance().setShardOffset(5);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
@@ -501,6 +499,178 @@
{gaugeEventTimeNs3, gaugeEventTimeNs6});
}
+TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSampling) {
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+ StatsdStats::getInstance();
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ AtomMatcher appCrashMatcher =
+ CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+ *config.add_atom_matcher() = appCrashMatcher;
+
+ GaugeMetric sampledGaugeMetric =
+ createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
+ GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+ *sampledGaugeMetric.mutable_dimensions_in_what() =
+ CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
+ sampledGaugeMetric.set_sampling_percentage(50);
+ *config.add_gauge_metric() = sampledGaugeMetric;
+
+ const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ for (int i = 0; i < 10; i++) {
+ events.push_back(
+ CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC), 1000 + i));
+ }
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ(5, gaugeMetrics.data_size());
+
+ GaugeMetricData data = gaugeMetrics.data(0);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1000);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs, {configAddedTimeNs});
+
+ data = gaugeMetrics.data(1);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1002);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs,
+ {configAddedTimeNs + (10 * 2 * NS_PER_SEC)});
+
+ data = gaugeMetrics.data(2);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs,
+ {configAddedTimeNs + (10 * 3 * NS_PER_SEC)});
+
+ data = gaugeMetrics.data(3);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1007);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs,
+ {configAddedTimeNs + (10 * 7 * NS_PER_SEC)});
+
+ data = gaugeMetrics.data(4);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1009);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs,
+ {configAddedTimeNs + (10 * 9 * NS_PER_SEC)});
+}
+
+TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSamplingWithDimensionalSampling) {
+ ShardOffsetProvider::getInstance().setShardOffset(5);
+ // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+ StatsdStats::getInstance();
+ // Set srand seed to make rand deterministic for testing.
+ srand(0);
+
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ AtomMatcher appCrashMatcher =
+ CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+ *config.add_atom_matcher() = appCrashMatcher;
+
+ GaugeMetric sampledGaugeMetric =
+ createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
+ GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+ *sampledGaugeMetric.mutable_dimensions_in_what() =
+ CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
+ *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
+ sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ sampledGaugeMetric.set_sampling_percentage(50);
+ *config.add_gauge_metric() = sampledGaugeMetric;
+
+ const int64_t configAddedTimeNs = 10 * NS_PER_SEC; // 0:10
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ for (int i = 0; i < 30; i++) {
+ // Generate events with three different app uids: 1001, 1002, 1003.
+ events.push_back(CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC),
+ 1001 + (i % 3)));
+ }
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ(2, gaugeMetrics.data_size());
+
+ // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
+ GaugeMetricData data = gaugeMetrics.data(0);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1001);
+ ValidateGaugeBucketTimes(
+ data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+ {10 * NS_PER_SEC, 40 * NS_PER_SEC, 220 * NS_PER_SEC, 280 * NS_PER_SEC});
+
+ data = gaugeMetrics.data(1);
+ ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
+ ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+ configAddedTimeNs + bucketSizeNs,
+ {60 * NS_PER_SEC, 120 * NS_PER_SEC, 150 * NS_PER_SEC, 180 * NS_PER_SEC,
+ 210 * NS_PER_SEC, 300 * NS_PER_SEC});
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/e2e/KllMetric_e2e_test.cpp b/statsd/tests/e2e/KllMetric_e2e_test.cpp
index a6100c2..7cb5807 100644
--- a/statsd/tests/e2e/KllMetric_e2e_test.cpp
+++ b/statsd/tests/e2e/KllMetric_e2e_test.cpp
@@ -50,8 +50,6 @@
metric = createKllMetric("ScreenBrightness", whatMatcher, /*valueField=*/1,
/*condition=*/nullopt);
- config.add_allowed_log_source("AID_ROOT");
-
*config.add_atom_matcher() = whatMatcher;
*config.add_kll_metric() = metric;
@@ -93,6 +91,7 @@
ConfigMetricsReport report = reports.reports(0);
ASSERT_EQ(report.metrics_size(), 1);
StatsLogReport metricReport = report.metrics(0);
+ EXPECT_TRUE(metricReport.has_estimated_data_bytes());
EXPECT_EQ(metricReport.metric_id(), metric.id());
EXPECT_TRUE(metricReport.has_kll_metrics());
ASSERT_EQ(metricReport.kll_metrics().data_size(), 1);
@@ -177,7 +176,6 @@
TEST_F(KllMetricE2eTest, TestInitWithKllFieldPositionALL) {
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -209,7 +207,6 @@
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher bleScanResultReceivedMatcher = CreateSimpleAtomMatcher(
"BleScanResultReceivedAtomMatcher", util::BLE_SCAN_RESULT_RECEIVED);
diff --git a/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 5452636..6da3f99 100644
--- a/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -30,7 +30,6 @@
StatsdConfig CreateStatsdConfig() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
@@ -62,7 +61,6 @@
StatsdConfig CreateStatsdConfigWithOneDeactivation() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
@@ -97,7 +95,6 @@
StatsdConfig CreateStatsdConfigWithTwoDeactivations() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
@@ -136,7 +133,6 @@
StatsdConfig CreateStatsdConfigWithSameDeactivations() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
@@ -172,7 +168,6 @@
StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
auto crashMatcher = CreateProcessCrashAtomMatcher();
auto foregroundMatcher = CreateMoveToForegroundAtomMatcher();
@@ -267,7 +262,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &processor)).Times(1);
@@ -485,7 +480,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &processor)).Times(1);
@@ -813,7 +808,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &processor)).Times(1);
@@ -1153,7 +1148,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &processor)).Times(1);
@@ -1357,7 +1352,7 @@
activeConfigs.end());
return true;
},
- mockLogEventFilter);
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, mockLogEventFilter);
const LogEventFilter::AtomIdSet atomIdsList = CreateAtomIdSetFromConfig(config);
EXPECT_CALL(*mockLogEventFilter, setAtomIds(atomIdsList, &processor)).Times(1);
diff --git a/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 5e77ee0..bdf1a46 100644
--- a/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -29,7 +29,6 @@
StatsdConfig CreateStatsdConfig() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
diff --git a/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index c9d7c48..c3503ea 100644
--- a/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -37,7 +37,6 @@
StatsdConfig MakeCountMetricConfig(const std::optional<bool> splitBucket) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto appCrashMatcher = CreateProcessCrashAtomMatcher();
*config.add_atom_matcher() = appCrashMatcher;
@@ -53,7 +52,6 @@
StatsdConfig MakeValueMetricConfig(int64_t minTime) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
@@ -79,7 +77,6 @@
StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
diff --git a/statsd/tests/e2e/RestrictedConfig_e2e_test.cpp b/statsd/tests/e2e/RestrictedConfig_e2e_test.cpp
new file mode 100644
index 0000000..5d6a05e
--- /dev/null
+++ b/statsd/tests/e2e/RestrictedConfig_e2e_test.cpp
@@ -0,0 +1,474 @@
+// Copyright (C) 2023 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 "flags/FlagProvider.h"
+#include "storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+const int32_t atomTag = 666;
+const string delegatePackageName = "com.test.restricted.metrics.package";
+const int32_t delegateUid = 10200;
+const string configPackageName = "com.test.config.package";
+int64_t metricId;
+int64_t anotherMetricId;
+
+StatsdConfig CreateConfigWithOneMetric() {
+ StatsdConfig config;
+ AtomMatcher atomMatcher = CreateSimpleAtomMatcher("testmatcher", atomTag);
+ *config.add_atom_matcher() = atomMatcher;
+
+ EventMetric eventMetric = createEventMetric("EventMetric", atomMatcher.id(), nullopt);
+ metricId = eventMetric.id();
+ *config.add_event_metric() = eventMetric;
+ return config;
+}
+StatsdConfig CreateConfigWithTwoMetrics() {
+ StatsdConfig config;
+ AtomMatcher atomMatcher = CreateSimpleAtomMatcher("testmatcher", atomTag);
+ *config.add_atom_matcher() = atomMatcher;
+
+ EventMetric eventMetric = createEventMetric("EventMetric", atomMatcher.id(), nullopt);
+ metricId = eventMetric.id();
+ *config.add_event_metric() = eventMetric;
+ EventMetric anotherEventMetric =
+ createEventMetric("AnotherEventMetric", atomMatcher.id(), nullopt);
+ anotherMetricId = anotherEventMetric.id();
+ *config.add_event_metric() = anotherEventMetric;
+ return config;
+}
+
+std::vector<std::unique_ptr<LogEvent>> CreateLogEvents(int64_t configAddedTimeNs) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateNonRestrictedLogEvent(atomTag, configAddedTimeNs + 10 * NS_PER_SEC));
+ events.push_back(CreateNonRestrictedLogEvent(atomTag, configAddedTimeNs + 20 * NS_PER_SEC));
+ events.push_back(CreateNonRestrictedLogEvent(atomTag, configAddedTimeNs + 30 * NS_PER_SEC));
+ return events;
+}
+
+} // Anonymous namespace
+
+class RestrictedConfigE2ETest : public StatsServiceConfigTest {
+protected:
+ shared_ptr<MockStatsQueryCallback> mockStatsQueryCallback;
+ const ConfigKey configKey = ConfigKey(kCallingUid, kConfigKey);
+ vector<string> queryDataResult;
+ vector<string> columnNamesResult;
+ vector<int32_t> columnTypesResult;
+ int32_t rowCountResult = 0;
+ string error;
+
+ void SetUp() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ StatsServiceConfigTest::SetUp();
+
+ mockStatsQueryCallback = SharedRefBase::make<StrictMock<MockStatsQueryCallback>>();
+ EXPECT_CALL(*mockStatsQueryCallback, sendResults(_, _, _, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke(
+ [this](const vector<string>& queryData, const vector<string>& columnNames,
+ const vector<int32_t>& columnTypes, int32_t rowCount) {
+ queryDataResult = queryData;
+ columnNamesResult = columnNames;
+ columnTypesResult = columnTypes;
+ rowCountResult = rowCount;
+ error = "";
+ return Status::ok();
+ }));
+ EXPECT_CALL(*mockStatsQueryCallback, sendFailure(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke([this](const string& err) {
+ error = err;
+ queryDataResult.clear();
+ columnNamesResult.clear();
+ columnTypesResult.clear();
+ rowCountResult = 0;
+ return Status::ok();
+ }));
+
+ int64_t startTimeNs = getElapsedRealtimeNs();
+ UidData uidData;
+ *uidData.add_app_info() = createApplicationInfo(delegateUid, 1, "v2", delegatePackageName);
+ *uidData.add_app_info() = createApplicationInfo(kCallingUid, 1, "v2", configPackageName);
+ service->mUidMap->updateMap(startTimeNs, uidData);
+ }
+ void TearDown() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ Mock::VerifyAndClear(mockStatsQueryCallback.get());
+ queryDataResult.clear();
+ columnNamesResult.clear();
+ columnTypesResult.clear();
+ rowCountResult = 0;
+ error = "";
+ StatsServiceConfigTest::TearDown();
+ FlagProvider::getInstance().resetOverrides();
+ dbutils::deleteDb(configKey);
+ }
+
+ void verifyRestrictedData(int32_t expectedNumOfMetrics, int64_t metricIdToVerify = metricId,
+ bool shouldExist = true) {
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(metricIdToVerify);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ if (shouldExist) {
+ EXPECT_TRUE(
+ dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), expectedNumOfMetrics);
+ } else {
+ // Expect that table is deleted.
+ EXPECT_FALSE(
+ dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ }
+ }
+};
+
+TEST_F(RestrictedConfigE2ETest, RestrictedConfigNoReport) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name("delegate");
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+
+ for (auto& event : CreateLogEvents(configAddedTimeNs)) {
+ service->OnLogEvent(event.get());
+ }
+
+ vector<uint8_t> output;
+ ConfigKey configKey(kCallingUid, kConfigKey);
+ service->getData(kConfigKey, kCallingUid, &output);
+
+ EXPECT_TRUE(output.empty());
+}
+
+TEST_F(RestrictedConfigE2ETest, NonRestrictedConfigGetReport) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+
+ for (auto& event : CreateLogEvents(configAddedTimeNs)) {
+ service->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReport report = getReports(service->mProcessor, /*timestamp=*/10);
+ EXPECT_EQ(report.metrics_size(), 1);
+}
+
+TEST_F(RestrictedConfigE2ETest, RestrictedShutdownFlushToRestrictedDB) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name("delegate");
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+ std::vector<std::unique_ptr<LogEvent>> logEvents = CreateLogEvents(configAddedTimeNs);
+ for (const auto& e : logEvents) {
+ service->OnLogEvent(e.get());
+ }
+
+ service->informDeviceShutdown();
+
+ // Should not be written to non-restricted storage.
+ EXPECT_FALSE(StorageManager::hasConfigMetricsReport(ConfigKey(kCallingUid, kConfigKey)));
+ verifyRestrictedData(logEvents.size());
+}
+
+TEST_F(RestrictedConfigE2ETest, NonRestrictedOnShutdownWriteDataToDisk) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+ for (auto& event : CreateLogEvents(configAddedTimeNs)) {
+ service->OnLogEvent(event.get());
+ }
+
+ service->informDeviceShutdown();
+
+ EXPECT_TRUE(StorageManager::hasConfigMetricsReport(ConfigKey(kCallingUid, kConfigKey)));
+}
+
+TEST_F(RestrictedConfigE2ETest, RestrictedConfigOnTerminateFlushToRestrictedDB) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name("delegate");
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+ std::vector<std::unique_ptr<LogEvent>> logEvents = CreateLogEvents(configAddedTimeNs);
+ for (auto& event : logEvents) {
+ service->OnLogEvent(event.get());
+ }
+
+ service->Terminate();
+
+ EXPECT_FALSE(StorageManager::hasConfigMetricsReport(ConfigKey(kCallingUid, kConfigKey)));
+ verifyRestrictedData(logEvents.size());
+}
+
+TEST_F(RestrictedConfigE2ETest, NonRestrictedConfigOnTerminateWriteDataToDisk) {
+ StatsdConfig config = CreateConfigWithOneMetric();
+ sendConfig(config);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+ for (auto& event : CreateLogEvents(configAddedTimeNs)) {
+ service->OnLogEvent(event.get());
+ }
+
+ service->Terminate();
+
+ EXPECT_TRUE(StorageManager::hasConfigMetricsReport(ConfigKey(kCallingUid, kConfigKey)));
+}
+
+TEST_F(RestrictedConfigE2ETest, RestrictedConfigOnUpdateWithMetricRemoval) {
+ StatsdConfig complexConfig = CreateConfigWithTwoMetrics();
+ complexConfig.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(complexConfig);
+ int64_t configAddedTimeNs = getElapsedRealtimeNs();
+ std::vector<std::unique_ptr<LogEvent>> logEvents = CreateLogEvents(configAddedTimeNs);
+ for (auto& event : logEvents) {
+ service->OnLogEvent(event.get());
+ }
+
+ // Use query API to make sure data is flushed.
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(metricId);
+ service->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/kConfigKey, /*configPackage=*/configPackageName,
+ /*callingUid=*/delegateUid);
+ EXPECT_EQ(error, "");
+ EXPECT_EQ(rowCountResult, logEvents.size());
+ verifyRestrictedData(logEvents.size(), anotherMetricId, true);
+
+ // Update config to have only one metric
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+
+ // Make sure metric data is deleted.
+ verifyRestrictedData(logEvents.size(), metricId, true);
+ verifyRestrictedData(logEvents.size(), anotherMetricId, false);
+}
+
+TEST_F(RestrictedConfigE2ETest, TestSendRestrictedMetricsChangedBroadcast) {
+ vector<int64_t> receivedMetricIds;
+ int receiveCount = 0;
+ shared_ptr<MockPendingIntentRef> pir = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir, sendRestrictedMetricsChangedBroadcast(_))
+ .Times(7)
+ .WillRepeatedly(Invoke([&receivedMetricIds, &receiveCount](const vector<int64_t>& ids) {
+ receiveCount++;
+ receivedMetricIds = ids;
+ return Status::ok();
+ }));
+
+ // Set the operation. No configs present so empty list is returned.
+ vector<int64_t> returnedMetricIds;
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName, pir, delegateUid,
+ &returnedMetricIds);
+ EXPECT_EQ(receiveCount, 0);
+ EXPECT_THAT(returnedMetricIds, IsEmpty());
+
+ // Add restricted config. Should receive one metric
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 1);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId));
+
+ // Config update, should receive two metrics.
+ config = CreateConfigWithTwoMetrics();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 2);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId, anotherMetricId));
+
+ // Make config unrestricted. Should receive empty list.
+ config.clear_restricted_metrics_delegate_package_name();
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 3);
+ EXPECT_THAT(receivedMetricIds, IsEmpty());
+
+ // Update the unrestricted config. Nothing should be sent.
+ config = CreateConfigWithOneMetric();
+ sendConfig(config);
+
+ // Update config and make it restricted. Should receive one metric.
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 4);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId));
+
+ // Send an invalid config. Should receive empty list.
+ auto invalidCountMetric = config.add_count_metric();
+ invalidCountMetric->set_what(0);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 5);
+ EXPECT_THAT(receivedMetricIds, IsEmpty());
+
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName, delegateUid);
+
+ // Nothing should be sent since the operation is removed.
+ config = CreateConfigWithTwoMetrics();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+
+ // Set the operation. Two metrics should be returned.
+ returnedMetricIds.clear();
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName, pir, delegateUid,
+ &returnedMetricIds);
+ EXPECT_THAT(returnedMetricIds, UnorderedElementsAre(metricId, anotherMetricId));
+ EXPECT_EQ(receiveCount, 5);
+
+ // Config update, should receive two metrics.
+ config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 6);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId));
+
+ // Remove the config and verify an empty list is received
+ service->removeConfiguration(kConfigKey, kCallingUid);
+ EXPECT_EQ(receiveCount, 7);
+ EXPECT_THAT(receivedMetricIds, IsEmpty());
+
+ // Cleanup.
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName, delegateUid);
+}
+
+TEST_F(RestrictedConfigE2ETest, TestSendRestrictedMetricsChangedBroadcastMultipleListeners) {
+ const string configPackageName2 = "com.test.config.package2";
+ const int32_t delegateUid2 = delegateUid + 1, delegateUid3 = delegateUid + 2;
+ service->informOnePackage(configPackageName2, kCallingUid, 0, "", "", {});
+ service->informOnePackage(delegatePackageName, delegateUid2, 0, "", "", {});
+ service->informOnePackage("not.a.good.package", delegateUid3, 0, "", "", {});
+
+ vector<int64_t> receivedMetricIds;
+ int receiveCount = 0;
+ shared_ptr<MockPendingIntentRef> pir = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir, sendRestrictedMetricsChangedBroadcast(_))
+ .Times(2)
+ .WillRepeatedly(Invoke([&receivedMetricIds, &receiveCount](const vector<int64_t>& ids) {
+ receiveCount++;
+ receivedMetricIds = ids;
+ return Status::ok();
+ }));
+
+ int receiveCount2 = 0;
+ vector<int64_t> receivedMetricIds2;
+ shared_ptr<MockPendingIntentRef> pir2 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir2, sendRestrictedMetricsChangedBroadcast(_))
+ .Times(2)
+ .WillRepeatedly(
+ Invoke([&receivedMetricIds2, &receiveCount2](const vector<int64_t>& ids) {
+ receiveCount2++;
+ receivedMetricIds2 = ids;
+ return Status::ok();
+ }));
+
+ // This one should never be called.
+ shared_ptr<MockPendingIntentRef> pir3 = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ EXPECT_CALL(*pir3, sendRestrictedMetricsChangedBroadcast(_)).Times(0);
+
+ // Set the operations. No configs present so empty list is returned.
+ vector<int64_t> returnedMetricIds;
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName, pir, delegateUid,
+ &returnedMetricIds);
+ EXPECT_EQ(receiveCount, 0);
+ EXPECT_THAT(returnedMetricIds, IsEmpty());
+
+ vector<int64_t> returnedMetricIds2;
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName2, pir2,
+ delegateUid2, &returnedMetricIds2);
+ EXPECT_EQ(receiveCount2, 0);
+ EXPECT_THAT(returnedMetricIds2, IsEmpty());
+
+ // Represents a package listening for changes but doesn't match the restricted package in the
+ // config.
+ vector<int64_t> returnedMetricIds3;
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName, pir3, delegateUid3,
+ &returnedMetricIds3);
+ EXPECT_THAT(returnedMetricIds3, IsEmpty());
+
+ // Add restricted config. Should receive one metric on pir1 and 2.
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 1);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId));
+ EXPECT_EQ(receiveCount2, 1);
+ EXPECT_THAT(receivedMetricIds2, UnorderedElementsAre(metricId));
+
+ // Config update, should receive two metrics on pir1 and 2.
+ config = CreateConfigWithTwoMetrics();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+ EXPECT_EQ(receiveCount, 2);
+ EXPECT_THAT(receivedMetricIds, UnorderedElementsAre(metricId, anotherMetricId));
+ EXPECT_EQ(receiveCount2, 2);
+ EXPECT_THAT(receivedMetricIds2, UnorderedElementsAre(metricId, anotherMetricId));
+
+ // Cleanup.
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName, delegateUid);
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName2, delegateUid2);
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName, delegateUid3);
+}
+
+TEST_F(RestrictedConfigE2ETest, TestSendRestrictedMetricsChangedBroadcastMultipleMatchedConfigs) {
+ const int32_t callingUid2 = kCallingUid + 1;
+ service->informOnePackage(configPackageName, callingUid2, 0, "", "", {});
+
+ // Add restricted config.
+ StatsdConfig config = CreateConfigWithOneMetric();
+ config.set_restricted_metrics_delegate_package_name(delegatePackageName);
+ sendConfig(config);
+
+ // Add a second config.
+ const int64_t metricId2 = 42;
+ config.mutable_event_metric(0)->set_id(42);
+ string str;
+ config.SerializeToString(&str);
+ std::vector<uint8_t> configAsVec(str.begin(), str.end());
+ service->addConfiguration(kConfigKey, configAsVec, callingUid2);
+
+ // Set the operation. Matches multiple configs so a union of metrics are returned.
+ shared_ptr<MockPendingIntentRef> pir = SharedRefBase::make<StrictMock<MockPendingIntentRef>>();
+ vector<int64_t> returnedMetricIds;
+ service->setRestrictedMetricsChangedOperation(kConfigKey, configPackageName, pir, delegateUid,
+ &returnedMetricIds);
+ EXPECT_THAT(returnedMetricIds, UnorderedElementsAre(metricId, metricId2));
+
+ // Cleanup.
+ service->removeRestrictedMetricsChangedOperation(kConfigKey, configPackageName, delegateUid);
+
+ ConfigKey cfgKey(callingUid2, kConfigKey);
+ service->removeConfiguration(kConfigKey, callingUid2);
+ service->mProcessor->onDumpReport(cfgKey, getElapsedRealtimeNs(),
+ false /* include_current_bucket*/, true /* erase_data */,
+ ADB_DUMP, NO_TIME_CONSTRAINTS, nullptr);
+}
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/tests/e2e/RestrictedEventMetric_e2e_test.cpp b/statsd/tests/e2e/RestrictedEventMetric_e2e_test.cpp
new file mode 100644
index 0000000..d561769
--- /dev/null
+++ b/statsd/tests/e2e/RestrictedEventMetric_e2e_test.cpp
@@ -0,0 +1,1189 @@
+// Copyright (C) 2023 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 <vector>
+
+#include "android-base/stringprintf.h"
+#include "flags/FlagProvider.h"
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateTracker.h"
+#include "src/stats_log_util.h"
+#include "src/storage/StorageManager.h"
+#include "src/utils/RestrictedPolicyManager.h"
+#include "stats_annotations.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using base::StringPrintf;
+
+#ifdef __ANDROID__
+
+namespace {
+const int64_t oneMonthLater = getWallClockNs() + 31 * 24 * 3600 * NS_PER_SEC;
+const int64_t configId = 12345;
+const string delegate_package_name = "com.test.restricted.metrics.package";
+const int32_t delegate_uid = 1005;
+const string config_package_name = "com.test.config.package";
+const int32_t config_app_uid = 123;
+const ConfigKey configKey(config_app_uid, configId);
+const int64_t eightDaysAgo = getWallClockNs() - 8 * 24 * 3600 * NS_PER_SEC;
+const int64_t oneDayAgo = getWallClockNs() - 1 * 24 * 3600 * NS_PER_SEC;
+} // anonymous namespace
+
+// Setup for test fixture.
+class RestrictedEventMetricE2eTest : public ::testing::Test {
+protected:
+ shared_ptr<MockStatsQueryCallback> mockStatsQueryCallback;
+ vector<string> queryDataResult;
+ vector<string> columnNamesResult;
+ vector<int32_t> columnTypesResult;
+ int32_t rowCountResult = 0;
+ string error;
+ sp<UidMap> uidMap;
+ sp<StatsLogProcessor> processor;
+ int32_t atomTag;
+ int64_t restrictedMetricId;
+ int64_t configAddedTimeNs;
+ StatsdConfig config;
+
+private:
+ void SetUp() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+
+ mockStatsQueryCallback = SharedRefBase::make<StrictMock<MockStatsQueryCallback>>();
+ EXPECT_CALL(*mockStatsQueryCallback, sendResults(_, _, _, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke(
+ [this](const vector<string>& queryData, const vector<string>& columnNames,
+ const vector<int32_t>& columnTypes, int32_t rowCount) {
+ queryDataResult = queryData;
+ columnNamesResult = columnNames;
+ columnTypesResult = columnTypes;
+ rowCountResult = rowCount;
+ error = "";
+ return Status::ok();
+ }));
+ EXPECT_CALL(*mockStatsQueryCallback, sendFailure(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke([this](const string& err) {
+ error = err;
+ queryDataResult.clear();
+ columnNamesResult.clear();
+ columnTypesResult.clear();
+ rowCountResult = 0;
+ return Status::ok();
+ }));
+
+ atomTag = 999;
+ AtomMatcher restrictedAtomMatcher = CreateSimpleAtomMatcher("restricted_matcher", atomTag);
+ *config.add_atom_matcher() = restrictedAtomMatcher;
+
+ EventMetric restrictedEventMetric =
+ createEventMetric("RestrictedMetricLogged", restrictedAtomMatcher.id(), nullopt);
+ *config.add_event_metric() = restrictedEventMetric;
+ restrictedMetricId = restrictedEventMetric.id();
+
+ config.set_restricted_metrics_delegate_package_name(delegate_package_name.c_str());
+
+ const int64_t baseTimeNs = 0; // 0:00
+ configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+
+ uidMap = new UidMap();
+ uidMap->updateApp(configAddedTimeNs, delegate_package_name,
+ /*uid=*/delegate_uid, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+ uidMap->updateApp(configAddedTimeNs + 1, config_package_name,
+ /*uid=*/config_app_uid, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+
+ processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, configKey,
+ /*puller=*/nullptr, /*atomTag=*/0, uidMap);
+ }
+
+ void TearDown() override {
+ Mock::VerifyAndClear(mockStatsQueryCallback.get());
+ queryDataResult.clear();
+ columnNamesResult.clear();
+ columnTypesResult.clear();
+ rowCountResult = 0;
+ error = "";
+ dbutils::deleteDb(configKey);
+ dbutils::deleteDb(ConfigKey(config_app_uid + 1, configId));
+ FlagProvider::getInstance().resetOverrides();
+ }
+};
+
+TEST_F(RestrictedEventMetricE2eTest, TestQueryThreeEvents) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 200));
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 300));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 3);
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _, // field_1
+ to_string(atomTag), to_string(configAddedTimeNs + 200),
+ _, // wallClockNs
+ _, // field_1
+ to_string(atomTag), to_string(configAddedTimeNs + 300),
+ _, // wallClockNs
+ _ // field_1
+ ));
+
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaIncreasingFieldCount) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 200);
+ // This event has two extra fields
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeInt32(statsEvent, 11);
+ AStatsEvent_writeFloat(statsEvent, 11.0);
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ events.push_back(std::move(logEvent));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST,
+ event->GetElapsedTimestampNs() + 20 * NS_PER_SEC,
+ getWallClockNs());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ // Event 2 rejected.
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _ // field_1
+ ));
+
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaDecreasingFieldCount) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 100);
+ // This event has one extra field.
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeInt32(statsEvent, 11);
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+
+ events.push_back(std::move(logEvent));
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 200));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST,
+ event->GetElapsedTimestampNs() + 20 * NS_PER_SEC,
+ getWallClockNs());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ // Event 2 Rejected
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ "111", // field_1
+ to_string(11) // field_2
+ ));
+
+ EXPECT_THAT(columnNamesResult, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs",
+ "field_1", "field_2"));
+
+ EXPECT_THAT(columnTypesResult, ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER,
+ SQLITE_TEXT, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestInvalidSchemaDifferentFieldType) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_overwriteTimestamp(statsEvent, configAddedTimeNs + 200);
+ // This event has a string instead of an int field
+ AStatsEvent_writeString(statsEvent, "test_string");
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ events.push_back(std::move(logEvent));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST,
+ event->GetElapsedTimestampNs() + 20 * NS_PER_SEC,
+ getWallClockNs());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ // Event 2 rejected.
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestNewMetricSchemaAcrossReboot) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ processor->OnLogEvent(event1.get());
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ _, // wallTimestampNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+
+ // Create a new processor to simulate a reboot
+ auto processor2 =
+ CreateStatsLogProcessor(/*baseTimeNs=*/0, configAddedTimeNs, config, configKey,
+ /*puller=*/nullptr, /*atomTag=*/0, uidMap);
+
+ // Create a restricted event with one extra field.
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_overwriteTimestamp(statsEvent, originalEventElapsedTime + 100);
+ // This event has one extra field.
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeInt32(statsEvent, 11);
+ std::unique_ptr<LogEvent> event2 = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, event2.get());
+ processor2->OnLogEvent(event2.get());
+
+ processor2->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 100),
+ _, // wallTimestampNs
+ to_string(111), // field_1
+ to_string(11) // field_2
+ ));
+ EXPECT_THAT(columnNamesResult, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs",
+ "field_1", "field_2"));
+ EXPECT_THAT(columnTypesResult, ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER,
+ SQLITE_TEXT, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestOneEventMultipleUids) {
+ uidMap->updateApp(configAddedTimeNs, delegate_package_name,
+ /*uid=*/delegate_uid + 1, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+ uidMap->updateApp(configAddedTimeNs + 1, config_package_name,
+ /*uid=*/config_app_uid + 1, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _ // field_1
+ ));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestOneEventStaticUid) {
+ ConfigKey key2(2000, configId); // shell uid
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, key2, config);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/"AID_SHELL",
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _ // field_1
+ ));
+ dbutils::deleteDb(key2);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestTooManyConfigsAmbiguousQuery) {
+ ConfigKey key2(config_app_uid + 1, configId);
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, key2, config);
+
+ uidMap->updateApp(configAddedTimeNs, delegate_package_name,
+ /*uid=*/delegate_uid + 1, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+ uidMap->updateApp(configAddedTimeNs + 1, config_package_name.c_str(),
+ /*uid=*/config_app_uid + 1, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(error, "Ambiguous ConfigKey");
+ dbutils::deleteDb(key2);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestUnknownConfigPackage) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/"unknown.config.package",
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(error, "No configs found matching the config key");
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestUnknownDelegatePackage) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid + 1);
+
+ EXPECT_EQ(error, "No matching configs for restricted metrics delegate");
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestUnsupportedDatabaseVersion) {
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/INT_MAX,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_THAT(error, StartsWith("Unsupported sqlite version"));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestInvalidQuery) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM invalid_metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_THAT(error, StartsWith("failed to query db"));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestEnforceTtlRemovesOldEvents) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ // 8 days are used here because the TTL threshold is 7 days.
+ int64_t eightDaysAgo = currentWallTimeNs - 8 * 24 * 3600 * NS_PER_SEC;
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+ processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, originalEventElapsedTime + 20 * NS_PER_SEC,
+ getWallClockNs());
+ processor->EnforceDataTtls(currentWallTimeNs, originalEventElapsedTime + 100);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 0);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestConfigRemovalDeletesData) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ // Query to make sure data is flushed
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ processor->OnConfigRemoved(configKey);
+
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+
+ EXPECT_THAT(err, StartsWith("unable to open database file"));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestConfigRemovalDeletesDataWithoutFlush) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+ processor->OnConfigRemoved(configKey);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+
+ EXPECT_THAT(err, StartsWith("unable to open database file"));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestConfigUpdateRestrictedDelegateCleared) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Update the existing config with no delegate
+ config.clear_restricted_metrics_delegate_package_name();
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 0);
+ EXPECT_THAT(err, StartsWith("unable to open database file"));
+ dbutils::deleteDb(configKey);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestNonModularConfigUpdateRestrictedDelegate) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Update the existing config without modular update
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config, false);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 0);
+ EXPECT_THAT(err, StartsWith("no such table"));
+ dbutils::deleteDb(configKey);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestModularConfigUpdateNewRestrictedDelegate) {
+ config.clear_restricted_metrics_delegate_package_name();
+ // Update the existing config without a restricted delegate
+ processor->OnConfigUpdated(configAddedTimeNs + 10, configKey, config);
+
+ // Update the existing config with a new restricted delegate
+ config.set_restricted_metrics_delegate_package_name("new.delegate.package");
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 2 * NS_PER_SEC));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ uint64_t dumpTimeNs = configAddedTimeNs + 100 * NS_PER_SEC;
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(configKey, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ ASSERT_EQ(reports.reports_size(), 0);
+
+ // Assert the config update was not modular and a RestrictedEventMetricProducer was created.
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0],
+ ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 2 * NS_PER_SEC),
+ _, // wallClockNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestModularConfigUpdateChangeRestrictedDelegate) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Update the existing config with a new restricted delegate
+ int32_t newDelegateUid = delegate_uid + 1;
+ config.set_restricted_metrics_delegate_package_name("new.delegate.package");
+ uidMap->updateApp(configAddedTimeNs, "new.delegate.package",
+ /*uid=*/newDelegateUid, /*versionCode=*/1,
+ /*versionString=*/"v2",
+ /*installer=*/"", /*certificateHash=*/{});
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/newDelegateUid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(configAddedTimeNs + 100),
+ _, // wallClockNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestInvalidConfigUpdateRestrictedDelegate) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ EventMetric metricWithoutMatcher = createEventMetric("metricWithoutMatcher", 999999, nullopt);
+ *config.add_event_metric() = metricWithoutMatcher;
+ // Update the existing config with an invalid config update
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 0);
+ EXPECT_THAT(err, StartsWith("unable to open database file"));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestRestrictedConfigUpdateDoesNotUpdateUidMap) {
+ auto& configKeyMap = processor->getUidMap()->mLastUpdatePerConfigKey;
+ EXPECT_EQ(configKeyMap.find(configKey), configKeyMap.end());
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestRestrictedConfigUpdateAddsDelegateRemovesUidMapEntry) {
+ auto& configKeyMap = processor->getUidMap()->mLastUpdatePerConfigKey;
+ config.clear_restricted_metrics_delegate_package_name();
+ // Update the existing config without a restricted delegate
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+ EXPECT_NE(configKeyMap.find(configKey), configKeyMap.end());
+ // Update the existing config with a new restricted delegate
+ config.set_restricted_metrics_delegate_package_name(delegate_package_name.c_str());
+ processor->OnConfigUpdated(configAddedTimeNs + 1 * NS_PER_SEC, configKey, config);
+ EXPECT_EQ(configKeyMap.find(configKey), configKeyMap.end());
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestLogEventsEnforceTtls) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ // 2 hours used here because the TTL check period is 1 hour.
+ int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+ std::unique_ptr<LogEvent> event2 =
+ CreateRestrictedLogEvent(atomTag, originalEventElapsedTime + 100);
+ event2->setLogdWallClockTimestampNs(oneDayAgo);
+ std::unique_ptr<LogEvent> event3 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime);
+ event3->setLogdWallClockTimestampNs(currentWallTimeNs);
+
+ processor->mLastTtlTime = originalEventElapsedTime;
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+ processor->OnLogEvent(event2.get(), newEventElapsedTime);
+ processor->OnLogEvent(event3.get(), newEventElapsedTime + 100);
+ processor->flushRestrictedDataLocked(newEventElapsedTime);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 2);
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+ EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 100),
+ to_string(oneDayAgo), _));
+ EXPECT_THAT(rows[1], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime),
+ to_string(currentWallTimeNs), _));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestLogEventsDoesNotEnforceTtls) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ // 30 min used here because the TTL check period is 1 hour.
+ int64_t newEventElapsedTime = configAddedTimeNs + (3600 * NS_PER_SEC) / 2; // 30 min later
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime);
+ event2->setLogdWallClockTimestampNs(currentWallTimeNs);
+
+ processor->mLastTtlTime = originalEventElapsedTime;
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+ processor->OnLogEvent(event2.get(), newEventElapsedTime);
+ processor->flushRestrictedDataLocked(newEventElapsedTime);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 2);
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+ EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ to_string(eightDaysAgo), _));
+ EXPECT_THAT(rows[1], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime),
+ to_string(currentWallTimeNs), _));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestQueryEnforceTtls) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ // 30 min used here because the TTL check period is 1 hour.
+ int64_t newEventElapsedTime = configAddedTimeNs + (3600 * NS_PER_SEC) / 2; // 30 min later
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime);
+ event2->setLogdWallClockTimestampNs(currentWallTimeNs);
+
+ processor->mLastTtlTime = originalEventElapsedTime;
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+ processor->OnLogEvent(event2.get(), newEventElapsedTime);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult, ElementsAre(to_string(atomTag), to_string(newEventElapsedTime),
+ to_string(currentWallTimeNs),
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestNotFlushed) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs());
+ }
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_FALSE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 0);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestEnforceDbGuardrails) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime =
+ configAddedTimeNs + (3600 * NS_PER_SEC) * 2; // 2 hours after boot
+ // 2 hours used here because the TTL check period is 1 hour.
+ int64_t dbEnforcementTimeNs =
+ configAddedTimeNs + (3600 * NS_PER_SEC) * 4; // 4 hours after boot
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(currentWallTimeNs);
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+
+ EXPECT_TRUE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ to_string(currentWallTimeNs),
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+
+ processor->enforceDbGuardrailsIfNecessaryLocked(oneMonthLater, dbEnforcementTimeNs);
+
+ EXPECT_FALSE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestEnforceDbGuardrailsDoesNotDeleteBeforeGuardrail) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime =
+ configAddedTimeNs + (3600 * NS_PER_SEC) * 2; // 2 hours after boot
+ // 2 hours used here because the TTL check period is 1 hour.
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(currentWallTimeNs);
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+
+ EXPECT_TRUE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ to_string(currentWallTimeNs),
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+
+ processor->enforceDbGuardrailsIfNecessaryLocked(oneMonthLater, originalEventElapsedTime);
+
+ EXPECT_TRUE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestFlushInWriteDataToDisk) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs());
+ }
+
+ // Call WriteDataToDisk after 20 second because cooldown period is 15 second.
+ processor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST, 20 * NS_PER_SEC, getWallClockNs());
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ EXPECT_EQ(rows.size(), 1);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestFlushPeriodically) {
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100));
+ events.push_back(CreateRestrictedLogEvent(
+ atomTag, configAddedTimeNs + StatsdStats::kMinFlushRestrictedPeriodNs + 1));
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get(), event->GetElapsedTimestampNs());
+ }
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ // Only first event is flushed when second event is logged.
+ EXPECT_EQ(rows.size(), 1);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestOnLogEventMalformedDbNameDeleted) {
+ vector<string> emptyData;
+ string fileName = StringPrintf("%s/malformedname.db", STATS_RESTRICTED_DATA_DIR);
+ StorageManager::writeFile(fileName.c_str(), emptyData.data(), emptyData.size());
+ EXPECT_TRUE(StorageManager::hasFile(fileName.c_str()));
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ // 2 hours used here because the TTL check period is 1 hour.
+ int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime);
+ event2->setLogdWallClockTimestampNs(getWallClockNs());
+
+ processor->mLastTtlTime = originalEventElapsedTime;
+ // Send log events to StatsLogProcessor.
+ processor->OnLogEvent(event2.get(), newEventElapsedTime);
+
+ EXPECT_FALSE(StorageManager::hasFile(fileName.c_str()));
+ StorageManager::deleteFile(fileName.c_str());
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestRestrictedMetricSavesTtlToDisk) {
+ metadata::StatsMetadataList result;
+ processor->WriteMetadataToProto(getWallClockNs(), configAddedTimeNs, &result);
+
+ ASSERT_EQ(result.stats_metadata_size(), 1);
+ metadata::StatsMetadata statsMetadata = result.stats_metadata(0);
+ EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
+ EXPECT_EQ(statsMetadata.config_key().uid(), config_app_uid);
+
+ ASSERT_EQ(statsMetadata.metric_metadata_size(), 1);
+ metadata::MetricMetadata metricMetadata = statsMetadata.metric_metadata(0);
+ EXPECT_EQ(metricMetadata.metric_id(), restrictedMetricId);
+ EXPECT_EQ(metricMetadata.restricted_category(), CATEGORY_UNKNOWN);
+ result.Clear();
+
+ std::unique_ptr<LogEvent> event = CreateRestrictedLogEvent(atomTag, configAddedTimeNs + 100);
+ processor->OnLogEvent(event.get());
+ processor->WriteMetadataToProto(getWallClockNs(), configAddedTimeNs, &result);
+
+ ASSERT_EQ(result.stats_metadata_size(), 1);
+ statsMetadata = result.stats_metadata(0);
+ EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
+ EXPECT_EQ(statsMetadata.config_key().uid(), config_app_uid);
+
+ ASSERT_EQ(statsMetadata.metric_metadata_size(), 1);
+ metricMetadata = statsMetadata.metric_metadata(0);
+ EXPECT_EQ(metricMetadata.metric_id(), restrictedMetricId);
+ EXPECT_EQ(metricMetadata.restricted_category(), CATEGORY_DIAGNOSTIC);
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestRestrictedMetricLoadsTtlFromDisk) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+ processor->OnLogEvent(event1.get(), originalEventElapsedTime);
+ processor->flushRestrictedDataLocked(originalEventElapsedTime);
+ int64_t wallClockNs = 1584991200 * NS_PER_SEC; // random time
+ int64_t metadataWriteTime = originalEventElapsedTime + 5000 * NS_PER_SEC;
+ processor->SaveMetadataToDisk(wallClockNs, metadataWriteTime);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+ EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ to_string(eightDaysAgo), _));
+
+ auto processor2 =
+ CreateStatsLogProcessor(/*baseTimeNs=*/0, configAddedTimeNs, config, configKey,
+ /*puller=*/nullptr, /*atomTag=*/0, uidMap);
+ // 2 hours used here because the TTL check period is 1 hour.
+ int64_t newEventElapsedTime = configAddedTimeNs + 2 * 3600 * NS_PER_SEC + 1; // 2 hrs later
+ processor2->LoadMetadataFromDisk(wallClockNs, newEventElapsedTime);
+
+ // Log another event and check that the original TTL is maintained across reboot
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(atomTag, newEventElapsedTime);
+ event2->setLogdWallClockTimestampNs(currentWallTimeNs);
+ processor2->OnLogEvent(event2.get(), newEventElapsedTime);
+ processor2->flushRestrictedDataLocked(newEventElapsedTime);
+
+ columnTypes.clear();
+ columnNames.clear();
+ rows.clear();
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+ EXPECT_THAT(rows[0], ElementsAre(to_string(atomTag), to_string(newEventElapsedTime),
+ to_string(currentWallTimeNs), _));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestNewRestrictionCategoryEventDeletesTable) {
+ int64_t currentWallTimeNs = getWallClockNs();
+ int64_t originalEventElapsedTime = configAddedTimeNs + 100;
+ std::unique_ptr<LogEvent> event1 =
+ CreateNonRestrictedLogEvent(atomTag, originalEventElapsedTime);
+ processor->OnLogEvent(event1.get());
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << dbutils::reformatMetricId(restrictedMetricId);
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime),
+ _, // wallTimestampNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+
+ // Log a second event that will go into the cache
+ std::unique_ptr<LogEvent> event2 =
+ CreateNonRestrictedLogEvent(atomTag, originalEventElapsedTime + 100);
+ processor->OnLogEvent(event2.get());
+
+ // Log a third event with a different category
+ std::unique_ptr<LogEvent> event3 =
+ CreateRestrictedLogEvent(atomTag, originalEventElapsedTime + 200);
+ processor->OnLogEvent(event3.get());
+
+ processor->querySql(query.str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult,
+ ElementsAre(to_string(atomTag), to_string(originalEventElapsedTime + 200),
+ _, // wallTimestampNs
+ _ // field_1
+ ));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER));
+}
+
+TEST_F(RestrictedEventMetricE2eTest, TestDeviceInfoTableCreated) {
+ std::string query = "SELECT * FROM device_info";
+ processor->querySql(query.c_str(), /*minSqlClientVersion=*/0,
+ /*policyConfig=*/{}, mockStatsQueryCallback,
+ /*configKey=*/configId, /*configPackage=*/config_package_name,
+ /*callingUid=*/delegate_uid);
+ EXPECT_EQ(rowCountResult, 1);
+ EXPECT_THAT(queryDataResult, ElementsAre(_, _, _, _, _, _, _, _, _, _));
+ EXPECT_THAT(columnNamesResult,
+ ElementsAre("sdkVersion", "model", "product", "hardware", "device", "osBuild",
+ "fingerprint", "brand", "manufacturer", "board"));
+ EXPECT_THAT(columnTypesResult,
+ ElementsAre(SQLITE_INTEGER, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT,
+ SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT));
+}
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/statsd/tests/e2e/StringReplace_e2e_test.cpp b/statsd/tests/e2e/StringReplace_e2e_test.cpp
new file mode 100644
index 0000000..d92499d
--- /dev/null
+++ b/statsd/tests/e2e/StringReplace_e2e_test.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2024, 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 <gtest_matchers.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+const int64_t metricId = 123456;
+const int TEST_ATOM_REPORTED_STRING_FIELD_ID = 5;
+const int SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID = 1;
+const int SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID = 2;
+const int SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID = 4;
+const int SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID = 2;
+const int ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID = 1;
+const int ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID = 2;
+const int WAKELOCK_STATE_CHANGED_TAG_FIELD_ID = 3;
+const int ATTRIBUTION_CHAIN_FIELD_ID = 1;
+const int ATTRIBUTION_TAG_FIELD_ID = 2;
+
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventStringDim(uint64_t timestampNs,
+ const string& stringField) {
+ return CreateTestAtomReportedEventWithPrimitives(
+ timestampNs, 0 /* intField */, 0l /* longField */, 0.0f /* floatField */, stringField,
+ false /* boolField */, TestAtomReported::OFF /* enumField */);
+}
+
+StatsdConfig CreateStatsdConfig() {
+ StatsdConfig config;
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
+ config.set_hash_strings_in_metric_report(false);
+
+ return config;
+}
+
+} // namespace
+
+TEST(StringReplaceE2eTest, TestPushedDimension) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+ ->mutable_simple_atom_matcher()
+ ->add_field_value_matcher();
+ fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ CountMetric* countMetric = config.add_count_metric();
+ *countMetric = createCountMetric("TestCountMetric", config.atom_matcher(0).id() /* what */,
+ nullopt /* condition */, {} /* states */);
+ countMetric->mutable_dimensions_in_what()->set_field(util::TEST_ATOM_REPORTED);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(
+ TEST_ATOM_REPORTED_STRING_FIELD_ID);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ const int uid = 12345;
+ const int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
+ "dimA" /* stringField */)); // 0:30
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
+ "dimA123" /* stringField */)); // 0:50
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+ "dimA123B" /* stringField */)); // 1:10
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+ "dimC0" /* stringField */)); // 1:20
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
+ "dimC00000" /* stringField */)); // 1:30
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+ "dimC" /* stringField */)); // 1:40
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(3, countMetrics.data_size());
+
+ CountMetricData data = countMetrics.data(0);
+ DimensionsValue dimValue = data.dimensions_in_what();
+ EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+ ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+ TEST_ATOM_REPORTED_STRING_FIELD_ID);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA");
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).count());
+
+ data = countMetrics.data(1);
+ dimValue = data.dimensions_in_what();
+ EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+ ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+ TEST_ATOM_REPORTED_STRING_FIELD_ID);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA123B");
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(2);
+ dimValue = data.dimensions_in_what();
+ EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+ ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+ TEST_ATOM_REPORTED_STRING_FIELD_ID);
+ EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimC");
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(3, data.bucket_info(0).count());
+}
+
+TEST(StringReplaceE2eTest, TestPushedWhat) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+
+ FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+ ->mutable_simple_atom_matcher()
+ ->add_field_value_matcher();
+ fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "TestAtomGaugeMetric", config.atom_matcher(0).id() /* what */,
+ GaugeMetric::FIRST_N_SAMPLES, nullopt /* condition */, nullopt /* triggerEvent */);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000LL;
+ const int uid = 12345;
+ const int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
+ "dimA" /* stringField */)); // 0:30
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
+ "dimA123" /* stringField */)); // 0:50
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+ "dimA123B" /* stringField */)); // 1:10
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+ "dimC0" /* stringField */)); // 1:20
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
+ "dimC00000" /* stringField */)); // 1:30
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+ "dimC" /* stringField */)); // 1:40
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
+
+ ASSERT_EQ(6, data.bucket_info(0).atom_size());
+ EXPECT_EQ(data.bucket_info(0).atom(0).test_atom_reported().string_field(), "dimA");
+ EXPECT_EQ(data.bucket_info(0).atom(1).test_atom_reported().string_field(), "dimA");
+ EXPECT_EQ(data.bucket_info(0).atom(2).test_atom_reported().string_field(), "dimA123B");
+ EXPECT_EQ(data.bucket_info(0).atom(3).test_atom_reported().string_field(), "dimC");
+ EXPECT_EQ(data.bucket_info(0).atom(4).test_atom_reported().string_field(), "dimC");
+ EXPECT_EQ(data.bucket_info(0).atom(5).test_atom_reported().string_field(), "dimC");
+}
+
+TEST(StringReplaceE2eTest, TestPulledDimension) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
+ FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+ ->mutable_simple_atom_matcher()
+ ->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ stringReplacer->set_replacement("");
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
+ GaugeMetric::RANDOM_ONE_SAMPLE, nullopt /* condition */, nullopt /* triggerEvent */);
+ *config.mutable_gauge_metric(0)->mutable_dimensions_in_what() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ processor->mPullerManager->ForceClearPullerCache();
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID,
+ data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+
+ // Trailing numbers are trimmed from the dimension: subsystem_name_# --> subsystem_name_
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
+ "subsystem_name_");
+}
+
+TEST(StringReplaceE2eTest, TestPulledWhat) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
+ FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+ ->mutable_simple_atom_matcher()
+ ->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(foo)");
+ stringReplacer->set_replacement("bar");
+
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+ *config.add_predicate() = CreateScreenIsOffPredicate();
+
+ *config.add_gauge_metric() =
+ createGaugeMetric("SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
+ GaugeMetric::RANDOM_ONE_SAMPLE,
+ config.predicate(0).id() /* condition */, nullopt /* triggerEvent */);
+
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ processor->mPullerManager->ForceClearPullerCache();
+
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+ processor->OnLogEvent(screenOffEvent.get());
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+ auto data = gaugeMetrics.data(0);
+ ASSERT_EQ(2, data.bucket_info_size());
+
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().subname(),
+ "subsystem_subname bar");
+
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(data.bucket_info(1).atom(0).subsystem_sleep_state().subname(),
+ "subsystem_subname bar");
+}
+
+TEST(StringReplaceE2eTest, TestCondition) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ AtomMatcher matcher = CreateStartScheduledJobAtomMatcher();
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
+ fvm->set_eq_string("foo");
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(com.google.)");
+ stringReplacer->set_replacement("");
+ *config.add_atom_matcher() = matcher;
+ matcher = CreateFinishScheduledJobAtomMatcher();
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
+ fvm->set_eq_string("foo");
+ stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(com.google.)");
+ stringReplacer->set_replacement("");
+ *config.add_atom_matcher() = matcher;
+
+ Predicate predicate = CreateScheduledJobPredicate();
+ *config.add_predicate() = predicate;
+
+ matcher = CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+ *config.add_atom_matcher() = matcher;
+
+ CountMetric* countMetric = config.add_count_metric();
+ *countMetric = createCountMetric("TestCountMetric", matcher.id() /* what */,
+ predicate.id() /* condition */, {} /* states */);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(countMetric->bucket()) * 1000000LL;
+ const int uid = 12345;
+ const int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
+ {1001} /* uids */, {"App1"},
+ "com.google.job1")); // 0:30
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 30 * NS_PER_SEC,
+ "str" /* stringField */)); // 0:40
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 40 * NS_PER_SEC,
+ {1002} /* uids */, {"App1"},
+ "com.google.foo")); // 0:50
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 50 * NS_PER_SEC,
+ "str" /* stringField */)); // 1:00
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+ "str" /* stringField */)); // 1:10
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 70 * NS_PER_SEC,
+ {1001} /* uids */, {"App1"},
+ "com.google.job1")); // 1:20
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+ "str" /* stringField */)); // 1:30
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 90 * NS_PER_SEC,
+ {1002} /* uids */, {"App1"},
+ "com.google.foo")); // 1:40
+ events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+ "str" /* stringField */)); // 1:50
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(1, countMetrics.data_size());
+
+ CountMetricData data = countMetrics.data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(3, data.bucket_info(0).count());
+}
+
+TEST(StringReplaceE2eTest, TestDurationMetric) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ AtomMatcher matcher = CreateAcquireWakelockAtomMatcher();
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
+ fvm->set_position(Position::FIRST);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ StringReplacer* stringReplacer =
+ fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+)");
+ stringReplacer->set_replacement("#");
+ *config.add_atom_matcher() = matcher;
+
+ matcher = CreateReleaseWakelockAtomMatcher();
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
+ fvm->set_position(Position::FIRST);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
+ stringReplacer =
+ fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+)");
+ stringReplacer->set_replacement("#");
+ *config.add_atom_matcher() = matcher;
+
+ matcher = CreateMoveToBackgroundAtomMatcher();
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
+ stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+)");
+ stringReplacer->set_replacement("#");
+ *config.add_atom_matcher() = matcher;
+
+ matcher = CreateMoveToForegroundAtomMatcher();
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
+ stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"([0-9]+)");
+ stringReplacer->set_replacement("#");
+ *config.add_atom_matcher() = matcher;
+
+ Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid and tag.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED,
+ {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
+ ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ DurationMetric durationMetric =
+ createDurationMetric("WakelockDuration", holdingWakelockPredicate.id() /* what */,
+ isInBackgroundPredicate.id() /* condition */, {} /* states */);
+ *durationMetric.mutable_dimensions_in_what() =
+ CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric.set_bucket(FIVE_MINUTES);
+ *config.add_duration_metric() = durationMetric;
+
+ // Links between wakelock state atom and condition of app is in background.
+ MetricConditionLink* link = durationMetric.add_links();
+ link->set_condition(isInBackgroundPredicate.id());
+ *link->mutable_fields_in_what() =
+ CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *link->mutable_fields_in_condition() =
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED,
+ {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
+ ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(durationMetric.bucket()) * 1000000LL;
+ const int uid = 12345;
+ const int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+ bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+ int appUid = 123;
+ std::vector<int> attributionUids1 = {appUid};
+ std::vector<string> attributionTags1 = {"App1"};
+ std::vector<string> attributionTags2 = {"App2"};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wl1")); // 0:10
+ events.push_back(CreateActivityForegroundStateChangedEvent(
+ bucketStartTimeNs + 22 * NS_PER_SEC, appUid, "App1", "class_name",
+ ActivityForegroundStateChanged::BACKGROUND)); // 0:22
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wl1")); // 1:00
+ events.push_back(CreateActivityForegroundStateChangedEvent(
+ bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, appUid, "App1", "class_name",
+ ActivityForegroundStateChanged::FOREGROUND)); // 3:15
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + (3 * 60 + 20) * NS_PER_SEC,
+ attributionUids1, attributionTags2,
+ "wl2")); // 3:20
+ events.push_back(CreateActivityForegroundStateChangedEvent(
+ bucketStartTimeNs + (3 * 60 + 30) * NS_PER_SEC, appUid, "App1", "class_name",
+ ActivityForegroundStateChanged::BACKGROUND)); // 3:30
+ events.push_back(CreateActivityForegroundStateChangedEvent(
+ bucketStartTimeNs + (3 * 60 + 40) * NS_PER_SEC, appUid, "App2", "class_name",
+ ActivityForegroundStateChanged::FOREGROUND)); // 3:40
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + (3 * 60 + 50) * NS_PER_SEC,
+ attributionUids1, attributionTags2,
+ "wl2")); // 3:50
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
+
+ DurationMetricData data = durationMetrics.data(0);
+ // Validate dimension value.
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+ appUid, "App#");
+ // Validate bucket info.
+ ASSERT_EQ(1, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(48 * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(StringReplaceE2eTest, TestMultipleMatchersForAtom) {
+ StatsdConfig config = CreateStatsdConfig();
+
+ {
+ AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher1", util::SUBSYSTEM_SLEEP_STATE);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+ fvm->set_eq_string("subsystem_name_1");
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+ fvm->set_eq_string("subsystem_subname bar");
+ StringReplacer* stringReplacer = fvm->mutable_replace_string();
+ stringReplacer->set_regex(R"(foo)");
+ stringReplacer->set_replacement("bar");
+ *config.add_atom_matcher() = matcher;
+
+ *config.add_value_metric() =
+ createValueMetric("Value1", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
+ nullopt /* condition */, {} /* states */);
+ }
+ {
+ AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher2", util::SUBSYSTEM_SLEEP_STATE);
+ FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+ fvm->set_eq_string("subsystem_name_2");
+ fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+ fvm->set_eq_string("subsystem_subname foo");
+ *config.add_atom_matcher() = matcher;
+
+ *config.add_value_metric() =
+ createValueMetric("Value2", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
+ nullopt /* condition */, {} /* states */);
+ }
+
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ processor->mPullerManager->ForceClearPullerCache();
+
+ // Pulling alarm arrives on time and reset the sequential pulling alarm.
+ processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+ backfillAggregatedAtoms(&reports);
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(2, reports.reports(0).metrics_size());
+
+ {
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(),
+ &valueMetrics);
+ ASSERT_EQ(valueMetrics.data_size(), 1);
+
+ ValueMetricData data = valueMetrics.data(0);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ ASSERT_EQ(data.bucket_info(0).values_size(), 1);
+ }
+ {
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).value_metrics(),
+ &valueMetrics);
+ ASSERT_EQ(valueMetrics.data_size(), 1);
+
+ ValueMetricData data = valueMetrics.data(0);
+ ASSERT_EQ(data.bucket_info_size(), 1);
+ ASSERT_EQ(data.bucket_info(0).values_size(), 1);
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 1d8307b..323ed17 100644
--- a/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -35,7 +35,6 @@
StatsdConfig CreateStatsdConfig(bool useCondition = true) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
@@ -67,7 +66,6 @@
StatsdConfig CreateStatsdConfigWithStates() {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
@@ -276,6 +274,7 @@
ASSERT_EQ(1, reports.reports_size());
ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_estimated_data_bytes());
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
ASSERT_GT((int)valueMetrics.data_size(), 1);
@@ -652,7 +651,6 @@
TEST(ValueMetricE2eTest, TestInitWithSlicedState) {
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto pulledAtomMatcher =
CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
@@ -706,7 +704,6 @@
TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) {
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto cpuTimePerUidMatcher =
CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
@@ -766,7 +763,6 @@
TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) {
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto cpuTimePerUidMatcher =
CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
@@ -809,7 +805,6 @@
TEST(ValueMetricE2eTest, TestInitWithValueFieldPositionALL) {
// Create config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
AtomMatcher testAtomReportedMatcher =
CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
@@ -836,6 +831,76 @@
ASSERT_EQ(0, processor->mMetricsManagers.size());
}
+TEST(ValueMetricE2eTest, TestInitWithMultipleAggTypes) {
+ // Create config.
+ StatsdConfig config;
+
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ // Create value metric.
+ int64_t metricId = 123456;
+ ValueMetric* valueMetric = config.add_value_metric();
+ valueMetric->set_id(metricId);
+ valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ valueMetric->set_what(testAtomReportedMatcher.id());
+ *valueMetric->mutable_value_field() = CreateDimensions(
+ util::TEST_ATOM_REPORTED, {2 /*int_field*/, 2 /*int_field*/, 3 /*long_field*/,
+ 3 /*long_field*/, 3 /*long_field*/});
+ valueMetric->add_aggregation_types(ValueMetric::SUM);
+ valueMetric->add_aggregation_types(ValueMetric::MIN);
+ valueMetric->add_aggregation_types(ValueMetric::MAX);
+ valueMetric->add_aggregation_types(ValueMetric::AVG);
+ valueMetric->add_aggregation_types(ValueMetric::MIN);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+}
+
+TEST(ValueMetricE2eTest, TestInitWithDefaultAggType) {
+ // Create config.
+ StatsdConfig config;
+
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TestAtomReportedMatcher", util::TEST_ATOM_REPORTED);
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ // Create value metric.
+ int64_t metricId = 123456;
+ ValueMetric* valueMetric = config.add_value_metric();
+ valueMetric->set_id(metricId);
+ valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ valueMetric->set_what(testAtomReportedMatcher.id());
+ *valueMetric->mutable_value_field() =
+ CreateDimensions(util::TEST_ATOM_REPORTED, {3 /*long_field*/, 2 /*int_field*/});
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 52bc222..1567db0 100644
--- a/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -30,7 +30,6 @@
StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
diff --git a/statsd/tests/external/puller_util_test.cpp b/statsd/tests/external/puller_util_test.cpp
index 25c0c7e..71f4de4 100644
--- a/statsd/tests/external/puller_util_test.cpp
+++ b/statsd/tests/external/puller_util_test.cpp
@@ -22,7 +22,6 @@
#include "../metrics/metrics_test_helper.h"
#include "FieldValue.h"
-#include "annotations.h"
#include "stats_event.h"
#include "tests/statsd_test_util.h"
diff --git a/statsd/tests/guardrail/StatsdStats_test.cpp b/statsd/tests/guardrail/StatsdStats_test.cpp
index d37a810..1d56caa 100644
--- a/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -27,13 +27,43 @@
#ifdef __ANDROID__
+namespace std {
+void PrintTo(const tuple<int, size_t>& atomIdDimensionLimitTuple, ostream* os) {
+ *os << get<0>(atomIdDimensionLimitTuple) << "_" << get<1>(atomIdDimensionLimitTuple);
+}
+} // namespace std
+
namespace android {
namespace os {
namespace statsd {
+namespace {
+using namespace testing;
using PerSubscriptionStats = StatsdStatsReport_SubscriptionStats_PerSubscriptionStats;
+using std::tuple;
using std::vector;
+class StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap
+ : public TestWithParam<tuple<int, size_t>> {};
+INSTANTIATE_TEST_SUITE_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap,
+ StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap,
+ Combine(Values(10022 /* BINDER_CALLS */, 10024 /* LOOPER_STATS */,
+ 10010 /* CPU_TIME_PER_UID_FREQ */),
+ Values(-1, 0, 500, 800, 1000, 3000, 3300)),
+ PrintToStringParamName());
+
+class StatsdStatsTest_GetAtomDimensionKeySizeLimit_NotInMap
+ : public StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap {};
+
+INSTANTIATE_TEST_SUITE_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_NotInMap,
+ StatsdStatsTest_GetAtomDimensionKeySizeLimit_NotInMap,
+ Combine(Values(util::TEST_ATOM_REPORTED, util::SCREEN_STATE_CHANGED,
+ util::SUBSYSTEM_SLEEP_STATE),
+ Values(-1, 0, 500, 800, 1000, 3000, 3300)),
+ PrintToStringParamName());
+
+} // anonymous namespace
+
TEST(StatsdStatsTest, TestValidConfigAdd) {
StatsdStats stats;
ConfigKey key(0, 12345);
@@ -218,9 +248,9 @@
stats.noteDataDropped(key, 123);
// dump report -> 3
- stats.noteMetricsReportSent(key, 0);
- stats.noteMetricsReportSent(key, 0);
- stats.noteMetricsReportSent(key, 0);
+ stats.noteMetricsReportSent(key, 0, 1);
+ stats.noteMetricsReportSent(key, 0, 2);
+ stats.noteMetricsReportSent(key, 0, 3);
// activation_time_sec -> 2
stats.noteActiveStatusChanged(key, true);
@@ -238,6 +268,10 @@
EXPECT_EQ(123, configReport.data_drop_bytes(0));
ASSERT_EQ(3, configReport.dump_report_time_sec_size());
ASSERT_EQ(3, configReport.dump_report_data_size_size());
+ ASSERT_EQ(3, configReport.dump_report_number_size());
+ EXPECT_EQ(1, configReport.dump_report_number(0));
+ EXPECT_EQ(2, configReport.dump_report_number(1));
+ EXPECT_EQ(3, configReport.dump_report_number(2));
ASSERT_EQ(2, configReport.activation_time_sec_size());
ASSERT_EQ(1, configReport.deactivation_time_sec_size());
ASSERT_EQ(1, configReport.annotation_size());
@@ -439,6 +473,98 @@
EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
}
+TEST(StatsdStatsTest, TestRestrictedMetricsStats) {
+ StatsdStats stats;
+ const int64_t metricId = -1234556L;
+ ConfigKey key(0, 12345);
+ stats.noteConfigReceived(key, 2, 3, 4, 5, {}, nullopt);
+ stats.noteRestrictedMetricInsertError(key, metricId);
+ stats.noteRestrictedMetricTableCreationError(key, metricId);
+ stats.noteRestrictedMetricTableDeletionError(key, metricId);
+ stats.noteDeviceInfoTableCreationFailed(key);
+ stats.noteRestrictedMetricFlushLatency(key, metricId, 3000);
+ stats.noteRestrictedMetricFlushLatency(key, metricId, 3001);
+ stats.noteRestrictedMetricCategoryChanged(key, metricId);
+ stats.noteRestrictedConfigFlushLatency(key, 4000);
+ ConfigKey configKeyWithoutError(0, 666);
+ stats.noteConfigReceived(configKeyWithoutError, 2, 3, 4, 5, {}, nullopt);
+ stats.noteDbCorrupted(key);
+ stats.noteDbCorrupted(key);
+ stats.noteDbSizeExceeded(key);
+ stats.noteDbStatFailed(key);
+ stats.noteDbConfigInvalid(key);
+ stats.noteDbTooOld(key);
+ stats.noteDbDeletionConfigRemoved(key);
+ stats.noteDbDeletionConfigUpdated(key);
+ stats.noteRestrictedConfigDbSize(key, 999, 111);
+
+ StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false);
+ ASSERT_EQ(2, report.config_stats().size());
+ ASSERT_EQ(0, report.config_stats(0).restricted_metric_stats().size());
+ ASSERT_EQ(1, report.config_stats(1).restricted_metric_stats().size());
+ EXPECT_EQ(1, report.config_stats(1).restricted_metric_stats(0).insert_error());
+ EXPECT_EQ(1, report.config_stats(1).restricted_metric_stats(0).table_creation_error());
+ EXPECT_EQ(1, report.config_stats(1).restricted_metric_stats(0).table_deletion_error());
+ EXPECT_EQ(1, report.config_stats(1).restricted_metric_stats(0).category_changed_count());
+ ASSERT_EQ(2, report.config_stats(1).restricted_metric_stats(0).flush_latency_ns().size());
+ EXPECT_EQ(3000, report.config_stats(1).restricted_metric_stats(0).flush_latency_ns(0));
+ EXPECT_EQ(3001, report.config_stats(1).restricted_metric_stats(0).flush_latency_ns(1));
+ ASSERT_EQ(1, report.config_stats(1).restricted_db_size_time_sec().size());
+ EXPECT_EQ(999, report.config_stats(1).restricted_db_size_time_sec(0));
+ ASSERT_EQ(1, report.config_stats(1).restricted_db_size_bytes().size());
+ EXPECT_EQ(111, report.config_stats(1).restricted_db_size_bytes(0));
+ ASSERT_EQ(1, report.config_stats(1).restricted_flush_latency().size());
+ EXPECT_EQ(4000, report.config_stats(1).restricted_flush_latency(0));
+ EXPECT_TRUE(report.config_stats(1).device_info_table_creation_failed());
+ EXPECT_EQ(metricId, report.config_stats(1).restricted_metric_stats(0).restricted_metric_id());
+ EXPECT_EQ(2, report.config_stats(1).restricted_db_corrupted_count());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_stat_failed());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_size_exceeded_limit());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_config_invalid());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_too_old());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_config_removed());
+ EXPECT_EQ(1, report.config_stats(1).db_deletion_config_updated());
+}
+
+TEST(StatsdStatsTest, TestRestrictedMetricsQueryStats) {
+ StatsdStats stats;
+ const int32_t callingUid = 100;
+ ConfigKey configKey(0, 12345);
+ const string configPackage = "com.google.android.gm";
+ int64_t beforeNoteMetricSucceed = getWallClockNs();
+ stats.noteQueryRestrictedMetricSucceed(configKey.GetId(), configPackage, configKey.GetUid(),
+ callingUid, /*queryLatencyNs=*/5 * NS_PER_SEC);
+ int64_t afterNoteMetricSucceed = getWallClockNs();
+
+ const int64_t configIdWithError = 111;
+ stats.noteQueryRestrictedMetricFailed(configIdWithError, configPackage, std::nullopt,
+ callingUid, InvalidQueryReason(AMBIGUOUS_CONFIG_KEY));
+ stats.noteQueryRestrictedMetricFailed(configIdWithError, configPackage, std::nullopt,
+ callingUid, InvalidQueryReason(AMBIGUOUS_CONFIG_KEY),
+ "error_message");
+
+ StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false);
+ ASSERT_EQ(3, report.restricted_metric_query_stats().size());
+ EXPECT_EQ(configKey.GetId(), report.restricted_metric_query_stats(0).config_id());
+ EXPECT_EQ(configKey.GetUid(), report.restricted_metric_query_stats(0).config_uid());
+ EXPECT_EQ(callingUid, report.restricted_metric_query_stats(0).calling_uid());
+ EXPECT_EQ(configPackage, report.restricted_metric_query_stats(0).config_package());
+ EXPECT_FALSE(report.restricted_metric_query_stats(0).has_query_error());
+ EXPECT_LT(beforeNoteMetricSucceed,
+ report.restricted_metric_query_stats(0).query_wall_time_ns());
+ EXPECT_GT(afterNoteMetricSucceed, report.restricted_metric_query_stats(0).query_wall_time_ns());
+ EXPECT_EQ(5 * NS_PER_SEC, report.restricted_metric_query_stats(0).query_latency_ns());
+ EXPECT_EQ(configIdWithError, report.restricted_metric_query_stats(1).config_id());
+ EXPECT_EQ(AMBIGUOUS_CONFIG_KEY, report.restricted_metric_query_stats(1).invalid_query_reason());
+ EXPECT_EQ(false, report.restricted_metric_query_stats(1).has_config_uid());
+ EXPECT_FALSE(report.restricted_metric_query_stats(1).has_query_error());
+ EXPECT_FALSE(report.restricted_metric_query_stats(1).has_query_latency_ns());
+ EXPECT_EQ("error_message", report.restricted_metric_query_stats(2).query_error());
+ EXPECT_FALSE(report.restricted_metric_query_stats(2).has_query_latency_ns());
+ EXPECT_NE(report.restricted_metric_query_stats(1).query_wall_time_ns(),
+ report.restricted_metric_query_stats(0).query_wall_time_ns());
+}
+
TEST(StatsdStatsTest, TestAnomalyMonitor) {
StatsdStats stats;
stats.noteRegisteredAnomalyAlarmChanged();
@@ -460,7 +586,7 @@
for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
stats.noteDataDropped(key, timestamps[i]);
stats.noteBroadcastSent(key, timestamps[i]);
- stats.noteMetricsReportSent(key, 0, timestamps[i]);
+ stats.noteMetricsReportSent(key, 0, timestamps[i], i + 1);
stats.noteActiveStatusChanged(key, true, timestamps[i]);
stats.noteActiveStatusChanged(key, false, timestamps[i]);
}
@@ -470,7 +596,7 @@
// now it should trigger removing oldest timestamp
stats.noteDataDropped(key, 123, 10000);
stats.noteBroadcastSent(key, 10000);
- stats.noteMetricsReportSent(key, 0, 10000);
+ stats.noteMetricsReportSent(key, 0, 10000, 21);
stats.noteActiveStatusChanged(key, true, 10000);
stats.noteActiveStatusChanged(key, false, 10000);
@@ -487,7 +613,7 @@
// the oldest timestamp is the second timestamp in history
EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
EXPECT_EQ(1, configStats->data_drop_bytes.front());
- EXPECT_EQ(1, configStats->dump_report_stats.front().first);
+ EXPECT_EQ(1, configStats->dump_report_stats.front().mDumpReportTimeSec);
EXPECT_EQ(1, configStats->activation_time_sec.front());
EXPECT_EQ(1, configStats->deactivation_time_sec.front());
@@ -495,7 +621,7 @@
EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
EXPECT_EQ(123, configStats->data_drop_bytes.back());
- EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
+ EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().mDumpReportTimeSec);
EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back());
EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back());
}
@@ -624,6 +750,16 @@
EXPECT_FALSE(nonPlatformPushedAtomStats.has_skip_count());
}
+TEST(StatsdStatsTest, TestQueueStats) {
+ StatsdStats stats;
+
+ stats.noteEventQueueSize(100, 1000);
+ StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ true);
+
+ ASSERT_EQ(100, report.event_queue_stats().max_size_observed());
+ ASSERT_EQ(1000, report.event_queue_stats().max_size_observed_elapsed_nanos());
+}
+
TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats) {
StatsdStats stats;
@@ -872,6 +1008,111 @@
Contains(Property(&PerSubscriptionStats::id, Eq(maxSubs + 1))));
}
+TEST(StatsdStatsTest, TestEnforceDimensionKeySizeLimit) {
+ EXPECT_EQ(StatsdStats::clampDimensionKeySizeLimit(-1),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ EXPECT_EQ(StatsdStats::clampDimensionKeySizeLimit(0),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ EXPECT_EQ(StatsdStats::clampDimensionKeySizeLimit(500),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ EXPECT_EQ(StatsdStats::clampDimensionKeySizeLimit(1000), 1000);
+ EXPECT_EQ(StatsdStats::clampDimensionKeySizeLimit(3500),
+ StatsdStats::kDimensionKeySizeHardLimitMax);
+}
+
+TEST(StatsdStatsTest, TestSocketLossStats) {
+ StatsdStats stats;
+
+ const int maxLossEvents = StatsdStats::kMaxSocketLossStatsSize;
+
+ // Note maxLossEvents + 1
+ for (int eventId = 0; eventId <= maxLossEvents; eventId++) {
+ SocketLossInfo info;
+
+ info.uid = eventId;
+ info.firstLossTsNanos = 10 * eventId;
+ info.lastLossTsNanos = 10 * eventId + 1;
+
+ info.atomIds.push_back(eventId * 10);
+ info.errors.push_back(eventId * 20);
+ info.counts.push_back(eventId * 30);
+
+ stats.noteAtomSocketLoss(info);
+ }
+
+ StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false);
+
+ auto socketLossStats = report.socket_loss_stats();
+ ASSERT_EQ(socketLossStats.loss_stats_per_uid().size(), maxLossEvents);
+
+ for (int i = 0; i < socketLossStats.loss_stats_per_uid().size(); i++) {
+ const auto& info = report.socket_loss_stats().loss_stats_per_uid(i);
+
+ // due to the very first one with id 0 is popped out from the list ids (index) start from 1
+ const int index = i + 1;
+
+ ASSERT_EQ(info.uid(), index);
+ ASSERT_EQ(info.first_timestamp_nanos(), 10 * index);
+ ASSERT_EQ(info.last_timestamp_nanos(), 10 * index + 1);
+
+ ASSERT_EQ(info.atom_id_loss_stats().size(), 1);
+
+ ASSERT_EQ(info.atom_id_loss_stats(0).atom_id(), index * 10);
+ ASSERT_EQ(info.atom_id_loss_stats(0).error(), index * 20);
+ ASSERT_EQ(info.atom_id_loss_stats(0).count(), index * 30);
+ }
+}
+
+TEST(StatsdStatsTest, TestSocketLossStatsOverflowCounter) {
+ StatsdStats stats;
+
+ const int uidsCount = 5;
+ const int lossEventCount = 5;
+
+ for (int uid = 0; uid < uidsCount; uid++) {
+ for (int eventId = 0; eventId < lossEventCount; eventId++) {
+ SocketLossInfo info;
+
+ info.uid = uid;
+ info.firstLossTsNanos = 10 * eventId;
+ info.lastLossTsNanos = 10 * eventId + 1;
+ // the counter value will be accumulated
+ info.overflowCounter = 1;
+
+ info.atomIds.push_back(eventId * 10);
+ info.errors.push_back(eventId * 20);
+ info.counts.push_back(eventId * 30);
+
+ stats.noteAtomSocketLoss(info);
+ }
+ }
+ StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ false);
+
+ auto socketLossStatsOverflowCounters =
+ report.socket_loss_stats().loss_stats_overflow_counters();
+ ASSERT_EQ(socketLossStatsOverflowCounters.size(), uidsCount);
+
+ for (int i = 0; i < socketLossStatsOverflowCounters.size(); i++) {
+ const auto& counters = report.socket_loss_stats().loss_stats_overflow_counters(i);
+
+ ASSERT_EQ(counters.uid(), i);
+ ASSERT_EQ(counters.count(), lossEventCount);
+ }
+}
+
+TEST_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_InMap, TestGetAtomDimensionKeySizeLimits) {
+ const auto& [atomId, defaultHardLimit] = GetParam();
+ EXPECT_EQ(StatsdStats::getAtomDimensionKeySizeLimits(atomId, defaultHardLimit),
+ StatsdStats::kAtomDimensionKeySizeLimitMap.at(atomId));
+}
+
+TEST_P(StatsdStatsTest_GetAtomDimensionKeySizeLimit_NotInMap, TestGetAtomDimensionKeySizeLimits) {
+ const auto& [atomId, defaultHardLimit] = GetParam();
+ EXPECT_EQ(
+ StatsdStats::getAtomDimensionKeySizeLimits(atomId, defaultHardLimit),
+ (std::pair<size_t, size_t>(StatsdStats::kDimensionKeySizeSoftLimit, defaultHardLimit)));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/indexed_priority_queue_test.cpp b/statsd/tests/indexed_priority_queue_test.cpp
index 3a65456..1d365e8 100644
--- a/statsd/tests/indexed_priority_queue_test.cpp
+++ b/statsd/tests/indexed_priority_queue_test.cpp
@@ -30,7 +30,7 @@
const std::string b;
struct Smaller {
- bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
+ bool operator()(const sp<const AATest>& a, const sp<const AATest>& b) const {
return (a->val < b->val);
}
};
diff --git a/statsd/tests/log_event/LogEventQueue_test.cpp b/statsd/tests/log_event/LogEventQueue_test.cpp
index a15f95b..fe6a27a 100644
--- a/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -20,6 +20,7 @@
#include <thread>
+#include "socket/StatsSocketListener.h"
#include "stats_event.h"
#include "tests/statsd_test_util.h"
@@ -34,37 +35,43 @@
namespace {
-std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+AStatsEvent* makeStatsEvent(uint64_t timestampNs) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 10);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_build(statsEvent);
+ return statsEvent;
+}
+std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+ AStatsEvent* statsEvent = makeStatsEvent(timestampNs);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ EXPECT_EQ(logEvent->GetElapsedTimestampNs(), timestampNs);
return logEvent;
}
-} // anonymous namespace
+} // anonymous namespace
#ifdef __ANDROID__
TEST(LogEventQueue_test, TestGoodConsumer) {
LogEventQueue queue(50);
- int64_t timeBaseNs = 100;
- std::thread writer([&queue, timeBaseNs] {
+ int64_t eventTimeNs = 100;
+ std::thread writer([&queue, eventTimeNs] {
+ LogEventQueue::Result result;
for (int i = 0; i < 100; i++) {
- int64_t oldestEventNs;
- bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
- EXPECT_TRUE(success);
+ result = queue.push(makeLogEvent(eventTimeNs + i * 1000));
+ EXPECT_TRUE(result.success);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
});
- std::thread reader([&queue, timeBaseNs] {
+ std::thread reader([&queue, eventTimeNs] {
for (int i = 0; i < 100; i++) {
auto event = queue.waitPop();
EXPECT_TRUE(event != nullptr);
// All events are in right order.
- EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
+ EXPECT_EQ(eventTimeNs + i * 1000, event->GetElapsedTimestampNs());
}
});
@@ -74,13 +81,15 @@
TEST(LogEventQueue_test, TestSlowConsumer) {
LogEventQueue queue(50);
- int64_t timeBaseNs = 100;
- std::thread writer([&queue, timeBaseNs] {
+ int64_t eventTimeNs = 100;
+ std::thread writer([&queue, eventTimeNs] {
int failure_count = 0;
- int64_t oldestEventNs;
+ LogEventQueue::Result result;
for (int i = 0; i < 100; i++) {
- bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
- if (!success) failure_count++;
+ result = queue.push(makeLogEvent(eventTimeNs + i * 1000));
+ if (!result.success) {
+ failure_count++;
+ }
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@@ -89,16 +98,16 @@
// There will be at least 45 events lost due to overflow.
EXPECT_TRUE(failure_count >= 45);
// The oldest event must be at least the 6th event.
- EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000));
+ EXPECT_TRUE(result.oldestTimestampNs <= (100 + 5 * 1000));
});
- std::thread reader([&queue, timeBaseNs] {
+ std::thread reader([&queue, eventTimeNs] {
// The consumer quickly processed 5 events, then it got stuck (not reading anymore).
for (int i = 0; i < 5; i++) {
auto event = queue.waitPop();
EXPECT_TRUE(event != nullptr);
// All events are in right order.
- EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
+ EXPECT_EQ(eventTimeNs + i * 1000, event->GetElapsedTimestampNs());
}
});
@@ -106,6 +115,87 @@
writer.join();
}
+TEST(LogEventQueue_test, TestQueueMaxSize) {
+ StatsdStats::getInstance().reset();
+
+ LogEventQueue queue(50);
+ LogEventFilter filter;
+ filter.setFilteringEnabled(false);
+
+ int64_t eventTimeNs = 100;
+ int64_t oldestEventNs = 0;
+ int32_t newSize = 0;
+ for (int i = 0; i < 30; i++, eventTimeNs++) {
+ auto statsEvent = makeStatsEvent(eventTimeNs);
+ size_t bufferSize;
+ const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+ StatsSocketListener::processStatsEventBuffer(buffer, bufferSize, 0, 0, queue, filter);
+ AStatsEvent_release(statsEvent);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, i + 1);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, eventTimeNs);
+ }
+
+ const int32_t lastMaxSizeObserved = StatsdStats::getInstance().mEventQueueMaxSizeObserved;
+ const int64_t lastMaxSizeElapsedNanos =
+ StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos;
+
+ // consumer reads the entire queue
+ int64_t nextEventTs = 100;
+ for (int i = 0; i < 30; i++, nextEventTs++) {
+ auto event = queue.waitPop();
+ EXPECT_TRUE(event != nullptr);
+ // All events are in right order.
+ EXPECT_EQ(nextEventTs, event->GetElapsedTimestampNs());
+ }
+
+ // the expectation after queue drained entirely the max count & ts do not update for
+ // smaller values
+ {
+ auto statsEvent = makeStatsEvent(eventTimeNs);
+ size_t bufferSize;
+ const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+ StatsSocketListener::processStatsEventBuffer(buffer, bufferSize, 0, 0, queue, filter);
+ AStatsEvent_release(statsEvent);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, lastMaxSizeObserved);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos,
+ lastMaxSizeElapsedNanos);
+ eventTimeNs++;
+ }
+
+ for (int i = 0; i < 1; i++, nextEventTs++) {
+ auto event = queue.waitPop();
+ EXPECT_TRUE(event != nullptr);
+ // All events are in right order.
+ EXPECT_EQ(nextEventTs, event->GetElapsedTimestampNs());
+ }
+
+ // the expectation after queue drained entirely the max count & ts do update for
+ // bigger values
+ // fill up to the the previous max values observed - stats are not changed
+ for (int i = 0; i < lastMaxSizeObserved; i++, eventTimeNs++) {
+ auto statsEvent = makeStatsEvent(eventTimeNs);
+ size_t bufferSize;
+ const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+ StatsSocketListener::processStatsEventBuffer(buffer, bufferSize, 0, 0, queue, filter);
+ AStatsEvent_release(statsEvent);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, lastMaxSizeObserved);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos,
+ lastMaxSizeElapsedNanos);
+ }
+
+ // add extra elements to update the stats
+ for (int i = 0; i < 10; i++, eventTimeNs++) {
+ auto statsEvent = makeStatsEvent(eventTimeNs);
+ size_t bufferSize;
+ const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+ StatsSocketListener::processStatsEventBuffer(buffer, bufferSize, 0, 0, queue, filter);
+ AStatsEvent_release(statsEvent);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved,
+ lastMaxSizeObserved + i + 1);
+ EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, eventTimeNs);
+ }
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/metrics/CountMetricProducer_test.cpp b/statsd/tests/metrics/CountMetricProducer_test.cpp
index 32c48b0..8db5bce 100644
--- a/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -75,8 +75,10 @@
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+ wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
+ provider);
EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, countProducer.mCurrentBucketNum);
EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -94,9 +96,10 @@
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs,
+ provider);
// 2 events in bucket 1.
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -158,8 +161,9 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
assertConditionTimer(countProducer.mConditionTimer, false, 0, 0);
countProducer.onConditionChanged(true, bucketStartTimeNs);
@@ -229,9 +233,10 @@
EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
{ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs, bucketStartTimeNs);
+ bucketStartTimeNs, bucketStartTimeNs, provider);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -268,9 +273,9 @@
alert.set_trigger_if_sum_gt(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
sp<AnomalyTracker> anomalyTracker =
countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
@@ -337,9 +342,9 @@
metric.set_split_bucket_for_app_upgrade(true);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
// Bucket is flushed yet.
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -399,8 +404,9 @@
alert.set_trigger_if_sum_gt(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
sp<AnomalyTracker> anomalyTracker =
countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
@@ -467,9 +473,10 @@
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs,
+ provider);
sp<AnomalyTracker> anomalyTracker =
countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
@@ -531,8 +538,9 @@
int64_t oneDayNs = 24 * 60 * 60 * 1e9;
int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
- protoHash, oneDayNs, fiveWeeksNs);
+ protoHash, oneDayNs, fiveWeeksNs, provider);
int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
diff --git a/statsd/tests/metrics/DurationMetricProducer_test.cpp b/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 3b24d85..5cf0221 100644
--- a/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -71,11 +71,12 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+ wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, provider);
EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
@@ -99,11 +100,12 @@
makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -142,12 +144,13 @@
makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
-1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
+ bucketStartTimeNs, bucketStartTimeNs, provider);
durationProducer.mCondition = ConditionState::kFalse;
assertConditionTimer(durationProducer.mConditionTimer, false, 0, 0);
@@ -200,12 +203,13 @@
makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
-1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
+ bucketStartTimeNs, bucketStartTimeNs, provider);
EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -248,11 +252,12 @@
metric.set_split_bucket_for_app_upgrade(true);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -312,11 +317,12 @@
metric.set_split_bucket_for_app_upgrade(true);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -377,11 +383,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
sp<AnomalyTracker> anomalyTracker =
durationProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
@@ -425,11 +432,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -480,11 +488,12 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
int64_t startTimeNs = bucketStartTimeNs + 1;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -542,11 +551,12 @@
metric.set_split_bucket_for_app_upgrade(false);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+ wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -585,6 +595,7 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeLogEvent(&event1, bucketStartTimeNs + 50, tagId);
@@ -599,7 +610,7 @@
kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
-1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
+ bucketStartTimeNs, bucketStartTimeNs, provider);
durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + 5);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
diff --git a/statsd/tests/metrics/EventMetricProducer_test.cpp b/statsd/tests/metrics/EventMetricProducer_test.cpp
index d5fe932..6e25f79 100644
--- a/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -80,9 +80,10 @@
CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, provider);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -117,10 +118,11 @@
CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
{ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs);
+ bucketStartTimeNs, provider);
eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -174,10 +176,11 @@
EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
// Condition is true for second event.
EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
{ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs);
+ bucketStartTimeNs, provider);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -213,8 +216,9 @@
makeLogEvent(&event4, tagId, bucketStartTimeNs + 40, "222");
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, provider);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -265,8 +269,9 @@
makeLogEvent(&event4, tagId, bucketStartTimeNs + 40, "111", &bytesField2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, provider);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -314,8 +319,9 @@
makeLogEvent(&event3, tagId2, bucketStartTimeNs + 40, "222");
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs);
+ wizard, protoHash, bucketStartTimeNs, provider);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -343,6 +349,7 @@
}
}
}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 9f010a4..ff3d8d1 100644
--- a/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -98,13 +98,14 @@
createEventMatcherWizard(tagId, logEventMatcherIndex);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
// statsd started long ago.
// The metric starts in the middle of the bucket
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
-1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
@@ -140,10 +141,12 @@
return true;
}));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -228,10 +231,12 @@
sp<EventMatcherWizard> eventMatcherWizard =
createEventMatcherWizard(tagId, logEventMatcherIndex);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
-1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ bucketStartTimeNs, pullerManager, provider);
gaugeProducer.prepareFirstBucket();
sp<AnomalyTracker> anomalyTracker =
@@ -310,6 +315,8 @@
sp<EventMatcherWizard> eventMatcherWizard =
createEventMatcherWizard(tagId, logEventMatcherIndex);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
@@ -326,7 +333,7 @@
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -392,10 +399,12 @@
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
.WillOnce(Return(false));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -446,10 +455,12 @@
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
+ GaugeMetricProducer gaugeProducer(
+ kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
gaugeProducer.prepareFirstBucket();
gaugeProducer.onConditionChanged(true, conditionChangeNs);
@@ -534,10 +545,12 @@
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
+ GaugeMetricProducer gaugeProducer(
+ kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
+ protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
gaugeProducer.prepareFirstBucket();
gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
@@ -579,10 +592,12 @@
sp<EventMatcherWizard> eventMatcherWizard =
createEventMatcherWizard(tagId, logEventMatcherIndex);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
Alert alert;
@@ -679,10 +694,12 @@
.WillOnce(Return(true));
int triggerId = 5;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -737,10 +754,12 @@
return true;
}));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, /*triggerId=*/-1, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ bucketStartTimeNs, pullerManager, provider);
EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
gaugeProducer.prepareFirstBucket();
@@ -808,10 +827,12 @@
.WillOnce(Return(true));
int triggerId = 5;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
@@ -878,10 +899,12 @@
}));
int triggerId = 5;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ pullerManager, provider);
gaugeProducer.prepareFirstBucket();
LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
@@ -914,7 +937,6 @@
ShardOffsetProvider::getInstance().setShardOffset(5);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
int triggerId = 5;
int shardCount = 2;
@@ -951,11 +973,11 @@
data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1003, 18, 10));
return true;
}));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, sampledGaugeMetric,
- -1 /*-1 meaning no condition*/, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, triggerId,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ GaugeMetricProducer gaugeProducer(
+ kConfigKey, sampledGaugeMetric, -1 /*-1 meaning no condition*/, {}, wizard, protoHash,
+ logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager, provider);
SamplingInfo samplingInfo;
samplingInfo.shardCount = shardCount;
translateFieldMatcher(sampledGaugeMetric.dimensional_sampling_info().sampled_what_field(),
diff --git a/statsd/tests/metrics/KllMetricProducer_test.cpp b/statsd/tests/metrics/KllMetricProducer_test.cpp
index e84efb0..58d8a73 100644
--- a/statsd/tests/metrics/KllMetricProducer_test.cpp
+++ b/statsd/tests/metrics/KllMetricProducer_test.cpp
@@ -128,7 +128,8 @@
translateFieldMatcher(metric.kll_field(), &fieldMatchers);
const auto [dimensionSoftLimit, dimensionHardLimit] =
- StatsdStats::getAtomDimensionKeySizeLimits(atomId);
+ StatsdStats::getAtomDimensionKeySizeLimits(
+ atomId, StatsdStats::kDimensionKeySizeHardLimitMin);
int conditionIndex = initialCondition ? 0 : -1;
vector<ConditionState> initialConditionCache;
@@ -136,6 +137,7 @@
initialConditionCache.push_back(initialCondition.value());
}
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
return new KllMetricProducer(
kConfigKey, metric, protoHash, {/*pullAtomId=*/-1, /*pullerManager=*/nullptr},
{timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
@@ -146,7 +148,7 @@
{conditionIndex, metric.links(), initialConditionCache, wizard},
{metric.state_link(), slicedStateAtoms, stateGroupMap},
{/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
- {dimensionSoftLimit, dimensionHardLimit});
+ {dimensionSoftLimit, dimensionHardLimit}, provider);
}
static KllMetric createMetric() {
diff --git a/statsd/tests/metrics/MaxDurationTracker_test.cpp b/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 8367320..1df37e9 100644
--- a/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -67,14 +67,17 @@
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
+ tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Event starts again. This would not change anything as it already starts.
- tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey());
+ tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Stopped.
tracker.noteStop(key1, bucketStartTimeNs + 10, false);
// Another event starts in this bucket.
- tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
+ tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, emptyThreshold, &buckets);
@@ -101,10 +104,12 @@
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
+ tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Another event starts in this bucket.
- tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
+ tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, emptyThreshold, &buckets);
tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
EXPECT_TRUE(tracker.mInfos.empty());
@@ -136,11 +141,12 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
// The event starts.
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Starts again. Does not DEFAULT_DIMENSION_KEY anything.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
- ConditionKey());
+ ConditionKey(), StatsdStats::kDimensionKeySizeHardLimitMin);
// The event stops at early 4th bucket.
// Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls
@@ -175,8 +181,10 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
// 2 starts
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// one stop
tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/);
@@ -220,7 +228,8 @@
0, bucketStartTimeNs, bucketSizeNs, true, false, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
- tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
+ tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteConditionChanged(key1, true, conditionStarts1);
tracker.noteConditionChanged(key1, false, conditionStops1);
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -268,7 +277,8 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
{anomalyTracker});
- tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
+ tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
@@ -327,10 +337,12 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
{anomalyTracker});
- tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
+ tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteConditionChanged(key1, true, conditionStarts1);
tracker.noteConditionChanged(key1, false, conditionStops1);
- tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already.
+ tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2,
+ StatsdStats::kDimensionKeySizeHardLimitMin); // Condition is on already.
tracker.noteConditionChanged(key1, true, conditionStarts2);
ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
auto alarm = anomalyTracker->mAlarms.begin()->second;
@@ -352,7 +364,8 @@
int64_t eventStopTimeNs = anomalyFireTimeSec * NS_PER_SEC + 10;
tracker.noteStop(key1, eventStopTimeNs, false);
tracker.noteStop(key2, eventStopTimeNs, false);
- tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1);
+ tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Anomaly is ongoing, but we're still in the refractory period.
ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
@@ -361,7 +374,8 @@
// Makes sure it is correct after the refractory period is over.
tracker.noteStop(key1, eventStopTimeNs + 2000000, false);
int64_t justBeforeRefPeriodNs = (refractoryPeriodEndsSec - 2) * NS_PER_SEC;
- tracker.noteStart(key1, true, justBeforeRefPeriodNs, conditionKey1);
+ tracker.noteStart(key1, true, justBeforeRefPeriodNs, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ(justBeforeRefPeriodNs + 40 * NS_PER_SEC,
(unsigned long long)(alarm->timestampSec * NS_PER_SEC));
@@ -409,8 +423,10 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
{anomalyTracker});
- tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1);
- tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2);
+ tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(key1, eventStopTimeNs1, false);
ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
auto alarm = anomalyTracker->mAlarms.begin()->second;
@@ -437,13 +453,15 @@
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
// Duration below the gt_int threshold should not be added to past buckets.
- tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(key1, eventStartTimeNs + thresholdDurationNs, false);
tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, threshold, &buckets);
EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
// Duration above the gt_int threshold should be added to past buckets.
- tracker.noteStart(key1, true, event2StartTimeNs, ConditionKey());
+ tracker.noteStart(key1, true, event2StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(key1, event2StartTimeNs + thresholdDurationNs + 1, false);
tracker.flushIfNeeded(event2StartTimeNs + bucketSizeNs + 1, threshold, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -469,12 +487,14 @@
MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
+ tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(key1, bucketStartTimeNs + 50, false);
EXPECT_TRUE(tracker.hasAccumulatedDuration());
EXPECT_FALSE(tracker.hasStartedDuration());
- tracker.noteStart(key1, true, bucketStartTimeNs + 100, ConditionKey());
+ tracker.noteStart(key1, true, bucketStartTimeNs + 100, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_TRUE(tracker.hasStartedDuration());
tracker.noteConditionChanged(key1, false, bucketStartTimeNs + 150);
EXPECT_TRUE(tracker.hasAccumulatedDuration());
@@ -485,6 +505,33 @@
EXPECT_FALSE(tracker.hasAccumulatedDuration());
}
+class MaxDurationTrackerTest_DimLimit : public Test {
+protected:
+ ~MaxDurationTrackerTest_DimLimit() {
+ StatsdStats::getInstance().reset();
+ }
+};
+
+TEST_F(MaxDurationTrackerTest_DimLimit, TestDimLimit) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1 /* conditionIndex */,
+ false /* nesting */, 0 /* currentBucketStartNs */, 0 /* bucketNum */,
+ 0 /* startTimeNs */, bucketSizeNs, false /* conditionSliced */,
+ false /* fullLink */, {} /* anomalyTrackers */);
+
+ const size_t dimensionHardLimit = 900;
+ for (int i = 1; i <= dimensionHardLimit; i++) {
+ const HashableDimensionKey key = getMockedDimensionKey(TagId, i, "maps");
+ tracker.noteStart(key, false /* condition */, i /* eventTime */, ConditionKey(),
+ dimensionHardLimit);
+ }
+ ASSERT_FALSE(tracker.mHasHitGuardrail);
+ const HashableDimensionKey key = getMockedDimensionKey(TagId, dimensionHardLimit + 1, "maps");
+ tracker.noteStart(key, false /* condition */, dimensionHardLimit + 1 /* eventTime */,
+ ConditionKey(), dimensionHardLimit);
+ EXPECT_TRUE(tracker.mHasHitGuardrail);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
index 85c5dd1..972dc85 100644
--- a/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
+++ b/statsd/tests/metrics/NumericValueMetricProducer_test.cpp
@@ -200,7 +200,8 @@
translateFieldMatcher(metric.value_field(), &fieldMatchers);
const auto [dimensionSoftLimit, dimensionHardLimit] =
- StatsdStats::getAtomDimensionKeySizeLimits(tagId);
+ StatsdStats::getAtomDimensionKeySizeLimits(
+ tagId, StatsdStats::kDimensionKeySizeHardLimitMin);
int conditionIndex = conditionAfterFirstBucketPrepared ? 0 : -1;
vector<ConditionState> initialConditionCache;
@@ -214,17 +215,27 @@
? optional<int64_t>(metric.condition_correction_threshold_nanos())
: nullopt;
+ std::vector<ValueMetric::AggregationType> aggregationTypes;
+ if (metric.aggregation_types_size() != 0) {
+ for (int i = 0; i < metric.aggregation_types_size(); i++) {
+ aggregationTypes.push_back(metric.aggregation_types(i));
+ }
+ } else { // aggregation_type() is set or default is used.
+ aggregationTypes.push_back(metric.aggregation_type());
+ }
+
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
sp<NumericValueMetricProducer> valueProducer = new NumericValueMetricProducer(
kConfigKey, metric, protoHash, {pullAtomId, pullerManager},
{timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
conditionCorrectionThresholdNs, metric.split_bucket_for_app_upgrade()},
{containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions,
logEventMatcherIndex, eventMatcherWizard, metric.dimensions_in_what(),
- fieldMatchers},
+ fieldMatchers, aggregationTypes},
{conditionIndex, metric.links(), initialConditionCache, wizard},
{metric.state_link(), slicedStateAtoms, stateGroupMap},
{/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
- {dimensionSoftLimit, dimensionHardLimit});
+ {dimensionSoftLimit, dimensionHardLimit}, provider);
valueProducer->prepareFirstBucket();
if (conditionAfterFirstBucketPrepared) {
@@ -7733,6 +7744,266 @@
0); // Diff of 15 and 18
}
+TEST(NumericValueMetricProducerTest, TestMultipleAggTypesPulled) {
+ ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
+ // createMetricWithCondition() adds field 2 as first value field.
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(1);
+ metric.add_aggregation_types(ValueMetric::MIN);
+ metric.add_aggregation_types(ValueMetric::MAX);
+ metric.add_aggregation_types(ValueMetric::SUM);
+ metric.add_aggregation_types(ValueMetric::AVG);
+ metric.add_aggregation_types(ValueMetric::SUM);
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Screen On Pull 1.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 1, 2));
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 2, 4));
+ return true;
+ }))
+ // Screen Off Pull 2.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 3, 5));
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 4, 9));
+ return true;
+ }))
+ // Screen On Pull 3.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 5, 10));
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 6, 20));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 55 * NS_PER_SEC,
+ 25, 60));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 55 * NS_PER_SEC,
+ 35, 80));
+
+ return true;
+ }));
+
+ sp<NumericValueMetricProducer> valueProducer =
+ NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kFalse);
+
+ EXPECT_EQ(5, valueProducer->mFieldMatchers.size());
+ ASSERT_EQ(5, valueProducer->mAggregationTypes.size());
+ EXPECT_EQ(ValueMetric::MIN, valueProducer->mAggregationTypes[0]);
+ EXPECT_EQ(ValueMetric::MAX, valueProducer->mAggregationTypes[1]);
+ EXPECT_EQ(ValueMetric::SUM, valueProducer->mAggregationTypes[2]);
+ EXPECT_EQ(ValueMetric::AVG, valueProducer->mAggregationTypes[3]);
+ EXPECT_EQ(ValueMetric::SUM, valueProducer->mAggregationTypes[4]);
+
+ // Screen On. Pull 1.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Screen Off.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Screen On. Pull 2.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50 * NS_PER_SEC);
+
+ // Bucket 2 start. Pull 4.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 15, 30));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 20, 40));
+ valueProducer->onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucket2StartTimeNs + 55 * NS_PER_SEC;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
+ EXPECT_TRUE(report.has_value_metrics());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
+ ASSERT_EQ(1, valueMetrics.data_size());
+ EXPECT_EQ(0, report.value_metrics().skipped_size());
+
+ // Bucket 1.
+ // Value field 1
+ // Diff from pulls 1 and 2: (3+4)-(1+2) = 4
+ // Diff from pulls 3 and 4: (15+20)-(5+6) = 24
+
+ // Value field 2
+ // Diff from pulls 1 and 2: (5+9)-(2+4) = 8
+ // Diff from pulls 3 and 4: (30+40)-(10+20) = 40
+
+ // Bucket 2
+ // Value field 1
+ // Diff from pulls 4 and 5: (25+35)-(15+20) = 25
+
+ // Value field 2
+ // Diff from pulls 4 and 5: (60+80)-(30+40) = 70
+
+ // Output values are calculated for these agg type - value field combinations
+ // MIN-2, MAX-2, SUM-2, AVG-2, SUM-1
+ ValueMetricData data = valueMetrics.data(0);
+ ASSERT_EQ(2, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs,
+ {8, 40, 48, 24, 28}, 20 * NS_PER_SEC, 0);
+ ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs,
+ {70, 70, 70, 70, 25}, 55 * NS_PER_SEC, 0);
+}
+
+TEST(NumericValueMetricProducerTest, TestMultipleAggTypesPushed) {
+ ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ // createMetric() adds field 2 as first value field.
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.mutable_value_field()->add_child()->set_field(3);
+ metric.add_aggregation_types(ValueMetric::MIN);
+ metric.add_aggregation_types(ValueMetric::MAX);
+ metric.add_aggregation_types(ValueMetric::SUM);
+ metric.add_aggregation_types(ValueMetric::AVG);
+ metric.add_aggregation_types(ValueMetric::SUM);
+
+ sp<EventMatcherWizard> eventMatcherWizard =
+ createEventMatcherWizard(tagId, logEventMatcherIndex);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ sp<NumericValueMetricProducer> valueProducer =
+ NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
+ pullerManager, metric, /*pullAtomId=*/-1);
+
+ EXPECT_EQ(5, valueProducer->mFieldMatchers.size());
+ ASSERT_EQ(5, valueProducer->mAggregationTypes.size());
+ EXPECT_EQ(ValueMetric::MIN, valueProducer->mAggregationTypes[0]);
+ EXPECT_EQ(ValueMetric::MAX, valueProducer->mAggregationTypes[1]);
+ EXPECT_EQ(ValueMetric::SUM, valueProducer->mAggregationTypes[2]);
+ EXPECT_EQ(ValueMetric::AVG, valueProducer->mAggregationTypes[3]);
+ EXPECT_EQ(ValueMetric::SUM, valueProducer->mAggregationTypes[4]);
+
+ // Bucket 1 events.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 5, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 6, 8);
+
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event3, tagId, bucketStartTimeNs + 40, 2, 3, 10);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event4, tagId, bucketStartTimeNs + 50, 2, 4, 6);
+
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event5, tagId, bucketStartTimeNs + 30, 1, 19, 9);
+
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event6, tagId, bucketStartTimeNs + 60, 2, 20, 8);
+
+ // Bucket 2 events.
+ LogEvent event7(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event7, tagId, bucket2StartTimeNs + 10, 2, 7, 41);
+
+ LogEvent event8(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event8, tagId, bucket2StartTimeNs + 20, 1, 21, 40);
+
+ LogEvent event9(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event9, tagId, bucket2StartTimeNs + 30, 1, 10, 4);
+
+ LogEvent event10(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event10, tagId, bucket2StartTimeNs + 40, 2, 3, 50);
+
+ LogEvent event11(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event11, tagId, bucket2StartTimeNs + 50, 1, 20, 7);
+
+ LogEvent event12(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event12, tagId, bucket2StartTimeNs + 60, 2, 20, 2);
+
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event5);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event6);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event7);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event8);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event9);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event10);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event11);
+ valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event12);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ valueProducer->onDumpReport(bucket3StartTimeNs + 10000, false /* include recent buckets */,
+ true, FAST /* dumpLatency */, nullptr, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ backfillDimensionPath(&report);
+ backfillStartEndTimestamp(&report);
+ EXPECT_TRUE(report.has_value_metrics());
+ StatsLogReport::ValueMetricDataWrapper valueMetrics;
+ sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
+ ASSERT_EQ(2, valueMetrics.data_size());
+ EXPECT_EQ(0, report.value_metrics().skipped_size());
+
+ // Bucket 1.
+ // Value field 2
+ // dim 1 pushed values: 5, 6, 19
+ // dim 2 pushed values: 3, 4, 20
+
+ // Value field 3
+ // dim 1 pushed values: 10, 8, 9
+ // dim 2 pushed values: 10, 6, 8
+
+ // Bucket 2
+ // Value field 2
+ // dim 1 pushed values: 21, 10, 20
+ // dim 2 pushed values: 7, 3, 20
+
+ // Value field 3
+ // dim 1 pushed values: 40, 4, 7
+ // dim 2 pushed values: 41, 50, 2
+
+ // Output values are calculated for these agg type - value field combinations
+ // MIN-2, MAX-2, SUM-2, AVG-2, SUM-1
+ ValueMetricData data = valueMetrics.data(0);
+ ASSERT_EQ(2, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs,
+ {5, 19, 30, 10, 27}, 0, 0);
+ ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, bucket3StartTimeNs,
+ {10, 21, 51, 17, 51}, 0, 0);
+
+ data = valueMetrics.data(1);
+ ASSERT_EQ(2, data.bucket_info_size());
+ ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs,
+ {3, 20, 27, 9, 24}, 0, 0);
+ ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, bucket3StartTimeNs,
+ {3, 20, 30, 10, 93}, 0, 0);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/metrics/OringDurationTracker_test.cpp b/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6d4f0a2..7fce270 100644
--- a/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -66,9 +66,11 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin); // overlapping wl
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
@@ -96,8 +98,10 @@
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin); // overlapping wl
tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
@@ -127,8 +131,10 @@
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin); // overlapping wl
tracker.noteStopAll(eventStartTimeNs + 2003);
@@ -156,10 +162,12 @@
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, emptyThreshold, &buckets);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
ASSERT_EQ(2u, buckets[eventKey].size());
@@ -200,7 +208,8 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
@@ -239,7 +248,8 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
true, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// condition to false; record duration 5n
tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
// condition to true.
@@ -276,8 +286,10 @@
OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1,
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
@@ -318,7 +330,8 @@
{anomalyTracker});
// Nothing in the past bucket.
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
@@ -326,7 +339,8 @@
ASSERT_EQ(0u, buckets[eventKey].size());
int64_t event1StartTimeNs = eventStartTimeNs + 10;
- tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// No past buckets. The anomaly will happen in bucket #0.
EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
@@ -345,7 +359,8 @@
// One past buckets. The anomaly will happen in bucket #1.
int64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
- tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
bucket1Duration),
tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
@@ -354,7 +369,8 @@
// Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
// bucket #2.
int64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
- tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
}
@@ -380,7 +396,8 @@
bucketSizeNs, true, false, {anomalyTracker});
int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
// Anomaly happens in the bucket #1.
EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC),
tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
@@ -424,7 +441,8 @@
bucketSizeNs, true, false, {anomalyTracker});
int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs),
tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
int64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC;
@@ -437,7 +455,8 @@
// Acquire and release a wakelock in the next bucket.
int64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey());
+ tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
int64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC;
tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false);
@@ -481,7 +500,8 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, false, {anomalyTracker});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
EXPECT_TRUE(tracker.mStarted.empty());
@@ -489,7 +509,8 @@
ASSERT_EQ(0u, tracker.mStarted.size());
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
(long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
@@ -530,7 +551,8 @@
bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false,
false, {anomalyTracker});
- tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
+ tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey,
+ StatsdStats::kDimensionKeySizeHardLimitMin); // start key1
ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
@@ -540,13 +562,15 @@
ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
+ tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey,
+ StatsdStats::kDimensionKeySizeHardLimitMin); // start key1 again
ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
+ tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey,
+ StatsdStats::kDimensionKeySizeHardLimitMin); // start key2
ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
@@ -589,13 +613,15 @@
false, false, {});
// Duration below the gt_int threshold should not be added to past buckets.
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, eventStartTimeNs + thresholdDurationNs, false);
tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, threshold, &buckets);
EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
// Duration above the gt_int threshold should be added to past buckets.
- tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, event2StartTimeNs + thresholdDurationNs + 1, false);
tracker.flushIfNeeded(event2StartTimeNs + bucketSizeNs + 1, threshold, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -627,11 +653,13 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, false, {anomalyTracker});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, bucketStartTimeNs + 50, false);
EXPECT_TRUE(tracker.hasAccumulatedDuration());
- tracker.noteStart(kEventKey1, true, bucketStartTimeNs + 100, ConditionKey());
+ tracker.noteStart(kEventKey1, true, bucketStartTimeNs + 100, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
EXPECT_FALSE(tracker.mStarted.empty());
tracker.onConditionChanged(false, bucketStartTimeNs + 150);
EXPECT_TRUE(tracker.mStarted.empty());
@@ -669,7 +697,8 @@
bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
false, false, {});
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
+ tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey(),
+ StatsdStats::kDimensionKeySizeHardLimitMin);
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
tracker.flushCurrentBucket(eventStartTimeNs + 20, emptyThreshold, 0, &buckets);
@@ -679,6 +708,34 @@
EXPECT_FALSE(tracker.hasAccumulatedDuration());
}
+class OringDurationTrackerTest_DimLimit : public Test {
+protected:
+ ~OringDurationTrackerTest_DimLimit() {
+ StatsdStats::getInstance().reset();
+ }
+};
+
+TEST_F(OringDurationTrackerTest_DimLimit, TestDimLimit) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1 /* conditionIndex */,
+ false /* nesting */, 0 /* currentBucketStartNs */,
+ 0 /* currentBucketNum */, 0 /* startTimeNs */, bucketSizeNs,
+ true /* conditionSliced */, false /* fullLink */,
+ {} /* anomalyTrackers */);
+
+ const size_t dimensionHardLimit = 900;
+ for (int i = 1; i <= dimensionHardLimit; i++) {
+ const HashableDimensionKey key = getMockedDimensionKey(TagId, i, "maps");
+ tracker.noteStart(key, false /* condition */, i /* eventTime */, ConditionKey(),
+ dimensionHardLimit);
+ }
+ ASSERT_FALSE(tracker.mHasHitGuardrail);
+ const HashableDimensionKey key = getMockedDimensionKey(TagId, dimensionHardLimit + 1, "maps");
+ tracker.noteStart(key, false /* condition */, dimensionHardLimit + 1 /* eventTime */,
+ ConditionKey(), dimensionHardLimit);
+ EXPECT_TRUE(tracker.mHasHitGuardrail);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/metrics/RestrictedEventMetricProducer_test.cpp b/statsd/tests/metrics/RestrictedEventMetricProducer_test.cpp
new file mode 100644
index 0000000..2085e11
--- /dev/null
+++ b/statsd/tests/metrics/RestrictedEventMetricProducer_test.cpp
@@ -0,0 +1,270 @@
+#include "src/metrics/RestrictedEventMetricProducer.h"
+
+#include <gtest/gtest.h>
+
+#include "flags/FlagProvider.h"
+#include "metrics_test_helper.h"
+#include "stats_annotations.h"
+#include "tests/statsd_test_util.h"
+#include "utils/DbUtils.h"
+
+using namespace testing;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+const ConfigKey configKey(/*uid=*/0, /*id=*/12345);
+const int64_t metricId1 = 123;
+const int64_t metricId2 = 456;
+
+bool metricTableExist(int64_t metricId) {
+ stringstream query;
+ query << "SELECT * FROM metric_" << metricId;
+ vector<int32_t> columnTypes;
+ vector<vector<string>> rows;
+ vector<string> columnNames;
+ string err;
+ return dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err);
+}
+} // anonymous namespace
+
+class RestrictedEventMetricProducerTest : public Test {
+protected:
+ void SetUp() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ }
+ void TearDown() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ dbutils::deleteDb(configKey);
+ FlagProvider::getInstance().resetOverrides();
+ }
+};
+
+TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventMultipleEvents) {
+ EventMetric metric;
+ metric.set_id(metricId1);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1);
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3);
+
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2);
+ producer.flushRestrictedData();
+
+ stringstream query;
+ query << "SELECT * FROM metric_" << metricId1;
+ string err;
+ vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ vector<vector<string>> rows;
+ dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err);
+ ASSERT_EQ(rows.size(), 2);
+ EXPECT_EQ(columnTypes.size(),
+ 3 + event1->getValues().size()); // col 0:2 are reserved for metadata.
+ EXPECT_EQ(/*tagId=*/rows[0][0], to_string(event1->GetTagId()));
+ EXPECT_EQ(/*elapsedTimestampNs=*/rows[0][1], to_string(event1->GetElapsedTimestampNs()));
+ EXPECT_EQ(/*elapsedTimestampNs=*/rows[1][1], to_string(event2->GetElapsedTimestampNs()));
+
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventMultipleFields) {
+ EventMetric metric;
+ metric.set_id(metricId2);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 1);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_overwriteTimestamp(statsEvent, 1);
+
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeInt32(statsEvent, 11);
+ AStatsEvent_writeFloat(statsEvent, 11.0);
+ LogEvent logEvent(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, &logEvent);
+
+ producer.onMatchedLogEvent(/*matcherIndex=1*/ 1, logEvent);
+ producer.flushRestrictedData();
+
+ stringstream query;
+ query << "SELECT * FROM metric_" << metricId2;
+ string err;
+ vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ vector<vector<string>> rows;
+ EXPECT_TRUE(dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_EQ(columnTypes.size(),
+ 3 + logEvent.getValues().size()); // col 0:2 are reserved for metadata.
+ EXPECT_EQ(/*field1=*/rows[0][3], "111");
+ EXPECT_EQ(/*field2=*/rows[0][4], "11");
+ EXPECT_FLOAT_EQ(/*field3=*/std::stof(rows[0][5]), 11.0);
+
+ EXPECT_THAT(columnNames, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs",
+ "field_1", "field_2", "field_3"));
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestOnMatchedLogEventWithCondition) {
+ EventMetric metric;
+ metric.set_id(metricId1);
+ metric.set_condition(StringToId("SCREEN_ON"));
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/0,
+ /*initialConditionCache=*/{ConditionState::kUnknown},
+ new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1);
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3);
+
+ producer.onConditionChanged(true, 0);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1);
+ producer.onConditionChanged(false, 1);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2);
+ producer.flushRestrictedData();
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << metricId1;
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err);
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_EQ(columnTypes.size(), 3 + event1->getValues().size());
+ EXPECT_EQ(/*elapsedTimestampNs=*/rows[0][1], to_string(event1->GetElapsedTimestampNs()));
+
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestOnDumpReportNoOp) {
+ EventMetric metric;
+ metric.set_id(metricId1);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*timestampNs=*/1);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1);
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ producer.onDumpReport(/*dumpTimeNs=*/10,
+ /*include_current_partial_bucket=*/true,
+ /*erase_data=*/true, FAST, &strSet, &output);
+
+ ASSERT_EQ(output.size(), 0);
+ ASSERT_EQ(strSet.size(), 0);
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestOnMetricRemove) {
+ EventMetric metric;
+ metric.set_id(metricId1);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+ EXPECT_FALSE(metricTableExist(metricId1));
+
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*timestampNs=*/1);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1);
+ producer.flushRestrictedData();
+ EXPECT_TRUE(metricTableExist(metricId1));
+
+ producer.onMetricRemove();
+ EXPECT_FALSE(metricTableExist(metricId1));
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestRestrictedEventMetricTtlDeletesFirstEvent) {
+ EventMetric metric;
+ metric.set_id(metricId1);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+
+ int64_t currentTimeNs = getWallClockNs();
+ int64_t eightDaysAgo = currentTimeNs - 8 * 24 * 3600 * NS_PER_SEC;
+ std::unique_ptr<LogEvent> event1 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/1);
+ event1->setLogdWallClockTimestampNs(eightDaysAgo);
+ std::unique_ptr<LogEvent> event2 = CreateRestrictedLogEvent(/*atomTag=*/123, /*timestampNs=*/3);
+ event2->setLogdWallClockTimestampNs(currentTimeNs);
+
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event1);
+ producer.onMatchedLogEvent(/*matcherIndex=*/1, *event2);
+ producer.flushRestrictedData();
+ sqlite3* dbHandle = dbutils::getDb(configKey);
+ producer.enforceRestrictedDataTtl(dbHandle, currentTimeNs + 100);
+ dbutils::closeDb(dbHandle);
+
+ std::stringstream query;
+ query << "SELECT * FROM metric_" << metricId1;
+ string err;
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ dbutils::query(configKey, query.str(), rows, columnTypes, columnNames, err);
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_EQ(columnTypes.size(), 3 + event1->getValues().size());
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+ EXPECT_THAT(rows[0], ElementsAre(to_string(event2->GetTagId()),
+ to_string(event2->GetElapsedTimestampNs()),
+ to_string(currentTimeNs), _));
+}
+
+TEST_F(RestrictedEventMetricProducerTest, TestLoadMetricMetadataSetsCategory) {
+ metadata::MetricMetadata metricMetadata;
+ metricMetadata.set_metric_id(metricId1);
+ metricMetadata.set_restricted_category(1); // CATEGORY_DIAGNOSTIC
+ EventMetric metric;
+ metric.set_id(metricId1);
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ RestrictedEventMetricProducer producer(configKey, metric,
+ /*conditionIndex=*/-1,
+ /*initialConditionCache=*/{}, new ConditionWizard(),
+ /*protoHash=*/0x1234567890,
+ /*startTimeNs=*/0, provider);
+
+ producer.loadMetricMetadataFromProto(metricMetadata);
+
+ EXPECT_EQ(producer.getRestrictionCategory(), CATEGORY_DIAGNOSTIC);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/statsd/tests/metrics/metrics_test_helper.h b/statsd/tests/metrics/metrics_test_helper.h
index 39232c1..d750149 100644
--- a/statsd/tests/metrics/metrics_test_helper.h
+++ b/statsd/tests/metrics/metrics_test_helper.h
@@ -34,18 +34,18 @@
class MockStatsPullerManager : public StatsPullerManager {
public:
MOCK_METHOD5(RegisterReceiver,
- void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver,
+ void(int tagId, const ConfigKey& key, const wp<PullDataReceiver>& receiver,
int64_t nextPulltimeNs, int64_t intervalNs));
MOCK_METHOD3(UnRegisterReceiver,
- void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+ void(int tagId, const ConfigKey& key, const wp<PullDataReceiver>& receiver));
+ MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data));
MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
MOCK_METHOD2(RegisterPullUidProvider,
- void(const ConfigKey& configKey, wp<PullUidProvider> provider));
+ void(const ConfigKey& configKey, const wp<PullUidProvider>& provider));
MOCK_METHOD2(UnregisterPullUidProvider,
- void(const ConfigKey& configKey, wp<PullUidProvider> provider));
+ void(const ConfigKey& configKey, const wp<PullUidProvider>& provider));
};
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
@@ -55,7 +55,7 @@
MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t 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, int atomFieldNum, FieldMatcher* matcher);
void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
} // namespace statsd
diff --git a/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index e083cab..0861812 100644
--- a/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -26,6 +26,7 @@
#include "src/condition/CombinationConditionTracker.h"
#include "src/condition/SimpleConditionTracker.h"
#include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/CountMetricProducer.h"
#include "src/metrics/DurationMetricProducer.h"
#include "src/metrics/GaugeMetricProducer.h"
#include "src/metrics/KllMetricProducer.h"
@@ -52,7 +53,8 @@
namespace {
-ConfigKey key(123, 456);
+const int configId = 456;
+const ConfigKey key(123, configId);
const int64_t timeBaseNs = 1000 * NS_PER_SEC;
sp<UidMap> uidMap = new UidMap();
@@ -62,6 +64,7 @@
/*minDiffToUpdateRegisteredAlarmTimeSec=*/0,
[](const shared_ptr<IStatsCompanionService>&, int64_t) {},
[](const shared_ptr<IStatsCompanionService>&) {});
+sp<ConfigMetadataProvider> configMetadataProvider;
unordered_map<int, vector<int>> allTagIdsToMatchersMap;
vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
@@ -85,10 +88,11 @@
// initStatsdConfig returns nullopt if config is valid
return !initStatsdConfig(
key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, timeBaseNs, allTagIdsToMatchersMap, oldAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, oldConditionTrackers, oldConditionTrackerMap,
- oldMetricProducers, oldMetricProducerMap, oldAnomalyTrackers, oldAlarmTrackers,
- tmpConditionToMetricMap, tmpTrackerToMetricMap, tmpTrackerToConditionMap,
+ timeBaseNs, timeBaseNs, configMetadataProvider, allTagIdsToMatchersMap,
+ oldAtomMatchingTrackers, oldAtomMatchingTrackerMap, oldConditionTrackers,
+ oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
+ oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap,
+ tmpTrackerToMetricMap, tmpTrackerToConditionMap,
tmpActivationAtomTrackerToMetricMap, tmpDeactivationAtomTrackerToMetricMap,
oldAlertTrackerMap, metricsWithActivation, oldStateHashes, noReportMetricIds)
.has_value();
@@ -109,8 +113,6 @@
return result;
}
-} // anonymous namespace
-
class ConfigUpdateTest : public ::testing::Test {
public:
void SetUp() override {
@@ -136,6 +138,29 @@
}
};
+struct DimLimitTestCase {
+ int oldLimit;
+ int newLimit;
+ int actualLimit;
+
+ friend void PrintTo(const DimLimitTestCase& testCase, ostream* os) {
+ *os << testCase.oldLimit << "To" << testCase.newLimit;
+ }
+};
+
+class ConfigUpdateDimLimitTest : public ConfigUpdateTest,
+ public WithParamInterface<DimLimitTestCase> {};
+
+const vector<DimLimitTestCase> dimLimitTestCases = {
+ {900, 900, 900}, {1000, 850, 850}, {1100, 1500, 1500},
+ {800, 799, 800}, {3000, 3001, 3000}, {800, 0, 800},
+};
+
+INSTANTIATE_TEST_SUITE_P(DimLimit, ConfigUpdateDimLimitTest, ValuesIn(dimLimitTestCases),
+ PrintToStringParamName());
+
+} // anonymous namespace
+
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
StatsdConfig config;
AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
@@ -146,7 +171,7 @@
EXPECT_TRUE(initConfig(config));
vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newAtomMatchingTrackerMap;
newAtomMatchingTrackerMap[matcherId] = 0;
EXPECT_EQ(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap,
@@ -171,7 +196,7 @@
*newConfig.add_atom_matcher() = newMatcher;
vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newAtomMatchingTrackerMap;
newAtomMatchingTrackerMap[matcherId] = 0;
EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
@@ -196,7 +221,7 @@
*newConfig.add_atom_matcher() = newMatcher;
vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newAtomMatchingTrackerMap;
newAtomMatchingTrackerMap[matcherId] = 0;
EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
@@ -238,7 +263,7 @@
newAtomMatchingTrackerMap[matcher1Id] = 2;
vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination. It should recurse the two child matchers and preserve all 3.
EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
@@ -283,7 +308,7 @@
newAtomMatchingTrackerMap[matcher1Id] = 2;
vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination. The simple matchers should not be evaluated.
EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
@@ -328,7 +353,7 @@
newAtomMatchingTrackerMap[matcher1Id] = 2;
vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination.
EXPECT_EQ(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
@@ -452,19 +477,13 @@
EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
- // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
+ // Validation, make sure the matchers have the proper ids. Could do more checks here.
EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
- EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
- EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
- EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
- EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
- EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
- EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
// Verify child indices of Combination Matchers are correct.
CombinationAtomMatchingTracker* combinationTracker1 =
@@ -508,7 +527,7 @@
set<int64_t> replacedMatchers;
vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newConditionTrackerMap;
newConditionTrackerMap[predicate.id()] = 0;
EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
@@ -535,7 +554,7 @@
set<int64_t> replacedMatchers;
vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newConditionTrackerMap;
newConditionTrackerMap[predicate.id()] = 0;
EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
@@ -563,7 +582,7 @@
replacedMatchers.insert(startMatcherId);
vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
+ vector<uint8_t> cycleTracker(1, false);
unordered_map<int64_t, int> newConditionTrackerMap;
newConditionTrackerMap[predicate.id()] = 0;
EXPECT_EQ(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
@@ -607,7 +626,7 @@
set<int64_t> replacedMatchers;
vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination. It should recurse the two child predicates and preserve all 3.
EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
oldConditionTrackers, newConditionTrackerMap,
@@ -654,7 +673,7 @@
set<int64_t> replacedMatchers;
vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination. The simple conditions should not be evaluated.
EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
oldConditionTrackers, newConditionTrackerMap,
@@ -700,7 +719,7 @@
set<int64_t> replacedMatchers;
vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
+ vector<uint8_t> cycleTracker(3, false);
// Only update the combination. Simple2 and combination1 must be evaluated.
EXPECT_EQ(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
oldConditionTrackers, newConditionTrackerMap,
@@ -792,7 +811,7 @@
vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
eventMatcherValues[1] = MatchingState::kMatched;
vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
- vector<bool> conditionChangeCache(6, false);
+ vector<uint8_t> conditionChangeCache(6, false);
oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
tmpConditionCache, conditionChangeCache);
EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
@@ -1957,16 +1976,17 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers,
conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- replacedMetrics),
+ provider, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics),
nullopt);
unordered_map<int64_t, int> expectedMetricProducerMap = {
@@ -2190,14 +2210,15 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers,
conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap,
- noReportMetricIds, activationAtomTrackerToMetricMap,
+ oldMetricProducerMap, oldMetricProducers, provider,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
replacedMetrics),
nullopt);
@@ -2403,16 +2424,17 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers,
conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- replacedMetrics),
+ provider, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics),
nullopt);
unordered_map<int64_t, int> expectedMetricProducerMap = {
@@ -2607,7 +2629,7 @@
vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
matchingStates[2] = kMatched;
vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
- vector<bool> changedCache(5, false);
+ vector<uint8_t> changedCache(5, false);
unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
conditionCache, changedCache);
@@ -2683,7 +2705,7 @@
newConditionTrackerMap),
nullopt);
}
- vector<bool> cycleTracker(5, false);
+ vector<uint8_t> cycleTracker(5, false);
fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
for (int i = 0; i < newConditionTrackers.size(); i++) {
EXPECT_EQ(
@@ -2726,12 +2748,13 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
- replacedStates, oldMetricProducerMap, oldMetricProducers,
+ replacedStates, oldMetricProducerMap, oldMetricProducers, provider,
newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
@@ -2994,12 +3017,13 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
- replacedStates, oldMetricProducerMap, oldMetricProducers,
+ replacedStates, oldMetricProducerMap, oldMetricProducers, provider,
newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
@@ -3211,13 +3235,14 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers,
conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, provider,
newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics),
@@ -3384,16 +3409,17 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers,
conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- replacedMetrics),
+ provider, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics),
nullopt);
// Verify event activation/deactivation maps.
@@ -3546,16 +3572,17 @@
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, /*replacedConditions=*/{}, newConditionTrackers,
conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{},
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- replacedMetrics),
+ provider, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics),
nullopt);
unordered_map<int64_t, int> expectedMetricProducerMap = {
@@ -3777,13 +3804,14 @@
vector<int> metricsWithActivation;
set<int64_t> replacedMetrics;
int64_t currentTimeNs = 12345;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(
key, config, /*timeBaseNs=*/123, currentTimeNs, new StatsPullerManager(),
oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {},
oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{},
oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {},
/*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers,
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, provider,
newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics),
@@ -3966,14 +3994,15 @@
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
set<int64_t> replacedStates;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers,
conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap,
- noReportMetricIds, activationAtomTrackerToMetricMap,
+ oldMetricProducerMap, oldMetricProducers, provider,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
replacedMetrics),
InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_HAS_MULTIPLE_ACTIVATIONS, metricId));
@@ -4003,14 +4032,15 @@
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
set<int64_t> replacedStates;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
new StatsPullerManager(), oldAtomMatchingTrackerMap,
newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
newConditionTrackerMap, replacedConditions, newConditionTrackers,
conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap,
- noReportMetricIds, activationAtomTrackerToMetricMap,
+ oldMetricProducerMap, oldMetricProducers, provider,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
replacedMetrics),
InvalidConfigReason(INVALID_CONFIG_REASON_NO_REPORT_METRIC_NOT_FOUND, metricId));
@@ -4047,6 +4077,7 @@
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
set<int64_t> replacedStates;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
newAtomMatchingTrackerMap[StringToId("ScreenTurnedOn")] = 0;
stateAtomIdMap[StringToId("ScreenState")] = util::SCREEN_STATE_CHANGED;
@@ -4057,7 +4088,7 @@
replacedMatchers, newAtomMatchingTrackers, newConditionTrackerMap,
replacedConditions, newConditionTrackers, conditionCache, stateAtomIdMap,
allStateGroupMaps, replacedStates, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ provider, newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics),
InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SLICED_STATE_ATOM_ALLOWED_FROM_ANY_UID,
@@ -4104,6 +4135,149 @@
StringToId("ScreenIsOn")));
}
+TEST_F(ConfigUpdateTest, TestUpdateConfigNonEventMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ CountMetric* metric = config.add_count_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ unordered_map<int64_t, int> newMetricProducerMap;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ vector<int> metricsWithActivation;
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
+ vector<sp<ConditionTracker>> newConditionTrackers;
+ vector<sp<MetricProducer>> newMetricProducers;
+ vector<ConditionState> conditionCache;
+ set<int64_t> replacedMetrics;
+ set<int64_t> replacedMatchers;
+ set<int64_t> replacedConditions;
+ set<int64_t> replacedStates;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+
+ EXPECT_EQ(updateMetrics(key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
+ new StatsPullerManager(), oldAtomMatchingTrackerMap,
+ newAtomMatchingTrackerMap, replacedMatchers, newAtomMatchingTrackers,
+ newConditionTrackerMap, replacedConditions, newConditionTrackers,
+ conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+ oldMetricProducerMap, oldMetricProducers, provider,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ replacedMetrics),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_P(ConfigUpdateDimLimitTest, TestDimLimit) {
+ StatsdConfig config = buildGoodConfig(configId);
+ const auto& [oldLimit, newLimit, actualLimit] = GetParam();
+ if (oldLimit > 0) {
+ config.mutable_count_metric(0)->set_max_dimensions_per_bucket(oldLimit);
+ config.mutable_duration_metric(0)->set_max_dimensions_per_bucket(oldLimit);
+ config.mutable_gauge_metric(0)->set_max_dimensions_per_bucket(oldLimit);
+ config.mutable_value_metric(0)->set_max_dimensions_per_bucket(oldLimit);
+ config.mutable_kll_metric(0)->set_max_dimensions_per_bucket(oldLimit);
+ }
+
+ EXPECT_TRUE(initConfig(config));
+
+ StatsdConfig newConfig = config;
+ if (newLimit == 0) {
+ newConfig.mutable_count_metric(0)->clear_max_dimensions_per_bucket();
+ newConfig.mutable_duration_metric(0)->clear_max_dimensions_per_bucket();
+ newConfig.mutable_gauge_metric(0)->clear_max_dimensions_per_bucket();
+ newConfig.mutable_value_metric(0)->clear_max_dimensions_per_bucket();
+ newConfig.mutable_kll_metric(0)->clear_max_dimensions_per_bucket();
+ } else {
+ newConfig.mutable_count_metric(0)->set_max_dimensions_per_bucket(newLimit);
+ newConfig.mutable_duration_metric(0)->set_max_dimensions_per_bucket(newLimit);
+ newConfig.mutable_gauge_metric(0)->set_max_dimensions_per_bucket(newLimit);
+ newConfig.mutable_value_metric(0)->set_max_dimensions_per_bucket(newLimit);
+ newConfig.mutable_kll_metric(0)->set_max_dimensions_per_bucket(newLimit);
+ }
+
+ unordered_map<int64_t, int> newMetricProducerMap;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ vector<int> metricsWithActivation;
+ vector<sp<MetricProducer>> newMetricProducers;
+ set<int64_t> replacedMetrics;
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ EXPECT_EQ(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345,
+ new StatsPullerManager(), oldAtomMatchingTrackerMap,
+ oldAtomMatchingTrackerMap, /*replacedMatchers=*/{}, oldAtomMatchingTrackers,
+ oldConditionTrackerMap, /*replacedConditions=*/{}, oldConditionTrackers,
+ /*conditionCache=*/{}, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, provider,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, replacedMetrics),
+ nullopt);
+
+ ASSERT_EQ(5u, oldMetricProducers.size());
+ ASSERT_EQ(5u, newMetricProducers.size());
+
+ // Check that old MetricProducers have the old dimension limit and the new producers have the
+ // new dimension limit.
+
+ // Count
+ sp<MetricProducer> producer =
+ oldMetricProducers[oldMetricProducerMap.at(config.count_metric(0).id())];
+ CountMetricProducer* countProducer = static_cast<CountMetricProducer*>(producer.get());
+ EXPECT_EQ(countProducer->mDimensionHardLimit, oldLimit);
+
+ producer = newMetricProducers[newMetricProducerMap.at(newConfig.count_metric(0).id())];
+ countProducer = static_cast<CountMetricProducer*>(producer.get());
+ EXPECT_EQ(countProducer->mDimensionHardLimit, actualLimit);
+
+ // Duration
+ producer = oldMetricProducers[oldMetricProducerMap.at(config.duration_metric(0).id())];
+ DurationMetricProducer* durationProducer = static_cast<DurationMetricProducer*>(producer.get());
+ EXPECT_EQ(durationProducer->mDimensionHardLimit, oldLimit);
+
+ producer = newMetricProducers[newMetricProducerMap.at(newConfig.duration_metric(0).id())];
+ durationProducer = static_cast<DurationMetricProducer*>(producer.get());
+ EXPECT_EQ(durationProducer->mDimensionHardLimit, actualLimit);
+
+ // Gauge
+ producer = oldMetricProducers[oldMetricProducerMap.at(config.gauge_metric(0).id())];
+ GaugeMetricProducer* gaugeProducer = static_cast<GaugeMetricProducer*>(producer.get());
+ EXPECT_EQ(gaugeProducer->mDimensionHardLimit, oldLimit);
+
+ producer = newMetricProducers[newMetricProducerMap.at(newConfig.gauge_metric(0).id())];
+ gaugeProducer = static_cast<GaugeMetricProducer*>(producer.get());
+ EXPECT_EQ(gaugeProducer->mDimensionHardLimit, actualLimit);
+
+ // Value
+ producer = oldMetricProducers[oldMetricProducerMap.at(config.value_metric(0).id())];
+ NumericValueMetricProducer* numericValueProducer =
+ static_cast<NumericValueMetricProducer*>(producer.get());
+ EXPECT_EQ(numericValueProducer->mDimensionHardLimit, oldLimit);
+
+ producer = newMetricProducers[newMetricProducerMap.at(newConfig.value_metric(0).id())];
+ numericValueProducer = static_cast<NumericValueMetricProducer*>(producer.get());
+ EXPECT_EQ(numericValueProducer->mDimensionHardLimit, actualLimit);
+
+ // KLL
+ producer = oldMetricProducers[oldMetricProducerMap.at(config.kll_metric(0).id())];
+ KllMetricProducer* kllProducer = static_cast<KllMetricProducer*>(producer.get());
+ EXPECT_EQ(kllProducer->mDimensionHardLimit, oldLimit);
+
+ producer = newMetricProducers[newMetricProducerMap.at(newConfig.kll_metric(0).id())];
+ kllProducer = static_cast<KllMetricProducer*>(producer.get());
+ EXPECT_EQ(kllProducer->mDimensionHardLimit, actualLimit);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
index 2d010a3..1a64c20 100644
--- a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
+++ b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -28,6 +28,7 @@
#include "src/metrics/CountMetricProducer.h"
#include "src/metrics/DurationMetricProducer.h"
#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/KllMetricProducer.h"
#include "src/metrics/MetricProducer.h"
#include "src/metrics/NumericValueMetricProducer.h"
#include "src/state/StateManager.h"
@@ -50,7 +51,8 @@
namespace statsd {
namespace {
-const ConfigKey kConfigKey(0, 12345);
+const int kConfigId = 12345;
+const ConfigKey kConfigKey(0, kConfigId);
const long timeBaseSec = 1000;
const long kAlertId = 3;
@@ -58,6 +60,7 @@
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> periodicAlarmMonitor;
+sp<ConfigMetadataProvider> configMetadataProvider;
unordered_map<int, vector<int>> allTagIdsToMatchersMap;
vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
unordered_map<int64_t, int> atomMatchingTrackerMap;
@@ -81,62 +84,12 @@
// initStatsdConfig returns nullopt if config is valid
return initStatsdConfig(
kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIdsToMatchersMap, allAtomMatchingTrackers,
- atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap, allMetricProducers,
- metricProducerMap, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
- trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds);
-}
-
-StatsdConfig buildGoodConfig() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(SCREEN_STATE_ATOM_ID);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- combination->add_matcher(StringToId("SCREEN_IS_OFF"));
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(SCREEN_STATE_ATOM_ID);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- config.add_no_report_metric(3);
-
- auto alert = config.add_alert();
- alert->set_id(kAlertId);
- alert->set_metric_id(3);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
+ timeBaseSec, timeBaseSec, configMetadataProvider, allTagIdsToMatchersMap,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, allMetricProducers, metricProducerMap, allAnomalyTrackers,
+ allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
+ metricsWithActivation, stateProtoHashes, noReportMetricIds);
}
StatsdConfig buildCircleMatchers() {
@@ -409,7 +362,6 @@
return config;
}
-} // anonymous namespace
class MetricsManagerUtilTest : public ::testing::Test {
public:
@@ -436,6 +388,25 @@
}
};
+struct DimLimitTestCase {
+ int configLimit;
+ int actualLimit;
+
+ friend void PrintTo(const DimLimitTestCase& testCase, ostream* os) {
+ *os << testCase.configLimit;
+ }
+};
+
+class MetricsManagerUtilDimLimitTest : public MetricsManagerUtilTest,
+ public WithParamInterface<DimLimitTestCase> {};
+
+const vector<DimLimitTestCase> dimLimitTestCases = {{900, 900}, {799, 800}, {3001, 3000}, {0, 800}};
+
+INSTANTIATE_TEST_SUITE_P(DimLimit, MetricsManagerUtilDimLimitTest, ValuesIn(dimLimitTestCases),
+ PrintToStringParamName());
+
+} // anonymous namespace
+
TEST_F(MetricsManagerUtilTest, TestInitialConditions) {
// initConfig returns nullopt if config is valid
EXPECT_EQ(initConfig(buildConfigWithDifferentPredicates()), nullopt);
@@ -465,11 +436,15 @@
}
TEST_F(MetricsManagerUtilTest, TestGoodConfig) {
- StatsdConfig config = buildGoodConfig();
+ StatsdConfig config = buildGoodConfig(kConfigId, kAlertId);
// initConfig returns nullopt if config is valid
EXPECT_EQ(initConfig(config), nullopt);
- ASSERT_EQ(1u, allMetricProducers.size());
- EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0)));
+ ASSERT_EQ(5u, allMetricProducers.size());
+ EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Key(config.count_metric(0).id()),
+ Key(config.duration_metric(0).id()),
+ Key(config.value_metric(0).id()),
+ Key(config.kll_metric(0).id()),
+ Key(config.gauge_metric(0).id())));
ASSERT_EQ(1u, allAnomalyTrackers.size());
ASSERT_EQ(1u, noReportMetricIds.size());
ASSERT_EQ(1u, alertTrackerMap.size());
@@ -641,6 +616,171 @@
StringToId("Event")));
}
+TEST_F(MetricsManagerUtilTest, TestEventMetricInvalidSamplingPercentage) {
+ StatsdConfig config;
+ EventMetric* metric = config.add_event_metric();
+ *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"),
+ /*condition=*/nullopt);
+ metric->set_sampling_percentage(101);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+ StringToId("Event")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestEventMetricInvalidSamplingPercentageZero) {
+ StatsdConfig config;
+ EventMetric* metric = config.add_event_metric();
+ *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"),
+ /*condition=*/nullopt);
+ metric->set_sampling_percentage(0);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+ StringToId("Event")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestEventMetricValidSamplingPercentage) {
+ StatsdConfig config;
+ EventMetric* metric = config.add_event_metric();
+ *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"),
+ /*condition=*/nullopt);
+ metric->set_sampling_percentage(50);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidSamplingPercentage) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_sampling_percentage(101);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidSamplingPercentageZero) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_sampling_percentage(0);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricValidSamplingPercentage) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_sampling_percentage(50);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestPulledGaugeMetricWithSamplingPercentage) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_sampling_percentage(50);
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidPullProbability) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_pull_probability(101);
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_PULL_PROBABILITY,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidPullProbabilityZero) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_pull_probability(0);
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_PULL_PROBABILITY,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricValidPullProbability) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_pull_probability(50);
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestPushedGaugeMetricWithPullProbability) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+ GaugeMetric::FIRST_N_SAMPLES,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_pull_probability(50);
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_PUSHED_WITH_PULL_PROBABILITY,
+ StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricRandomOneSampleWithPullProbability) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+ GaugeMetric::RANDOM_ONE_SAMPLE,
+ /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+ metric->set_pull_probability(50);
+ *config.add_atom_matcher() =
+ CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(
+ INVALID_CONFIG_REASON_GAUGE_METRIC_RANDOM_ONE_SAMPLE_WITH_PULL_PROBABILITY,
+ StringToId("Gauge")));
+}
+
TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMissingIdOrWhat) {
StatsdConfig config;
int64_t metricId = 1;
@@ -666,6 +806,88 @@
StringToId("NumericValue")));
}
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricHasBothSingleAndMultipleAggTypes) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ ValueMetric* metric = config.add_value_metric();
+ *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(),
+ /*valueField=*/2, /*condition=*/nullopt, /*states=*/{});
+ metric->set_aggregation_type(ValueMetric::SUM);
+ metric->add_aggregation_types(ValueMetric::SUM);
+ metric->add_aggregation_types(ValueMetric::MIN);
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(
+ INVALID_CONFIG_REASON_VALUE_METRIC_DEFINES_SINGLE_AND_MULTIPLE_AGG_TYPES,
+ StringToId("NumericValue")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMoreAggTypesThanValueFields) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ ValueMetric* metric = config.add_value_metric();
+ *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(),
+ /*valueField=*/2, /*condition=*/nullopt, /*states=*/{});
+ metric->add_aggregation_types(ValueMetric::SUM);
+ metric->add_aggregation_types(ValueMetric::MIN);
+
+ EXPECT_EQ(
+ initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_AGG_TYPES_DNE_VALUE_FIELDS_SIZE,
+ StringToId("NumericValue")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMoreValueFieldsThanAggTypes) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ ValueMetric* metric = config.add_value_metric();
+ *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(),
+ /*valueField=*/2, /*condition=*/nullopt, /*states=*/{});
+ // This only fails if the repeated aggregation field is used. If the single field is used,
+ // we will apply this aggregation type to all value fields.
+ metric->add_aggregation_types(ValueMetric::SUM);
+ metric->add_aggregation_types(ValueMetric::MIN);
+ *metric->mutable_value_field() = CreateDimensions(
+ util::SUBSYSTEM_SLEEP_STATE, {3 /* count */, 4 /* time_millis */, 3 /* count */});
+
+ EXPECT_EQ(
+ initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_VALUE_METRIC_AGG_TYPES_DNE_VALUE_FIELDS_SIZE,
+ StringToId("NumericValue")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricDefaultAggTypeOutOfOrderFields) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ ValueMetric* metric = config.add_value_metric();
+ *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(),
+ /*valueField=*/2, /*condition=*/nullopt, /*states=*/{});
+ *metric->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time_millis */, 3 /* count */});
+
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMultipleAggTypesOutOfOrderFields) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+ ValueMetric* metric = config.add_value_metric();
+ *metric = createValueMetric(/*name=*/"NumericValue", /*what=*/CreateScreenTurnedOnAtomMatcher(),
+ /*valueField=*/2, /*condition=*/nullopt, /*states=*/{});
+ metric->add_aggregation_types(ValueMetric::SUM);
+ metric->add_aggregation_types(ValueMetric::MIN);
+ metric->add_aggregation_types(ValueMetric::SUM);
+ *metric->mutable_value_field() = CreateDimensions(
+ util::SUBSYSTEM_SLEEP_STATE, {3 /* count */, 4 /* time_millis */, 3 /* count */});
+
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
TEST_F(MetricsManagerUtilTest, TestKllMetricMissingIdOrWhat) {
StatsdConfig config;
int64_t metricId = 1;
@@ -1163,14 +1385,13 @@
// Matcher has no contents_case (simple/combination), so it is invalid.
matcher.set_id(21);
optional<InvalidConfigReason> invalidConfigReason;
- EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap, invalidConfigReason), nullptr);
+ EXPECT_EQ(createAtomMatchingTracker(matcher, uidMap, invalidConfigReason), nullptr);
EXPECT_EQ(invalidConfigReason,
createInvalidConfigReasonWithMatcher(
INVALID_CONFIG_REASON_MATCHER_MALFORMED_CONTENTS_CASE, matcher.id()));
}
TEST_F(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerSimple) {
- int index = 1;
int64_t id = 123;
sp<UidMap> uidMap = new UidMap();
AtomMatcher matcher;
@@ -1184,20 +1405,18 @@
optional<InvalidConfigReason> invalidConfigReason;
sp<AtomMatchingTracker> tracker =
- createAtomMatchingTracker(matcher, index, uidMap, invalidConfigReason);
+ createAtomMatchingTracker(matcher, uidMap, invalidConfigReason);
EXPECT_NE(tracker, nullptr);
EXPECT_EQ(invalidConfigReason, nullopt);
EXPECT_TRUE(tracker->mInitialized);
EXPECT_EQ(tracker->getId(), id);
- EXPECT_EQ(tracker->mIndex, index);
const set<int>& atomIds = tracker->getAtomIds();
ASSERT_EQ(atomIds.size(), 1);
EXPECT_EQ(atomIds.count(SCREEN_STATE_ATOM_ID), 1);
}
TEST_F(MetricsManagerUtilTest, TestCreateAtomMatchingTrackerCombination) {
- int index = 1;
int64_t id = 123;
sp<UidMap> uidMap = new UidMap();
AtomMatcher matcher;
@@ -1209,14 +1428,13 @@
optional<InvalidConfigReason> invalidConfigReason;
sp<AtomMatchingTracker> tracker =
- createAtomMatchingTracker(matcher, index, uidMap, invalidConfigReason);
+ createAtomMatchingTracker(matcher, uidMap, invalidConfigReason);
EXPECT_NE(tracker, nullptr);
EXPECT_EQ(invalidConfigReason, nullopt);
// Combination matchers need to be initialized first.
EXPECT_FALSE(tracker->mInitialized);
EXPECT_EQ(tracker->getId(), id);
- EXPECT_EQ(tracker->mIndex, index);
const set<int>& atomIds = tracker->getAtomIds();
ASSERT_EQ(atomIds.size(), 0);
}
@@ -1321,8 +1539,10 @@
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ vector<sp<MetricProducer>> metricProducers(
+ {new CountMetricProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
+ 0x0123456789, 0, 0, provider)});
sp<AlarmMonitor> anomalyAlarmMonitor;
optional<InvalidConfigReason> invalidConfigReason;
EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123,
@@ -1344,8 +1564,10 @@
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ vector<sp<MetricProducer>> metricProducers(
+ {new CountMetricProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
+ 0x0123456789, 0, 0, provider)});
sp<AlarmMonitor> anomalyAlarmMonitor;
optional<InvalidConfigReason> invalidConfigReason;
EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123,
@@ -1368,8 +1590,10 @@
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
+ vector<sp<MetricProducer>> metricProducers(
+ {new CountMetricProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
+ 0x0123456789, 0, 0, provider)});
sp<AlarmMonitor> anomalyAlarmMonitor;
optional<InvalidConfigReason> invalidConfigReason;
EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123,
@@ -1393,10 +1617,11 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
FieldMatcher dimensions;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer(
kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/,
1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, 0x0123456789, dimensions, 0, 0)});
+ wizard, 0x0123456789, dimensions, 0, 0, provider)});
sp<AlarmMonitor> anomalyAlarmMonitor;
optional<InvalidConfigReason> invalidConfigReason;
EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, UPDATE_NEW, /*updateTime=*/123,
@@ -1409,7 +1634,6 @@
TEST_F(MetricsManagerUtilTest, TestCreateDurationProducerDimensionsInWhatInvalid) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
@@ -1446,7 +1670,6 @@
TEST_F(MetricsManagerUtilTest, TestSampledMetrics) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
AtomMatcher appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
@@ -1598,7 +1821,6 @@
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = appCrashMatcher;
CountMetric metric =
@@ -1618,7 +1840,6 @@
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = appCrashMatcher;
CountMetric metric =
@@ -1639,7 +1860,6 @@
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = appCrashMatcher;
CountMetric metric =
@@ -1660,7 +1880,6 @@
CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = testAtomReportedMatcher;
CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
@@ -1678,54 +1897,11 @@
metric.id()));
}
-TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionFIRST) {
- AtomMatcher testAtomReportedMatcher =
- CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
-
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
- *config.add_atom_matcher() = testAtomReportedMatcher;
-
- CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
- testAtomReportedMatcher.id(), nullopt, {});
- *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
- util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::FIRST});
- *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
- CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
- {Position::FIRST});
- metric.mutable_dimensional_sampling_info()->set_shard_count(2);
- *config.add_count_metric() = metric;
-
- EXPECT_EQ(initConfig(config), nullopt);
-}
-
-TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionLAST) {
- AtomMatcher testAtomReportedMatcher =
- CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
-
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
- *config.add_atom_matcher() = testAtomReportedMatcher;
-
- CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
- testAtomReportedMatcher.id(), nullopt, {});
- *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
- util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::LAST});
- *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
- CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
- {Position::LAST});
- metric.mutable_dimensional_sampling_info()->set_shard_count(2);
- *config.add_count_metric() = metric;
-
- EXPECT_EQ(initConfig(config), nullopt);
-}
-
TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_PositionANY) {
AtomMatcher testAtomReportedMatcher =
CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = testAtomReportedMatcher;
CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
@@ -1743,12 +1919,11 @@
metric.id()));
}
-TEST_F(MetricsManagerUtilTest, TestMetricSampledFieldNotSubsetDimension) {
+TEST_F(MetricsManagerUtilTest, TestMetricSampledField_DifferentFieldsNotSubsetDimension) {
AtomMatcher appCrashMatcher =
CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT");
*config.add_atom_matcher() = appCrashMatcher;
CountMetric metric =
@@ -1764,6 +1939,522 @@
metric.id()));
}
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_LastNotSubsetDimensionsFirst) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::FIRST});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::LAST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+
+ EXPECT_EQ(
+ initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT,
+ metric.id()));
+}
+
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_FirstNotSubsetDimensionsLast) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::LAST});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::FIRST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+
+ EXPECT_EQ(
+ initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_SAMPLED_FIELDS_NOT_SUBSET_DIM_IN_WHAT,
+ metric.id()));
+}
+
+// dimensions_in_what position ALL, sampled_what_field position FIRST
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_FirstSubsetDimensionsAll) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ALL});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::FIRST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+// dimensions_in_what position ALL, sampled_what_field position LAST
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_LastSubsetDimensionsAll) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::ALL});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::LAST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+// dimensions_in_what position FIRST, sampled_what_field position FIRST
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_FirstSubsetDimensionsFirst) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::FIRST});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::FIRST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+// dimensions_in_what position LAST, sampled_what_field position LAST
+TEST_F(MetricsManagerUtilTest, TestMetricHasRepeatedSampledField_LastSubsetDimensionsLast) {
+ AtomMatcher testAtomReportedMatcher =
+ CreateSimpleAtomMatcher("TEST_ATOM_REPORTED", util::TEST_ATOM_REPORTED);
+
+ StatsdConfig config;
+ *config.add_atom_matcher() = testAtomReportedMatcher;
+
+ CountMetric metric = createCountMetric("CountSampledTestAtomReportedPerRepeatedIntField",
+ testAtomReportedMatcher.id(), nullopt, {});
+ *metric.mutable_dimensions_in_what() = CreateRepeatedDimensions(
+ util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/}, {Position::LAST});
+ *metric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+ CreateRepeatedDimensions(util::TEST_ATOM_REPORTED, {9 /*repeated_int_field*/},
+ {Position::LAST});
+ metric.mutable_dimensional_sampling_info()->set_shard_count(2);
+ *config.add_count_metric() = metric;
+ EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestCountMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ CountMetric* metric = config.add_count_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_F(MetricsManagerUtilTest, TestDurationMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ DurationMetric* metric = config.add_duration_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ GaugeMetric* metric = config.add_gauge_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_F(MetricsManagerUtilTest, TestNumericValueMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ ValueMetric* metric = config.add_value_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_F(MetricsManagerUtilTest, TestKllMetricHasRestrictedDelegate) {
+ StatsdConfig config;
+ KllMetric* metric = config.add_kll_metric();
+ config.set_restricted_metrics_delegate_package_name("com.android.app.test");
+
+ EXPECT_EQ(initConfig(config),
+ InvalidConfigReason(INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED));
+}
+
+TEST_P(MetricsManagerUtilDimLimitTest, TestDimLimit) {
+ StatsdConfig config = buildGoodConfig(kConfigId, kAlertId);
+ const auto& [configLimit, actualLimit] = GetParam();
+ if (configLimit > 0) {
+ config.mutable_count_metric(0)->set_max_dimensions_per_bucket(configLimit);
+ config.mutable_duration_metric(0)->set_max_dimensions_per_bucket(configLimit);
+ config.mutable_gauge_metric(0)->set_max_dimensions_per_bucket(configLimit);
+ config.mutable_value_metric(0)->set_max_dimensions_per_bucket(configLimit);
+ config.mutable_kll_metric(0)->set_max_dimensions_per_bucket(configLimit);
+ }
+
+ // initConfig returns nullopt if config is valid
+ EXPECT_EQ(initConfig(config), nullopt);
+ ASSERT_EQ(5u, allMetricProducers.size());
+
+ sp<MetricProducer> producer =
+ allMetricProducers[metricProducerMap.at(config.count_metric(0).id())];
+ CountMetricProducer* countProducer = static_cast<CountMetricProducer*>(producer.get());
+ EXPECT_EQ(countProducer->mDimensionHardLimit, actualLimit);
+
+ producer = allMetricProducers[metricProducerMap.at(config.duration_metric(0).id())];
+ DurationMetricProducer* durationProducer = static_cast<DurationMetricProducer*>(producer.get());
+ EXPECT_EQ(durationProducer->mDimensionHardLimit, actualLimit);
+
+ producer = allMetricProducers[metricProducerMap.at(config.gauge_metric(0).id())];
+ GaugeMetricProducer* gaugeProducer = static_cast<GaugeMetricProducer*>(producer.get());
+ EXPECT_EQ(gaugeProducer->mDimensionHardLimit, actualLimit);
+
+ producer = allMetricProducers[metricProducerMap.at(config.value_metric(0).id())];
+ NumericValueMetricProducer* numericValueProducer =
+ static_cast<NumericValueMetricProducer*>(producer.get());
+ EXPECT_EQ(numericValueProducer->mDimensionHardLimit, actualLimit);
+
+ producer = allMetricProducers[metricProducerMap.at(config.kll_metric(0).id())];
+ KllMetricProducer* kllProducer = static_cast<KllMetricProducer*>(producer.get());
+ EXPECT_EQ(kllProducer->mDimensionHardLimit, actualLimit);
+}
+
+TEST_F(MetricsManagerUtilTest, TestMissingValueMatcherAndStringReplacer) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+ matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithValueMatcherOnly) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(2 /*int_field*/);
+ fvm->set_eq_int(1);
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithStringReplacerOnly) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(5 /*string_field*/);
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("#");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherWithPositionAll) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(9 /*repeated_int_field*/);
+ fvm->set_position(Position::ALL);
+ fvm->set_eq_int(1);
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherAndStringReplaceWithPositionAll) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(12 /*repeated_string_field*/);
+ fvm->set_position(Position::ALL);
+ fvm->set_eq_string("foo");
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherWithPositionAllNested) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ // Match on attribution_node[ALL].uid = 1
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(1 /*attribution_node*/);
+ fvm->set_position(Position::ALL);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(1 /* uid */);
+ fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_int(1);
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherAndStringReplaceWithPositionAllNested) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ // Match on attribution_node[ALL].uid = 1
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(1 /*attribution_node*/);
+ fvm->set_position(Position::ALL);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+ fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("foo");
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_regex(R"([0-9]+$)");
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithNoValueMatcherWithPositionAny) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(12 /*repeated_string_field*/);
+ fvm->set_position(Position::ANY);
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithNoValueMatcherWithPositionAnyNested) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ // Match on attribution_node[ALL].uid = 1
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(1 /*attribution_node*/);
+ fvm->set_position(Position::ANY);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_regex(R"([0-9]+$)");
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithValueMatcherWithPositionAny) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(12 /*repeated_string_field*/);
+ fvm->set_position(Position::ANY);
+ fvm->set_eq_string("bar");
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithValueMatcherWithPositionAnyNested) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ // Match on attribution_node[ALL].uid = 1
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(1 /*attribution_node*/);
+ fvm->set_position(Position::ANY);
+ fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+ fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("bar");
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_regex(R"([0-9]+$)");
+ fvm->mutable_matches_tuple()
+ ->mutable_field_value_matcher(0)
+ ->mutable_replace_string()
+ ->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithPositionAllNested) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ // Replace attribution_node[ALL].tag using "[0-9]+$" -> "".
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(1 /*attribution_node*/);
+ fvm->set_position(Position::ALL);
+ fvm = fvm->mutable_matches_tuple()->add_field_value_matcher();
+ fvm->set_field(2 /* tag */);
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithStringReplaceAndNonStringValueMatcher) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(2 /*int_field*/);
+ fvm->set_eq_int(1);
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("#");
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestCombinationMatcherWithStringReplace) {
+ StatsdConfig config;
+ config.set_id(12345);
+
+ AtomMatcher* matcher = config.add_atom_matcher();
+ matcher->set_id(111);
+ matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+ fvm->set_field(5 /*string_field*/);
+ fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+ fvm->mutable_replace_string()->set_replacement("#");
+
+ matcher = config.add_atom_matcher();
+ matcher->set_id(222);
+ matcher->mutable_combination()->set_operation(LogicalOperation::NOT);
+ matcher->mutable_combination()->add_matcher(111);
+
+ optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+ ASSERT_NE(actualInvalidConfigReason, nullopt);
+ EXPECT_EQ(actualInvalidConfigReason->reason,
+ INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE);
+ EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(222));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/shell/ShellSubscriber_test.cpp b/statsd/tests/shell/ShellSubscriber_test.cpp
index 3f40b38..bc4bd9c 100644
--- a/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -160,7 +160,7 @@
const vector<std::shared_ptr<LogEvent>>& pushedEvents,
const vector<ShellData>& expectedData, int numClients) {
sp<ShellSubscriber> shellManager =
- new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
+ new ShellSubscriber(uidMap, pullerManager, std::make_shared<LogEventFilter>());
size_t bufferSize = config.ByteSize();
vector<uint8_t> buffer(bufferSize);
@@ -222,7 +222,7 @@
TestAtomReported t;
auto* attributionNode = t.add_attribution_node();
attributionNode->set_uid(1001);
- attributionNode->set_tag("app1");
+ attributionNode->set_tag("app"); // String transformation removes trailing digits.
t.set_int_field(intFieldValue);
t.set_long_field(0);
t.set_float_field(0.0f);
@@ -252,6 +252,15 @@
ShellSubscription config;
config.add_pushed()->set_atom_id(TEST_ATOM_REPORTED);
+ FieldValueMatcher* fvm = config.mutable_pushed(0)->add_field_value_matcher();
+ fvm->set_field(1); // attribution_chain
+ fvm->set_position(Position::FIRST);
+ fvm = fvm->mutable_matches_tuple()->add_field_value_matcher();
+ fvm->set_field(2); // tag field
+ fvm->mutable_replace_string()->set_regex(
+ R"([0-9]+$)"); // match trailing digits, example "42" in "foo42".
+ fvm->mutable_replace_string()->set_replacement("");
+
config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
config.add_pushed()->set_atom_id(PHONE_SIGNAL_STRENGTH_CHANGED);
configBytes = protoToBytes(config);
@@ -364,7 +373,7 @@
.Times(1)
.RetiresOnSaturation();
- vector<bool> results(maxSubs, false);
+ vector<uint8_t> results(maxSubs, false);
std::shared_ptr<MockStatsSubscriptionCallback> callbacks[maxSubs];
for (int i = 0; i < maxSubs; i++) {
@@ -806,7 +815,7 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
sp<ShellSubscriber> shellManager =
- new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
+ new ShellSubscriber(uidMap, pullerManager, std::make_shared<LogEventFilter>());
// set up 2 pipes for read/write config and data
int fds_config[2];
@@ -830,7 +839,7 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
sp<ShellSubscriber> shellManager =
- new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
+ new ShellSubscriber(uidMap, pullerManager, std::make_shared<LogEventFilter>());
// create a simple config to get screen events
ShellSubscription config;
@@ -880,7 +889,7 @@
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
sp<ShellSubscriber> shellManager =
- new ShellSubscriber(uidMap, pullerManager, /*LogEventFilter=*/nullptr);
+ new ShellSubscriber(uidMap, pullerManager, std::make_shared<LogEventFilter>());
// number of different configs
int numConfigs = 2;
@@ -957,6 +966,28 @@
// Not closing fds_datas[i][0] because this causes writes within ShellSubscriberClient to hang
}
+TEST(ShellSubscriberTest, testPushedSubscriptionRestrictedEvent) {
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ std::vector<shared_ptr<LogEvent>> pushedList;
+ pushedList.push_back(CreateRestrictedLogEvent(/*atomTag=*/10, /*timestamp=*/1000));
+
+ // create a simple config to get screen events
+ ShellSubscription config;
+ config.add_pushed()->set_atom_id(10);
+
+ // expect empty data
+ vector<ShellData> expectedData;
+
+ // Test with single client
+ TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData,
+ kSingleClient);
+
+ // Test with multiple client
+ TRACE_CALL(runShellTest, config, uidMap, pullerManager, pushedList, expectedData, kNumClients);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/statsd/tests/state/StateTracker_test.cpp b/statsd/tests/state/StateTracker_test.cpp
index d2fbb3f..de53bd0 100644
--- a/statsd/tests/state/StateTracker_test.cpp
+++ b/statsd/tests/state/StateTracker_test.cpp
@@ -31,6 +31,7 @@
namespace statsd {
const int32_t timestampNs = 1000;
+const int32_t kStateUnknown = -1;
/**
* Mock StateListener class for testing.
@@ -451,28 +452,6 @@
getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
}
-/**
- * Test StateManager's onLogEvent and StateListener's onStateChanged
- * when there is an error extracting state from log event. Listener is not
- * updated of state change.
- */
-TEST(StateTrackerTest, TestStateChangeEventError) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-
- // log event
- std::shared_ptr<LogEvent> event1 =
- buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
- std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
-
- // check listener was updated
- mgr.onLogEvent(*event1);
- ASSERT_EQ(0, listener1->updates.size());
- mgr.onLogEvent(*event2);
- ASSERT_EQ(0, listener1->updates.size());
-}
-
TEST(StateTrackerTest, TestStateQuery) {
sp<TestStateListener> listener1 = new TestStateListener();
sp<TestStateListener> listener2 = new TestStateListener();
@@ -564,6 +543,54 @@
getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
}
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestMalformedStateEvent_NoExistingStateValue) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event1 =
+ buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+ std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
+
+ // check listener was not updated
+ mgr.onLogEvent(*event1);
+ ASSERT_EQ(0, listener1->updates.size());
+ mgr.onLogEvent(*event2);
+ ASSERT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestMalformedStateEvent_ExistingStateValue) {
+ sp<TestStateListener> listener = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::PLUGGED_STATE_CHANGED, listener);
+
+ std::unique_ptr<LogEvent> event1 = CreateBatteryStateChangedEvent(
+ timestampNs, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ mgr.onLogEvent(*event1);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(BatteryPluggedStateEnum::BATTERY_PLUGGED_USB, listener->updates[0].mState);
+ FieldValue stateFieldValue;
+ mgr.getStateValue(util::PLUGGED_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+ EXPECT_EQ(BatteryPluggedStateEnum::BATTERY_PLUGGED_USB, stateFieldValue.mValue.int_value);
+ listener->updates.clear();
+
+ // Malformed event.
+ std::unique_ptr<LogEvent> event2 = CreateMalformedBatteryStateChangedEvent(timestampNs + 1000);
+ mgr.onLogEvent(*event2);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(kStateUnknown, listener->updates[0].mState);
+ EXPECT_FALSE(mgr.getStateValue(util::PLUGGED_STATE_CHANGED, listener->updates[0].mKey,
+ &stateFieldValue));
+ EXPECT_EQ(kStateUnknown, stateFieldValue.mValue.int_value);
+ listener->updates.clear();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/statsd_test_util.cpp b/statsd/tests/statsd_test_util.cpp
index fc99528..539c890 100644
--- a/statsd/tests/statsd_test_util.cpp
+++ b/statsd/tests/statsd_test_util.cpp
@@ -20,7 +20,6 @@
#include <android-base/stringprintf.h>
#include "matchers/SimpleAtomMatchingTracker.h"
-#include "stats_annotations.h"
#include "stats_event.h"
#include "stats_util.h"
@@ -50,7 +49,7 @@
ConfigMetricsReportList reports;
reports.ParseFromArray(output.data(), output.size());
EXPECT_EQ(1, reports.reports_size());
- return reports.reports(kCallingUid);
+ return reports.reports(0);
}
StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
@@ -812,7 +811,7 @@
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32(statsEvent, data2);
@@ -825,7 +824,7 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32Array(statsEvent, data2.data(), data2.size());
@@ -855,7 +854,7 @@
AStatsEvent* statsEvent = makeUidStatsEvent(atomId, eventTimeNs, uid1, data1, data2);
for (const int extraUid : extraUids) {
AStatsEvent_writeInt32(statsEvent, extraUid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
}
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
@@ -869,7 +868,7 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
AStatsEvent_writeInt32Array(statsEvent, uids.data(), uids.size());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -883,7 +882,7 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
AStatsEvent_writeInt32Array(statsEvent, uids.data(), uids.size());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32(statsEvent, data2);
@@ -900,7 +899,7 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
AStatsEvent_writeInt32Array(statsEvent, uids.data(), uids.size());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32Array(statsEvent, data2.data(), data2.size());
@@ -953,8 +952,8 @@
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, false);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(loggerUid, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -966,8 +965,8 @@
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, false);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -979,19 +978,36 @@
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, false);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
-std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) {
+std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs,
+ const BatteryPluggedStateEnum state,
+ int32_t uid) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/uid, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateMalformedBatteryStateChangedEvent(const uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeString(statsEvent, "bad_state");
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -1063,6 +1079,18 @@
repeatedBoolFieldLength, repeatedEnumField);
}
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventWithPrimitives(
+ uint64_t timestampNs, int intField, long longField, float floatField,
+ const string& stringField, bool boolField, TestAtomReported::State enumField) {
+ return CreateTestAtomReportedEvent(
+ timestampNs, /* attributionUids */ {1001},
+ /* attributionTags */ {"app1"}, intField, longField, floatField, stringField, boolField,
+ enumField, /* bytesField */ {},
+ /* repeatedIntField */ {}, /* repeatedLongField */ {}, /* repeatedFloatField */ {},
+ /* repeatedStringField */ {}, /* repeatedBoolField */ {},
+ /* repeatedBoolFieldLength */ 0, /* repeatedEnumField */ {});
+}
+
std::unique_ptr<LogEvent> CreateTestAtomReportedEvent(
uint64_t timestampNs, const vector<int>& attributionUids,
const vector<string>& attributionTags, const int intField, const long longField,
@@ -1112,14 +1140,15 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
+ true);
AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeString(statsEvent, wakelockName.c_str());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, true);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -1143,14 +1172,15 @@
}
std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
- uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) {
+ uint64_t timestampNs, const int uid, const string& pkgName, const string& className,
+ const ActivityForegroundStateChanged::State state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::ACTIVITY_FOREGROUND_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, "pkg_name");
- AStatsEvent_writeString(statsEvent, "class_name");
+ AStatsEvent_writeString(statsEvent, pkgName.c_str());
+ AStatsEvent_writeString(statsEvent, className.c_str());
AStatsEvent_writeInt32(statsEvent, state);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
@@ -1159,12 +1189,12 @@
}
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) {
- return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+ return CreateActivityForegroundStateChangedEvent(timestampNs, uid, "pkg_name", "class_name",
ActivityForegroundStateChanged::BACKGROUND);
}
std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) {
- return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+ return CreateActivityForegroundStateChangedEvent(timestampNs, uid, "pkg_name", "class_name",
ActivityForegroundStateChanged::FOREGROUND);
}
@@ -1258,11 +1288,11 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, false);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -1280,20 +1310,21 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID,
+ true);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, true);
if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) {
- AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET,
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET,
util::BLE_SCAN_STATE_CHANGED__STATE__OFF);
}
AStatsEvent_writeBool(statsEvent, filtered); // filtered
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeBool(statsEvent, firstMatch); // first match
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -1309,14 +1340,14 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeString(statsEvent, packageName.c_str());
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD, true);
AStatsEvent_writeBool(statsEvent, usingAlertWindow);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_STATE_NESTED, false);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, ASTATSLOG_ANNOTATION_ID_STATE_NESTED, false);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -1360,6 +1391,28 @@
return logEvent;
}
+std::unique_ptr<LogEvent> CreateRestrictedLogEvent(int atomTag, int64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_addInt32Annotation(statsEvent, ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY,
+ ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC);
+ AStatsEvent_writeInt32(statsEvent, 10);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateNonRestrictedLogEvent(int atomTag, int64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomTag);
+ AStatsEvent_writeInt32(statsEvent, 10);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
std::unique_ptr<LogEvent> CreatePhoneSignalStrengthChangedEvent(
int64_t timestampNs, ::telephony::SignalStrengthEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -1394,7 +1447,8 @@
sp<StatsLogProcessor> processor = new StatsLogProcessor(
uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs,
[](const ConfigKey&) { return true; },
- [](const int&, const vector<int64_t>&) { return true; }, logEventFilter);
+ [](const int&, const vector<int64_t>&) { return true; },
+ [](const ConfigKey&, const string&, const vector<int64_t>&) {}, logEventFilter);
processor->OnConfigUpdated(currentTimeNs, key, config);
return processor;
@@ -1450,8 +1504,8 @@
}
uint64_t matcherHash = 0x12345678;
int64_t matcherId = 678;
- return new EventMatcherWizard({new SimpleAtomMatchingTracker(
- matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)});
+ return new EventMatcherWizard(
+ {new SimpleAtomMatchingTracker(matcherId, matcherHash, atomMatcher, uidMap)});
}
StatsDimensionsValueParcel CreateAttributionUidDimensionsValueParcel(const int atomId,
@@ -2114,7 +2168,7 @@
vector<PackageInfo> buildPackageInfos(
const vector<string>& names, const vector<int32_t>& uids, const vector<int64_t>& versions,
const vector<string>& versionStrings, const vector<string>& installers,
- const vector<vector<uint8_t>>& certHashes, const vector<bool>& deleted,
+ const vector<vector<uint8_t>>& certHashes, const vector<uint8_t>& deleted,
const vector<uint32_t>& installerIndices, const bool hashStrings) {
vector<PackageInfo> packageInfos;
for (int i = 0; i < uids.size(); i++) {
@@ -2207,6 +2261,66 @@
return statsReport;
}
+StatsdConfig buildGoodConfig(int configId) {
+ StatsdConfig config;
+ config.set_id(configId);
+
+ AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+ AtomMatcher* eventMatcher = config.add_atom_matcher();
+ eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+ AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher(screenOnMatcher.id());
+ combination->add_matcher(StringToId("ScreenTurnedOff"));
+
+ CountMetric* countMetric = config.add_count_metric();
+ *countMetric = createCountMetric("Count", screenOnMatcher.id() /* what */,
+ nullopt /* condition */, {} /* states */);
+ countMetric->mutable_dimensions_in_what()->set_field(SCREEN_STATE_ATOM_ID);
+ countMetric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ config.add_no_report_metric(StringToId("Count"));
+
+ *config.add_predicate() = CreateScreenIsOnPredicate();
+ *config.add_duration_metric() =
+ createDurationMetric("Duration", StringToId("ScreenIsOn") /* what */,
+ nullopt /* condition */, {} /* states */);
+
+ *config.add_gauge_metric() = createGaugeMetric(
+ "Gauge", screenOnMatcher.id() /* what */, GaugeMetric_SamplingType_FIRST_N_SAMPLES,
+ nullopt /* condition */, nullopt /* triggerEvent */);
+
+ *config.add_value_metric() =
+ createValueMetric("Value", screenOnMatcher /* what */, 2 /* valueField */,
+ nullopt /* condition */, {} /* states */);
+
+ *config.add_kll_metric() = createKllMetric("Kll", screenOnMatcher /* what */, 2 /* kllField */,
+ nullopt /* condition */);
+
+ return config;
+}
+
+StatsdConfig buildGoodConfig(int configId, int alertId) {
+ StatsdConfig config = buildGoodConfig(configId);
+ auto alert = config.add_alert();
+ alert->set_id(alertId);
+ alert->set_metric_id(StringToId("Count"));
+ alert->set_num_buckets(10);
+ alert->set_refractory_period_secs(100);
+ alert->set_trigger_if_sum_gt(100);
+
+ return config;
+}
+
+sp<MockConfigMetadataProvider> makeMockConfigMetadataProvider(bool enabled) {
+ sp<MockConfigMetadataProvider> metadataProvider = new StrictMock<MockConfigMetadataProvider>();
+ EXPECT_CALL(*metadataProvider, useV2SoftMemoryCalculation()).Times(AnyNumber());
+ EXPECT_CALL(*metadataProvider, useV2SoftMemoryCalculation()).WillRepeatedly(Return(enabled));
+ return nullptr;
+}
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/statsd_test_util.h b/statsd/tests/statsd_test_util.h
index 4cd84e4..8fe6440 100644
--- a/statsd/tests/statsd_test_util.h
+++ b/statsd/tests/statsd_test_util.h
@@ -16,6 +16,7 @@
#include <aidl/android/os/BnPendingIntentRef.h>
#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/BnStatsQueryCallback.h>
#include <aidl/android/os/BnStatsSubscriptionCallback.h>
#include <aidl/android/os/IPullAtomCallback.h>
#include <aidl/android/os/IPullAtomResultReceiver.h>
@@ -33,6 +34,7 @@
#include "src/stats_log.pb.h"
#include "src/stats_log_util.h"
#include "src/statsd_config.pb.h"
+#include "stats_annotations.h"
#include "stats_event.h"
#include "statslog_statsdtest.h"
@@ -42,6 +44,7 @@
using namespace testing;
using ::aidl::android::os::BnPullAtomCallback;
+using ::aidl::android::os::BnStatsQueryCallback;
using ::aidl::android::os::BnStatsSubscriptionCallback;
using ::aidl::android::os::IPullAtomCallback;
using ::aidl::android::os::IPullAtomResultReceiver;
@@ -93,6 +96,7 @@
public:
MOCK_METHOD1(sendDataBroadcast, Status(int64_t lastReportTimeNs));
MOCK_METHOD1(sendActiveConfigsChangedBroadcast, Status(const vector<int64_t>& configIds));
+ MOCK_METHOD1(sendRestrictedMetricsChangedBroadcast, Status(const vector<int64_t>& metricIds));
MOCK_METHOD6(sendSubscriberBroadcast,
Status(int64_t configUid, int64_t configId, int64_t subscriptionId,
int64_t subscriptionRuleId, const vector<string>& cookies,
@@ -101,6 +105,14 @@
typedef StrictMock<BasicMockLogEventFilter> MockLogEventFilter;
+class MockStatsQueryCallback : public BnStatsQueryCallback {
+public:
+ MOCK_METHOD4(sendResults,
+ Status(const vector<string>& queryData, const vector<string>& columnNames,
+ const vector<int32_t>& columnTypes, int32_t rowCount));
+ MOCK_METHOD1(sendFailure, Status(const string& in_error));
+};
+
class MockStatsSubscriptionCallback : public BnStatsSubscriptionCallback {
public:
MOCK_METHOD(Status, onSubscriptionData,
@@ -113,7 +125,8 @@
protected:
shared_ptr<StatsService> service;
const int kConfigKey = 789130123; // Randomly chosen
- const int kCallingUid = 0; // Randomly chosen
+ const int kCallingUid = 10100; // Randomly chosen
+
void SetUp() override {
service = createStatsService();
// Removing config file from data/misc/stats-service and data/misc/stats-data if present
@@ -135,7 +148,7 @@
virtual shared_ptr<StatsService> createStatsService() {
return SharedRefBase::make<StatsService>(new UidMap(), /* queue */ nullptr,
- /* LogEventFilter */ nullptr);
+ std::make_shared<LogEventFilter>());
}
bool sendConfig(const StatsdConfig& config);
@@ -321,31 +334,29 @@
const std::vector<Position>& positions,
const std::vector<int>& fields);
-EventMetric createEventMetric(const string& name, const int64_t what,
- const optional<int64_t>& condition);
+EventMetric createEventMetric(const string& name, int64_t what, const optional<int64_t>& condition);
-CountMetric createCountMetric(const string& name, const int64_t what,
- const optional<int64_t>& condition, const vector<int64_t>& states);
+CountMetric createCountMetric(const string& name, int64_t what, const optional<int64_t>& condition,
+ const vector<int64_t>& states);
-DurationMetric createDurationMetric(const string& name, const int64_t what,
+DurationMetric createDurationMetric(const string& name, int64_t what,
const optional<int64_t>& condition,
const vector<int64_t>& states);
-GaugeMetric createGaugeMetric(const string& name, const int64_t what,
+GaugeMetric createGaugeMetric(const string& name, int64_t what,
const GaugeMetric::SamplingType samplingType,
const optional<int64_t>& condition,
const optional<int64_t>& triggerEvent);
-ValueMetric createValueMetric(const string& name, const AtomMatcher& what, const int valueField,
+ValueMetric createValueMetric(const string& name, const AtomMatcher& what, int valueField,
const optional<int64_t>& condition, const vector<int64_t>& states);
-KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int kllField,
+KllMetric createKllMetric(const string& name, const AtomMatcher& what, int kllField,
const optional<int64_t>& condition);
-Alert createAlert(const string& name, const int64_t metricId, const int buckets,
- const int64_t triggerSum);
+Alert createAlert(const string& name, int64_t metricId, int buckets, const int64_t triggerSum);
-Alarm createAlarm(const string& name, const int64_t offsetMillis, const int64_t periodMillis);
+Alarm createAlarm(const string& name, int64_t offsetMillis, int64_t periodMillis);
Subscription createSubscription(const string& name, const Subscription_RuleType type,
const int64_t ruleId);
@@ -457,13 +468,22 @@
std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
// Create log event when battery state changes.
-std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state);
+std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs,
+ const BatteryPluggedStateEnum state,
+ int32_t uid = AID_ROOT);
+
+// Create malformed log event for battery state change.
+std::unique_ptr<LogEvent> CreateMalformedBatteryStateChangedEvent(const uint64_t timestampNs);
+
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+ uint64_t timestampNs, const int uid, const string& pkgName, const string& className,
+ const ActivityForegroundStateChanged::State state);
// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, int uid);
// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, int uid);
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, const vector<int>& uids,
@@ -474,10 +494,10 @@
const vector<string>& tags, const string& name);
// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, int uid);
// Create log event for an app crash.
-std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, int uid);
// Create log event for acquiring wakelock.
std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
@@ -510,7 +530,7 @@
const OverlayStateChanged::State state);
std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
- uint64_t timestampNs, const int uid, const string& pkg_name,
+ uint64_t timestampNs, int uid, const string& pkg_name,
AppStartOccurred::TransitionType type, const string& activity_name,
const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec);
@@ -525,12 +545,19 @@
const vector<string>& repeatedStringField, const bool* repeatedBoolField,
const size_t repeatedBoolFieldLength, const vector<int>& repeatedEnumField);
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventWithPrimitives(
+ uint64_t timestampNs, int intField, long longField, float floatField,
+ const string& stringField, bool boolField, TestAtomReported::State enumField);
+
+std::unique_ptr<LogEvent> CreateRestrictedLogEvent(int atomTag, int64_t timestampNs = 0);
+std::unique_ptr<LogEvent> CreateNonRestrictedLogEvent(int atomTag, int64_t timestampNs = 0);
+
std::unique_ptr<LogEvent> CreatePhoneSignalStrengthChangedEvent(
int64_t timestampNs, ::telephony::SignalStrengthEnum state);
std::unique_ptr<LogEvent> CreateTestAtomReportedEvent(
uint64_t timestampNs, const vector<int>& attributionUids,
- const vector<string>& attributionTags, const int intField, const long longField,
+ const vector<string>& attributionTags, int intField, const long longField,
const float floatField, const string& stringField, const bool boolField,
const TestAtomReported::State enumField, const vector<uint8_t>& bytesField,
const vector<int>& repeatedIntField, const vector<int64_t>& repeatedLongField,
@@ -544,10 +571,10 @@
// Create a statsd log event processor upon the start time in seconds, config and key.
sp<StatsLogProcessor> CreateStatsLogProcessor(
- const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config,
+ const int64_t timeBaseNs, int64_t currentTimeNs, const StatsdConfig& config,
const ConfigKey& key, const shared_ptr<IPullAtomCallback>& puller = nullptr,
const int32_t atomTag = 0 /*for puller only*/, const sp<UidMap> = new UidMap(),
- const shared_ptr<LogEventFilter>& logEventFilter = nullptr);
+ const shared_ptr<LogEventFilter>& logEventFilter = std::make_shared<LogEventFilter>());
LogEventFilter::AtomIdSet CreateAtomIdSetDefault();
LogEventFilter::AtomIdSet CreateAtomIdSetFromConfig(const StatsdConfig& config);
@@ -564,7 +591,7 @@
const int uid);
void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid);
-void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
+void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, int atomId,
const int uid, const string& tag);
void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
@@ -691,8 +718,8 @@
}
template <typename T>
-void backfillStartEndTimestampForFullBucket(
- const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) {
+void backfillStartEndTimestampForFullBucket(const int64_t timeBaseNs, int64_t bucketSizeNs,
+ T* bucket) {
bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num());
bucket->set_end_bucket_elapsed_nanos(
timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs);
@@ -714,7 +741,7 @@
}
template <typename T>
-void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs,
+void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, int64_t bucketSizeNs,
T* metrics) {
for (int i = 0; i < metrics->data_size(); ++i) {
auto data = metrics->mutable_data(i);
@@ -769,10 +796,10 @@
PackageInfoSnapshot getPackageInfoSnapshot(const sp<UidMap> uidMap);
-ApplicationInfo createApplicationInfo(const int32_t uid, const int64_t version,
- const string& versionString, const string& package);
+ApplicationInfo createApplicationInfo(int32_t uid, int64_t version, const string& versionString,
+ const string& package);
-PackageInfo buildPackageInfo(const std::string& name, const int32_t uid, const int64_t version,
+PackageInfo buildPackageInfo(const std::string& name, const int32_t uid, int64_t version,
const std::string& versionString,
const std::optional<std::string> installer,
const std::vector<uint8_t>& certHash, const bool deleted,
@@ -782,7 +809,7 @@
const std::vector<string>& names, const std::vector<int32_t>& uids,
const std::vector<int64_t>& versions, const std::vector<std::string>& versionStrings,
const std::vector<std::string>& installers,
- const std::vector<std::vector<uint8_t>>& certHashes, const std::vector<bool>& deleted,
+ const std::vector<std::vector<uint8_t>>& certHashes, const std::vector<uint8_t>& deleted,
const std::vector<uint32_t>& installerIndices, const bool hashStrings);
template <typename T>
@@ -805,6 +832,18 @@
proto.SerializeToArray(bytes.data(), byteSize);
return bytes;
}
+
+StatsdConfig buildGoodConfig(int configId);
+
+StatsdConfig buildGoodConfig(int configId, int alertId);
+
+class MockConfigMetadataProvider : public ConfigMetadataProvider {
+public:
+ MOCK_METHOD(bool, useV2SoftMemoryCalculation, (), (override));
+};
+
+sp<MockConfigMetadataProvider> makeMockConfigMetadataProvider(bool enabled);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/storage/StorageManager_test.cpp b/statsd/tests/storage/StorageManager_test.cpp
index 96409e6..27b8e98 100644
--- a/statsd/tests/storage/StorageManager_test.cpp
+++ b/statsd/tests/storage/StorageManager_test.cpp
@@ -21,6 +21,8 @@
#include "android-base/stringprintf.h"
#include "stats_log_util.h"
+#include "tests/statsd_test_util.h"
+#include "utils/DbUtils.h"
#ifdef __ANDROID__
@@ -29,6 +31,7 @@
namespace statsd {
using namespace testing;
+
using std::make_shared;
using std::shared_ptr;
using std::vector;
@@ -278,6 +281,42 @@
EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
}
+TEST(StorageManagerTest, DeleteUnmodifiedOldDbFiles) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ ConfigKey key(123, 12345);
+ unique_ptr<LogEvent> event = CreateRestrictedLogEvent(/*atomTag=*/10, /*timestampNs=*/1000);
+ dbutils::createTableIfNeeded(key, /*metricId=*/1, *event);
+ EXPECT_TRUE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+
+ int64_t wallClockSec = getWallClockSec() + (StatsdStats::kMaxAgeSecond + 1);
+ StorageManager::enforceDbGuardrails(STATS_RESTRICTED_DATA_DIR, wallClockSec,
+ /*maxBytes=*/INT_MAX);
+
+ EXPECT_FALSE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+}
+
+TEST(StorageManagerTest, DeleteLargeDbFiles) {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ ConfigKey key(123, 12345);
+ unique_ptr<LogEvent> event = CreateRestrictedLogEvent(/*atomTag=*/10, /*timestampNs=*/1000);
+ dbutils::createTableIfNeeded(key, /*metricId=*/1, *event);
+ EXPECT_TRUE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+
+ StorageManager::enforceDbGuardrails(STATS_RESTRICTED_DATA_DIR,
+ /*wallClockSec=*/getWallClockSec(),
+ /*maxBytes=*/0);
+
+ EXPECT_FALSE(StorageManager::hasFile(
+ base::StringPrintf("%s/%s", STATS_RESTRICTED_DATA_DIR, "123_12345.db").c_str()));
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/statsd/tests/utils/DbUtils_test.cpp b/statsd/tests/utils/DbUtils_test.cpp
new file mode 100644
index 0000000..9bc1d61
--- /dev/null
+++ b/statsd/tests/utils/DbUtils_test.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2023 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 "utils/DbUtils.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/stringprintf.h"
+#include "storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+namespace dbutils {
+
+using base::StringPrintf;
+
+namespace {
+const ConfigKey key = ConfigKey(111, 222);
+const int64_t metricId = 111;
+const int32_t tagId = 1;
+
+AStatsEvent* makeAStatsEvent(int32_t atomId, int64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ return statsEvent;
+}
+
+LogEvent makeLogEvent(AStatsEvent* statsEvent) {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, &event);
+ return event;
+}
+
+class DbUtilsTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ }
+ void TearDown() override {
+ if (!isAtLeastU()) {
+ GTEST_SKIP();
+ }
+ deleteDb(key);
+ }
+};
+} // Anonymous namespace.
+
+TEST_F(DbUtilsTest, TestInsertString) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "test_string");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, "test_string"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestMaliciousString) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "111); DROP TABLE metric_111;--");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _,
+ "111); DROP TABLE metric_111;--"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestInsertStringNegativeMetricId) {
+ int64_t eventElapsedTimeNs = 10000000000;
+ int64_t metricId2 = -111;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "111");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId2, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId2, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_n111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, "111"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestInsertInteger) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeInt32(statsEvent, 11);
+ AStatsEvent_writeInt64(statsEvent, 111);
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, "11", "111"));
+ EXPECT_THAT(columnTypes, ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER,
+ SQLITE_INTEGER, SQLITE_INTEGER));
+ EXPECT_THAT(columnNames, ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs",
+ "field_1", "field_2"));
+}
+
+TEST_F(DbUtilsTest, TestInsertFloat) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeFloat(statsEvent, 11.0);
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, _));
+ EXPECT_FLOAT_EQ(/*field1=*/std::stof(rows[0][3]), 11.0);
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_FLOAT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestInsertTwoEvents) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent1 = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent1, "111");
+ LogEvent logEvent1 = makeLogEvent(statsEvent1);
+
+ AStatsEvent* statsEvent2 = makeAStatsEvent(tagId, eventElapsedTimeNs + 20);
+ AStatsEvent_writeString(statsEvent2, "222");
+ LogEvent logEvent2 = makeLogEvent(statsEvent2);
+
+ vector<LogEvent> events{logEvent1, logEvent2};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent1));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 2);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, "111"));
+ EXPECT_THAT(rows[1], ElementsAre("1", to_string(eventElapsedTimeNs + 20), _, "222"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestInsertTwoEventsEnforceTtl) {
+ int64_t eventElapsedTimeNs = 10000000000;
+ int64_t eventWallClockNs = 50000000000;
+
+ AStatsEvent* statsEvent1 = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent1, "111");
+ LogEvent logEvent1 = makeLogEvent(statsEvent1);
+ logEvent1.setLogdWallClockTimestampNs(eventWallClockNs);
+
+ AStatsEvent* statsEvent2 = makeAStatsEvent(tagId, eventElapsedTimeNs + 20);
+ AStatsEvent_writeString(statsEvent2, "222");
+ LogEvent logEvent2 = makeLogEvent(statsEvent2);
+ logEvent2.setLogdWallClockTimestampNs(eventWallClockNs + eventElapsedTimeNs);
+
+ vector<LogEvent> events{logEvent1, logEvent2};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent1));
+ sqlite3* db = getDb(key);
+ string err;
+ EXPECT_TRUE(insert(db, metricId, events, err));
+ EXPECT_TRUE(flushTtl(db, metricId, eventWallClockNs));
+ closeDb(db);
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 20), _, "222"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestMaliciousQuery) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "111");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "DROP TABLE metric_111";
+ EXPECT_FALSE(query(key, zSql, rows, columnTypes, columnNames, err));
+ EXPECT_THAT(err, StartsWith("attempt to write a readonly database"));
+}
+
+TEST_F(DbUtilsTest, TestInsertStringIntegrityCheckPasses) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "111");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+ verifyIntegrityAndDeleteIfNecessary(key);
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre("1", to_string(eventElapsedTimeNs + 10), _, "111"));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_INTEGER, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("atomId", "elapsedTimestampNs", "wallTimestampNs", "field_1"));
+}
+
+TEST_F(DbUtilsTest, TestInsertStringIntegrityCheckFails) {
+ int64_t eventElapsedTimeNs = 10000000000;
+
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, eventElapsedTimeNs + 10);
+ AStatsEvent_writeString(statsEvent, "111");
+ LogEvent logEvent = makeLogEvent(statsEvent);
+ vector<LogEvent> events{logEvent};
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+ string err;
+ EXPECT_TRUE(insert(key, metricId, events, err));
+
+ vector<string> randomData{"1232hasha14125ashfas21512sh31321"};
+ string fileName = StringPrintf("%s/%d_%lld.db", STATS_RESTRICTED_DATA_DIR, key.GetUid(),
+ (long long)key.GetId());
+ StorageManager::writeFile(fileName.c_str(), randomData.data(), randomData.size());
+ EXPECT_TRUE(StorageManager::hasFile(fileName.c_str()));
+ verifyIntegrityAndDeleteIfNecessary(key);
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM metric_111 ORDER BY elapsedTimestampNs";
+ EXPECT_FALSE(query(key, zSql, rows, columnTypes, columnNames, err));
+ EXPECT_THAT(err, StartsWith("unable to open database file"));
+ EXPECT_FALSE(StorageManager::hasFile(fileName.c_str()));
+}
+
+TEST_F(DbUtilsTest, TestEventCompatibilityEventMatchesTable) {
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, /*eventElapsedTime=*/10000000000);
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeFloat(statsEvent, 111.0);
+ AStatsEvent_writeInt32(statsEvent, 23);
+ LogEvent logEvent = makeLogEvent(statsEvent);
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+
+ EXPECT_TRUE(isEventCompatible(key, metricId, logEvent));
+}
+
+TEST_F(DbUtilsTest, TestEventCompatibilityEventDoesNotMatchesTable) {
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, /*eventElapsedTime=*/10000000000);
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeFloat(statsEvent, 111.0);
+ AStatsEvent_writeInt32(statsEvent, 23);
+ LogEvent logEvent = makeLogEvent(statsEvent);
+
+ AStatsEvent* statsEvent2 = makeAStatsEvent(tagId, /*eventElapsedTime=*/10000000000);
+ AStatsEvent_writeString(statsEvent2, "111");
+ AStatsEvent_writeFloat(statsEvent2, 111.0);
+ AStatsEvent_writeInt32(statsEvent2, 23);
+ AStatsEvent_writeInt32(statsEvent2, 25);
+ LogEvent logEvent2 = makeLogEvent(statsEvent2);
+
+ EXPECT_TRUE(createTableIfNeeded(key, metricId, logEvent));
+
+ EXPECT_FALSE(isEventCompatible(key, metricId, logEvent2));
+}
+
+TEST_F(DbUtilsTest, TestEventCompatibilityTableNotCreated) {
+ AStatsEvent* statsEvent = makeAStatsEvent(tagId, /*eventElapsedTime=*/10000000000);
+ AStatsEvent_writeString(statsEvent, "111");
+ AStatsEvent_writeFloat(statsEvent, 111.0);
+ AStatsEvent_writeInt32(statsEvent, 23);
+ LogEvent logEvent = makeLogEvent(statsEvent);
+
+ EXPECT_TRUE(isEventCompatible(key, metricId, logEvent));
+}
+
+TEST_F(DbUtilsTest, TestUpdateDeviceInfoTable) {
+ string err;
+ updateDeviceInfoTable(key, err);
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM device_info";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre(_, _, _, _, _, _, _, _, _, _));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT,
+ SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("sdkVersion", "model", "product", "hardware", "device", "osBuild",
+ "fingerprint", "brand", "manufacturer", "board"));
+}
+
+TEST_F(DbUtilsTest, TestUpdateDeviceInfoTableInvokeTwice) {
+ string err;
+ updateDeviceInfoTable(key, err);
+ updateDeviceInfoTable(key, err);
+
+ std::vector<int32_t> columnTypes;
+ std::vector<string> columnNames;
+ std::vector<std::vector<std::string>> rows;
+ string zSql = "SELECT * FROM device_info";
+ EXPECT_TRUE(query(key, zSql, rows, columnTypes, columnNames, err));
+
+ ASSERT_EQ(rows.size(), 1);
+ EXPECT_THAT(rows[0], ElementsAre(_, _, _, _, _, _, _, _, _, _));
+ EXPECT_THAT(columnTypes,
+ ElementsAre(SQLITE_INTEGER, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT,
+ SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT, SQLITE_TEXT));
+ EXPECT_THAT(columnNames,
+ ElementsAre("sdkVersion", "model", "product", "hardware", "device", "osBuild",
+ "fingerprint", "brand", "manufacturer", "board"));
+}
+
+} // namespace dbutils
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/statsd/tools/localtools/Android.bp b/statsd/tools/localtools/Android.bp
index c07cb4e..b4d3f91 100644
--- a/statsd/tools/localtools/Android.bp
+++ b/statsd/tools/localtools/Android.bp
@@ -11,6 +11,7 @@
],
static_libs: [
"platformprotos",
+ "statsd_extension_atoms_registry_lib",
"guava",
],
}
@@ -23,10 +24,21 @@
],
static_libs: [
"platformprotos",
+ "statsd_extension_atoms_registry_lib",
"guava",
],
}
+java_library_host {
+ name: "statsd_extension_atoms_registry_lib",
+ srcs: [
+ "src/com/android/statsd/shelltools/ExtensionAtomsRegistry.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ ],
+}
+
java_binary_host {
name: "statsd_testdrive",
diff --git a/statsd/tools/localtools/src/com/android/statsd/shelltools/ExtensionAtomsRegistry.java b/statsd/tools/localtools/src/com/android/statsd/shelltools/ExtensionAtomsRegistry.java
new file mode 100644
index 0000000..489288e
--- /dev/null
+++ b/statsd/tools/localtools/src/com/android/statsd/shelltools/ExtensionAtomsRegistry.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 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.statsd.shelltools;
+
+import com.android.internal.os.ExperimentIdsProto;
+import com.android.internal.os.UidDataProto;
+import com.android.os.ActiveConfigProto;
+import com.android.os.ShellConfig;
+import com.android.os.adservices.AdservicesExtensionAtoms;
+import com.android.os.art.ArtExtensionAtoms;
+import com.android.os.automotive.caruilib.AutomotiveCaruilibAtoms;
+import com.android.os.bluetooth.BluetoothExtensionAtoms;
+import com.android.os.devicelogs.DeviceLogsAtoms;
+import com.android.os.dnd.DndAtoms;
+import com.android.os.dnd.DndExtensionAtoms;
+import com.android.os.expresslog.ExpresslogExtensionAtoms;
+import com.android.os.framework.FrameworkExtensionAtoms;
+import com.android.os.gps.GpsAtoms;
+import com.android.os.grammaticalinflection.GrammaticalInflection;
+import com.android.os.hardware.biometrics.BiometricsAtoms;
+import com.android.os.healthfitness.api.ApiExtensionAtoms;
+import com.android.os.healthfitness.ui.UiExtensionAtoms;
+import com.android.os.hotword.HotwordAtoms;
+import com.android.os.kernel.KernelAtoms;
+import com.android.os.locale.LocaleAtoms;
+import com.android.os.location.LocationAtoms;
+import com.android.os.location.LocationExtensionAtoms;
+import com.android.os.media.MediaDrmAtoms;
+import com.android.os.memorysafety.MemorysafetyExtensionAtoms;
+import com.android.os.permissioncontroller.PermissioncontrollerExtensionAtoms;
+import com.android.os.providers.mediaprovider.MediaProviderAtoms;
+import com.android.os.settings.SettingsExtensionAtoms;
+import com.android.os.statsd.ShellDataProto;
+import com.android.os.sysui.SysuiAtoms;
+import com.android.os.telecom.TelecomExtensionAtom;
+import com.android.os.telephony.SatelliteExtensionAtoms;
+import com.android.os.telephony.TelephonyExtensionAtoms;
+import com.android.os.telephony.qns.QnsExtensionAtoms;
+import com.android.os.usb.UsbAtoms;
+import com.android.os.uwb.UwbExtensionAtoms;
+import com.android.os.view.inputmethod.InputmethodAtoms;
+import com.android.os.wear.media.WearMediaAtoms;
+import com.android.os.wear.media.WearMediaExtensionAtoms;
+import com.android.os.wearpas.WearpasExtensionAtoms;
+import com.android.os.wearservices.WearservicesAtoms;
+import com.android.os.wearservices.WearservicesExtensionAtoms;
+import com.android.os.wearsysui.WearsysuiAtoms;
+import com.android.os.wifi.WifiExtensionAtoms;
+import android.os.statsd.media.MediaCodecExtensionAtoms;
+import com.android.os.credentials.CredentialsExtensionAtoms;
+import com.android.os.sdksandbox.SdksandboxExtensionAtoms;
+import com.android.os.apex.ApexExtensionAtoms;
+
+import com.google.protobuf.ExtensionRegistry;
+
+/**
+ * CustomExtensionRegistry for local use of statsd.
+ */
+public class ExtensionAtomsRegistry {
+
+ public static ExtensionRegistry REGISTRY;
+
+ static {
+ /** In Java, when parsing a message containing extensions, you must provide an
+ * ExtensionRegistry which contains definitions of all of the extensions which you
+ * want the parser to recognize. This is necessary because Java's bytecode loading
+ * semantics do not provide any way for the protocol buffers library to automatically
+ * discover all extensions defined in your binary.
+ *
+ * See http://sites/protocol-buffers/user-docs/miscellaneous-howtos/extensions
+ * #Java_ExtensionRegistry_
+ */
+ REGISTRY = ExtensionRegistry.newInstance();
+ registerAllExtensions(REGISTRY);
+ REGISTRY = REGISTRY.getUnmodifiable();
+ }
+
+ /**
+ * Registers all proto2 extensions.
+ */
+ private static void registerAllExtensions(ExtensionRegistry extensionRegistry) {
+ ExperimentIdsProto.registerAllExtensions(extensionRegistry);
+ UidDataProto.registerAllExtensions(extensionRegistry);
+ ActiveConfigProto.registerAllExtensions(extensionRegistry);
+ ShellConfig.registerAllExtensions(extensionRegistry);
+ AdservicesExtensionAtoms.registerAllExtensions(extensionRegistry);
+ AutomotiveCaruilibAtoms.registerAllExtensions(extensionRegistry);
+ BluetoothExtensionAtoms.registerAllExtensions(extensionRegistry);
+ DeviceLogsAtoms.registerAllExtensions(extensionRegistry);
+ DndAtoms.registerAllExtensions(extensionRegistry);
+ DndExtensionAtoms.registerAllExtensions(extensionRegistry);
+ ExpresslogExtensionAtoms.registerAllExtensions(extensionRegistry);
+ FrameworkExtensionAtoms.registerAllExtensions(extensionRegistry);
+ GpsAtoms.registerAllExtensions(extensionRegistry);
+ GrammaticalInflection.registerAllExtensions(extensionRegistry);
+ BiometricsAtoms.registerAllExtensions(extensionRegistry);
+ ApiExtensionAtoms.registerAllExtensions(extensionRegistry);
+ UiExtensionAtoms.registerAllExtensions(extensionRegistry);
+ HotwordAtoms.registerAllExtensions(extensionRegistry);
+ KernelAtoms.registerAllExtensions(extensionRegistry);
+ LocaleAtoms.registerAllExtensions(extensionRegistry);
+ LocationAtoms.registerAllExtensions(extensionRegistry);
+ LocationExtensionAtoms.registerAllExtensions(extensionRegistry);
+ MediaDrmAtoms.registerAllExtensions(extensionRegistry);
+ MemorysafetyExtensionAtoms.registerAllExtensions(extensionRegistry);
+ PermissioncontrollerExtensionAtoms.registerAllExtensions(extensionRegistry);
+ MediaProviderAtoms.registerAllExtensions(extensionRegistry);
+ SettingsExtensionAtoms.registerAllExtensions(extensionRegistry);
+ ShellDataProto.registerAllExtensions(extensionRegistry);
+ SysuiAtoms.registerAllExtensions(extensionRegistry);
+ TelecomExtensionAtom.registerAllExtensions(extensionRegistry);
+ SatelliteExtensionAtoms.registerAllExtensions(extensionRegistry);
+ TelephonyExtensionAtoms.registerAllExtensions(extensionRegistry);
+ QnsExtensionAtoms.registerAllExtensions(extensionRegistry);
+ UsbAtoms.registerAllExtensions(extensionRegistry);
+ UwbExtensionAtoms.registerAllExtensions(extensionRegistry);
+ InputmethodAtoms.registerAllExtensions(extensionRegistry);
+ WearMediaAtoms.registerAllExtensions(extensionRegistry);
+ WearMediaExtensionAtoms.registerAllExtensions(extensionRegistry);
+ WearpasExtensionAtoms.registerAllExtensions(extensionRegistry);
+ WearservicesAtoms.registerAllExtensions(extensionRegistry);
+ WearservicesExtensionAtoms.registerAllExtensions(extensionRegistry);
+ WearsysuiAtoms.registerAllExtensions(extensionRegistry);
+ WifiExtensionAtoms.registerAllExtensions(extensionRegistry);
+ MediaCodecExtensionAtoms.registerAllExtensions(extensionRegistry);
+ CredentialsExtensionAtoms.registerAllExtensions(extensionRegistry);
+ SdksandboxExtensionAtoms.registerAllExtensions(extensionRegistry);
+ ArtExtensionAtoms.registerAllExtensions(extensionRegistry);
+ ApexExtensionAtoms.registerAllExtensions(extensionRegistry);
+ }
+}
diff --git a/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 65fdb07..93293a8 100644
--- a/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -40,6 +40,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.android.statsd.shelltools.ExtensionAtomsRegistry;
+
/**
* Utilities for local use of statsd.
*/
@@ -112,7 +114,8 @@
"--include_current_bucket",
"--proto");
ConfigMetricsReportList reportList =
- ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
+ ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile),
+ ExtensionAtomsRegistry.REGISTRY);
return reportList;
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
logger.severe("Failed to fetch and parse the statsd output report. "
diff --git a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 2b7602c..05b7fb5 100644
--- a/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -30,6 +30,7 @@
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsLogReport;
+import com.android.os.telephony.qns.QnsExtensionAtoms;
import com.android.statsd.shelltools.Utils;
import com.google.common.annotations.VisibleForTesting;
@@ -98,6 +99,9 @@
"com.google.android.healthconnect.controller",
"com.android.telephony.qns",
"com.android.car",
+ "com.android.ondevicepersonalization.services",
+ "com.google.android.ondevicepersonalization.services",
+ "AID_UPROBESTATS",
};
private static final String[] DEFAULT_PULL_SOURCES = {
"AID_KEYSTORE", "AID_RADIO", "AID_SYSTEM",
@@ -154,7 +158,8 @@
LOGGER.severe("-e");
LOGGER.severe("\tWait for Enter key press before collecting report");
LOGGER.severe("-d delay_ms");
- LOGGER.severe("\tWait for delay_ms before collecting report, default is 60000 ms");
+ LOGGER.severe("\tWait for delay_ms before collecting report, default is 60000 ms. Only");
+ LOGGER.severe("\taffects collection of pushed atoms.");
LOGGER.severe("-v");
LOGGER.severe("\tDebug logging level");
}
@@ -597,6 +602,21 @@
PullAtomPackages.newBuilder()
.setAtomId(Atom.LAUNCHER_LAYOUT_SNAPSHOT_FIELD_NUMBER)
.addPackages("com.google.android.apps.nexuslauncher"))
+ .addPullAtomPackages(
+ PullAtomPackages.newBuilder()
+ .setAtomId(QnsExtensionAtoms
+ .QNS_RAT_PREFERENCE_MISMATCH_INFO_FIELD_NUMBER)
+ .addPackages("com.android.telephony.qns"))
+ .addPullAtomPackages(
+ PullAtomPackages.newBuilder()
+ .setAtomId(QnsExtensionAtoms
+ .QNS_HANDOVER_TIME_MILLIS_FIELD_NUMBER)
+ .addPackages("com.android.telephony.qns"))
+ .addPullAtomPackages(
+ PullAtomPackages.newBuilder()
+ .setAtomId(QnsExtensionAtoms
+ .QNS_HANDOVER_PINGPONG_FIELD_NUMBER)
+ .addPackages("com.android.telephony.qns"))
.setHashStringsInMetricReport(false);
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 532ce63..71784a9 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -37,14 +37,17 @@
"host-libprotobuf-java-full",
"platformprotos",
"tradefed",
- "truth-prebuilt",
+ "truth",
],
static_libs: [
"core_cts_test_resources",
"perfetto_config-full",
+ "cts-statsd-atom-host-test-utils",
],
data: [
"**/*.pbtxt",
":CtsStatsdApp",
+ ":StatsdAtomStormApp",
+ ":StatsdAtomStormApp2",
],
}
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index e33c2a0..781813a 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -36,4 +36,10 @@
<option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
<option name="restore-settings" value="true" />
</target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="StatsdAtomStormApp.apk" />
+ <option name="test-file-name" value="StatsdAtomStormApp2.apk" />
+ </target_preparer>
</configuration>
diff --git a/tests/OWNERS b/tests/OWNERS
index e69ed54..6e97551 100644
--- a/tests/OWNERS
+++ b/tests/OWNERS
@@ -1,8 +1,2 @@
# Bug component: 366902
-jeffreyhuang@google.com
-muhammadq@google.com
-sharaienko@google.com
-singhtejinder@google.com
-tsaichristine@google.com
-yaochen@google.com
-yro@google.com
+file:platform/packages/modules/StatsD:/OWNERS
diff --git a/tests/apps/atomstormapp/Android.bp b/tests/apps/atomstormapp/Android.bp
new file mode 100644
index 0000000..86e5953
--- /dev/null
+++ b/tests/apps/atomstormapp/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "StatsdAtomStormApp",
+ defaults: ["cts_defaults"],
+ platform_apis: true,
+ min_sdk_version: "24",
+ srcs: [
+ "src/**/*.java",
+ ],
+ libs: [
+ "android.test.runner",
+ "junit",
+ "org.apache.http.legacy",
+ ],
+ privileged: true,
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.test.rules",
+ ],
+ compile_multilib: "both",
+}
+
+android_test_helper_app {
+ name: "StatsdAtomStormApp2",
+ manifest: "AndroidManifest2.xml",
+ defaults: ["cts_defaults"],
+ platform_apis: true,
+ min_sdk_version: "24",
+ srcs: [
+ "src/**/*.java",
+ ],
+ libs: [
+ "android.test.runner",
+ "junit",
+ "org.apache.http.legacy",
+ ],
+ privileged: true,
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.test.rules",
+ ],
+ compile_multilib: "both",
+}
diff --git a/tests/apps/atomstormapp/AndroidManifest.xml b/tests/apps/atomstormapp/AndroidManifest.xml
new file mode 100644
index 0000000..2993928
--- /dev/null
+++ b/tests/apps/atomstormapp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.statsd.app.atomstorm">
+ <!-- Using gms shared uid is necessary for the receivers to work. -->
+
+ <!-- GTS started at 14; do not change minSdkVersion. -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.statsd.app.atomstorm"
+ android:label="CTS tests of android.os.statsd stats collection">
+ </instrumentation>
+</manifest>
diff --git a/tests/apps/atomstormapp/AndroidManifest2.xml b/tests/apps/atomstormapp/AndroidManifest2.xml
new file mode 100644
index 0000000..310397f
--- /dev/null
+++ b/tests/apps/atomstormapp/AndroidManifest2.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.statsd.app.atomstorm.copy">
+ <!-- Using gms shared uid is necessary for the receivers to work. -->
+
+ <!-- GTS started at 14; do not change minSdkVersion. -->
+ <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.statsd.app.atomstorm.copy"
+ android:label="CTS tests of android.os.statsd stats collection">
+ </instrumentation>
+</manifest>
diff --git a/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java b/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java
new file mode 100644
index 0000000..ce4ced9
--- /dev/null
+++ b/tests/apps/atomstormapp/src/com/android/statsd/app/atomstorm/StatsdAtomStorm.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 Google LLC.
+ *
+ * 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.statsd.app.atomstorm;
+
+import android.util.StatsLog;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class StatsdAtomStorm {
+ private static final int EventStormAtomsCount = 100000;
+
+ /** Tests socket overflow. */
+ @Test
+ public void testLogManyAtomsBackToBack() throws Exception {
+ // logging back to back many atoms to force socket overflow
+ performAtomStorm(EventStormAtomsCount);
+ // make pause to resolve socket overflow
+ Thread.sleep(100);
+ // give chance for libstatssocket send loss stats to statsd triggering successful logging
+ performAtomStorm(1);
+ }
+
+ private void performAtomStorm(int iterations) {
+ // single atom logging takes ~2us excluding JNI interactions
+ for (int i = 0; i < iterations; i++) {
+ StatsLog.logStart(i);
+ StatsLog.logStop(i);
+ }
+ }
+}
diff --git a/tests/apps/statsdapp/Android.bp b/tests/apps/statsdapp/Android.bp
index a45b95c..7dd3d19 100644
--- a/tests/apps/statsdapp/Android.bp
+++ b/tests/apps/statsdapp/Android.bp
@@ -51,7 +51,7 @@
"androidx.legacy_legacy-support-v4",
"androidx.test.rules",
"cts-net-utils",
- "BlobStoreTestUtils"
+ "BlobStoreTestUtils",
],
jni_libs: ["liblmkhelper"],
compile_multilib: "both",
@@ -60,6 +60,10 @@
genrule {
name: "statslog-statsd-cts-java-gen",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module cts --javaPackage com.android.server.cts.device.statsd --javaClass StatsLogStatsdCts",
+ cmd: "$(location stats-log-api-gen) " +
+ "--java $(out) " +
+ "--module cts " +
+ "--javaPackage com.android.server.cts.device.statsd " +
+ "--javaClass StatsLogStatsdCts",
out: ["com/android/server/cts/device/statsd/StatsLogStatsdCts.java"],
}
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/RestrictedPermissionTests.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/RestrictedPermissionTests.java
new file mode 100644
index 0000000..7896e5b
--- /dev/null
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/RestrictedPermissionTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 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.cts.device.statsd;
+
+import static org.junit.Assert.fail;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.CddTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Build, install and run tests with following command:
+ * atest CtsStatsdHostTestCases
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RestrictedPermissionTests {
+
+ /**
+ * Verify that the {@link android.Manifest.permission#READ_RESTRICTED_STATS}
+ * permission is only held by at most one package.
+ */
+ @Test
+ @CddTest(requirements={"9.8.17/C-0-1"})
+ public void testReadRestrictedStatsPermission() throws Exception {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+ android.Manifest.permission.READ_RESTRICTED_STATS
+ }, PackageManager.MATCH_ALL);
+
+ int count = 0;
+ String pkgNames = "";
+ for (PackageInfo pkg : holding) {
+ int uid = pm.getApplicationInfo(pkg.packageName, 0).uid;
+ if (UserHandle.isApp(uid)) {
+ pkgNames += pkg.packageName + "\n";
+ count++;
+ }
+ }
+ if (count > 1) {
+ fail("Only one app may hold the READ_RESTRICTED_STATS permission; found packages: \n"
+ + pkgNames);
+ }
+ }
+}
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdStressLogging.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdStressLogging.java
new file mode 100644
index 0000000..62dfe78
--- /dev/null
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdStressLogging.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 Google LLC.
+ *
+ * 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.cts.device.statsd;
+
+import android.os.SystemClock;
+import android.util.StatsLog;
+
+import org.junit.Test;
+
+public class StatsdStressLogging {
+ private static final int EVENT_STORM_ATOMS_COUNT = 100000;
+
+ /** Tests that logging many atoms back to back leads to socket overflow and data loss. */
+ @Test
+ public void testLogAtomsBackToBack() throws Exception {
+ // logging back to back many atoms to force socket overflow
+ logAtoms(EVENT_STORM_ATOMS_COUNT);
+
+ // Using sleep to allow bypass libstatsocket dumpAtomsLossStats() cooldown timer
+ SystemClock.sleep(100);
+
+ // Try to log atoms into socket successfully to trigger libstatsocket dumpAtomsLossStats()
+ logAtoms(1);
+ }
+
+ private void logAtoms(int iterations) {
+ // single atom logging takes ~2us excluding JNI interactions
+ for (int i = 0; i < iterations; i++) {
+ StatsLog.logStart(i);
+ StatsLog.logStop(i);
+ }
+ }
+}
diff --git a/tests/src/android/cts/statsd/alarm/AlarmTests.java b/tests/src/android/cts/statsd/alarm/AlarmTests.java
index 032297e..0f49e9a 100644
--- a/tests/src/android/cts/statsd/alarm/AlarmTests.java
+++ b/tests/src/android/cts/statsd/alarm/AlarmTests.java
@@ -17,21 +17,29 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.AtomTestCase;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
-import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.Alarm;
import com.android.internal.os.StatsdConfigProto.IncidentdDetails;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.Subscription;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
-import java.util.List;
+import java.text.SimpleDateFormat;
+import java.util.Date;
/**
* Statsd Anomaly Detection tests.
*/
-public class AlarmTests extends AtomTestCase {
+public class AlarmTests extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "Statsd.AnomalyDetectionTests";
@@ -42,36 +50,61 @@
private static final int SUBSCRIPTION_ID_INCIDENTD = 41;
private static final int INCIDENTD_SECTION = -1;
+ private IBuildInfo mCtsBuild;
+
@Override
protected void setUp() throws Exception {
super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
if (!INCIDENTD_TESTS_ENABLED) {
CLog.w(TAG, TAG + " alarm tests are disabled by a flag. Change flag to true to run");
}
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
}
public void testAlarm() throws Exception {
StatsdConfig.Builder config = getBaseConfig();
- turnScreenOn();
- uploadConfig(config);
+ DeviceUtils.turnScreenOn(getDevice());
+ ConfigUtils.uploadConfig(getDevice(), config);
- String markTime = getCurrentLogcatDate();
- Thread.sleep(9_000);
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ RunUtil.getDefault().sleep(9_000);
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isTrue();
+ }
}
private final StatsdConfig.Builder getBaseConfig() throws Exception {
- return createConfigBuilder()
- .addAlarm(Alarm.newBuilder().setId(ALARM_ID).setOffsetMillis(2).setPeriodMillis(
- 5_000) // every 5 seconds.
- )
- .addSubscription(Subscription.newBuilder()
- .setId(SUBSCRIPTION_ID_INCIDENTD)
- .setRuleType(Subscription.RuleType.ALARM)
- .setRuleId(ALARM_ID)
- .setIncidentdDetails(
- IncidentdDetails.newBuilder().addSection(INCIDENTD_SECTION)));
+ return ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
+ .addAlarm(Alarm.newBuilder()
+ .setId(ALARM_ID)
+ .setOffsetMillis(2)
+ .setPeriodMillis(5_000) // every 5 seconds.
+ )
+ .addSubscription(Subscription.newBuilder()
+ .setId(SUBSCRIPTION_ID_INCIDENTD)
+ .setRuleType(Subscription.RuleType.ALARM)
+ .setRuleId(ALARM_ID)
+ .setIncidentdDetails(IncidentdDetails.newBuilder()
+ .addSection(INCIDENTD_SECTION)));
}
}
diff --git a/tests/src/android/cts/statsd/alert/AnomalyDetectionTests.java b/tests/src/android/cts/statsd/alert/AnomalyDetectionTests.java
index ea47cc3..74fe1b5 100644
--- a/tests/src/android/cts/statsd/alert/AnomalyDetectionTests.java
+++ b/tests/src/android/cts/statsd/alert/AnomalyDetectionTests.java
@@ -18,7 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.cts.statsd.atom.AtomTestCase;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.Alert;
@@ -38,13 +42,30 @@
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.DebugElapsedClock;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.log.LogUtil;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.RunUtil;
+
+import com.google.protobuf.ByteString;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.List;
+import java.util.Random;
+
+import perfetto.protos.PerfettoConfig.DataSourceConfig;
+import perfetto.protos.PerfettoConfig.FtraceConfig;
+import perfetto.protos.PerfettoConfig.TraceConfig;
/**
* Statsd Anomaly Detection tests.
*/
-public class AnomalyDetectionTests extends AtomTestCase {
+public class AnomalyDetectionTests extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "Statsd.AnomalyDetectionTests";
@@ -53,6 +74,8 @@
private static final int WAIT_AFTER_BREADCRUMB_MS = 2000;
+ private static final int SHELL_UID = 2000;
+
// Config constants
private static final int APP_BREADCRUMB_REPORTED_MATCH_START_ID = 1;
private static final int APP_BREADCRUMB_REPORTED_MATCH_STOP_ID = 2;
@@ -66,14 +89,22 @@
private boolean defaultSystemTracingConfigurationHasChanged = false;
+ private IBuildInfo mCtsBuild;
+
@Override
protected void setUp() throws Exception {
super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
if (!INCIDENTD_TESTS_ENABLED) {
CLog.w(TAG, TAG + " anomaly tests are disabled by a flag. Change flag to true to run");
}
if (PERFETTO_TESTS_ENABLED) {
- // Default Android configuration can only change for device type that doesn't require SystemTracingEnabled
+ // Default Android configuration can only change for device type that doesn't require
+ // SystemTracingEnabled
// by default in CDD.
String chars = getDevice().getProperty("ro.build.characteristics");
if (!isSystemTracingEnabled() && chars.contains("automotive")) {
@@ -81,11 +112,20 @@
defaultSystemTracingConfigurationHasChanged = true;
}
}
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
if (PERFETTO_TESTS_ENABLED) {
// Disable SystemTracing if previously enabled at test setUp()
if (defaultSystemTracingConfigurationHasChanged) {
@@ -95,11 +135,12 @@
final long deadLine = System.currentTimeMillis() + 10000;
while (isSystemTracingEnabled()) {
if (System.currentTimeMillis() > deadLine) {
- CLog.w("/sys/kernel/debug/tracing/tracing_on is still 1 after 10 secs : " + isSystemTracingEnabled());
+ CLog.w("/sys/kernel/debug/tracing/tracing_on is still 1 after 10 secs : "
+ + isSystemTracingEnabled());
break;
}
CLog.d("Waiting to finish collecting traces. ");
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
}
}
}
@@ -120,34 +161,50 @@
)
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- String markTime = getCurrentLogcatDate();
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
// count(label=6) -> 1 (not an anomaly, since not "greater than 2")
- doAppBreadcrumbReportedStart(6);
- Thread.sleep(500);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 6);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isFalse();
+ }
// count(label=6) -> 2 (not an anomaly, since not "greater than 2")
- doAppBreadcrumbReportedStart(6);
- Thread.sleep(500);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 6);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isFalse();
+ }
// count(label=12) -> 1 (not an anomaly, since not "greater than 2")
- doAppBreadcrumbReportedStart(12);
- Thread.sleep(1000);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 12);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isFalse();
+ }
- doAppBreadcrumbReportedStart(6); // count(label=6) -> 3 (anomaly, since "greater than 2"!)
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 6); // count(label=6) -> 3 (anomaly, since "greater than 2"!)
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isTrue();
+ }
}
// Tests that anomaly detection for duration works.
@@ -169,49 +226,64 @@
.setStop(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID)
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
// Since timing is crucial and checking logcat for incidentd is slow, we don't test for it.
// Test that alarm doesn't fire early.
- String markTime = getCurrentLogcatDate();
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(6_000); // Recorded duration at end: 6s
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(6_000); // Recorded duration at end: 6s
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(4_000); // Recorded duration at end: 6s
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(4_000); // Recorded duration at end: 6s
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
// Test that alarm does fire when it is supposed to (after 4s, plus up to 5s alarm delay).
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(9_000); // Recorded duration at end: 13s
- doAppBreadcrumbReported(2);
- List<EventMetricData> data = getEventMetricDataList();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(9_000); // Recorded duration at end: 13s
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 2);
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
// Now test that the refractory period is obeyed.
- markTime = getCurrentLogcatDate();
- doAppBreadcrumbReportedStop(1);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(3_000); // Recorded duration at end: 13s
+ markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(3_000); // Recorded duration at end: 13s
// NB: the previous getEventMetricDataList also removes the report, so size is back to 0.
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
// Test that detection works again after refractory period finishes.
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(8_000); // Recorded duration at end: 9s
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(15_000); // Recorded duration at end: 15s
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(8_000); // Recorded duration at end: 9s
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(15_000); // Recorded duration at end: 15s
// We can do an incidentd test now that all the timing issues are done.
- doAppBreadcrumbReported(2);
- data = getEventMetricDataList();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 2);
+ data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isTrue();
+ }
- doAppBreadcrumbReportedStop(1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
}
// Tests that anomaly detection for duration works even when the alarm fires too late.
@@ -233,24 +305,29 @@
.setStop(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID)
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(5_000);
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(2_000);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(5_000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(2_000);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
// Test that alarm does fire when it is supposed to.
// The anomaly occurs in 1s, but alarms won't fire that quickly.
// It is likely that the alarm will only fire after this period is already over, but the
// anomaly should nonetheless be detected when the event stops.
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(1_200);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(1_200);
// Anomaly should be detected here if the alarm didn't fire yet.
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(200);
- List<EventMetricData> data = getEventMetricDataList();
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(200);
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
if (data.size() == 2) {
// Although we expect that the alarm won't fire, we certainly cannot demand that.
CLog.w(TAG, "The anomaly was detected twice. Presumably the alarm did manage to fire.");
@@ -274,28 +351,37 @@
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- String markTime = getCurrentLogcatDate();
- doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 6); // value = 6, which is NOT > trigger
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isFalse();
+ }
- doAppBreadcrumbReportedStart(14); // value = 14 > trigger
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 14); // value = 14 > trigger
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isTrue();
+ }
}
// Test that anomaly detection integrates with perfetto properly.
public void testPerfetto() throws Exception {
String chars = getDevice().getProperty("ro.build.characteristics");
if (chars.contains("watch")) {
- return;
+ return;
}
if (PERFETTO_TESTS_ENABLED) resetPerfettoGuardrails();
@@ -320,32 +406,38 @@
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- String markTime = getCurrentLogcatDate();
- doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 6); // value = 6, which is NOT > trigger
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
if (PERFETTO_TESTS_ENABLED) assertThat(isSystemTracingEnabled()).isFalse();
- doAppBreadcrumbReportedStart(14); // value = 14 > trigger
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 14); // value = 14 > trigger
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
- // Pool a few times to allow for statsd <-> traced <-> traced_probes communication to happen.
+ // Pool a few times to allow for statsd <-> traced <-> traced_probes communication to
+ // happen.
if (PERFETTO_TESTS_ENABLED) {
- boolean tracingEnabled = false;
- for (int i = 0; i < 5; i++) {
- if (isSystemTracingEnabled()) {
- tracingEnabled = true;
- break;
- }
- Thread.sleep(1000);
+ boolean tracingEnabled = false;
+ for (int i = 0; i < 5; i++) {
+ if (isSystemTracingEnabled()) {
+ tracingEnabled = true;
+ break;
}
- assertThat(tracingEnabled).isTrue();
+ RunUtil.getDefault().sleep(1000);
+ }
+ assertThat(tracingEnabled).isTrue();
}
}
@@ -365,22 +457,32 @@
)
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- String markTime = getCurrentLogcatDate();
- doAppBreadcrumbReportedStart(6); // gauge = 6, which is NOT > trigger
- Thread.sleep(Math.max(WAIT_AFTER_BREADCRUMB_MS, 1_100)); // Must be >1s to push next bucket.
- assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
+ String markTime = MetricsUtils.getCurrentLogcatDate(getDevice());
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 6); // gauge = 6, which is NOT > trigger
+ RunUtil.getDefault().sleep(
+ Math.max(WAIT_AFTER_BREADCRUMB_MS, 1_100)); // Must be >1s to push next bucket.
+ assertWithMessage("Premature anomaly").that(
+ ReportUtils.getEventMetricDataList(getDevice())).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isFalse();
+ }
// We waited for >1s above, so we are now in the next bucket (which is essential).
- doAppBreadcrumbReportedStart(14); // gauge = 14 > trigger
- Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(),
+ 14); // gauge = 14 > trigger
+ RunUtil.getDefault().sleep(WAIT_AFTER_BREADCRUMB_MS);
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Expected anomaly").that(data).hasSize(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
- if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
+ if (INCIDENTD_TESTS_ENABLED) {
+ assertThat(MetricsUtils.didIncidentdFireSince(getDevice(), markTime)).isTrue();
+ }
}
// Test that anomaly detection for pulled metrics work.
@@ -410,73 +512,165 @@
)
)
);
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
- Thread.sleep(6_000); // Wait long enough to ensure AlarmManager signals >= 1 pull
+ RunUtil.getDefault().sleep(
+ 6_000); // Wait long enough to ensure AlarmManager signals >= 1 pull
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
}
private final StatsdConfig.Builder getBaseConfig(int numBuckets,
- int refractorySecs,
- long triggerIfSumGt) throws Exception {
- return createConfigBuilder()
- // Items of relevance for detecting the anomaly:
- .addAtomMatcher(
- StatsdConfigProto.AtomMatcher.newBuilder()
- .setId(APP_BREADCRUMB_REPORTED_MATCH_START_ID)
- .setSimpleAtomMatcher(
- StatsdConfigProto.SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- // Event only when the uid is this app's uid.
- .addFieldValueMatcher(createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER)
- .setEqInt(getHostUid()))
- .addFieldValueMatcher(
- createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(AppBreadcrumbReported.State.START.ordinal()))))
- .addAtomMatcher(
- StatsdConfigProto.AtomMatcher.newBuilder()
- .setId(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID)
- .setSimpleAtomMatcher(
- StatsdConfigProto.SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- // Event only when the uid is this app's uid.
- .addFieldValueMatcher(createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER)
- .setEqInt(getHostUid()))
- .addFieldValueMatcher(
- createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(AppBreadcrumbReported.State.STOP.ordinal()))))
- .addAlert(Alert.newBuilder()
+ int refractorySecs,
+ long triggerIfSumGt) throws Exception {
+ return ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
+ // Items of relevance for detecting the anomaly:
+ .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
+ .setId(APP_BREADCRUMB_REPORTED_MATCH_START_ID)
+ .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) // Event
+ // only when the uid is this app's uid.
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER)
+ .setEqInt(SHELL_UID))
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .setEqInt(AppBreadcrumbReported.State.START.getNumber()))))
+ .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
+ .setId(APP_BREADCRUMB_REPORTED_MATCH_STOP_ID)
+ .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ // Event only when the uid is this app's uid.
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AppBreadcrumbReported.UID_FIELD_NUMBER)
+ .setEqInt(SHELL_UID))
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .setEqInt(AppBreadcrumbReported.State.STOP.getNumber()))))
+ .addAlert(Alert.newBuilder()
.setId(ALERT_ID)
.setMetricId(METRIC_ID) // The metric itself must yet be added by the test.
.setNumBuckets(numBuckets)
.setRefractoryPeriodSecs(refractorySecs)
.setTriggerIfSumGt(triggerIfSumGt))
- .addSubscription(
- Subscription.newBuilder()
- .setId(SUBSCRIPTION_ID_INCIDENTD)
- .setRuleType(Subscription.RuleType.ALERT)
- .setRuleId(ALERT_ID)
- .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(INCIDENTD_SECTION)))
- // We want to trigger anomalies on METRIC_ID, but don't want the actual data.
- .addNoReportMetric(METRIC_ID)
+ .addSubscription(Subscription.newBuilder()
+ .setId(SUBSCRIPTION_ID_INCIDENTD)
+ .setRuleType(Subscription.RuleType.ALERT)
+ .setRuleId(ALERT_ID)
+ .setIncidentdDetails(IncidentdDetails.newBuilder().addSection(
+ INCIDENTD_SECTION)))
+ // We want to trigger anomalies on METRIC_ID, but don't want the actual data.
+ .addNoReportMetric(METRIC_ID)
- // Items of relevance to reporting the anomaly (we do want this data):
- .addAtomMatcher(
- StatsdConfigProto.AtomMatcher.newBuilder()
- .setId(ANOMALY_DETECT_MATCH_ID)
- .setSimpleAtomMatcher(
- StatsdConfigProto.SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.ANOMALY_DETECTED_FIELD_NUMBER)
- .addFieldValueMatcher(createFvm(AnomalyDetected.CONFIG_UID_FIELD_NUMBER)
- .setEqInt(getHostUid()))
- .addFieldValueMatcher(createFvm(AnomalyDetected.CONFIG_ID_FIELD_NUMBER)
- .setEqInt(CONFIG_ID))))
- .addEventMetric(StatsdConfigProto.EventMetric.newBuilder()
- .setId(ANOMALY_EVENT_ID)
- .setWhat(ANOMALY_DETECT_MATCH_ID));
+ // Items of relevance to reporting the anomaly (we do want this data):
+ .addAtomMatcher(StatsdConfigProto.AtomMatcher.newBuilder()
+ .setId(ANOMALY_DETECT_MATCH_ID)
+ .setSimpleAtomMatcher(StatsdConfigProto.SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.ANOMALY_DETECTED_FIELD_NUMBER)
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AnomalyDetected.CONFIG_UID_FIELD_NUMBER)
+ .setEqInt(SHELL_UID))
+ .addFieldValueMatcher(ConfigUtils
+ .createFvm(AnomalyDetected.CONFIG_ID_FIELD_NUMBER)
+ .setEqInt(ConfigUtils.CONFIG_ID))))
+ .addEventMetric(StatsdConfigProto.EventMetric.newBuilder()
+ .setId(ANOMALY_EVENT_ID)
+ .setWhat(ANOMALY_DETECT_MATCH_ID));
+ }
+
+ /**
+ * Determines whether perfetto enabled the kernel ftrace tracer.
+ */
+ protected boolean isSystemTracingEnabled() throws Exception {
+ final String traceFsPath = "/sys/kernel/tracing/tracing_on";
+ String tracing_on = probe(traceFsPath);
+ if (tracing_on.startsWith("0")) return false;
+ if (tracing_on.startsWith("1")) return true;
+
+ // fallback to debugfs
+ LogUtil.CLog.d("Unexpected state for %s = %s. Falling back to debugfs", traceFsPath,
+ tracing_on);
+
+ final String debugFsPath = "/sys/kernel/debug/tracing/tracing_on";
+ tracing_on = probe(debugFsPath);
+ if (tracing_on.startsWith("0")) return false;
+ if (tracing_on.startsWith("1")) return true;
+ throw new Exception(String.format("Unexpected state for %s = %s", traceFsPath, tracing_on));
+ }
+
+ private String probe(String path) throws Exception {
+ return getDevice().executeShellCommand("if [ -e " + path + " ] ; then"
+ + " cat " + path + " ; else echo -1 ; fi");
+ }
+
+ protected void enableSystemTracing() throws Exception {
+ getDevice().executeShellCommand("setprop persist.traced.enable 1");
+ }
+
+ protected void disableSystemTracing() throws Exception {
+ getDevice().executeShellCommand("setprop persist.traced.enable 0");
+ }
+
+ /**
+ * Resets the state of the Perfetto guardrails. This avoids that the test fails if it's
+ * run too close of for too many times and hits the upload limit.
+ */
+ private void resetPerfettoGuardrails() throws Exception {
+ final String cmd = "perfetto --reset-guardrails";
+ CommandResult cr = getDevice().executeShellV2Command(cmd);
+ if (cr.getStatus() != CommandStatus.SUCCESS) {
+ throw new Exception(
+ String.format("Error while executing %s: %s %s", cmd, cr.getStdout(),
+ cr.getStderr()));
+ }
+ }
+
+ /**
+ * Returns a protobuf-encoded perfetto config that enables the kernel
+ * ftrace tracer with sched_switch for 10 seconds.
+ */
+ private ByteString getPerfettoConfig() {
+ TraceConfig.Builder builder = TraceConfig.newBuilder();
+
+ TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig
+ .newBuilder()
+ .setSizeKb(128)
+ .build();
+ builder.addBuffers(buffer);
+
+ FtraceConfig ftraceConfig = FtraceConfig.newBuilder()
+ .addFtraceEvents("sched/sched_switch")
+ .build();
+ DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
+ .setName("linux.ftrace")
+ .setTargetBuffer(0)
+ .setFtraceConfig(ftraceConfig)
+ .build();
+ TraceConfig.DataSource dataSource = TraceConfig.DataSource
+ .newBuilder()
+ .setConfig(dataSourceConfig)
+ .build();
+ builder.addDataSources(dataSource);
+
+ builder.setDurationMs(10000);
+ builder.setAllowUserBuildTracing(true);
+
+ TraceConfig.IncidentReportConfig incident = TraceConfig.IncidentReportConfig
+ .newBuilder()
+ .setSkipIncidentd(true)
+ .build();
+ builder.setIncidentReportConfig(incident);
+
+ // To avoid being hit with guardrails firing in multiple test runs back
+ // to back, we set a unique session key for each config.
+ Random random = new Random();
+ StringBuilder sessionNameBuilder = new StringBuilder("statsd-cts-");
+ sessionNameBuilder.append(random.nextInt() & Integer.MAX_VALUE);
+ builder.setUniqueSessionName(sessionNameBuilder.toString());
+
+ return builder.build().toByteString();
}
}
diff --git a/tests/src/android/cts/statsd/apex/BootstrapApexTests.java b/tests/src/android/cts/statsd/apex/BootstrapApexTests.java
index ab6093f..2db635a 100644
--- a/tests/src/android/cts/statsd/apex/BootstrapApexTests.java
+++ b/tests/src/android/cts/statsd/apex/BootstrapApexTests.java
@@ -18,11 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.BaseTestCase;
import com.android.apex.ApexInfo;
import com.android.apex.XmlParser;
import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
@@ -30,7 +31,7 @@
/**
* Verify statsd is not in the bootstrap apexes
*/
-public class BootstrapApexTests extends BaseTestCase {
+public class BootstrapApexTests extends DeviceTestCase {
private static final String TAG = "Statsd.BootstrapApexTests";
// Constants for the paths to apex-info-list.xml
@@ -39,6 +40,7 @@
// - legacy location
private static final String BOOTSTRAP_APEX_FILE2 = "/apex/.bootstrap-apex-info-list.xml";
+
private boolean sdkLevelAtLeast(int sdkLevel, String codename) throws Exception {
return ApiLevelUtil.isAtLeast(getDevice(), sdkLevel)
|| ApiLevelUtil.codenameEquals(getDevice(), codename);
diff --git a/tests/src/android/cts/statsd/atom/AtomTestCase.java b/tests/src/android/cts/statsd/atom/AtomTestCase.java
deleted file mode 100644
index fc37543..0000000
--- a/tests/src/android/cts/statsd/atom/AtomTestCase.java
+++ /dev/null
@@ -1,1263 +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.
- */
-package android.cts.statsd.atom;
-
-import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_APK;
-import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.os.BatteryStatsProto;
-import android.os.StatsDataDumpProto;
-import android.service.battery.BatteryServiceDumpProto;
-import android.service.batterystats.BatteryStatsServiceDumpProto;
-import android.service.procstats.ProcessStatsServiceDumpProto;
-
-import com.android.annotations.Nullable;
-import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.EventMetric;
-import com.android.internal.os.StatsdConfigProto.FieldFilter;
-import com.android.internal.os.StatsdConfigProto.FieldMatcher;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
-import com.android.internal.os.StatsdConfigProto.Predicate;
-import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
-import com.android.internal.os.StatsdConfigProto.SimplePredicate;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.ProcessStatsPackageProto;
-import com.android.os.AtomsProto.ProcessStatsProto;
-import com.android.os.AtomsProto.ProcessStatsStateProto;
-import com.android.os.StatsLog;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.CountMetricData;
-import com.android.os.StatsLog.DurationMetricData;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.os.StatsLog.GaugeBucketInfo;
-import com.android.os.StatsLog.GaugeMetricData;
-import com.android.os.StatsLog.StatsLogReport;
-import com.android.os.StatsLog.StatsLogReport.GaugeMetricDataWrapper;
-import com.android.os.StatsLog.ValueMetricData;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-import com.android.tradefed.util.Pair;
-
-import com.google.common.collect.Range;
-import com.google.common.io.Files;
-import com.google.protobuf.ByteString;
-import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Random;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.function.Function;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import perfetto.protos.PerfettoConfig.DataSourceConfig;
-import perfetto.protos.PerfettoConfig.FtraceConfig;
-import perfetto.protos.PerfettoConfig.TraceConfig;
-
-/**
- * Base class for testing Statsd atoms.
- * Validates reporting of statsd logging based on different events
- */
-public class AtomTestCase extends BaseTestCase {
-
- /**
- * Run tests that are optional; they are not valid CTS tests per se, since not all devices can
- * be expected to pass them, but can be run, if desired, to ensure they work when appropriate.
- */
- public static final boolean OPTIONAL_TESTS_ENABLED = false;
-
- public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
- public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
- public static final String DUMP_BATTERY_CMD = "dumpsys battery";
- public static final String DUMP_BATTERYSTATS_CMD = "dumpsys batterystats";
- public static final String DUMPSYS_STATS_CMD = "dumpsys stats";
- public static final String DUMP_PROCSTATS_CMD = "dumpsys procstats";
- public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
- /** ID of the config, which evaluates to -1572883457. */
- public static final long CONFIG_ID = "cts_config".hashCode();
-
- public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
- public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
- public static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
- public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
- public static final String FEATURE_CAMERA = "android.hardware.camera";
- public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
- public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
- public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
- public static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
- public static final String FEATURE_PC = "android.hardware.type.pc";
- public static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
- public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
- public static final String FEATURE_WATCH = "android.hardware.type.watch";
- public static final String FEATURE_WIFI = "android.hardware.wifi";
- public static final String FEATURE_INCREMENTAL_DELIVERY =
- "android.software.incremental_delivery";
-
- public static final int SHELL_UID = 2000;
-
- // Telephony phone types
- public static final int PHONE_TYPE_GSM = 1;
- public static final int PHONE_TYPE_CDMA = 2;
- public static final int PHONE_TYPE_CDMA_LTE = 6;
-
- protected static final int WAIT_TIME_SHORT = 500;
- protected static final int WAIT_TIME_LONG = 2_000;
-
- protected static final long SCREEN_STATE_CHANGE_TIMEOUT = 4000;
- protected static final long SCREEN_STATE_POLLING_INTERVAL = 500;
-
- protected static final long NS_PER_SEC = (long) 1E+9;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- // Uninstall to clear the history in case it's still on the device.
- removeConfig(CONFIG_ID);
- getReportList(); // Clears data.
- }
-
- @Override
- protected void tearDown() throws Exception {
- removeConfig(CONFIG_ID);
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- super.tearDown();
- }
-
- /**
- * Determines whether logcat indicates that incidentd fired since the given device date.
- */
- protected boolean didIncidentdFireSince(String date) throws Exception {
- final String INCIDENTD_TAG = "incidentd";
- final String INCIDENTD_STARTED_STRING = "reportIncident";
- // TODO: Do something more robust than this in case of delayed logging.
- Thread.sleep(1000);
- String log = getLogcatSince(date, String.format(
- "-s %s -e %s", INCIDENTD_TAG, INCIDENTD_STARTED_STRING));
- return log.contains(INCIDENTD_STARTED_STRING);
- }
-
- protected boolean checkDeviceFor(String methodName) throws Exception {
- try {
- installPackage(DEVICE_SIDE_TEST_APK, true);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".Checkers", methodName);
- // Test passes, meaning that the answer is true.
- LogUtil.CLog.d(methodName + "() indicates true.");
- return true;
- } catch (AssertionError e) {
- // Method is designed to fail if the answer is false.
- LogUtil.CLog.d(methodName + "() indicates false.");
- return false;
- }
- }
-
- /**
- * Returns a protobuf-encoded perfetto config that enables the kernel
- * ftrace tracer with sched_switch for 10 seconds.
- */
- protected ByteString getPerfettoConfig() {
- TraceConfig.Builder builder = TraceConfig.newBuilder();
-
- TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig
- .newBuilder()
- .setSizeKb(128)
- .build();
- builder.addBuffers(buffer);
-
- FtraceConfig ftraceConfig = FtraceConfig.newBuilder()
- .addFtraceEvents("sched/sched_switch")
- .build();
- DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
- .setName("linux.ftrace")
- .setTargetBuffer(0)
- .setFtraceConfig(ftraceConfig)
- .build();
- TraceConfig.DataSource dataSource = TraceConfig.DataSource
- .newBuilder()
- .setConfig(dataSourceConfig)
- .build();
- builder.addDataSources(dataSource);
-
- builder.setDurationMs(10000);
- builder.setAllowUserBuildTracing(true);
-
- TraceConfig.IncidentReportConfig incident = TraceConfig.IncidentReportConfig
- .newBuilder()
- .setDestinationPackage("foo.bar.baz")
- .build();
- builder.setIncidentReportConfig(incident);
-
- // To avoid being hit with guardrails firing in multiple test runs back
- // to back, we set a unique session key for each config.
- Random random = new Random();
- StringBuilder sessionNameBuilder = new StringBuilder("statsd-cts-");
- sessionNameBuilder.append(random.nextInt() & Integer.MAX_VALUE);
- builder.setUniqueSessionName(sessionNameBuilder.toString());
-
- return builder.build().toByteString();
- }
-
- /**
- * Resets the state of the Perfetto guardrails. This avoids that the test fails if it's
- * run too close of for too many times and hits the upload limit.
- */
- protected void resetPerfettoGuardrails() throws Exception {
- final String cmd = "perfetto --reset-guardrails";
- CommandResult cr = getDevice().executeShellV2Command(cmd);
- if (cr.getStatus() != CommandStatus.SUCCESS)
- throw new Exception(String.format("Error while executing %s: %s %s", cmd, cr.getStdout(), cr.getStderr()));
- }
-
- private String probe(String path) throws Exception {
- return getDevice().executeShellCommand("if [ -e " + path + " ] ; then"
- + " cat " + path + " ; else echo -1 ; fi");
- }
-
- protected void enableSystemTracing() throws Exception {
- getDevice().executeShellCommand("setprop persist.traced.enable 1");
- }
-
- protected void disableSystemTracing() throws Exception {
- getDevice().executeShellCommand("setprop persist.traced.enable 0");
- }
-
- /**
- * Determines whether perfetto enabled the kernel ftrace tracer.
- */
- protected boolean isSystemTracingEnabled() throws Exception {
- final String traceFsPath = "/sys/kernel/tracing/tracing_on";
- String tracing_on = probe(traceFsPath);
- if (tracing_on.startsWith("0")) return false;
- if (tracing_on.startsWith("1")) return true;
-
- // fallback to debugfs
- LogUtil.CLog.d("Unexpected state for %s = %s. Falling back to debugfs", traceFsPath,
- tracing_on);
-
- final String debugFsPath = "/sys/kernel/debug/tracing/tracing_on";
- tracing_on = probe(debugFsPath);
- if (tracing_on.startsWith("0")) return false;
- if (tracing_on.startsWith("1")) return true;
- throw new Exception(String.format("Unexpected state for %s = %s", traceFsPath, tracing_on));
- }
-
- protected static StatsdConfig.Builder createConfigBuilder() {
- return StatsdConfig.newBuilder()
- .setId(CONFIG_ID)
- .addAllowedLogSource("AID_SYSTEM")
- .addAllowedLogSource("AID_BLUETOOTH")
- // TODO(b/134091167): Fix bluetooth source name issue in Auto platform.
- .addAllowedLogSource("com.android.bluetooth")
- .addAllowedLogSource("AID_LMKD")
- .addAllowedLogSource("AID_RADIO")
- .addAllowedLogSource("AID_ROOT")
- .addAllowedLogSource("AID_STATSD")
- .addAllowedLogSource("com.android.systemui")
- .addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE)
- .addDefaultPullPackages("AID_RADIO")
- .addDefaultPullPackages("AID_SYSTEM")
- .addWhitelistedAtomIds(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
- }
-
- protected void createAndUploadConfig(int atomTag) throws Exception {
- StatsdConfig.Builder conf = createConfigBuilder();
- addAtomEvent(conf, atomTag);
- uploadConfig(conf);
- }
-
- protected void uploadConfig(StatsdConfig.Builder config) throws Exception {
- uploadConfig(config.build());
- }
-
- protected void uploadConfig(StatsdConfig config) throws Exception {
- LogUtil.CLog.d("Uploading the following config:\n" + config.toString());
- File configFile = File.createTempFile("statsdconfig", ".config");
- configFile.deleteOnExit();
- Files.write(config.toByteArray(), configFile);
- String remotePath = "/data/local/tmp/" + configFile.getName();
- getDevice().pushFile(configFile, remotePath);
- getDevice().executeShellCommand(
- String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
- String.valueOf(SHELL_UID), String.valueOf(CONFIG_ID)));
- getDevice().executeShellCommand("rm " + remotePath);
- }
-
- protected void removeConfig(long configId) throws Exception {
- getDevice().executeShellCommand(
- String.join(" ", REMOVE_CONFIG_CMD,
- String.valueOf(SHELL_UID), String.valueOf(configId)));
- }
-
- /** Gets the statsd report and sorts it. Note that this also deletes that report from statsd. */
- protected List<EventMetricData> getEventMetricDataList() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- return getEventMetricDataList(reportList);
- }
-
- /**
- * Gets a List of sorted ConfigMetricsReports from ConfigMetricsReportList.
- */
- protected List<ConfigMetricsReport> getSortedConfigMetricsReports(
- ConfigMetricsReportList configMetricsReportList) {
- return configMetricsReportList.getReportsList().stream()
- .sorted(Comparator.comparing(ConfigMetricsReport::getCurrentReportWallClockNanos))
- .collect(Collectors.toList());
- }
-
- /**
- * Extracts and sorts the EventMetricData from the given ConfigMetricsReportList (which must
- * contain a single report).
- */
- protected List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
- throws Exception {
- assertThat(reportList.getReportsCount()).isEqualTo(1);
- ConfigMetricsReport report = reportList.getReports(0);
-
- List<EventMetricData> data = new ArrayList<>();
- for (StatsLogReport metric : report.getMetricsList()) {
- for (EventMetricData metricData :
- metric.getEventMetrics().getDataList()) {
- if (metricData.hasAtom()) {
- data.add(metricData);
- } else {
- data.addAll(backfillAggregatedAtomsInEventMetric(metricData));
- }
- }
- }
- data.sort(Comparator.comparing(EventMetricData::getElapsedTimestampNanos));
-
- LogUtil.CLog.d("Get EventMetricDataList as following:\n");
- for (EventMetricData d : data) {
- LogUtil.CLog.d("Atom at " + d.getElapsedTimestampNanos() + ":\n" + d.getAtom().toString());
- }
- return data;
- }
-
- protected List<Atom> getGaugeMetricDataList() throws Exception {
- return getGaugeMetricDataList(/*checkTimestampTruncated=*/false);
- }
-
- protected List<Atom> getGaugeMetricDataList(boolean checkTimestampTruncated) throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- assertThat(reportList.getReportsCount()).isEqualTo(1);
-
- // only config
- ConfigMetricsReport report = reportList.getReports(0);
- assertThat(report.getMetricsCount()).isEqualTo(1);
-
- List<Atom> data = new ArrayList<>();
- for (GaugeMetricData gaugeMetricData :
- report.getMetrics(0).getGaugeMetrics().getDataList()) {
- assertThat(gaugeMetricData.getBucketInfoCount()).isEqualTo(1);
- GaugeBucketInfo bucketInfo = gaugeMetricData.getBucketInfo(0);
- if (bucketInfo.getAtomCount() != 0) {
- for (Atom atom : bucketInfo.getAtomList()) {
- data.add(atom);
- }
- } else {
- backFillGaugeBucketAtoms(bucketInfo.getAggregatedAtomInfoList());
- }
- if (checkTimestampTruncated) {
- for (long timestampNs : bucketInfo.getElapsedTimestampNanosList()) {
- assertTimestampIsTruncated(timestampNs);
- }
- }
- }
-
- LogUtil.CLog.d("Get GaugeMetricDataList as following:\n");
- for (Atom d : data) {
- LogUtil.CLog.d("Atom:\n" + d.toString());
- }
- return data;
- }
-
- private List<Atom> backFillGaugeBucketAtoms(
- List<StatsLog.AggregatedAtomInfo> atomInfoList) {
- List<Pair<Atom, Long>> atomTimestamp = new ArrayList<>();
- for (StatsLog.AggregatedAtomInfo atomInfo : atomInfoList) {
- for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) {
- atomTimestamp.add(Pair.create(atomInfo.getAtom(), timestampNs));
- }
- }
- atomTimestamp.sort(Comparator.comparing(o -> o.second));
- return atomTimestamp.stream().map(p -> p.first).collect(Collectors.toList());
- }
-
- protected GaugeMetricDataWrapper backfillGaugeMetricData(GaugeMetricDataWrapper dataWrapper) {
- GaugeMetricDataWrapper.Builder dataWrapperBuilder = dataWrapper.toBuilder();
- List<GaugeMetricData> backfilledMetricData = new ArrayList<>();
- for (GaugeMetricData gaugeMetricData : dataWrapperBuilder.getDataList()) {
- GaugeMetricData.Builder gaugeMetricDataBuilder = gaugeMetricData.toBuilder();
- List<GaugeBucketInfo> backfilledBuckets = new ArrayList<>();
- for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) {
- backfilledBuckets.add(backfillGaugeBucket(bucketInfo.toBuilder()));
- }
- gaugeMetricDataBuilder.clearBucketInfo();
- gaugeMetricDataBuilder.addAllBucketInfo(backfilledBuckets);
- backfilledMetricData.add(gaugeMetricDataBuilder.build());
- }
- dataWrapperBuilder.clearData();
- dataWrapperBuilder.addAllData(backfilledMetricData);
- return dataWrapperBuilder.build();
- }
-
- private GaugeBucketInfo backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder) {
- if (bucketInfoBuilder.getAtomCount() != 0) {
- return bucketInfoBuilder.build();
- }
- List<Pair<Atom, Long>> atomTimestampData = new ArrayList<>();
- for (StatsLog.AggregatedAtomInfo atomInfo : bucketInfoBuilder.getAggregatedAtomInfoList()) {
- for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) {
- atomTimestampData.add(Pair.create(atomInfo.getAtom(), timestampNs));
- }
- }
- atomTimestampData.sort(Comparator.comparing(o -> o.second));
- bucketInfoBuilder.clearAggregatedAtomInfo();
- for (Pair<Atom, Long> atomTimestamp : atomTimestampData) {
- bucketInfoBuilder.addAtom(atomTimestamp.first);
- bucketInfoBuilder.addElapsedTimestampNanos(atomTimestamp.second);
- }
- return bucketInfoBuilder.build();
- }
-
- /**
- * Gets the statsd report and extract duration metric data.
- * Note that this also deletes that report from statsd.
- */
- protected List<DurationMetricData> getDurationMetricDataList() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- assertThat(reportList.getReportsCount()).isEqualTo(1);
- ConfigMetricsReport report = reportList.getReports(0);
-
- List<DurationMetricData> data = new ArrayList<>();
- for (StatsLogReport metric : report.getMetricsList()) {
- data.addAll(metric.getDurationMetrics().getDataList());
- }
-
- LogUtil.CLog.d("Got DurationMetricDataList as following:\n");
- for (DurationMetricData d : data) {
- LogUtil.CLog.d("Duration " + d);
- }
- return data;
- }
-
- /**
- * Gets the statsd report and extract count metric data.
- * Note that this also deletes that report from statsd.
- */
- protected List<CountMetricData> getCountMetricDataList() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- assertThat(reportList.getReportsCount()).isEqualTo(1);
- ConfigMetricsReport report = reportList.getReports(0);
-
- List<CountMetricData> data = new ArrayList<>();
- for (StatsLogReport metric : report.getMetricsList()) {
- data.addAll(metric.getCountMetrics().getDataList());
- }
-
- LogUtil.CLog.d("Got CountMetricDataList as following:\n");
- for (CountMetricData d : data) {
- LogUtil.CLog.d("Count " + d);
- }
- return data;
- }
-
- /**
- * Gets the statsd report and extract value metric data.
- * Note that this also deletes that report from statsd.
- */
- protected List<ValueMetricData> getValueMetricDataList() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- assertThat(reportList.getReportsCount()).isEqualTo(1);
- ConfigMetricsReport report = reportList.getReports(0);
-
- List<ValueMetricData> data = new ArrayList<>();
- for (StatsLogReport metric : report.getMetricsList()) {
- data.addAll(metric.getValueMetrics().getDataList());
- }
-
- LogUtil.CLog.d("Got ValueMetricDataList as following:\n");
- for (ValueMetricData d : data) {
- LogUtil.CLog.d("Value " + d);
- }
- return data;
- }
-
- protected StatsLogReport getStatsLogReport() throws Exception {
- ConfigMetricsReport report = getConfigMetricsReport();
- assertThat(report.hasUidMap()).isTrue();
- assertThat(report.getMetricsCount()).isEqualTo(1);
- return report.getMetrics(0);
- }
-
- protected ConfigMetricsReport getConfigMetricsReport() throws Exception {
- ConfigMetricsReportList reportList = getReportList();
- assertThat(reportList.getReportsCount()).isEqualTo(1);
- return reportList.getReports(0);
- }
-
- /** Gets the statsd report. Note that this also deletes that report from statsd. */
- protected ConfigMetricsReportList getReportList() throws Exception {
- try {
- ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
- String.join(" ", DUMP_REPORT_CMD, String.valueOf(SHELL_UID),
- String.valueOf(CONFIG_ID), "--include_current_bucket", "--proto"));
- return reportList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to fetch and parse the statsd output report. "
- + "Perhaps there is not a valid statsd config for the requested "
- + "uid=" + getHostUid() + ", id=" + CONFIG_ID + ".");
- throw (e);
- }
- }
-
- protected BatteryStatsProto getBatteryStatsProto() throws Exception {
- try {
- BatteryStatsProto batteryStatsProto = getDump(BatteryStatsServiceDumpProto.parser(),
- String.join(" ", DUMP_BATTERYSTATS_CMD,
- "--proto")).getBatterystats();
- LogUtil.CLog.d("Got batterystats:\n " + batteryStatsProto.toString());
- return batteryStatsProto;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dump batterystats proto");
- throw (e);
- }
- }
-
- /** Gets reports from the statsd data incident section from the stats dumpsys. */
- protected List<ConfigMetricsReportList> getReportsFromStatsDataDumpProto() throws Exception {
- try {
- StatsDataDumpProto statsProto = getDump(StatsDataDumpProto.parser(),
- String.join(" ", DUMPSYS_STATS_CMD, "--proto"));
- // statsProto holds repeated bytes, which we must parse into ConfigMetricsReportLists.
- List<ConfigMetricsReportList> reports
- = new ArrayList<>(statsProto.getConfigMetricsReportListCount());
- for (ByteString reportListBytes : statsProto.getConfigMetricsReportListList()) {
- reports.add(ConfigMetricsReportList.parseFrom(reportListBytes));
- }
- LogUtil.CLog.d("Got dumpsys stats output:\n " + reports.toString());
- return reports;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dumpsys stats proto");
- throw (e);
- }
- }
-
- protected List<ProcessStatsProto> getProcStatsProto() throws Exception {
- try {
-
- List<ProcessStatsProto> processStatsProtoList =
- new ArrayList<ProcessStatsProto>();
- android.service.procstats.ProcessStatsSectionProto sectionProto = getDump(
- ProcessStatsServiceDumpProto.parser(),
- String.join(" ", DUMP_PROCSTATS_CMD,
- "--proto")).getProcstatsNow();
- for (android.service.procstats.ProcessStatsProto stats :
- sectionProto.getProcessStatsList()) {
- ProcessStatsProto procStats = ProcessStatsProto.parser().parseFrom(
- stats.toByteArray());
- processStatsProtoList.add(procStats);
- }
- LogUtil.CLog.d("Got procstats:\n ");
- for (ProcessStatsProto processStatsProto : processStatsProtoList) {
- LogUtil.CLog.d(processStatsProto.toString());
- }
- return processStatsProtoList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dump procstats proto");
- throw (e);
- }
- }
-
- /*
- * Get all procstats package data in proto
- */
- protected List<ProcessStatsPackageProto> getAllProcStatsProto() throws Exception {
- try {
- android.service.procstats.ProcessStatsSectionProto sectionProto = getDump(
- ProcessStatsServiceDumpProto.parser(),
- String.join(" ", DUMP_PROCSTATS_CMD,
- "--proto")).getProcstatsOver24Hrs();
- List<ProcessStatsPackageProto> processStatsProtoList =
- new ArrayList<ProcessStatsPackageProto>();
- for (android.service.procstats.ProcessStatsPackageProto pkgStast :
- sectionProto.getPackageStatsList()) {
- ProcessStatsPackageProto pkgAtom =
- ProcessStatsPackageProto.parser().parseFrom(pkgStast.toByteArray());
- processStatsProtoList.add(pkgAtom);
- }
- LogUtil.CLog.d("Got procstats:\n ");
- for (ProcessStatsPackageProto processStatsProto : processStatsProtoList) {
- LogUtil.CLog.d(processStatsProto.toString());
- }
- return processStatsProtoList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dump procstats proto");
- throw (e);
- }
- }
-
- /*
- * Get all processes' procstats statsd data in proto
- */
- protected List<android.service.procstats.ProcessStatsProto> getAllProcStatsProtoForStatsd()
- throws Exception {
- try {
- android.service.procstats.ProcessStatsSectionProto sectionProto = getDump(
- android.service.procstats.ProcessStatsSectionProto.parser(),
- String.join(" ", DUMP_PROCSTATS_CMD,
- "--statsd"));
- List<android.service.procstats.ProcessStatsProto> processStatsProtoList
- = sectionProto.getProcessStatsList();
- LogUtil.CLog.d("Got procstats:\n ");
- for (android.service.procstats.ProcessStatsProto processStatsProto
- : processStatsProtoList) {
- LogUtil.CLog.d(processStatsProto.toString());
- }
- return processStatsProtoList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dump procstats proto");
- throw (e);
- }
- }
-
- protected boolean hasBattery() throws Exception {
- try {
- BatteryServiceDumpProto batteryProto = getDump(BatteryServiceDumpProto.parser(),
- String.join(" ", DUMP_BATTERY_CMD, "--proto"));
- LogUtil.CLog.d("Got battery service dump:\n " + batteryProto.toString());
- return batteryProto.getIsPresent();
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- LogUtil.CLog.e("Failed to dump batteryservice proto");
- throw (e);
- }
- }
-
- /** Creates a FieldValueMatcher.Builder corresponding to the given field. */
- protected static FieldValueMatcher.Builder createFvm(int field) {
- return FieldValueMatcher.newBuilder().setField(field);
- }
-
- protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception {
- addAtomEvent(conf, atomTag, new ArrayList<FieldValueMatcher.Builder>());
- }
-
- /**
- * Adds an event to the config for an atom that matches the given key.
- *
- * @param conf configuration
- * @param atomTag atom tag (from atoms.proto)
- * @param fvm FieldValueMatcher.Builder for the relevant key
- */
- protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag,
- FieldValueMatcher.Builder fvm)
- throws Exception {
- addAtomEvent(conf, atomTag, Arrays.asList(fvm));
- }
-
- /**
- * Adds an event to the config for an atom that matches the given keys.
- *
- * @param conf configuration
- * @param atomId atom tag (from atoms.proto)
- * @param fvms list of FieldValueMatcher.Builders to attach to the atom. May be null.
- */
- protected void addAtomEvent(StatsdConfig.Builder conf, int atomId,
- List<FieldValueMatcher.Builder> fvms) throws Exception {
-
- final String atomName = "Atom" + System.nanoTime();
- final String eventName = "Event" + System.nanoTime();
-
- SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
- if (fvms != null) {
- for (FieldValueMatcher.Builder fvm : fvms) {
- sam.addFieldValueMatcher(fvm);
- }
- }
- conf.addAtomMatcher(AtomMatcher.newBuilder()
- .setId(atomName.hashCode())
- .setSimpleAtomMatcher(sam));
- conf.addEventMetric(EventMetric.newBuilder()
- .setId(eventName.hashCode())
- .setWhat(atomName.hashCode()));
- }
-
- /**
- * Adds an atom to a gauge metric of a config
- *
- * @param conf configuration
- * @param atomId atom id (from atoms.proto)
- * @param gaugeMetric the gauge metric to add
- */
- protected void addGaugeAtom(StatsdConfig.Builder conf, int atomId,
- GaugeMetric.Builder gaugeMetric) throws Exception {
- final String atomName = "Atom" + System.nanoTime();
- final String gaugeName = "Gauge" + System.nanoTime();
- final String predicateName = "APP_BREADCRUMB";
- SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
- conf.addAtomMatcher(AtomMatcher.newBuilder()
- .setId(atomName.hashCode())
- .setSimpleAtomMatcher(sam));
- final String predicateTrueName = "APP_BREADCRUMB_1";
- final String predicateFalseName = "APP_BREADCRUMB_2";
- conf.addAtomMatcher(AtomMatcher.newBuilder()
- .setId(predicateTrueName.hashCode())
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addFieldValueMatcher(FieldValueMatcher.newBuilder()
- .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
- .setEqInt(1)
- )
- )
- )
- // Used to trigger predicate
- .addAtomMatcher(AtomMatcher.newBuilder()
- .setId(predicateFalseName.hashCode())
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addFieldValueMatcher(FieldValueMatcher.newBuilder()
- .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
- .setEqInt(2)
- )
- )
- );
- conf.addPredicate(Predicate.newBuilder()
- .setId(predicateName.hashCode())
- .setSimplePredicate(SimplePredicate.newBuilder()
- .setStart(predicateTrueName.hashCode())
- .setStop(predicateFalseName.hashCode())
- .setCountNesting(false)
- )
- );
- gaugeMetric
- .setId(gaugeName.hashCode())
- .setWhat(atomName.hashCode())
- .setCondition(predicateName.hashCode());
- conf.addGaugeMetric(gaugeMetric.build());
- }
-
- /**
- * Adds an atom to a gauge metric of a config
- *
- * @param conf configuration
- * @param atomId atom id (from atoms.proto)
- * @param dimension dimension is needed for most pulled atoms
- */
- protected void addGaugeAtomWithDimensions(StatsdConfig.Builder conf, int atomId,
- @Nullable FieldMatcher.Builder dimension) throws Exception {
- GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
- .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
- .setSamplingType(GaugeMetric.SamplingType.CONDITION_CHANGE_TO_TRUE)
- .setMaxNumGaugeAtomsPerBucket(10000)
- .setBucket(TimeUnit.CTS);
- if (dimension != null) {
- gaugeMetric.setDimensionsInWhat(dimension.build());
- }
- addGaugeAtom(conf, atomId, gaugeMetric);
- }
-
- /**
- * Asserts that each set of states in stateSets occurs at least once in data.
- * Asserts that the states in data occur in the same order as the sets in stateSets.
- *
- * @param stateSets A list of set of states, where each set represents an equivalent
- * state of the device for the purpose of CTS.
- * @param data list of EventMetricData from statsd, produced by
- * getReportMetricListData()
- * @param wait expected duration (in ms) between state changes; asserts that the
- * actual wait
- * time was wait/2 <= actual_wait <= 5*wait. Use 0 to ignore this
- * assertion.
- * @param getStateFromAtom expression that takes in an Atom and returns the state it contains
- */
- public void assertStatesOccurred(List<Set<Integer>> stateSets, List<EventMetricData> data,
- int wait, Function<Atom, Integer> getStateFromAtom) {
- // Sometimes, there are more events than there are states.
- // Eg: When the screen turns off, it may go into OFF and then DOZE immediately.
- assertWithMessage("Too few states found").that(data.size()).isAtLeast(stateSets.size());
- int stateSetIndex = 0; // Tracks which state set we expect the data to be in.
- for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
- Atom atom = data.get(dataIndex).getAtom();
- int state = getStateFromAtom.apply(atom);
- // If state is in the current state set, we do not assert anything.
- // If it is not, we expect to have transitioned to the next state set.
- if (stateSets.get(stateSetIndex).contains(state)) {
- // No need to assert anything. Just log it.
- LogUtil.CLog.i("The following atom at dataIndex=" + dataIndex + " is "
- + "in stateSetIndex " + stateSetIndex + ":\n"
- + data.get(dataIndex).getAtom().toString());
- } else {
- stateSetIndex += 1;
- LogUtil.CLog.i("Assert that the following atom at dataIndex=" + dataIndex + " is"
- + " in stateSetIndex " + stateSetIndex + ":\n"
- + data.get(dataIndex).getAtom().toString());
- assertWithMessage("Missed first state").that(dataIndex).isNotEqualTo(0);
- assertWithMessage("Too many states").that(stateSetIndex)
- .isLessThan(stateSets.size());
- assertWithMessage(String.format("Is in wrong state (%d)", state))
- .that(stateSets.get(stateSetIndex)).contains(state);
- if (wait > 0) {
- assertTimeDiffBetween(data.get(dataIndex - 1), data.get(dataIndex),
- wait / 2, wait * 5);
- }
- }
- }
- assertWithMessage("Too few states").that(stateSetIndex).isEqualTo(stateSets.size() - 1);
- }
-
- /**
- * Removes all elements from data prior to the first occurrence of an element of state. After
- * this method is called, the first element of data (if non-empty) is guaranteed to be an
- * element in state.
- *
- * @param getStateFromAtom expression that takes in an Atom and returns the state it contains
- */
- public void popUntilFind(List<EventMetricData> data, Set<Integer> state,
- Function<Atom, Integer> getStateFromAtom) {
- int firstStateIdx;
- for (firstStateIdx = 0; firstStateIdx < data.size(); firstStateIdx++) {
- Atom atom = data.get(firstStateIdx).getAtom();
- if (state.contains(getStateFromAtom.apply(atom))) {
- break;
- }
- }
- if (firstStateIdx == 0) {
- // First first element already is in state, so there's nothing to do.
- return;
- }
- data.subList(0, firstStateIdx).clear();
- }
-
- /**
- * Removes all elements from data after to the last occurrence of an element of state. After
- * this method is called, the last element of data (if non-empty) is guaranteed to be an
- * element in state.
- *
- * @param getStateFromAtom expression that takes in an Atom and returns the state it contains
- */
- public void popUntilFindFromEnd(List<EventMetricData> data, Set<Integer> state,
- Function<Atom, Integer> getStateFromAtom) {
- int lastStateIdx;
- for (lastStateIdx = data.size() - 1; lastStateIdx >= 0; lastStateIdx--) {
- Atom atom = data.get(lastStateIdx).getAtom();
- if (state.contains(getStateFromAtom.apply(atom))) {
- break;
- }
- }
- if (lastStateIdx == data.size()-1) {
- // Last element already is in state, so there's nothing to do.
- return;
- }
- data.subList(lastStateIdx+1, data.size()).clear();
- }
-
- /** Returns the UID of the host, which should always either be SHELL (2000). */
- protected int getHostUid() throws DeviceNotAvailableException {
- return SHELL_UID;
- }
-
- protected String getProperty(String prop) throws Exception {
- return getDevice().executeShellCommand("getprop " + prop).replace("\n", "");
- }
-
- protected void turnScreenOn() throws Exception {
- getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
- getDevice().executeShellCommand("wm dismiss-keyguard");
- }
-
- protected void turnScreenOff() throws Exception {
- getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
- }
-
- protected void setChargingState(int state) throws Exception {
- getDevice().executeShellCommand("cmd battery set status " + state);
- }
-
- protected void unplugDevice() throws Exception {
- // On batteryless devices on Android P or above, the 'unplug' command
- // alone does not simulate the really unplugged state.
- //
- // This is because charging state is left as "unknown". Unless a valid
- // state like 3 = BatteryManager.BATTERY_STATUS_DISCHARGING is set,
- // framework does not consider the device as running on battery.
- setChargingState(3);
-
- getDevice().executeShellCommand("cmd battery unplug");
- }
-
- protected void plugInAc() throws Exception {
- getDevice().executeShellCommand("cmd battery set ac 1");
- }
-
- protected void plugInUsb() throws Exception {
- getDevice().executeShellCommand("cmd battery set usb 1");
- }
-
- protected void plugInWireless() throws Exception {
- getDevice().executeShellCommand("cmd battery set wireless 1");
- }
-
- protected void enableLooperStats() throws Exception {
- getDevice().executeShellCommand("cmd looper_stats enable");
- }
-
- protected void resetLooperStats() throws Exception {
- getDevice().executeShellCommand("cmd looper_stats reset");
- }
-
- protected void disableLooperStats() throws Exception {
- getDevice().executeShellCommand("cmd looper_stats disable");
- }
-
- protected void enableBinderStats() throws Exception {
- getDevice().executeShellCommand("dumpsys binder_calls_stats --enable");
- }
-
- protected void resetBinderStats() throws Exception {
- getDevice().executeShellCommand("dumpsys binder_calls_stats --reset");
- }
-
- protected void disableBinderStats() throws Exception {
- getDevice().executeShellCommand("dumpsys binder_calls_stats --disable");
- }
-
- protected void binderStatsNoSampling() throws Exception {
- getDevice().executeShellCommand("dumpsys binder_calls_stats --no-sampling");
- }
-
- protected void setUpLooperStats() throws Exception {
- getDevice().executeShellCommand("cmd looper_stats enable");
- getDevice().executeShellCommand("cmd looper_stats sampling_interval 1");
- getDevice().executeShellCommand("cmd looper_stats reset");
- }
-
- protected void cleanUpLooperStats() throws Exception {
- getDevice().executeShellCommand("cmd looper_stats disable");
- }
-
- public void setAppBreadcrumbPredicate() throws Exception {
- doAppBreadcrumbReportedStart(1);
- }
-
- public void clearAppBreadcrumbPredicate() throws Exception {
- doAppBreadcrumbReportedStart(2);
- }
-
- public void doAppBreadcrumbReportedStart(int label) throws Exception {
- doAppBreadcrumbReported(label, AppBreadcrumbReported.State.START.ordinal());
- }
-
- public void doAppBreadcrumbReportedStop(int label) throws Exception {
- doAppBreadcrumbReported(label, AppBreadcrumbReported.State.STOP.ordinal());
- }
-
- public void doAppBreadcrumbReported(int label) throws Exception {
- doAppBreadcrumbReported(label, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
- }
-
- public void doAppBreadcrumbReported(int label, int state) throws Exception {
- getDevice().executeShellCommand(String.format(
- "cmd stats log-app-breadcrumb %d %d %d", SHELL_UID, label, state));
- }
-
- protected void setBatteryLevel(int level) throws Exception {
- getDevice().executeShellCommand("cmd battery set level " + level);
- }
-
- protected void resetBatteryStatus() throws Exception {
- getDevice().executeShellCommand("cmd battery reset");
- }
-
- protected int getScreenBrightness() throws Exception {
- return Integer.parseInt(
- getDevice().executeShellCommand("settings get system screen_brightness").trim());
- }
-
- protected void setScreenBrightness(int brightness) throws Exception {
- getDevice().executeShellCommand("settings put system screen_brightness " + brightness);
- }
-
- // Gets whether "Always on Display" setting is enabled.
- // In rare cases, this is different from whether the device can enter SCREEN_STATE_DOZE.
- protected String getAodState() throws Exception {
- return getDevice().executeShellCommand("settings get secure doze_always_on");
- }
-
- protected void setAodState(String state) throws Exception {
- getDevice().executeShellCommand("settings put secure doze_always_on " + state);
- }
-
- protected boolean isScreenBrightnessModeManual() throws Exception {
- String mode = getDevice().executeShellCommand("settings get system screen_brightness_mode");
- return Integer.parseInt(mode.trim()) == 0;
- }
-
- protected void setScreenBrightnessMode(boolean manual) throws Exception {
- getDevice().executeShellCommand(
- "settings put system screen_brightness_mode " + (manual ? 0 : 1));
- }
-
- protected void enterDozeModeLight() throws Exception {
- getDevice().executeShellCommand("dumpsys deviceidle force-idle light");
- }
-
- protected void enterDozeModeDeep() throws Exception {
- getDevice().executeShellCommand("dumpsys deviceidle force-idle deep");
- }
-
- protected void leaveDozeMode() throws Exception {
- getDevice().executeShellCommand("dumpsys deviceidle unforce");
- getDevice().executeShellCommand("dumpsys deviceidle disable");
- getDevice().executeShellCommand("dumpsys deviceidle enable");
- }
-
- protected void turnBatterySaverOn() throws Exception {
- unplugDevice();
- getDevice().executeShellCommand("settings put global low_power 1");
- }
-
- protected void turnBatterySaverOff() throws Exception {
- getDevice().executeShellCommand("settings put global low_power 0");
- getDevice().executeShellCommand("cmd battery reset");
- }
-
- protected void turnBatteryStatsAutoResetOn() throws Exception {
- getDevice().executeShellCommand("dumpsys batterystats enable no-auto-reset");
- }
-
- protected void turnBatteryStatsAutoResetOff() throws Exception {
- getDevice().executeShellCommand("dumpsys batterystats enable no-auto-reset");
- }
-
- protected void flushBatteryStatsHandlers() throws Exception {
- // Dumping batterystats will flush everything in the batterystats handler threads.
- getDevice().executeShellCommand(DUMP_BATTERYSTATS_CMD);
- }
-
- protected void rebootDevice() throws Exception {
- getDevice().rebootUntilOnline();
- }
-
- /**
- * Asserts that the two events are within the specified range of each other.
- *
- * @param d0 the event that should occur first
- * @param d1 the event that should occur second
- * @param minDiffMs d0 should precede d1 by at least this amount
- * @param maxDiffMs d0 should precede d1 by at most this amount
- */
- public static void assertTimeDiffBetween(EventMetricData d0, EventMetricData d1,
- int minDiffMs, int maxDiffMs) {
- long diffMs = (d1.getElapsedTimestampNanos() - d0.getElapsedTimestampNanos()) / 1_000_000;
- assertWithMessage("Illegal time difference")
- .that(diffMs).isIn(Range.closed((long) minDiffMs, (long) maxDiffMs));
- }
-
- protected String getCurrentLogcatDate() throws Exception {
- // TODO: Do something more robust than this for getting logcat markers.
- long timestampMs = getDevice().getDeviceDate();
- return new SimpleDateFormat("MM-dd HH:mm:ss.SSS")
- .format(new Date(timestampMs));
- }
-
- protected String getLogcatSince(String date, String logcatParams) throws Exception {
- return getDevice().executeShellCommand(String.format(
- "logcat -v threadtime -t '%s' -d %s", date, logcatParams));
- }
-
- // TODO: Remove this and migrate all usages to createConfigBuilder()
- protected StatsdConfig.Builder getPulledConfig() {
- return createConfigBuilder();
- }
- /**
- * Determines if the device has the given feature.
- * Prints a warning if its value differs from requiredAnswer.
- */
- protected boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception {
- final String features = getDevice().executeShellCommand("pm list features");
- StringTokenizer featureToken = new StringTokenizer(features, "\n");
- boolean hasIt = false;
-
- while (featureToken.hasMoreTokens()) {
- if (("feature:" + featureName).equals(featureToken.nextToken())) {
- hasIt = true;
- break;
- }
- }
-
- if (hasIt != requiredAnswer) {
- LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature "
- + featureName);
- }
- return hasIt == requiredAnswer;
- }
-
- /**
- * Determines if the device has |file|.
- */
- protected boolean doesFileExist(String file) throws Exception {
- return getDevice().doesFileExist(file);
- }
-
- protected void turnOnAirplaneMode() throws Exception {
- getDevice().executeShellCommand("cmd connectivity airplane-mode enable");
- }
-
- protected void turnOffAirplaneMode() throws Exception {
- getDevice().executeShellCommand("cmd connectivity airplane-mode disable");
- }
-
- /**
- * Returns a list of fields and values for {@code className} from {@link TelephonyDebugService}
- * output.
- *
- * <p>Telephony dumpsys output does not support proto at the moment. This method provides
- * limited support for parsing its output. Specifically, it does not support arrays or
- * multi-line values.
- */
- private List<Map<String, String>> getTelephonyDumpEntries(String className) throws Exception {
- // Matches any line with indentation, except for lines with only spaces
- Pattern indentPattern = Pattern.compile("^(\\s*)[^ ].*$");
- // Matches pattern for class, e.g. " Phone:"
- Pattern classNamePattern = Pattern.compile("^(\\s*)" + Pattern.quote(className) + ":.*$");
- // Matches pattern for key-value pairs, e.g. " mPhoneId=1"
- Pattern keyValuePattern = Pattern.compile("^(\\s*)([a-zA-Z]+[a-zA-Z0-9_]*)\\=(.+)$");
- String response =
- getDevice().executeShellCommand("dumpsys activity service TelephonyDebugService");
- Queue<String> responseLines = new LinkedList<>(Arrays.asList(response.split("[\\r\\n]+")));
-
- List<Map<String, String>> results = new ArrayList<>();
- while (responseLines.peek() != null) {
- Matcher matcher = classNamePattern.matcher(responseLines.poll());
- if (matcher.matches()) {
- final int classIndentLevel = matcher.group(1).length();
- final Map<String, String> instanceEntries = new HashMap<>();
- while (responseLines.peek() != null) {
- // Skip blank lines
- matcher = indentPattern.matcher(responseLines.peek());
- if (responseLines.peek().length() == 0 || !matcher.matches()) {
- responseLines.poll();
- continue;
- }
- // Finish (without consuming the line) if already parsed past this instance
- final int indentLevel = matcher.group(1).length();
- if (indentLevel <= classIndentLevel) {
- break;
- }
- // Parse key-value pair if it belongs to the instance directly
- matcher = keyValuePattern.matcher(responseLines.poll());
- if (indentLevel == classIndentLevel + 1 && matcher.matches()) {
- instanceEntries.put(matcher.group(2), matcher.group(3));
- }
- }
- results.add(instanceEntries);
- }
- }
- return results;
- }
-
- protected int getActiveSimSlotCount() throws Exception {
- List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
- long count = slots.stream().filter(slot -> "true".equals(slot.get("mActive"))).count();
- return Math.toIntExact(count);
- }
-
- /**
- * Returns the upper bound of active SIM profile count.
- *
- * <p>The value is an upper bound as eSIMs without profiles are also counted in.
- */
- protected int getActiveSimCountUpperBound() throws Exception {
- List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
- long count = slots.stream().filter(slot ->
- "true".equals(slot.get("mActive"))
- && "CARDSTATE_PRESENT".equals(slot.get("mCardState"))).count();
- return Math.toIntExact(count);
- }
-
- /**
- * Returns the upper bound of active eSIM profile count.
- *
- * <p>The value is an upper bound as eSIMs without profiles are also counted in.
- */
- protected int getActiveEsimCountUpperBound() throws Exception {
- List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
- long count = slots.stream().filter(slot ->
- "true".equals(slot.get("mActive"))
- && "CARDSTATE_PRESENT".equals(slot.get("mCardState"))
- && "true".equals(slot.get("mIsEuicc"))).count();
- return Math.toIntExact(count);
- }
-
- protected boolean hasGsmPhone() throws Exception {
- // Not using log entries or ServiceState in the dump since they may or may not be present,
- // which can make the test flaky
- return getTelephonyDumpEntries("Phone").stream()
- .anyMatch(phone ->
- String.format("%d", PHONE_TYPE_GSM).equals(phone.get("getPhoneType()")));
- }
-
- protected boolean hasCdmaPhone() throws Exception {
- // Not using log entries or ServiceState in the dump due to the same reason as hasGsmPhone()
- return getTelephonyDumpEntries("Phone").stream()
- .anyMatch(phone ->
- String.format("%d", PHONE_TYPE_CDMA).equals(phone.get("getPhoneType()"))
- || String.format("%d", PHONE_TYPE_CDMA_LTE)
- .equals(phone.get("getPhoneType()")));
- }
-
- // Checks that a timestamp has been truncated to be a multiple of 5 min
- protected void assertTimestampIsTruncated(long timestampNs) {
- long fiveMinutesInNs = NS_PER_SEC * 5 * 60;
- assertWithMessage("Timestamp is not truncated")
- .that(timestampNs % fiveMinutesInNs).isEqualTo(0);
- }
-
- protected List<EventMetricData> backfillAggregatedAtomsInEventMetric(
- EventMetricData metricData) {
- if (!metricData.hasAggregatedAtomInfo()) {
- return Collections.singletonList(metricData);
- }
- List<EventMetricData> data = new ArrayList<>();
- StatsLog.AggregatedAtomInfo atomInfo = metricData.getAggregatedAtomInfo();
- for (long timestamp : atomInfo.getElapsedTimestampNanosList()) {
- data.add(EventMetricData.newBuilder()
- .setAtom(atomInfo.getAtom())
- .setElapsedTimestampNanos(timestamp)
- .build());
- }
- return data;
- }
-}
diff --git a/tests/src/android/cts/statsd/atom/BaseTestCase.java b/tests/src/android/cts/statsd/atom/BaseTestCase.java
deleted file mode 100644
index 0c9921e..0000000
--- a/tests/src/android/cts/statsd/atom/BaseTestCase.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.cts.statsd.validation.ValidationTestUtil;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.CollectingByteOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestDescription;
-import com.android.tradefed.result.TestResult;
-import com.android.tradefed.result.TestRunResult;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.MessageLite;
-import com.google.protobuf.Parser;
-
-import java.io.FileNotFoundException;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-// Largely copied from incident's ProtoDumpTestCase
-public class BaseTestCase extends DeviceTestCase implements IBuildReceiver {
-
- protected IBuildInfo mCtsBuild;
-
- private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- assertThat(mCtsBuild).isNotNull();
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
- public IBuildInfo getBuild() {
- return mCtsBuild;
- }
-
- /**
- * Create and return {@link ValidationTestUtil} and give it the current build.
- */
- public ValidationTestUtil createValidationUtil() {
- ValidationTestUtil util = new ValidationTestUtil();
- util.setBuild(getBuild());
- return util;
- }
-
- /**
- * Call onto the device with an adb shell command and get the results of
- * that as a proto of the given type.
- *
- * @param parser A protobuf parser object. e.g. MyProto.parser()
- * @param command The adb shell command to run. e.g. "dumpsys fingerprint --proto"
- *
- * @throws DeviceNotAvailableException If there was a problem communicating with
- * the test device.
- * @throws InvalidProtocolBufferException If there was an error parsing
- * the proto. Note that a 0 length buffer is not necessarily an error.
- */
- public <T extends MessageLite> T getDump(Parser<T> parser, String command)
- throws DeviceNotAvailableException, InvalidProtocolBufferException {
- final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
- getDevice().executeShellCommand(command, receiver);
- if (false) {
- CLog.d("Command output while parsing " + parser.getClass().getCanonicalName()
- + " for command: " + command + "\n"
- + BufferDebug.debugString(receiver.getOutput(), -1));
- }
- try {
- return parser.parseFrom(receiver.getOutput());
- } catch (Exception ex) {
- CLog.d("Error parsing " + parser.getClass().getCanonicalName() + " for command: "
- + command
- + BufferDebug.debugString(receiver.getOutput(), 16384));
- throw ex;
- }
- }
-
- /**
- * Install a device side test package.
- *
- * @param appFileName Apk file name, such as "CtsNetStatsApp.apk".
- * @param grantPermissions whether to give runtime permissions.
- */
- protected void installPackage(String appFileName, boolean grantPermissions)
- throws FileNotFoundException, DeviceNotAvailableException {
- CLog.d("Installing app " + appFileName);
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- final String result = getDevice().installPackage(
- buildHelper.getTestFile(appFileName), true, grantPermissions);
- assertWithMessage(String.format("Failed to install %s: %s", appFileName, result))
- .that(result).isNull();
- }
-
- protected CompatibilityBuildHelper getBuildHelper() {
- return new CompatibilityBuildHelper(mCtsBuild);
- }
-
- /**
- * Run a device side test.
- *
- * @param pkgName Test package name, such as "com.android.server.cts.netstats".
- * @param testClassName Test class name; either a fully qualified name, or "." + a class name.
- * @param testMethodName Test method name.
- * @return {@link TestRunResult} of this invocation.
- * @throws DeviceNotAvailableException
- */
- @Nonnull
- protected TestRunResult runDeviceTests(@Nonnull String pkgName,
- @Nullable String testClassName, @Nullable String testMethodName)
- throws DeviceNotAvailableException {
- if (testClassName != null && testClassName.startsWith(".")) {
- testClassName = pkgName + testClassName;
- }
-
- RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
- pkgName, TEST_RUNNER, getDevice().getIDevice());
- if (testClassName != null && testMethodName != null) {
- testRunner.setMethodName(testClassName, testMethodName);
- } else if (testClassName != null) {
- testRunner.setClassName(testClassName);
- }
-
- CollectingTestListener listener = new CollectingTestListener();
- assertThat(getDevice().runInstrumentationTests(testRunner, listener)).isTrue();
-
- final TestRunResult result = listener.getCurrentRunResults();
- if (result.isRunFailure()) {
- throw new Error("Failed to successfully run device tests for "
- + result.getName() + ": " + result.getRunFailureMessage());
- }
- if (result.getNumTests() == 0) {
- throw new Error("No tests were run on the device");
- }
-
- if (result.hasFailedTests()) {
- // build a meaningful error message
- StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
- for (Map.Entry<TestDescription, TestResult> resultEntry :
- result.getTestResults().entrySet()) {
- if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
- errorBuilder.append(resultEntry.getKey().toString());
- errorBuilder.append(":\n");
- errorBuilder.append(resultEntry.getValue().getStackTrace());
- }
- }
- throw new AssertionError(errorBuilder.toString());
- }
-
- return result;
- }
-}
diff --git a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
deleted file mode 100644
index d641ebc..0000000
--- a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ /dev/null
@@ -1,338 +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.
- */
-package android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.MessageMatcher;
-import com.android.internal.os.StatsdConfigProto.Position;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.tradefed.log.LogUtil;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Base class for testing Statsd atoms that report a uid. Tests are performed via a device-side app.
- */
-public class DeviceAtomTestCase extends AtomTestCase {
-
- public static final String DEVICE_SIDE_TEST_APK = "CtsStatsdApp.apk";
- public static final String DEVICE_SIDE_TEST_PACKAGE =
- "com.android.server.cts.device.statsd";
- public static final long DEVICE_SIDE_TEST_PACKAGE_VERSION = 10;
- public static final String DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME =
- "com.android.server.cts.device.statsd.StatsdCtsForegroundService";
- private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT =
- "com.android.server.cts.device.statsd/.StatsdCtsBackgroundService";
- public static final long DEVICE_SIDE_TEST_PKG_HASH =
- Long.parseUnsignedLong("15694052924544098582");
-
- // Constants from device side tests (not directly accessible here).
- public static final String KEY_ACTION = "action";
- public static final String ACTION_LMK = "action.lmk";
-
- public static final String CONFIG_NAME = "cts_config";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- installTestApp();
- Thread.sleep(1000);
- }
-
- @Override
- protected void tearDown() throws Exception {
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- super.tearDown();
- }
-
- /**
- * Performs a device-side test by calling a method on the app and returns its stats events.
- * @param methodName the name of the method in the app's AtomTests to perform
- * @param atom atom tag (from atoms.proto)
- * @param key atom's field corresponding to state
- * @param stateOn 'on' value
- * @param stateOff 'off' value
- * @param minTimeDiffMs max allowed time between start and stop
- * @param maxTimeDiffMs min allowed time between start and stop
- * @param demandExactlyTwo whether there must be precisely two events logged (1 start, 1 stop)
- * @return list of events with the app's uid matching the configuration defined by the params.
- */
- protected List<EventMetricData> doDeviceMethodOnOff(
- String methodName, int atom, int key, int stateOn, int stateOff,
- int minTimeDiffMs, int maxTimeDiffMs, boolean demandExactlyTwo) throws Exception {
- StatsdConfig.Builder conf = createConfigBuilder();
- addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOn));
- addAtomEvent(conf, atom, createFvm(key).setEqInt(stateOff));
- List<EventMetricData> data = doDeviceMethod(methodName, conf);
-
- if (demandExactlyTwo) {
- assertThat(data).hasSize(2);
- } else {
- assertThat(data.size()).isAtLeast(2);
- }
- assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs);
- return data;
- }
-
- /**
- *
- * @param methodName the name of the method in the app's AtomTests to perform
- * @param cfg statsd configuration
- * @return list of events with the app's uid matching the configuration.
- */
- protected List<EventMetricData> doDeviceMethod(String methodName, StatsdConfig.Builder cfg)
- throws Exception {
- removeConfig(CONFIG_ID);
- getReportList(); // Clears previous data on disk.
- uploadConfig(cfg);
- int appUid = getUid();
- LogUtil.CLog.d("\nPerforming device-side test of " + methodName + " for uid " + appUid);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", methodName);
-
- return getEventMetricDataList();
- }
-
- protected void createAndUploadConfig(int atomTag, boolean useAttribution) throws Exception {
- StatsdConfig.Builder conf = createConfigBuilder();
- addAtomEvent(conf, atomTag, useAttribution);
- uploadConfig(conf);
- }
-
- /**
- * Adds an event to the config for an atom that matches the given key AND has the app's uid.
- * @param conf configuration
- * @param atomTag atom tag (from atoms.proto)
- * @param fvm FieldValueMatcher.Builder for the relevant key
- */
- @Override
- protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, FieldValueMatcher.Builder fvm)
- throws Exception {
-
- final int UID_KEY = 1;
- FieldValueMatcher.Builder fvmUid = createAttributionFvm(UID_KEY);
- addAtomEvent(conf, atomTag, Arrays.asList(fvm, fvmUid));
- }
-
- /**
- * Adds an event to the config for an atom that matches the app's uid.
- * @param conf configuration
- * @param atomTag atom tag (from atoms.proto)
- * @param useAttribution If true, the atom has a uid within an attribution node. Else, the atom
- * has a uid but not in an attribution node.
- */
- protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag,
- boolean useAttribution) throws Exception {
- final int UID_KEY = 1;
- FieldValueMatcher.Builder fvmUid;
- if (useAttribution) {
- fvmUid = createAttributionFvm(UID_KEY);
- } else {
- fvmUid = createFvm(UID_KEY).setEqString(DEVICE_SIDE_TEST_PACKAGE);
- }
- addAtomEvent(conf, atomTag, Arrays.asList(fvmUid));
- }
-
- /**
- * Creates a FieldValueMatcher for atoms that use AttributionNode
- */
- protected FieldValueMatcher.Builder createAttributionFvm(int field) {
- final int ATTRIBUTION_NODE_UID_KEY = 1;
- return createFvm(field).setPosition(Position.ANY)
- .setMatchesTuple(MessageMatcher.newBuilder()
- .addFieldValueMatcher(createFvm(ATTRIBUTION_NODE_UID_KEY)
- .setEqString(DEVICE_SIDE_TEST_PACKAGE)));
- }
-
- /**
- * Gets the uid of the test app.
- */
- protected int getUid() throws Exception {
- int currentUser = getDevice().getCurrentUser();
- final String packages = getDevice().executeShellCommand("cmd package list packages -U"
- + " --user " + currentUser + " " + DEVICE_SIDE_TEST_PACKAGE);
-
- // Split package list by lines
- // Sample packages response:
- // package:com.android.server.cts.device.statsd.host uid:1010033
- // package:com.android.server.cts.device.statsd uid:1010034
- final String[] lines = packages.split("[\\r\\n]+");
- for (final String line : lines) {
- if (line.startsWith("package:" + DEVICE_SIDE_TEST_PACKAGE + " ")) {
- final int uidIndex = line.lastIndexOf(":") + 1;
- final int uid = Integer.parseInt(line.substring(uidIndex).trim());
- assertThat(uid).isGreaterThan(10_000);
- return uid;
- }
- }
- throw new Error(
- String.format("Could not find installed package: %s", DEVICE_SIDE_TEST_PACKAGE));
- }
-
- /**
- * Installs the test apk.
- */
- protected void installTestApp() throws Exception {
- installPackage(DEVICE_SIDE_TEST_APK, true);
- LogUtil.CLog.i("Installing device-side test app with uid " + getUid());
- allowBackgroundServices();
- }
-
- /**
- * Uninstalls the test apk.
- */
- protected void uninstallPackage() throws Exception{
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- }
-
- /**
- * Required to successfully start a background service from adb in Android O.
- */
- protected void allowBackgroundServices() throws Exception {
- getDevice().executeShellCommand(String.format(
- "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
- }
-
- /**
- * Runs a (background) service to perform the given action.
- * @param actionValue the action code constants indicating the desired action to perform.
- */
- protected void executeBackgroundService(String actionValue) throws Exception {
- allowBackgroundServices();
- getDevice().executeShellCommand(String.format(
- "am startservice -n '%s' -e %s %s",
- DEVICE_SIDE_BG_SERVICE_COMPONENT,
- KEY_ACTION, actionValue));
- }
-
-
- /** Make the test app standby-active so it can run syncs and jobs immediately. */
- protected void allowImmediateSyncs() throws Exception {
- getDevice().executeShellCommand("am set-standby-bucket "
- + DEVICE_SIDE_TEST_PACKAGE + " active");
- }
-
- /**
- * Runs the specified activity.
- */
- protected void runActivity(String activity, String actionKey, String actionValue)
- throws Exception {
- runActivity(activity, actionKey, actionValue, WAIT_TIME_LONG);
- }
-
- /**
- * Runs the specified activity.
- */
- protected void runActivity(String activity, String actionKey, String actionValue,
- long waitTime) throws Exception {
- try (AutoCloseable a = withActivity(activity, actionKey, actionValue)) {
- Thread.sleep(waitTime);
- }
- }
-
- /**
- * Starts the specified activity and returns an {@link AutoCloseable} that stops the activity
- * when closed.
- *
- * <p>Example usage:
- * <pre>
- * try (AutoClosable a = withActivity("activity", "action", "action-value")) {
- * doStuff();
- * }
- * </pre>
- */
- protected AutoCloseable withActivity(String activity, String actionKey, String actionValue)
- throws Exception {
- String intentString = null;
- if (actionKey != null && actionValue != null) {
- intentString = actionKey + " " + actionValue;
- }
- if (intentString == null) {
- getDevice().executeShellCommand(
- "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity);
- } else {
- getDevice().executeShellCommand(
- "am start -n " + DEVICE_SIDE_TEST_PACKAGE + "/." + activity + " -e " +
- intentString);
- }
- return () -> {
- getDevice().executeShellCommand(
- "am force-stop " + DEVICE_SIDE_TEST_PACKAGE);
- Thread.sleep(WAIT_TIME_SHORT);
- };
- }
-
- protected void resetBatteryStats() throws Exception {
- getDevice().executeShellCommand("dumpsys batterystats --reset");
- }
-
- protected void clearProcStats() throws Exception {
- getDevice().executeShellCommand("dumpsys procstats --clear");
- }
-
- protected void startProcStatsTesting() throws Exception {
- getDevice().executeShellCommand("dumpsys procstats --start-testing");
- }
-
- protected void stopProcStatsTesting() throws Exception {
- getDevice().executeShellCommand("dumpsys procstats --stop-testing");
- }
-
- protected void commitProcStatsToDisk() throws Exception {
- getDevice().executeShellCommand("dumpsys procstats --commit");
- }
-
- protected void rebootDeviceAndWaitUntilReady() throws Exception {
- rebootDevice();
- // Wait for 5 mins.
- assertWithMessage("Device failed to boot")
- .that(getDevice().waitForBootComplete(300_000)).isTrue();
- assertWithMessage("Stats service failed to start")
- .that(waitForStatsServiceStart(60_000)).isTrue();
- Thread.sleep(2_000);
- }
-
- protected boolean waitForStatsServiceStart(final long waitTime) throws Exception {
- LogUtil.CLog.i("Waiting %d ms for stats service to start", waitTime);
- int counter = 1;
- long startTime = System.currentTimeMillis();
- while ((System.currentTimeMillis() - startTime) < waitTime) {
- if ("running".equals(getProperty("init.svc.statsd"))) {
- return true;
- }
- Thread.sleep(Math.min(200 * counter, 2_000));
- counter++;
- }
- LogUtil.CLog.w("Stats service did not start after %d ms", waitTime);
- return false;
- }
-
- boolean getNetworkStatsCombinedSubTypeEnabled() throws Exception {
- final String output = getDevice().executeShellCommand(
- "settings get global netstats_combine_subtype_enabled").trim();
- return output.equals("1");
- }
-
- void setNetworkStatsCombinedSubTypeEnabled(boolean enable) throws Exception {
- getDevice().executeShellCommand("settings put global netstats_combine_subtype_enabled "
- + (enable ? "1" : "0"));
- }
-}
diff --git a/tests/src/android/cts/statsd/atom/ProcStateTestCase.java b/tests/src/android/cts/statsd/atom/ProcStateTestCase.java
index 2fa4233..09c508b 100644
--- a/tests/src/android/cts/statsd/atom/ProcStateTestCase.java
+++ b/tests/src/android/cts/statsd/atom/ProcStateTestCase.java
@@ -15,10 +15,20 @@
*/
package android.cts.statsd.atom;
+import static com.google.common.truth.Truth.assertThat;
+
import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
import java.util.Arrays;
import java.util.HashSet;
@@ -31,46 +41,74 @@
/**
* Base class for manipulating process states
*/
-public class ProcStateTestCase extends DeviceAtomTestCase {
+public class ProcStateTestCase extends DeviceTestCase implements IBuildReceiver {
- private static final String TAG = "Statsd.ProcStateTestCase";
+ private static final String TAG = "Statsd.ProcStateTestCase";
- private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
- = "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity";
- private static final String DEVICE_SIDE_FG_SERVICE_COMPONENT
- = "com.android.server.cts.device.statsd/.StatsdCtsForegroundService";
+ private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+ = "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity";
+ private static final String DEVICE_SIDE_FG_SERVICE_COMPONENT
+ = "com.android.server.cts.device.statsd/.StatsdCtsForegroundService";
- // Constants from the device-side tests (not directly accessible here).
- public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
- public static final String ACTION_BACKGROUND_SLEEP = "action.background_sleep";
- public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
- public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
- public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+ private static final String KEY_ACTION = "action";
- // Sleep times (ms) that actions invoke device-side.
- public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
- public static final int SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP = 60_000;
- public static final int SLEEP_OF_ACTION_BACKGROUND_SLEEP = 2_000;
- public static final int SLEEP_OF_FOREGROUND_SERVICE = 2_000;
+ // Constants from the device-side tests (not directly accessible here).
+ public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
+ public static final String ACTION_BACKGROUND_SLEEP = "action.background_sleep";
+ public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
+ public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
+ public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+ // Sleep times (ms) that actions invoke device-side.
+ public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
+ public static final int SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP = 60_000;
+ public static final int SLEEP_OF_ACTION_BACKGROUND_SLEEP = 2_000;
+ public static final int SLEEP_OF_FOREGROUND_SERVICE = 2_000;
- /**
- * Runs an activity (in the foreground) to perform the given action.
- * @param actionValue the action code constants indicating the desired action to perform.
- */
- protected void executeForegroundActivity(String actionValue) throws Exception {
- getDevice().executeShellCommand(String.format(
- "am start -n '%s' -e %s %s",
- DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
- KEY_ACTION, actionValue));
- }
+ protected IBuildInfo mCtsBuild;
- /**
- * Runs a simple foreground service.
- */
- protected void executeForegroundService() throws Exception {
- executeForegroundActivity(ACTION_END_IMMEDIATELY);
- getDevice().executeShellCommand(String.format(
- "am startservice -n '%s'", DEVICE_SIDE_FG_SERVICE_COMPONENT));
- }
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ /**
+ * Runs an activity (in the foreground) to perform the given action.
+ *
+ * @param actionValue the action code constants indicating the desired action to perform.
+ */
+ protected void executeForegroundActivity(String actionValue) throws Exception {
+ getDevice().executeShellCommand(String.format(
+ "am start -n '%s' -e %s %s",
+ DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+ KEY_ACTION, actionValue));
+ }
+
+ /**
+ * Runs a simple foreground service.
+ */
+ protected void executeForegroundService() throws Exception {
+ executeForegroundActivity(ACTION_END_IMMEDIATELY);
+ getDevice().executeShellCommand(String.format(
+ "am startservice -n '%s'", DEVICE_SIDE_FG_SERVICE_COMPONENT));
+ }
}
diff --git a/tests/src/android/cts/statsd/metadata/MetadataTestCase.java b/tests/src/android/cts/statsd/metadata/MetadataTestCase.java
index 0ccb13c..1f0e5a1 100644
--- a/tests/src/android/cts/statsd/metadata/MetadataTestCase.java
+++ b/tests/src/android/cts/statsd/metadata/MetadataTestCase.java
@@ -16,19 +16,37 @@
package android.cts.statsd.metadata;
-import android.cts.statsd.atom.AtomTestCase;
-import com.android.internal.os.StatsdConfigProto;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsd.atom.BufferDebug;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
-public class MetadataTestCase extends AtomTestCase {
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+public class MetadataTestCase extends DeviceTestCase implements IBuildReceiver {
public static final String DUMP_METADATA_CMD = "cmd stats print-stats";
+ protected IBuildInfo mCtsBuild;
+
protected StatsdStatsReport getStatsdStatsReport() throws Exception {
try {
- StatsdStatsReport report = getDump(StatsdStatsReport.parser(),
+ StatsdStatsReport report = MetricsUtils.getDump(getDevice(), StatsdStatsReport.parser(),
String.join(" ", DUMP_METADATA_CMD, "--proto"));
return report;
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -38,8 +56,33 @@
}
protected final StatsdConfig.Builder getBaseConfig() throws Exception {
- StatsdConfig.Builder builder = createConfigBuilder();
- addAtomEvent(builder, Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
- return builder;
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ ConfigUtils.addEventMetric(builder, Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
+ return builder;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
}
}
diff --git a/tests/src/android/cts/statsd/metadata/MetadataTests.java b/tests/src/android/cts/statsd/metadata/MetadataTests.java
index 5c59b61..1f498f9 100644
--- a/tests/src/android/cts/statsd/metadata/MetadataTests.java
+++ b/tests/src/android/cts/statsd/metadata/MetadataTests.java
@@ -15,25 +15,27 @@
*/
package android.cts.statsd.metadata;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.cts.statsd.atom.AtomTestCase;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
-import com.android.internal.os.StatsdConfigProto;
+import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.internal.os.StatsdConfigProto.Subscription;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.os.AtomsProto.AnomalyDetected;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.os.AtomsProto;
import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.EventMetricData;
import com.android.os.StatsLog.StatsdStatsReport;
import com.android.os.StatsLog.StatsdStatsReport.ConfigStats;
+import com.android.os.StatsLog.StatsdStatsReport.LogLossStats;
+import com.android.os.StatsLog.StatsdStatsReport.SocketLossStats.LossStatsPerUid;
+import com.android.os.StatsLog.StatsdStatsReport.SocketLossStats.LossStatsPerUid.AtomIdLossStats;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.util.RunUtil;
-
-import java.util.List;
+import java.util.HashSet;
/**
* Statsd Metadata tests.
@@ -42,24 +44,28 @@
private static final String TAG = "Statsd.MetadataTests";
+ private static final int SHELL_UID = 2000;
+
// Tests that the statsd config is reset after the specified ttl.
public void testConfigTtl() throws Exception {
final int TTL_TIME_SEC = 8;
StatsdConfig.Builder config = getBaseConfig();
config.setTtlInSeconds(TTL_TIME_SEC); // should reset in this many seconds.
- uploadConfig(config);
+ ConfigUtils.uploadConfig(getDevice(), config);
long startTime = System.currentTimeMillis();
- Thread.sleep(WAIT_TIME_SHORT);
- doAppBreadcrumbReportedStart(/* irrelevant val */ 6); // Event, within < TTL_TIME_SEC secs.
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.START.getNumber(), /* irrelevant val */
+ 6); // Event, within < TTL_TIME_SEC secs.
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
StatsdStatsReport report = getStatsdStatsReport(); // Has only been 1 second
LogUtil.CLog.d("got following statsdstats report: " + report.toString());
boolean foundActiveConfig = false;
int creationTime = 0;
- for (ConfigStats stats: report.getConfigStatsList()) {
- if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
- if(!stats.hasDeletionTimeSec()) {
+ for (ConfigStats stats : report.getConfigStatsList()) {
+ if (stats.getId() == ConfigUtils.CONFIG_ID && stats.getUid() == SHELL_UID) {
+ if (!stats.hasDeletionTimeSec()) {
assertWithMessage("Found multiple active CTS configs!")
.that(foundActiveConfig).isFalse();
foundActiveConfig = true;
@@ -69,17 +75,19 @@
}
assertWithMessage("Did not find an active CTS config").that(foundActiveConfig).isTrue();
- while(System.currentTimeMillis() - startTime < 8_000) {
- Thread.sleep(10);
+ while (System.currentTimeMillis() - startTime < 8_000) {
+ RunUtil.getDefault().sleep(10);
}
- doAppBreadcrumbReportedStart(/* irrelevant val */ 6); // Event, after TTL_TIME_SEC secs.
- Thread.sleep(WAIT_TIME_LONG);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.START.getNumber(), /* irrelevant val */
+ 6); // Event, after TTL_TIME_SEC secs.
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
report = getStatsdStatsReport();
LogUtil.CLog.d("got following statsdstats report: " + report.toString());
foundActiveConfig = false;
int expectedTime = creationTime + TTL_TIME_SEC;
- for (ConfigStats stats: report.getConfigStatsList()) {
- if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
+ for (ConfigStats stats : report.getConfigStatsList()) {
+ if (stats.getId() == ConfigUtils.CONFIG_ID && stats.getUid() == SHELL_UID) {
// Original config should be TTL'd
if (stats.getCreationTimeSec() == creationTime) {
assertWithMessage("Config should have TTL'd but is still active")
@@ -89,7 +97,7 @@
).that(Math.abs(stats.getDeletionTimeSec() - expectedTime)).isAtMost(2);
}
// There should still be one active config, that is marked as reset.
- if(!stats.hasDeletionTimeSec()) {
+ if (!stats.hasDeletionTimeSec()) {
assertWithMessage("Found multiple active CTS configs!")
.that(foundActiveConfig).isFalse();
foundActiveConfig = true;
@@ -107,4 +115,126 @@
assertWithMessage("Did not find an active CTS config after the TTL")
.that(foundActiveConfig).isTrue();
}
+
+ private static final int LIB_STATS_SOCKET_QUEUE_OVERFLOW_ERROR_CODE = 1;
+ private static final int EVENT_STORM_ITERATIONS_COUNT = 10;
+
+ /** Tests that logging many atoms back to back leads to socket overflow and data loss. */
+ public void testAtomLossInfoCollection() throws Exception {
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ ".StatsdStressLogging", "testLogAtomsBackToBack");
+
+ StatsdStatsReport report = getStatsdStatsReport();
+ assertThat(report).isNotNull();
+ boolean detectedLossEventForAppBreadcrumbAtom = false;
+ for (LogLossStats lossStats : report.getDetectedLogLossList()) {
+ if (lossStats.getLastTag() == Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) {
+ detectedLossEventForAppBreadcrumbAtom = true;
+ }
+ }
+
+ assertThat(detectedLossEventForAppBreadcrumbAtom).isTrue();
+ }
+
+ /** Tests that SystemServer logged atoms in case of loss event has error code 1. */
+ public void testSystemServerLossErrorCode() throws Exception {
+ // Starting from VanillaIceCream libstatssocket uses worker thread & dedicated logging queue
+ // to handle atoms for system server (logged with UID 1000)
+ // this test might fail for previous versions due to loss stats last error code check
+ // will not pass
+
+ // Due to info about system server atom loss could be overwritten by APP_BREADCRUMB_REPORTED
+ // loss info run several iterations of this test
+ for (int i = 0; i < EVENT_STORM_ITERATIONS_COUNT; i++) {
+ LogUtil.CLog.d("testSystemServerLossErrorCode iteration #" + i);
+ // logging back to back many atoms to force socket overflow
+ DeviceUtils.runDeviceTests(
+ getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, ".StatsdStressLogging",
+ "testLogAtomsBackToBack");
+
+ // Delay to allow statsd socket recover after overflow
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
+
+ // There is some un-deterministic component in AtomLossStats propagation:
+ // - the dumpAtomsLossStats() from the libstatssocket happens ONLY after the
+ // next successful atom write to socket.
+ // - to avoid socket flood there is also cooldown timer incorporated. If no new atoms -
+ // loss info will not be propagated, which is intention by design.
+ // Log atoms into socket successfully to trigger libstatsocket dumpAtomsLossStats()
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.START.getNumber(), /* irrelevant val */
+ 6); // Event, after TTL_TIME_SEC secs.
+
+ // Delay to allow libstatssocket loss info to be propagated to statsdstats
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
+
+ StatsdStatsReport report = getStatsdStatsReport();
+ assertThat(report).isNotNull();
+ boolean detectedLossEventForAppBreadcrumbAtom = false;
+ boolean detectedLossEventForSystemServer = false;
+ for (LogLossStats lossStats : report.getDetectedLogLossList()) {
+ if (lossStats.getLastTag() == Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) {
+ detectedLossEventForAppBreadcrumbAtom = true;
+ }
+
+ // it should not happen due to atoms from system servers logged via queue
+ // which should be sufficient to hold them for some time to overcome the
+ // socket overflow time frame
+ if (lossStats.getUid() == 1000) {
+ detectedLossEventForSystemServer = true;
+ // but if loss happens it should be annotated with predefined error code == 1
+ assertThat(lossStats.getLastError())
+ .isEqualTo(LIB_STATS_SOCKET_QUEUE_OVERFLOW_ERROR_CODE);
+ }
+ }
+
+ assertThat(detectedLossEventForAppBreadcrumbAtom).isTrue();
+ assertThat(detectedLossEventForSystemServer).isFalse();
+
+ boolean detectedLossEventForAppBreadcrumbAtomViaSocketLossStats = false;
+ for (LossStatsPerUid lossStats : report.getSocketLossStats().getLossStatsPerUidList()) {
+ for (AtomIdLossStats atomLossStats : lossStats.getAtomIdLossStatsList()) {
+ if (atomLossStats.getAtomId() == Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) {
+ detectedLossEventForAppBreadcrumbAtomViaSocketLossStats = true;
+ }
+ }
+ }
+ assertThat(detectedLossEventForAppBreadcrumbAtomViaSocketLossStats).isTrue();
+ }
+ }
+
+ /** Test libstatssocket logging queue atom id distribution collection */
+ public void testAtomIdLossDistributionCollection() throws Exception {
+ if (!ApiLevelUtil.codenameEquals(getDevice(), "VanillaIceCream")) {
+ return;
+ }
+
+ final String appTestApk = "StatsdAtomStormApp.apk";
+ final String app2TestApk = "StatsdAtomStormApp2.apk";
+
+ final String appTestPkg = "com.android.statsd.app.atomstorm";
+ final String app2TestPkg = "com.android.statsd.app.atomstorm.copy";
+
+ DeviceUtils.uninstallTestApp(getDevice(), appTestPkg);
+ DeviceUtils.uninstallTestApp(getDevice(), app2TestPkg);
+
+ DeviceUtils.installTestApp(getDevice(), appTestApk, appTestPkg, mCtsBuild);
+ DeviceUtils.installTestApp(getDevice(), app2TestApk, app2TestPkg, mCtsBuild);
+
+ // run reference test app with UID 1
+ DeviceUtils.runDeviceTests(getDevice(), appTestPkg, null, null);
+ // run reference test app with UID 2
+ DeviceUtils.runDeviceTests(getDevice(), app2TestPkg, null, null);
+
+ StatsdStatsReport report = getStatsdStatsReport();
+ assertThat(report).isNotNull();
+ HashSet<Integer> reportedUids = new HashSet<Integer>();
+ for (LossStatsPerUid lossStats : report.getSocketLossStats().getLossStatsPerUidList()) {
+ reportedUids.add(lossStats.getUid());
+ }
+ assertThat(reportedUids.size()).isGreaterThan(1);
+
+ getDevice().uninstallPackage(appTestPkg);
+ getDevice().uninstallPackage(app2TestPkg);
+ }
}
diff --git a/tests/src/android/cts/statsd/metric/CountMetricsTests.java b/tests/src/android/cts/statsd/metric/CountMetricsTests.java
index 22b4f9c..8e9b5a3 100644
--- a/tests/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/tests/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -18,48 +18,83 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
import com.android.internal.os.StatsdConfigProto.Position;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.BleScanStateChanged;
import com.android.os.AtomsProto.WakelockStateChanged;
import com.android.os.AttributionNode;
-import com.android.os.StatsLog;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.CountBucketInfo;
import com.android.os.StatsLog.CountMetricData;
import com.android.os.StatsLog.StatsLogReport;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
-import java.util.Arrays;
+import com.google.protobuf.ExtensionRegistry;
+
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
-public class CountMetricsTests extends DeviceAtomTestCase {
+public class CountMetricsTests extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
public void testSimpleEventCountMetric() throws Exception {
int matcherId = 1;
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
- .setId(MetricsUtils.COUNT_METRIC_ID)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .setWhat(matcherId))
+ .setId(MetricsUtils.COUNT_METRIC_ID)
+ .setBucket(StatsdConfigProto.TimeUnit.ONE_MINUTE)
+ .setWhat(matcherId))
.addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId));
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(100);
- doAppBreadcrumbReportedStop(0);
- Thread.sleep(2000); // Wait for the metrics to propagate to statsd.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
+ RunUtil.getDefault().sleep(100);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 0);
+ RunUtil.getDefault().sleep(2000); // Wait for the metrics to propagate to statsd.
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
assertThat(metricReport.hasCountMetrics()).isTrue();
@@ -69,6 +104,7 @@
assertThat(countData.getDataCount()).isGreaterThan(0);
assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
}
+
public void testEventCountWithCondition() throws Exception {
int startMatcherId = 1;
int endMatcherId = 2;
@@ -76,7 +112,7 @@
int conditionId = 4;
StatsdConfigProto.AtomMatcher whatMatcher =
- MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
+ MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
StatsdConfigProto.AtomMatcher predicateStartMatcher =
MetricsUtils.startAtomMatcher(startMatcherId);
@@ -92,7 +128,8 @@
.setId(conditionId)
.build();
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
.setId(MetricsUtils.COUNT_METRIC_ID)
.setBucket(StatsdConfigProto.TimeUnit.CTS)
@@ -103,20 +140,26 @@
.addAtomMatcher(predicateEndMatcher)
.addPredicate(p);
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
- doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(10);
- doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(0);
- Thread.sleep(10);
- doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal());
- Thread.sleep(2000); // Wait for the metrics to propagate to statsd.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 0);
+ RunUtil.getDefault().sleep(2000); // Wait for the metrics to propagate to statsd.
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
assertThat(metricReport.hasCountMetrics()).isTrue();
@@ -149,7 +192,7 @@
StatsdConfigProto.AtomMatcher activationMatcher =
MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
- activationMatcherLabel);
+ activationMatcherLabel);
StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder()
.setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder()
@@ -159,10 +202,11 @@
.setId(conditionId)
.build();
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
.setId(MetricsUtils.COUNT_METRIC_ID)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .setBucket(StatsdConfigProto.TimeUnit.ONE_MINUTE)
.setWhat(whatMatcherId)
.setCondition(conditionId)
)
@@ -178,66 +222,79 @@
.setAtomMatcherId(activationMatcherId)
.setTtlSeconds(ttlSec)));
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Activate the metric.
- doAppBreadcrumbReported(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to true.
- doAppBreadcrumbReportedStart(startMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), startMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should be counted. Bucket 1 Count 1.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should be counted. Bucket 1 Count 2.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to false.
- doAppBreadcrumbReportedStop(endMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), endMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should not be counted because condition is false.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Log an event that should not be counted.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Condition to true again.
- doAppBreadcrumbReportedStart(startMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), startMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Event should not be counted, metric is still not active.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Activate the metric.
- doAppBreadcrumbReported(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should be counted.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Log an event that should not be counted.
- doAppBreadcrumbReported(whatMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), whatMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.hasCountMetrics()).isTrue();
@@ -252,27 +309,30 @@
public void testPartialBucketCountMetric() throws Exception {
int matcherId = 1;
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
- builder
- .addCountMetric(
- StatsdConfigProto.CountMetric.newBuilder()
- .setId(MetricsUtils.COUNT_METRIC_ID)
- .setBucket(StatsdConfigProto.TimeUnit.ONE_DAY) // Ensures partial bucket.
- .setWhat(matcherId)
- .setSplitBucketForAppUpgrade(true))
- .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId));
- uploadConfig(builder);
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
+ .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
+ .setId(MetricsUtils.COUNT_METRIC_ID)
+ .setBucket(StatsdConfigProto.TimeUnit.ONE_DAY) // Ensures partial bucket.
+ .setWhat(matcherId)
+ .setSplitBucketForAppUpgrade(true))
+ .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId));
+ ConfigUtils.uploadConfig(getDevice(), builder);
- doAppBreadcrumbReportedStart(0);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
builder.getCountMetricBuilder(0).setBucket(StatsdConfigProto.TimeUnit.CTS);
- uploadConfig(builder); // The count metric had a partial bucket.
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(WAIT_TIME_LONG); // Finish the current bucket.
+ ConfigUtils.uploadConfig(getDevice(), builder); // The count metric had a partial bucket.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG); // Finish the current bucket.
- ConfigMetricsReportList reports = getReportList();
+ ConfigMetricsReportList reports = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Got following report list: " + reports.toString());
assertThat(reports.getReportsCount()).isEqualTo(2);
@@ -285,9 +345,9 @@
assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1);
}
CountMetricData data1 =
- reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0);
+ reports.getReports(inOrder ? 0 : 1).getMetrics(0).getCountMetrics().getData(0);
CountMetricData data2 =
- reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0);
+ reports.getReports(inOrder ? 1 : 0).getMetrics(0).getCountMetrics().getData(0);
// Data1 should have only 1 bucket, and it should be a partial bucket.
// The count should be 1.
assertThat(data1.getBucketInfoCount()).isEqualTo(1);
@@ -326,90 +386,93 @@
.build();
StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder()
- .setId(stateId)
- .setAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
- .setMap(StatsdConfigProto.StateMap.newBuilder()
- .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
- .setGroupId(onStateGroupId)
- .addValue(WakelockStateChanged.State.ACQUIRE_VALUE)
- .addValue(WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE)
- )
- .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
- .setGroupId(offStateGroupId)
- .addValue(WakelockStateChanged.State.RELEASE_VALUE)
- .addValue(WakelockStateChanged.State.CHANGE_RELEASE_VALUE)
- )
- )
- .build();
+ .setId(stateId)
+ .setAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .setMap(StatsdConfigProto.StateMap.newBuilder()
+ .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
+ .setGroupId(onStateGroupId)
+ .addValue(WakelockStateChanged.State.ACQUIRE_VALUE)
+ .addValue(WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE)
+ )
+ .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
+ .setGroupId(offStateGroupId)
+ .addValue(WakelockStateChanged.State.RELEASE_VALUE)
+ .addValue(WakelockStateChanged.State.CHANGE_RELEASE_VALUE)
+ )
+ )
+ .build();
StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder()
- .setStateAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
- .setFieldsInWhat(FieldMatcher.newBuilder()
- .setField(whatAtomId)
- .addChild(FieldMatcher.newBuilder()
- .setField(1)
- .setPosition(Position.FIRST)
- .addChild(FieldMatcher.newBuilder()
- .setField(AttributionNode.UID_FIELD_NUMBER)
- )
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(2)
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(3)
- )
- )
- .setFieldsInState(FieldMatcher.newBuilder()
- .setField(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder()
- .setField(WakelockStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
- .setPosition(Position.FIRST)
- .addChild(FieldMatcher.newBuilder()
- .setField(AttributionNode.UID_FIELD_NUMBER)
- )
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(WakelockStateChanged.TYPE_FIELD_NUMBER)
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(WakelockStateChanged.TAG_FIELD_NUMBER)
- )
- )
- .build();
-
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
- .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
- .setId(MetricsUtils.COUNT_METRIC_ID)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .setWhat(whatMatcherId)
- .addSliceByState(stateId)
- .addStateLink(stateLink)
- .setDimensionsInWhat(
- FieldMatcher.newBuilder()
- .setField(whatAtomId)
- .addChild(FieldMatcher.newBuilder()
- .setField(1)
- .setPosition(Position.FIRST)
- .addChild(FieldMatcher.newBuilder()
- .setField(AttributionNode.UID_FIELD_NUMBER)
- )
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(2)
- )
- .addChild(FieldMatcher.newBuilder()
- .setField(3)
+ .setStateAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .setFieldsInWhat(FieldMatcher.newBuilder()
+ .setField(whatAtomId)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(1)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
)
- )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(2)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(3)
+ )
+ )
+ .setFieldsInState(FieldMatcher.newBuilder()
+ .setField(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.TYPE_FIELD_NUMBER)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.TAG_FIELD_NUMBER)
+ )
+ )
+ .build();
+
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
+ .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
+ .setId(MetricsUtils.COUNT_METRIC_ID)
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .setWhat(whatMatcherId)
+ .addSliceByState(stateId)
+ .addStateLink(stateLink)
+ .setDimensionsInWhat(
+ FieldMatcher.newBuilder()
+ .setField(whatAtomId)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(1)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(2)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(3)
+ )
+ )
)
.addAtomMatcher(whatMatcher)
.addState(state);
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSliceByWakelockState");
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, ".AtomTests",
+ "testSliceByWakelockState");
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
assertThat(metricReport.hasCountMetrics()).isTrue();
@@ -420,10 +483,10 @@
List<CountMetricData> sortedDataList = IntStream.range(0, dataWrapper.getDataCount())
.mapToObj(i -> {
- CountMetricData data = dataWrapper.getData(i);
- assertWithMessage("Unexpected SliceByState count for data[%s]", "" + i)
- .that(data.getSliceByStateCount()).isEqualTo(1);
- return data;
+ CountMetricData data = dataWrapper.getData(i);
+ assertWithMessage("Unexpected SliceByState count for data[%s]", "" + i)
+ .that(data.getSliceByStateCount()).isEqualTo(1);
+ return data;
})
.sorted((data1, data2) ->
Long.compare(data1.getSliceByState(0).getGroupId(),
diff --git a/tests/src/android/cts/statsd/metric/DurationMetricsTests.java b/tests/src/android/cts/statsd/metric/DurationMetricsTests.java
index 65cef95..89c9bbc 100644
--- a/tests/src/android/cts/statsd/metric/DurationMetricsTests.java
+++ b/tests/src/android/cts/statsd/metric/DurationMetricsTests.java
@@ -17,45 +17,62 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.Position;
import com.android.internal.os.StatsdConfigProto.Predicate;
-import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimplePredicate;
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.DurationBucketInfo;
import com.android.os.StatsLog.StatsLogReport;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.RunUtil;
import com.google.common.collect.Range;
+import com.google.protobuf.ExtensionRegistry;
-import java.util.List;
-public class DurationMetricsTests extends DeviceAtomTestCase {
+public class DurationMetricsTests extends DeviceTestCase {
private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID = 3;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ super.tearDown();
+ }
+
public void testDurationMetric() throws Exception {
final int label = 1;
// Add AtomMatchers.
AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, label);
+ MetricsUtils.startAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID,
+ label);
AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, label);
+ MetricsUtils.stopAtomMatcherWithLabel(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID,
+ label);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
builder.addAtomMatcher(startAtomMatcher);
builder.addAtomMatcher(stopAtomMatcher);
@@ -65,31 +82,33 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
builder.addPredicate(predicate);
// Add DurationMetric.
- builder.addDurationMetric(
- StatsdConfigProto.DurationMetric.newBuilder()
+ builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
.setId(MetricsUtils.DURATION_METRIC_ID)
.setWhat(predicate.getId())
.setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
.setBucket(StatsdConfigProto.TimeUnit.CTS));
// Upload config.
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Create AppBreadcrumbReported Start/Stop events.
- doAppBreadcrumbReportedStart(label);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(label);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), label);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), label);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.hasDurationMetrics()).isTrue();
@@ -97,7 +116,7 @@
= metricReport.getDurationMetrics();
assertThat(durationData.getDataCount()).isEqualTo(1);
assertThat(durationData.getData(0).getBucketInfo(0).getDurationNanos())
- .isIn(Range.open(0L, (long)1e9));
+ .isIn(Range.open(0L, (long) 1e9));
}
public void testDurationMetricWithCondition() throws Exception {
@@ -114,7 +133,8 @@
AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ DeviceUtils.STATSD_ATOM_TEST_PKG)
.addAtomMatcher(startAtomMatcher)
.addAtomMatcher(stopAtomMatcher)
.addAtomMatcher(conditionStartAtomMatcher)
@@ -126,76 +146,82 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
.setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
.setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
.build();
Predicate conditionPredicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("ConditionPredicate"))
- .setSimplePredicate(conditionSimplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("ConditionPredicate"))
+ .setSimplePredicate(conditionSimplePredicate)
+ .build();
- builder
- .addPredicate(predicate)
- .addPredicate(conditionPredicate);
+ builder.addPredicate(predicate).addPredicate(conditionPredicate);
// Add DurationMetric.
- builder
- .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
- .setId(MetricsUtils.DURATION_METRIC_ID)
- .setWhat(predicate.getId())
- .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .setCondition(conditionPredicate.getId())
- );
+ builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+ .setId(MetricsUtils.DURATION_METRIC_ID)
+ .setWhat(predicate.getId())
+ .setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .setCondition(conditionPredicate.getId())
+ );
// Upload config.
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to true.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Start counted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop counted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to false.
- doAppBreadcrumbReportedStop(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
- StatsLogReport metricReport = getStatsLogReport();
+ RunUtil.getDefault().sleep(2_000);
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.hasDurationMetrics()).isTrue();
@@ -204,9 +230,9 @@
assertThat(durationData.getDataCount()).isEqualTo(1);
long totalDuration = durationData.getData(0).getBucketInfoList().stream()
.mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
- .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+ .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
.sum();
- assertThat(totalDuration).isIn(Range.open((long)2e9, (long)3e9));
+ assertThat(totalDuration).isIn(Range.open((long) 2e9, (long) 3e9));
}
public void testDurationMetricWithActivation() throws Exception {
@@ -222,9 +248,10 @@
APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, durationLabel);
StatsdConfigProto.AtomMatcher activationMatcher =
MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
- activationMatcherLabel);
+ activationMatcherLabel);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addAtomMatcher(startAtomMatcher)
.addAtomMatcher(stopAtomMatcher)
.addAtomMatcher(activationMatcher);
@@ -235,19 +262,17 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
builder.addPredicate(predicate);
// Add DurationMetric.
- builder
- .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+ builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
.setId(MetricsUtils.DURATION_METRIC_ID)
.setWhat(predicate.getId())
.setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- )
+ .setBucket(StatsdConfigProto.TimeUnit.CTS))
.addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
.setMetricId(MetricsUtils.DURATION_METRIC_ID)
.addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
@@ -257,34 +282,40 @@
.setTtlSeconds(ttlSec)));
// Upload config.
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Activate the metric.
- doAppBreadcrumbReported(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Start counted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop counted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
- StatsLogReport metricReport = getStatsLogReport();
+ RunUtil.getDefault().sleep(2_000);
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.hasDurationMetrics()).isTrue();
@@ -293,9 +324,9 @@
assertThat(durationData.getDataCount()).isEqualTo(1);
long totalDuration = durationData.getData(0).getBucketInfoList().stream()
.mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
- .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+ .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
.sum();
- assertThat(totalDuration).isIn(Range.open((long)2e9, (long)3e9));
+ assertThat(totalDuration).isIn(Range.open((long) 2e9, (long) 3e9));
}
public void testDurationMetricWithConditionAndActivation() throws Exception {
@@ -316,9 +347,10 @@
APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID, conditionLabel);
StatsdConfigProto.AtomMatcher activationMatcher =
MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId,
- activationMatcherLabel);
+ activationMatcherLabel);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addAtomMatcher(startAtomMatcher)
.addAtomMatcher(stopAtomMatcher)
.addAtomMatcher(conditionStartAtomMatcher)
@@ -331,9 +363,9 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
builder.addPredicate(predicate);
SimplePredicate conditionSimplePredicate = SimplePredicate.newBuilder()
@@ -341,20 +373,18 @@
.setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
.build();
Predicate conditionPredicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("ConditionPredicate"))
- .setSimplePredicate(conditionSimplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("ConditionPredicate"))
+ .setSimplePredicate(conditionSimplePredicate)
+ .build();
builder.addPredicate(conditionPredicate);
// Add DurationMetric.
- builder
- .addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
+ builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
.setId(MetricsUtils.DURATION_METRIC_ID)
.setWhat(predicate.getId())
.setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
.setBucket(StatsdConfigProto.TimeUnit.CTS)
- .setCondition(conditionPredicate.getId())
- )
+ .setCondition(conditionPredicate.getId()))
.addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder()
.setMetricId(MetricsUtils.DURATION_METRIC_ID)
.addEventActivation(StatsdConfigProto.EventActivation.newBuilder()
@@ -364,100 +394,118 @@
.setTtlSeconds(ttlSec)));
// Upload config.
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Activate the metric.
- doAppBreadcrumbReported(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to true.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Start counted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop counted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to false.
- doAppBreadcrumbReportedStop(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
//doAppBreadcrumbReported(99); // TODO: maybe remove?
- //Thread.sleep(10);
+ //RunUtil.getDefault().sleep(10);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Set condition to true again.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
- // Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ // Start uncounted duration
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Activate the metric.
- doAppBreadcrumbReported(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Start counted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop counted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Start uncounted duration.
- doAppBreadcrumbReportedStart(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
- Thread.sleep(2_000);
+ RunUtil.getDefault().sleep(2_000);
// Stop uncounted duration.
- doAppBreadcrumbReportedStop(durationLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), durationLabel);
+ RunUtil.getDefault().sleep(10);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
assertThat(metricReport.hasDurationMetrics()).isTrue();
@@ -466,23 +514,24 @@
assertThat(durationData.getDataCount()).isEqualTo(1);
long totalDuration = durationData.getData(0).getBucketInfoList().stream()
.mapToLong(bucketInfo -> bucketInfo.getDurationNanos())
- .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long)1e9)))
+ .peek(durationNs -> assertThat(durationNs).isIn(Range.openClosed(0L, (long) 1e9)))
.sum();
- assertThat(totalDuration).isIn(Range.open((long)4e9, (long)5e9));
+ assertThat(totalDuration).isIn(Range.open((long) 4e9, (long) 5e9));
}
public void testDurationMetricWithDimension() throws Exception {
// Add AtomMatchers.
AtomMatcher startAtomMatcherA =
- MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
+ MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
AtomMatcher stopAtomMatcherA =
- MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
+ MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
AtomMatcher startAtomMatcherB =
- MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
+ MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
AtomMatcher stopAtomMatcherB =
- MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID);
+ MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
builder.addAtomMatcher(startAtomMatcherA);
builder.addAtomMatcher(stopAtomMatcherA);
builder.addAtomMatcher(startAtomMatcherB);
@@ -494,9 +543,9 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicateA = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate_A"))
- .setSimplePredicate(simplePredicateA)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate_A"))
+ .setSimplePredicate(simplePredicateA)
+ .build();
builder.addPredicate(predicateA);
FieldMatcher.Builder dimensionsBuilder = FieldMatcher.newBuilder()
@@ -504,47 +553,50 @@
dimensionsBuilder
.addChild(FieldMatcher.newBuilder().setField(
AppBreadcrumbReported.LABEL_FIELD_NUMBER));
- Predicate predicateB =
- Predicate.newBuilder()
+ Predicate predicateB = Predicate.newBuilder()
.setId(MetricsUtils.StringToId("Predicate_B"))
.setSimplePredicate(SimplePredicate.newBuilder()
- .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
- .setDimensions(dimensionsBuilder.build())
- .build())
+ .setStart(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .setStop(APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID)
+ .setDimensions(dimensionsBuilder.build())
+ .build())
.build();
builder.addPredicate(predicateB);
// Add DurationMetric.
- builder.addDurationMetric(
- StatsdConfigProto.DurationMetric.newBuilder()
+ builder.addDurationMetric(StatsdConfigProto.DurationMetric.newBuilder()
.setId(MetricsUtils.DURATION_METRIC_ID)
.setWhat(predicateB.getId())
.setCondition(predicateA.getId())
.setAggregationType(StatsdConfigProto.DurationMetric.AggregationType.SUM)
.setBucket(StatsdConfigProto.TimeUnit.CTS)
.setDimensionsInWhat(
- FieldMatcher.newBuilder()
- .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder().setField(
- AppBreadcrumbReported.LABEL_FIELD_NUMBER))));
+ FieldMatcher.newBuilder()
+ .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ AppBreadcrumbReported.LABEL_FIELD_NUMBER))));
// Upload config.
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Trigger events.
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStart(2);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(2);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 2);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 2);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.DURATION_METRIC_ID);
assertThat(metricReport.hasDurationMetrics()).isTrue();
StatsLogReport.DurationMetricDataWrapper durationData
diff --git a/tests/src/android/cts/statsd/metric/GaugeMetricsTests.java b/tests/src/android/cts/statsd/metric/GaugeMetricsTests.java
index db6a818..a037a69 100644
--- a/tests/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/tests/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -17,7 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.ActivationType;
@@ -25,7 +27,6 @@
import com.android.internal.os.StatsdConfigProto.EventActivation;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.MetricActivation;
import com.android.internal.os.StatsdConfigProto.Predicate;
@@ -35,166 +36,205 @@
import com.android.internal.os.StatsdConfigProto.TimeUnit;
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog;
import com.android.os.StatsLog.GaugeBucketInfo;
import com.android.os.StatsLog.GaugeMetricData;
import com.android.os.StatsLog.StatsLogReport;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.Pair;
+import com.android.tradefed.util.RunUtil;
-public class GaugeMetricsTests extends DeviceAtomTestCase {
+import com.google.protobuf.ExtensionRegistry;
- private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
- private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
- private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
- public void testGaugeMetric() throws Exception {
- // Add AtomMatcher's.
- AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
- AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
- AtomMatcher atomMatcher =
- MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
+public class GaugeMetricsTests extends DeviceTestCase {
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(startAtomMatcher);
- builder.addAtomMatcher(stopAtomMatcher);
- builder.addAtomMatcher(atomMatcher);
+ private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
+ private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
+ private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
- // Add Predicate's.
- SimplePredicate simplePredicate = SimplePredicate.newBuilder()
- .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
- .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
- .build();
- Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
- builder.addPredicate(predicate);
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ RunUtil.getDefault().sleep(1000);
+ }
- // Add GaugeMetric.
- FieldMatcher fieldMatcher =
- FieldMatcher.newBuilder().setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID).build();
- builder.addGaugeMetric(
- StatsdConfigProto.GaugeMetric.newBuilder()
- .setId(MetricsUtils.GAUGE_METRIC_ID)
- .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .setCondition(predicate.getId())
- .setGaugeFieldsFilter(
- FieldFilter.newBuilder().setIncludeAll(false).setFields(fieldMatcher).build())
- .setDimensionsInWhat(
- FieldMatcher.newBuilder()
- .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .addChild(FieldMatcher.newBuilder()
- .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .build())
- .build())
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .build());
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ super.tearDown();
+ }
- // Upload config.
- uploadConfig(builder);
+ public void testGaugeMetric() throws Exception {
+ // Add AtomMatcher's.
+ AtomMatcher startAtomMatcher =
+ MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
+ AtomMatcher stopAtomMatcher =
+ MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
+ AtomMatcher atomMatcher =
+ MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
- // Create AppBreadcrumbReported Start/Stop events.
- doAppBreadcrumbReportedStart(0);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(2);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(0);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(1);
- doAppBreadcrumbReportedStart(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(2000);
- doAppBreadcrumbReportedStop(2);
- Thread.sleep(10);
- doAppBreadcrumbReportedStop(1);
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ builder.addAtomMatcher(startAtomMatcher);
+ builder.addAtomMatcher(stopAtomMatcher);
+ builder.addAtomMatcher(atomMatcher);
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ // Add Predicate's.
+ SimplePredicate simplePredicate = SimplePredicate.newBuilder()
+ .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID)
+ .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
+ .build();
+ Predicate predicate = Predicate.newBuilder()
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
+ builder.addPredicate(predicate);
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
- assertThat(metricReport.hasGaugeMetrics()).isTrue();
- StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
- gaugeData = backfillGaugeMetricData(gaugeData);
- assertThat(gaugeData.getDataCount()).isEqualTo(1);
+ // Add GaugeMetric.
+ FieldMatcher fieldMatcher = FieldMatcher.newBuilder()
+ .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .build();
+ builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder()
+ .setId(MetricsUtils.GAUGE_METRIC_ID)
+ .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .setCondition(predicate.getId())
+ .setGaugeFieldsFilter(FieldFilter.newBuilder()
+ .setIncludeAll(false)
+ .setFields(fieldMatcher)
+ .build())
+ .setDimensionsInWhat(FieldMatcher.newBuilder()
+ .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .build())
+ .build())
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .build());
- int bucketCount = gaugeData.getData(0).getBucketInfoCount();
- GaugeMetricData data = gaugeData.getData(0);
- assertThat(bucketCount).isGreaterThan(2);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
- assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1);
- assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel())
- .isEqualTo(0);
- assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState())
- .isEqualTo(AppBreadcrumbReported.State.START);
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
- assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1);
+ // Create AppBreadcrumbReported Start/Stop events.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 2);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 2);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 0);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 2);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(2000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 2);
+ RunUtil.getDefault().sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
- MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount-1));
- assertThat(data.getBucketInfo(bucketCount-1).getAtomCount()).isEqualTo(1);
- assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getLabel())
- .isEqualTo(2);
- assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getState())
- .isEqualTo(AppBreadcrumbReported.State.STOP);
- }
+ // Wait for the metrics to propagate to statsd.
+ RunUtil.getDefault().sleep(2000);
- public void testPulledGaugeMetricWithActivation() throws Exception {
- // Add AtomMatcher's.
- int activationAtomMatcherId = 1;
- int activationAtomMatcherLabel = 1;
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+ assertThat(metricReport.hasGaugeMetrics()).isTrue();
+ StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
+ gaugeData = backfillGaugeMetricData(gaugeData);
+ assertThat(gaugeData.getDataCount()).isEqualTo(1);
- int systemUptimeMatcherId = 2;
- AtomMatcher activationAtomMatcher =
- MetricsUtils.appBreadcrumbMatcherWithLabel(
- activationAtomMatcherId, activationAtomMatcherLabel);
- AtomMatcher systemUptimeMatcher =
- AtomMatcher.newBuilder()
- .setId(systemUptimeMatcherId)
- .setSimpleAtomMatcher(
- SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_UPTIME_FIELD_NUMBER))
- .build();
+ int bucketCount = gaugeData.getData(0).getBucketInfoCount();
+ GaugeMetricData data = gaugeData.getData(0);
+ assertThat(bucketCount).isGreaterThan(2);
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
+ assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1);
+ assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel())
+ .isEqualTo(0);
+ assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState())
+ .isEqualTo(AppBreadcrumbReported.State.START);
- StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(activationAtomMatcher);
- builder.addAtomMatcher(systemUptimeMatcher);
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
+ assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1);
- // Add GaugeMetric.
- builder.addGaugeMetric(
- StatsdConfigProto.GaugeMetric.newBuilder()
- .setId(MetricsUtils.GAUGE_METRIC_ID)
- .setWhat(systemUptimeMatcherId)
- .setGaugeFieldsFilter(
- FieldFilter.newBuilder().setIncludeAll(true).build())
- .setBucket(StatsdConfigProto.TimeUnit.CTS)
- .build());
+ MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount - 1));
+ assertThat(data.getBucketInfo(bucketCount - 1).getAtomCount()).isEqualTo(1);
+ assertThat(data.getBucketInfo(bucketCount - 1).getAtom(
+ 0).getAppBreadcrumbReported().getLabel())
+ .isEqualTo(2);
+ assertThat(data.getBucketInfo(bucketCount - 1).getAtom(
+ 0).getAppBreadcrumbReported().getState())
+ .isEqualTo(AppBreadcrumbReported.State.STOP);
+ }
- // Add activation.
- builder.addMetricActivation(MetricActivation.newBuilder()
- .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
- .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
- .addEventActivation(EventActivation.newBuilder()
- .setAtomMatcherId(activationAtomMatcherId)
- .setTtlSeconds(5)));
+ public void testPulledGaugeMetricWithActivation() throws Exception {
+ // Add AtomMatcher's.
+ int activationAtomMatcherId = 1;
+ int activationAtomMatcherLabel = 1;
- // Upload config.
- uploadConfig(builder);
+ int systemUptimeMatcherId = 2;
+ AtomMatcher activationAtomMatcher = MetricsUtils.appBreadcrumbMatcherWithLabel(
+ activationAtomMatcherId, activationAtomMatcherLabel);
+ AtomMatcher systemUptimeMatcher = AtomMatcher.newBuilder()
+ .setId(systemUptimeMatcherId)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.SYSTEM_UPTIME_FIELD_NUMBER))
+ .build();
- // Plenty of time to pull, but we should not keep the data since we are not active.
- Thread.sleep(20_000);
+ StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ builder.addAtomMatcher(activationAtomMatcher);
+ builder.addAtomMatcher(systemUptimeMatcher);
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
- assertThat(metricReport.hasGaugeMetrics()).isFalse();
- }
+ // Add GaugeMetric.
+ builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder()
+ .setId(MetricsUtils.GAUGE_METRIC_ID)
+ .setWhat(systemUptimeMatcherId)
+ .setGaugeFieldsFilter(
+ FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .build());
+
+ // Add activation.
+ builder.addMetricActivation(MetricActivation.newBuilder()
+ .setMetricId(MetricsUtils.GAUGE_METRIC_ID)
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .addEventActivation(EventActivation.newBuilder()
+ .setAtomMatcherId(activationAtomMatcherId)
+ .setTtlSeconds(5)));
+
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
+
+ // Plenty of time to pull, but we should not keep the data since we are not active.
+ RunUtil.getDefault().sleep(20_000);
+
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+ assertThat(metricReport.hasGaugeMetrics()).isFalse();
+ }
public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
final int conditionLabel = 2;
@@ -208,17 +248,16 @@
APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel);
AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel(
APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel);
- AtomMatcher activationMatcher =
- MetricsUtils.startAtomMatcherWithLabel(
- activationMatcherId, activationMatcherLabel);
- AtomMatcher whatMatcher =
- MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
+ AtomMatcher activationMatcher = MetricsUtils.startAtomMatcherWithLabel(
+ activationMatcherId, activationMatcherLabel);
+ AtomMatcher whatMatcher = MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
- StatsdConfig.Builder builder = createConfigBuilder()
- .addAtomMatcher(conditionStartAtomMatcher)
- .addAtomMatcher(conditionStopAtomMatcher)
- .addAtomMatcher(whatMatcher)
- .addAtomMatcher(activationMatcher);
+ StatsdConfig.Builder builder =
+ ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
+ .addAtomMatcher(conditionStartAtomMatcher)
+ .addAtomMatcher(conditionStopAtomMatcher)
+ .addAtomMatcher(whatMatcher)
+ .addAtomMatcher(activationMatcher);
// Add Predicates.
SimplePredicate simplePredicate = SimplePredicate.newBuilder()
@@ -226,95 +265,102 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
builder.addPredicate(predicate);
// Add GaugeMetric.
- builder
- .addGaugeMetric(GaugeMetric.newBuilder()
+ builder.addGaugeMetric(GaugeMetric.newBuilder()
.setId(MetricsUtils.GAUGE_METRIC_ID)
.setWhat(whatMatcher.getId())
.setBucket(TimeUnit.CTS)
.setCondition(predicate.getId())
- .setGaugeFieldsFilter(
- FieldFilter.newBuilder().setIncludeAll(false).setFields(
- FieldMatcher.newBuilder()
- .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- )
- )
- .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId))
- )
+ .setGaugeFieldsFilter(FieldFilter.newBuilder()
+ .setIncludeAll(false)
+ .setFields(FieldMatcher.newBuilder()
+ .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)))
+ .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId)))
.addMetricActivation(MetricActivation.newBuilder()
.setMetricId(MetricsUtils.GAUGE_METRIC_ID)
.addEventActivation(EventActivation.newBuilder()
.setAtomMatcherId(activationMatcherId)
.setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
- .setTtlSeconds(ttlSec)
- )
- );
+ .setTtlSeconds(ttlSec)));
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Activate the metric.
- doAppBreadcrumbReportedStart(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to true.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// This value is collected.
- doAppBreadcrumbReported(10);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 10);
+ RunUtil.getDefault().sleep(10);
// Ignored; value already collected.
- doAppBreadcrumbReported(20);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 20);
+ RunUtil.getDefault().sleep(10);
// Set the condition to false.
- doAppBreadcrumbReportedStop(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Value not updated because condition is false.
- doAppBreadcrumbReported(30);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 30);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Value not collected.
- doAppBreadcrumbReported(40);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 40);
+ RunUtil.getDefault().sleep(10);
// Condition to true again.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Value not collected.
- doAppBreadcrumbReported(50);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 50);
+ RunUtil.getDefault().sleep(10);
// Activate the metric.
- doAppBreadcrumbReportedStart(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Value collected.
- doAppBreadcrumbReported(60);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 60);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Value not collected.
- doAppBreadcrumbReported(70);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 70);
+ RunUtil.getDefault().sleep(10);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
assertThat(metricReport.hasGaugeMetrics()).isTrue();
@@ -335,4 +381,42 @@
assertThat(bucketInfo.getAtomCount()).isEqualTo(1);
assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(60);
}
+
+ private StatsLogReport.GaugeMetricDataWrapper backfillGaugeMetricData(
+ StatsLogReport.GaugeMetricDataWrapper dataWrapper) {
+ StatsLogReport.GaugeMetricDataWrapper.Builder dataWrapperBuilder = dataWrapper.toBuilder();
+ List<GaugeMetricData> backfilledMetricData = new ArrayList<>();
+ for (GaugeMetricData gaugeMetricData : dataWrapperBuilder.getDataList()) {
+ GaugeMetricData.Builder gaugeMetricDataBuilder = gaugeMetricData.toBuilder();
+ List<GaugeBucketInfo> backfilledBuckets = new ArrayList<>();
+ for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) {
+ backfilledBuckets.add(backfillGaugeBucket(bucketInfo.toBuilder()));
+ }
+ gaugeMetricDataBuilder.clearBucketInfo();
+ gaugeMetricDataBuilder.addAllBucketInfo(backfilledBuckets);
+ backfilledMetricData.add(gaugeMetricDataBuilder.build());
+ }
+ dataWrapperBuilder.clearData();
+ dataWrapperBuilder.addAllData(backfilledMetricData);
+ return dataWrapperBuilder.build();
+ }
+
+ private GaugeBucketInfo backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder) {
+ if (bucketInfoBuilder.getAtomCount() != 0) {
+ return bucketInfoBuilder.build();
+ }
+ List<Pair<Atom, Long>> atomTimestampData = new ArrayList<>();
+ for (StatsLog.AggregatedAtomInfo atomInfo : bucketInfoBuilder.getAggregatedAtomInfoList()) {
+ for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) {
+ atomTimestampData.add(Pair.create(atomInfo.getAtom(), timestampNs));
+ }
+ }
+ atomTimestampData.sort(Comparator.comparing(o -> o.second));
+ bucketInfoBuilder.clearAggregatedAtomInfo();
+ for (Pair<Atom, Long> atomTimestamp : atomTimestampData) {
+ bucketInfoBuilder.addAtom(atomTimestamp.first);
+ bucketInfoBuilder.addElapsedTimestampNanos(atomTimestamp.second);
+ }
+ return bucketInfoBuilder.build();
+ }
}
diff --git a/tests/src/android/cts/statsd/metric/MetricActivationTests.java b/tests/src/android/cts/statsd/metric/MetricActivationTests.java
index 06f95b9..5c6f2fb 100644
--- a/tests/src/android/cts/statsd/metric/MetricActivationTests.java
+++ b/tests/src/android/cts/statsd/metric/MetricActivationTests.java
@@ -16,32 +16,39 @@
package android.cts.statsd.metric;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
-import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.ActivationType;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventActivation;
import com.android.internal.os.StatsdConfigProto.EventMetric;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.MetricActivation;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.EventMetricData;
import com.android.os.StatsLog.StatsLogReport;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.RunUtil;
+
+import com.google.protobuf.ExtensionRegistry;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Test Statsd Metric activations and deactivations
*/
-public class MetricActivationTests extends DeviceAtomTestCase {
+public class MetricActivationTests extends DeviceTestCase {
private final long metric1Id = 1L;
private final int metric1MatcherId = 1;
@@ -57,6 +64,20 @@
private final int act2MatcherId = 20;
private final int act2CancelMatcherId = -20;
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ super.tearDown();
+ }
private StatsdConfig.Builder createConfig(final int act1TtlSecs, final int act2TtlSecs) {
AtomMatcher metric1Matcher =
@@ -65,49 +86,41 @@
MetricsUtils.simpleAtomMatcher(metric2MatcherId, metric2MatcherId);
AtomMatcher metric3Matcher =
MetricsUtils.simpleAtomMatcher(metric3MatcherId, metric3MatcherId);
- AtomMatcher act1Matcher =
- MetricsUtils.simpleAtomMatcher(act1MatcherId, act1MatcherId);
+ AtomMatcher act1Matcher = MetricsUtils.simpleAtomMatcher(act1MatcherId, act1MatcherId);
AtomMatcher act1CancelMatcher =
MetricsUtils.simpleAtomMatcher(act1CancelMatcherId, act1CancelMatcherId);
- AtomMatcher act2Matcher =
- MetricsUtils.simpleAtomMatcher(act2MatcherId, act2MatcherId);
+ AtomMatcher act2Matcher = MetricsUtils.simpleAtomMatcher(act2MatcherId, act2MatcherId);
AtomMatcher act2CancelMatcher =
MetricsUtils.simpleAtomMatcher(act2CancelMatcherId, act2CancelMatcherId);
- EventMetric metric1 = EventMetric.newBuilder()
- .setId(metric1Id)
- .setWhat(metric1MatcherId)
- .build();
+ EventMetric metric1 =
+ EventMetric.newBuilder().setId(metric1Id).setWhat(metric1MatcherId).build();
- EventMetric metric2 = EventMetric.newBuilder()
- .setId(metric2Id)
- .setWhat(metric2MatcherId)
- .build();
+ EventMetric metric2 =
+ EventMetric.newBuilder().setId(metric2Id).setWhat(metric2MatcherId).build();
- EventMetric metric3 = EventMetric.newBuilder()
- .setId(metric3Id)
- .setWhat(metric3MatcherId)
- .build();
+ EventMetric metric3 =
+ EventMetric.newBuilder().setId(metric3Id).setWhat(metric3MatcherId).build();
EventActivation metric1Act1 =
MetricsUtils.createEventActivation(act1TtlSecs, act1MatcherId, act1CancelMatcherId)
- .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
- .build();
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .build();
EventActivation metric1Act2 =
MetricsUtils.createEventActivation(act2TtlSecs, act2MatcherId, act2CancelMatcherId)
- .setActivationType(ActivationType.ACTIVATE_ON_BOOT)
- .build();
+ .setActivationType(ActivationType.ACTIVATE_ON_BOOT)
+ .build();
EventActivation metric2Act1 =
MetricsUtils.createEventActivation(act1TtlSecs, act1MatcherId, act1CancelMatcherId)
- .setActivationType(ActivationType.ACTIVATE_ON_BOOT)
- .build();
+ .setActivationType(ActivationType.ACTIVATE_ON_BOOT)
+ .build();
EventActivation metric2Act2 =
MetricsUtils.createEventActivation(act2TtlSecs, act2MatcherId, act2CancelMatcherId)
- .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
- .build();
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .build();
MetricActivation metric1Activation = MetricActivation.newBuilder()
.setMetricId(metric1Id)
@@ -121,7 +134,8 @@
.addEventActivation(metric2Act2)
.build();
- return createConfigBuilder()
+
+ return ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addAtomMatcher(metric1Matcher)
.addAtomMatcher(metric2Matcher)
.addAtomMatcher(metric3Matcher)
@@ -138,96 +152,113 @@
/**
* Metric 1:
- * Activation 1:
- * - Ttl: 5 seconds
- * - Type: IMMEDIATE
- * Activation 2:
- * - Ttl: 8 seconds
- * - Type: ON_BOOT
+ * Activation 1:
+ * - Ttl: 5 seconds
+ * - Type: IMMEDIATE
+ * Activation 2:
+ * - Ttl: 8 seconds
+ * - Type: ON_BOOT
*
* Metric 2:
- * Activation 1:
- * - Ttl: 5 seconds
- * - Type: ON_BOOT
- * Activation 2:
- * - Ttl: 8 seconds
- * - Type: IMMEDIATE
+ * Activation 1:
+ * - Ttl: 5 seconds
+ * - Type: ON_BOOT
+ * Activation 2:
+ * - Ttl: 8 seconds
+ * - Type: IMMEDIATE
*
* Metric 3: No activations; always active
**/
public void testCancellation() throws Exception {
final int act1TtlSecs = 5;
final int act2TtlSecs = 8;
- uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
+ ConfigUtils.uploadConfig(getDevice(), createConfig(act1TtlSecs, act2TtlSecs));
// Ignored, metric not active.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Trigger cancel for already inactive event activation 1.
- doAppBreadcrumbReported(act1CancelMatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1CancelMatcherId);
+ RunUtil.getDefault().sleep(10L);
// Trigger event activation 1.
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// First logged event.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Second logged event.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Cancel event activation 1.
- doAppBreadcrumbReported(act1CancelMatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1CancelMatcherId);
+ RunUtil.getDefault().sleep(10L);
// Ignored, metric not active.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Trigger event activation 1.
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Trigger event activation 2.
- doAppBreadcrumbReported(act2MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act2MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Third logged event.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Cancel event activation 2.
- doAppBreadcrumbReported(act2CancelMatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act2CancelMatcherId);
+ RunUtil.getDefault().sleep(10L);
// Fourth logged event.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Expire event activation 1
- Thread.sleep(act1TtlSecs * 1000);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000);
// Ignored, metric 1 not active. Activation 1 expired and Activation 2 was cancelled.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Trigger event activation 2.
- doAppBreadcrumbReported(act2MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act2MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Metric 1 log ignored, Activation 1 expired and Activation 2 needs reboot to activate.
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// First logged event for Metric 3.
- doAppBreadcrumbReported(metric3MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric3MatcherId);
+ RunUtil.getDefault().sleep(10L);
- ConfigMetricsReportList reportList = getReportList();
+ ConfigMetricsReportList reportList = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
ConfigMetricsReport report = reports.get(0);
verifyMetrics(report, 4, 0, 1);
@@ -235,27 +266,27 @@
/**
* Metric 1:
- * Activation 1:
- * - Ttl: 100 seconds
- * - Type: IMMEDIATE
- * Activation 2:
- * - Ttl: 200 seconds
- * - Type: ON_BOOT
+ * Activation 1:
+ * - Ttl: 100 seconds
+ * - Type: IMMEDIATE
+ * Activation 2:
+ * - Ttl: 200 seconds
+ * - Type: ON_BOOT
*
* Metric 2:
- * Activation 1:
- * - Ttl: 100 seconds
- * - Type: ON_BOOT
- * Activation 2:
- * - Ttl: 200 seconds
- * - Type: IMMEDIATE
+ * Activation 1:
+ * - Ttl: 100 seconds
+ * - Type: ON_BOOT
+ * Activation 2:
+ * - Ttl: 200 seconds
+ * - Type: IMMEDIATE
*
* Metric 3: No activations; always active
**/
public void testRestart() throws Exception {
final int act1TtlSecs = 200;
final int act2TtlSecs = 400;
- uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
+ ConfigUtils.uploadConfig(getDevice(), createConfig(act1TtlSecs, act2TtlSecs));
// Trigger Metric 1 Activation 1 and Metric 2 Activation 1.
// Time remaining:
@@ -263,8 +294,9 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 0 seconds
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// First logged event for Metric 1.
// Metric 2 event ignored, will activate after boot.
@@ -276,7 +308,7 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 200 seconds
// Metric 2 Activation 2: 0 seconds
- rebootDeviceAndWaitUntilReady();
+ DeviceUtils.rebootDeviceAndWaitUntilReady(getDevice());
// Second logged event for Metric 1.
// First logged event for Metric 2.
@@ -288,7 +320,7 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds
// Metric 2 Activation 2: 0 seconds
- Thread.sleep(act1TtlSecs * 1000L);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000L);
// Metric 1 event ignored, Activation 1 expired.
// Metric 2 event ignored, Activation 1 expired.
@@ -301,8 +333,9 @@
// Metric 1 Activation 2: 0 seconds (will activate after boot)
// Metric 2 Activation 1: 0 seconds
// Metric 2 Activation 2: 400 seconds
- doAppBreadcrumbReported(act2MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act2MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Metric 1 event ignored, will activate after boot.
// Second logged event for Metric 2.
@@ -315,8 +348,9 @@
// Metric 1 Activation 2: 0 seconds (will activate after boot)
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 400 seconds
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Third logged event for Metric 1.
// Third logged event for Metric 2.
@@ -328,14 +362,14 @@
// Metric 1 Activation 2: 0 seconds (will activate after boot)
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 300 seconds
- Thread.sleep(act1TtlSecs * 1000L / 2);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000L / 2);
// Time remaining:
// Metric 1 Activation 1: 100 seconds
// Metric 1 Activation 2: 400 seconds
// Metric 2 Activation 1: 200 seconds
// Metric 2 Activation 2: 300 seconds
- rebootDeviceAndWaitUntilReady();
+ DeviceUtils.rebootDeviceAndWaitUntilReady(getDevice());
// Fourth logged event for Metric 1.
// Fourth logged event for Metric 2.
@@ -348,7 +382,7 @@
// Metric 1 Activation 2: 300 seconds
// Metric 2 Activation 1: 100 seconds
// Metric 2 Activation 2: 200 seconds
- Thread.sleep(act1TtlSecs * 1000L / 2);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000L / 2);
// Fifth logged event for Metric 1.
// Fifth logged event for Metric 2.
@@ -361,14 +395,15 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds
// Metric 2 Activation 2: 0 seconds
- Thread.sleep(act2TtlSecs * 1000L);
+ RunUtil.getDefault().sleep(act2TtlSecs * 1000L);
// Metric 1 event ignored.
// Metric 2 event ignored.
// Eighth logged event for Metric 3.
logAllMetrics();
- ConfigMetricsReportList reportList = getReportList();
+ ConfigMetricsReportList reportList = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
assertThat(reports).hasSize(3);
@@ -387,27 +422,27 @@
/**
* Metric 1:
- * Activation 1:
- * - Ttl: 100 seconds
- * - Type: IMMEDIATE
- * Activation 2:
- * - Ttl: 200 seconds
- * - Type: ON_BOOT
+ * Activation 1:
+ * - Ttl: 100 seconds
+ * - Type: IMMEDIATE
+ * Activation 2:
+ * - Ttl: 200 seconds
+ * - Type: ON_BOOT
*
* Metric 2:
- * Activation 1:
- * - Ttl: 100 seconds
- * - Type: ON_BOOT
- * Activation 2:
- * - Ttl: 200 seconds
- * - Type: IMMEDIATE
+ * Activation 1:
+ * - Ttl: 100 seconds
+ * - Type: ON_BOOT
+ * Activation 2:
+ * - Ttl: 200 seconds
+ * - Type: IMMEDIATE
*
* Metric 3: No activations; always active
**/
public void testMultipleActivations() throws Exception {
final int act1TtlSecs = 200;
final int act2TtlSecs = 400;
- uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
+ ConfigUtils.uploadConfig(getDevice(), createConfig(act1TtlSecs, act2TtlSecs));
// Trigger Metric 1 Activation 1 and Metric 2 Activation 1.
// Time remaining:
@@ -415,8 +450,9 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 0 seconds
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// First logged event for Metric 1.
// Metric 2 event ignored, will activate after boot.
@@ -428,7 +464,7 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 0 seconds
- Thread.sleep(act1TtlSecs * 1000L / 2);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000L / 2);
// Second logged event for Metric 1.
// Metric 2 event ignored, will activate after boot.
@@ -441,8 +477,9 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds (will activate after boot)
// Metric 2 Activation 2: 0 seconds
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Third logged event for Metric 1.
// Metric 2 event ignored, will activate after boot.
@@ -454,7 +491,7 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 200 seconds
// Metric 2 Activation 2: 0 seconds
- rebootDeviceAndWaitUntilReady();
+ DeviceUtils.rebootDeviceAndWaitUntilReady(getDevice());
// Fourth logged event for Metric 1.
// First logged event for Metric 2.
@@ -467,8 +504,9 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 200 seconds
// Metric 2 Activation 2: 0 seconds
- doAppBreadcrumbReported(act1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), act1MatcherId);
+ RunUtil.getDefault().sleep(10L);
// Fifth logged event for Metric 1.
// Second logged event for Metric 2.
@@ -481,7 +519,7 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds
// Metric 2 Activation 2: 0 seconds
- Thread.sleep(act1TtlSecs * 1000L);
+ RunUtil.getDefault().sleep(act1TtlSecs * 1000L);
// Metric 1 event ignored.
// Metric 2 event ignored.
@@ -493,15 +531,16 @@
// Metric 1 Activation 2: 0 seconds
// Metric 2 Activation 1: 0 seconds
// Metric 2 Activation 2: 0 seconds
- rebootDeviceAndWaitUntilReady();
- Thread.sleep(6_000L);
+ DeviceUtils.rebootDeviceAndWaitUntilReady(getDevice());
+ RunUtil.getDefault().sleep(10_000L);
// Metric 1 event ignored.
// Metric 2 event ignored.
// Seventh logged event for Metric 3.
logAllMetrics();
- ConfigMetricsReportList reportList = getReportList();
+ ConfigMetricsReportList reportList = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
assertThat(reports).hasSize(3);
@@ -518,35 +557,45 @@
verifyMetrics(report, 0, 0, 1);
}
+ /**
+ * Gets a List of sorted ConfigMetricsReports from ConfigMetricsReportList.
+ */
+ private List<ConfigMetricsReport> getSortedConfigMetricsReports(
+ ConfigMetricsReportList configMetricsReportList) {
+ return configMetricsReportList.getReportsList().stream().sorted(
+ Comparator.comparing(ConfigMetricsReport::getCurrentReportWallClockNanos)).collect(
+ Collectors.toList());
+ }
+
private void logAllMetrics() throws Exception {
- doAppBreadcrumbReported(metric1MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric1MatcherId);
+ RunUtil.getDefault().sleep(10L);
- doAppBreadcrumbReported(metric2MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric2MatcherId);
+ RunUtil.getDefault().sleep(10L);
- doAppBreadcrumbReported(metric3MatcherId);
- Thread.sleep(10L);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), metric3MatcherId);
+ RunUtil.getDefault().sleep(10L);
}
private void verifyMetrics(ConfigMetricsReport report, int metric1Count, int metric2Count,
int metric3Count) throws Exception {
assertThat(report.getMetricsCount()).isEqualTo(3);
- verifyMetric(
- report.getMetrics(0), // StatsLogReport
+ verifyMetric(report.getMetrics(0), // StatsLogReport
1, // Metric Id
1, // Metric what atom matcher label
metric1Count // Data count
);
- verifyMetric(
- report.getMetrics(1), // StatsLogReport
+ verifyMetric(report.getMetrics(1), // StatsLogReport
2, // Metric Id
2, // Metric what atom matcher label
metric2Count // Data count
);
- verifyMetric(
- report.getMetrics(2), // StatsLogReport
+ verifyMetric(report.getMetrics(2), // StatsLogReport
3, // Metric Id
3, // Metric what atom matcher label
metric3Count // Data count
@@ -562,7 +611,8 @@
StatsLogReport.EventMetricDataWrapper eventData = metricReport.getEventMetrics();
List<EventMetricData> eventMetricDataList = new ArrayList<>();
for (EventMetricData eventMetricData : eventData.getDataList()) {
- eventMetricDataList.addAll(backfillAggregatedAtomsInEventMetric(eventMetricData));
+ eventMetricDataList.addAll(
+ ReportUtils.backfillAggregatedAtomsInEventMetric(eventMetricData));
}
assertThat(eventMetricDataList).hasSize(dataCount);
for (EventMetricData eventMetricData : eventMetricDataList) {
diff --git a/tests/src/android/cts/statsd/metric/MetricsUtils.java b/tests/src/android/cts/statsd/metric/MetricsUtils.java
index 8f559d2..010df49 100644
--- a/tests/src/android/cts/statsd/metric/MetricsUtils.java
+++ b/tests/src/android/cts/statsd/metric/MetricsUtils.java
@@ -17,6 +17,8 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import android.cts.statsd.atom.BufferDebug;
+
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventActivation;
@@ -24,11 +26,26 @@
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.util.RunUtil;
+
+import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
public class MetricsUtils {
+ public static final String DEVICE_SIDE_TEST_PACKAGE =
+ "com.android.server.cts.device.statsd";
+ public static final String DEVICE_SIDE_TEST_APK = "CtsStatsdApp.apk";
public static final long COUNT_METRIC_ID = 3333;
public static final long DURATION_METRIC_ID = 4444;
public static final long GAUGE_METRIC_ID = 5555;
@@ -38,20 +55,19 @@
public static AtomMatcher.Builder getAtomMatcher(int atomId) {
AtomMatcher.Builder builder = AtomMatcher.newBuilder();
builder.setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
- .setAtomId(atomId));
+ .setAtomId(atomId));
return builder;
}
public static AtomMatcher startAtomMatcher(int id) {
- return AtomMatcher.newBuilder()
- .setId(id)
- .setSimpleAtomMatcher(
- SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addFieldValueMatcher(FieldValueMatcher.newBuilder()
- .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(AppBreadcrumbReported.State.START.ordinal())))
- .build();
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .setEqInt(AppBreadcrumbReported.State.START.getNumber())))
+ .build();
}
public static AtomMatcher startAtomMatcherWithLabel(int id, int label) {
@@ -59,15 +75,14 @@
}
public static AtomMatcher stopAtomMatcher(int id) {
- return AtomMatcher.newBuilder()
- .setId(id)
- .setSimpleAtomMatcher(
- SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addFieldValueMatcher(FieldValueMatcher.newBuilder()
- .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(AppBreadcrumbReported.State.STOP.ordinal())))
- .build();
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
+ .setEqInt(AppBreadcrumbReported.State.STOP.getNumber())))
+ .build();
}
public static AtomMatcher stopAtomMatcherWithLabel(int id, int label) {
@@ -81,16 +96,16 @@
.setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
.addFieldValueMatcher(FieldValueMatcher.newBuilder()
.setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(AppBreadcrumbReported.State.UNSPECIFIED.ordinal())))
+ .setEqInt(AppBreadcrumbReported.State.UNSPECIFIED.getNumber())))
.build();
}
public static AtomMatcher simpleAtomMatcher(int id) {
- return AtomMatcher.newBuilder()
- .setId(id)
- .setSimpleAtomMatcher(
- SimpleAtomMatcher.newBuilder().setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER))
- .build();
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER))
+ .build();
}
public static AtomMatcher appBreadcrumbMatcherWithLabel(int id, int label) {
@@ -113,7 +128,7 @@
.setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
.addFieldValueMatcher(FieldValueMatcher.newBuilder()
.setField(AppBreadcrumbReported.STATE_FIELD_NUMBER)
- .setEqInt(state.ordinal()))
+ .setEqInt(state.getNumber()))
.addFieldValueMatcher(FieldValueMatcher.newBuilder()
.setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
.setEqInt(label)))
@@ -121,16 +136,16 @@
}
public static AtomMatcher simpleAtomMatcher(int id, int label) {
- return AtomMatcher.newBuilder()
- .setId(id)
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addFieldValueMatcher(FieldValueMatcher.newBuilder()
- .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
- .setEqInt(label)
- )
- )
- .build();
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+ .setEqInt(label)
+ )
+ )
+ .build();
}
public static EventActivation.Builder createEventActivation(int ttlSecs, int matcherId,
@@ -142,7 +157,14 @@
}
public static long StringToId(String str) {
- return str.hashCode();
+ return str.hashCode();
+ }
+
+ public static String getCurrentLogcatDate(ITestDevice device) throws Exception {
+ // TODO: Do something more robust than this for getting logcat markers.
+ long timestampMs = device.getDeviceDate();
+ return new SimpleDateFormat("MM-dd HH:mm:ss.SSS")
+ .format(new Date(timestampMs));
}
public static void assertBucketTimePresent(Message bucketInfo) {
@@ -154,11 +176,60 @@
if (bucketNum != null && bucketInfo.hasField(bucketNum)) {
found = true;
} else if (startMillis != null && bucketInfo.hasField(startMillis) &&
- endMillis != null && bucketInfo.hasField(endMillis)) {
+ endMillis != null && bucketInfo.hasField(endMillis)) {
found = true;
}
assertWithMessage(
"Bucket info did not have either bucket num or start and end elapsed millis"
).that(found).isTrue();
}
+
+ public static boolean didIncidentdFireSince(ITestDevice device, String date) throws Exception {
+ final String INCIDENTD_TAG = "incidentd";
+ final String INCIDENTD_STARTED_STRING = "reportIncident";
+ // TODO: Do something more robust than this in case of delayed logging.
+ RunUtil.getDefault().sleep(1000);
+ String log = getLogcatSince(device, date, String.format(
+ "-s %s -e %s", INCIDENTD_TAG, INCIDENTD_STARTED_STRING));
+ return log.contains(INCIDENTD_STARTED_STRING);
+ }
+
+ public static String getLogcatSince(ITestDevice device, String date, String logcatParams)
+ throws Exception {
+ return device.executeShellCommand(String.format(
+ "logcat -v threadtime -t '%s' -d %s", date, logcatParams));
+ }
+
+ /**
+ * Call onto the device with an adb shell command and get the results of
+ * that as a proto of the given type.
+ *
+ * @param parser A protobuf parser object. e.g. MyProto.parser()
+ * @param command The adb shell command to run. e.g. "dumpsys fingerprint --proto"
+ * @throws DeviceNotAvailableException If there was a problem communicating with
+ * the test device.
+ * @throws InvalidProtocolBufferException If there was an error parsing
+ * the proto. Note that a 0 length buffer is not
+ * necessarily an error.
+ */
+ public static <T extends MessageLite> T getDump(ITestDevice device, Parser<T> parser,
+ String command)
+ throws DeviceNotAvailableException, InvalidProtocolBufferException {
+ final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+ device.executeShellCommand(command, receiver);
+ if (false) {
+ LogUtil.CLog.d("Command output while parsing " + parser.getClass().getCanonicalName()
+ + " for command: " + command + "\n"
+ + BufferDebug.debugString(receiver.getOutput(), -1));
+ }
+ try {
+ return parser.parseFrom(receiver.getOutput());
+ } catch (Exception ex) {
+ LogUtil.CLog.d(
+ "Error parsing " + parser.getClass().getCanonicalName() + " for command: "
+ + command
+ + BufferDebug.debugString(receiver.getOutput(), 16384));
+ throw ex;
+ }
+ }
}
diff --git a/tests/src/android/cts/statsd/metric/ValueMetricsTests.java b/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
index 3b4ef56..4ac1d5e 100644
--- a/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/tests/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -17,14 +17,14 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import com.android.internal.os.StatsdConfigProto.ActivationType;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventActivation;
-import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.MetricActivation;
import com.android.internal.os.StatsdConfigProto.Predicate;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
@@ -41,286 +41,320 @@
import com.android.os.StatsLog.ValueMetricData;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.RunUtil;
-public class ValueMetricsTests extends DeviceAtomTestCase {
- private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
- private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
- private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
+import com.google.protobuf.ExtensionRegistry;
- public void testValueMetric() throws Exception {
- // Add AtomMatcher's.
- AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
- AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
- AtomMatcher atomMatcher =
- MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
+public class ValueMetricsTests extends DeviceTestCase {
+ private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0;
+ private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1;
+ private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
- StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(startAtomMatcher);
- builder.addAtomMatcher(stopAtomMatcher);
- builder.addAtomMatcher(atomMatcher);
-
- // Add ValueMetric.
- builder.addValueMetric(
- ValueMetric.newBuilder()
- .setId(MetricsUtils.VALUE_METRIC_ID)
- .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .setBucket(TimeUnit.CTS)
- .setValueField(FieldMatcher.newBuilder()
- .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder().setField(
- AppBreadcrumbReported.LABEL_FIELD_NUMBER)))
- .setDimensionsInWhat(FieldMatcher.newBuilder()
- .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
- .build())
- .build());
-
- // Upload config.
- uploadConfig(builder);
-
- // Create AppBreadcrumbReported Start/Stop events.
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(1000);
- doAppBreadcrumbReportedStop(1);
- doAppBreadcrumbReportedStart(3);
- doAppBreadcrumbReportedStop(3);
-
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(1000);
-
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
- assertThat(metricReport.hasValueMetrics()).isTrue();
- StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertThat(valueData.getDataCount()).isEqualTo(1);
-
- int bucketCount = valueData.getData(0).getBucketInfoCount();
- assertThat(bucketCount).isGreaterThan(1);
- ValueMetricData data = valueData.getData(0);
- int totalValue = 0;
- for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
- MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
- assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
- totalValue += (int) bucketInfo.getValues(0).getValueLong();
- }
- assertThat(totalValue).isEqualTo(8);
- }
-
- // Test value metric with pulled atoms and across multiple buckets
- public void testPullerAcrossBuckets() throws Exception {
- // Add AtomMatcher's.
- final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
- final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
- final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP";
-
- AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcher(predicateTrueName.hashCode());
- AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
-
- StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(startAtomMatcher);
- builder.addAtomMatcher(stopAtomMatcher);
- builder.addPredicate(Predicate.newBuilder()
- .setId(predicateName.hashCode())
- .setSimplePredicate(SimplePredicate.newBuilder()
- .setStart(predicateTrueName.hashCode())
- .setStop(predicateFalseName.hashCode())
- .setCountNesting(false)
- )
- );
-
- final String atomName = "SYSTEM_ELAPSED_REALTIME";
- SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
- builder.addAtomMatcher(AtomMatcher.newBuilder()
- .setId(atomName.hashCode())
- .setSimpleAtomMatcher(sam));
-
- // Add ValueMetric.
- builder.addValueMetric(
- ValueMetric.newBuilder()
- .setId(MetricsUtils.VALUE_METRIC_ID)
- .setWhat(atomName.hashCode())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setValueField(FieldMatcher.newBuilder()
- .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder().setField(
- SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
- .setCondition(predicateName.hashCode())
- .build());
-
- // Upload config.
- uploadConfig(builder);
-
- // Create AppBreadcrumbReported Start/Stop events.
- doAppBreadcrumbReportedStart(1);
- // Wait for 2 min and 1 sec to capture at least 2 buckets
- Thread.sleep(2*60_000 + 10_000);
- doAppBreadcrumbReportedStop(1);
-
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(1_000);
-
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
- assertThat(metricReport.hasValueMetrics()).isTrue();
- StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertThat(valueData.getDataCount()).isEqualTo(1);
-
- int bucketCount = valueData.getData(0).getBucketInfoCount();
- // should have at least 2 buckets
- assertThat(bucketCount).isAtLeast(2);
- ValueMetricData data = valueData.getData(0);
- int totalValue = 0;
- for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
- MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
- assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
- totalValue += (int) bucketInfo.getValues(0).getValueLong();
- }
- // At most we lose one full min bucket
- assertThat(totalValue).isGreaterThan(130_000 - 60_000);
- }
-
- // Test value metric with pulled atoms and across multiple buckets
- public void testMultipleEventsPerBucket() throws Exception {
- // Add AtomMatcher's.
- final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
- final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
- final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP";
-
- AtomMatcher startAtomMatcher =
- MetricsUtils.startAtomMatcher(predicateTrueName.hashCode());
- AtomMatcher stopAtomMatcher =
- MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
-
- StatsdConfig.Builder builder = createConfigBuilder();
- builder.addAtomMatcher(startAtomMatcher);
- builder.addAtomMatcher(stopAtomMatcher);
- builder.addPredicate(Predicate.newBuilder()
- .setId(predicateName.hashCode())
- .setSimplePredicate(SimplePredicate.newBuilder()
- .setStart(predicateTrueName.hashCode())
- .setStop(predicateFalseName.hashCode())
- .setCountNesting(false)
- )
- );
-
- final String atomName = "SYSTEM_ELAPSED_REALTIME";
- SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
- builder.addAtomMatcher(AtomMatcher.newBuilder()
- .setId(atomName.hashCode())
- .setSimpleAtomMatcher(sam));
-
- // Add ValueMetric.
- builder.addValueMetric(
- ValueMetric.newBuilder()
- .setId(MetricsUtils.VALUE_METRIC_ID)
- .setWhat(atomName.hashCode())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setValueField(FieldMatcher.newBuilder()
- .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder().setField(
- SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
- .setCondition(predicateName.hashCode())
- .build());
-
- // Upload config.
- uploadConfig(builder);
-
- final int NUM_EVENTS = 10;
- final long GAP_INTERVAL = 10_000;
- // Create AppBreadcrumbReported Start/Stop events.
- for (int i = 0; i < NUM_EVENTS; i ++) {
- doAppBreadcrumbReportedStart(1);
- Thread.sleep(GAP_INTERVAL);
- doAppBreadcrumbReportedStop(1);
- Thread.sleep(GAP_INTERVAL);
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ RunUtil.getDefault().sleep(1000);
}
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(1_000);
-
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
- assertThat(metricReport.hasValueMetrics()).isTrue();
- StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertThat(valueData.getDataCount()).isEqualTo(1);
-
- int bucketCount = valueData.getData(0).getBucketInfoCount();
- // should have at least 2 buckets
- assertThat(bucketCount).isAtLeast(2);
- ValueMetricData data = valueData.getData(0);
- int totalValue = 0;
- for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
- MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
- assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
- totalValue += (int) bucketInfo.getValues(0).getValueLong();
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ super.tearDown();
}
- // At most we lose one full min bucket
- assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000);
- }
-
- // Test value metric with pulled atoms and across multiple buckets
- public void testPullerAcrossBucketsWithActivation() throws Exception {
- StatsdConfig.Builder builder = createConfigBuilder();
-
- // Add AtomMatcher's.
- int activationAtomMatcherId = 1;
- int activationAtomMatcherLabel = 1;
- AtomMatcher activationAtomMatcher =
- MetricsUtils.appBreadcrumbMatcherWithLabel(
- activationAtomMatcherId, activationAtomMatcherLabel);
- final String atomName = "SYSTEM_ELAPSED_REALTIME";
- SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
- builder.addAtomMatcher(activationAtomMatcher)
- .addAtomMatcher(AtomMatcher.newBuilder()
- .setId(atomName.hashCode())
- .setSimpleAtomMatcher(sam));
-
- // Add ValueMetric.
- builder.addValueMetric(
- ValueMetric.newBuilder()
- .setId(MetricsUtils.VALUE_METRIC_ID)
- .setWhat(atomName.hashCode())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setValueField(FieldMatcher.newBuilder()
- .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
- .addChild(FieldMatcher.newBuilder().setField(
- SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
- .build());
- // Add activation.
- builder.addMetricActivation(MetricActivation.newBuilder()
- .setMetricId(MetricsUtils.VALUE_METRIC_ID)
- .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
- .addEventActivation(EventActivation.newBuilder()
- .setAtomMatcherId(activationAtomMatcherId)
- .setTtlSeconds(5)));
- // Upload config.
- uploadConfig(builder);
+ public void testValueMetric() throws Exception {
+ // Add AtomMatcher's.
+ AtomMatcher startAtomMatcher =
+ MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
+ AtomMatcher stopAtomMatcher =
+ MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID);
+ AtomMatcher atomMatcher =
+ MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID);
- // Wait for 1 min and 10 sec to capture at least 1 bucket
- Thread.sleep(60_000 + 10_000);
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ builder.addAtomMatcher(startAtomMatcher);
+ builder.addAtomMatcher(stopAtomMatcher);
+ builder.addAtomMatcher(atomMatcher);
- // Wait for the metrics to propagate to statsd.
- Thread.sleep(1_000);
+ // Add ValueMetric.
+ builder.addValueMetric(ValueMetric.newBuilder()
+ .setId(MetricsUtils.VALUE_METRIC_ID)
+ .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .setBucket(TimeUnit.CTS)
+ .setValueField(FieldMatcher.newBuilder()
+ .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ AppBreadcrumbReported.LABEL_FIELD_NUMBER)))
+ .setDimensionsInWhat(FieldMatcher.newBuilder()
+ .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID)
+ .build())
+ .build());
- StatsLogReport metricReport = getStatsLogReport();
- LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
- assertThat(metricReport.getValueMetrics().getDataList()).isEmpty();
- // Skipped buckets are not added when metric is empty.
- assertThat(metricReport.getValueMetrics().getSkippedList()).isEmpty();
- }
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
+
+ // Create AppBreadcrumbReported Start/Stop events.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(1000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 3);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 3);
+
+ // Wait for the metrics to propagate to statsd.
+ RunUtil.getDefault().sleep(1000);
+
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
+ StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
+ assertThat(valueData.getDataCount()).isEqualTo(1);
+
+ int bucketCount = valueData.getData(0).getBucketInfoCount();
+ assertThat(bucketCount).isGreaterThan(1);
+ ValueMetricData data = valueData.getData(0);
+ int totalValue = 0;
+ for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
+ MetricsUtils.assertBucketTimePresent(bucketInfo);
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
+ totalValue += (int) bucketInfo.getValues(0).getValueLong();
+ }
+ assertThat(totalValue).isEqualTo(8);
+ }
+
+ // Test value metric with pulled atoms and across multiple buckets
+ public void testPullerAcrossBuckets() throws Exception {
+ // Add AtomMatcher's.
+ final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
+ final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
+ final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP";
+
+ AtomMatcher startAtomMatcher =
+ MetricsUtils.startAtomMatcher(predicateTrueName.hashCode());
+ AtomMatcher stopAtomMatcher =
+ MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
+
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ builder.addAtomMatcher(startAtomMatcher);
+ builder.addAtomMatcher(stopAtomMatcher);
+ builder.addPredicate(Predicate.newBuilder()
+ .setId(predicateName.hashCode())
+ .setSimplePredicate(SimplePredicate.newBuilder()
+ .setStart(predicateTrueName.hashCode())
+ .setStop(predicateFalseName.hashCode())
+ .setCountNesting(false)
+ )
+ );
+
+ final String atomName = "SYSTEM_ELAPSED_REALTIME";
+ SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(
+ Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
+ builder.addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+
+ // Add ValueMetric.
+ builder.addValueMetric(ValueMetric.newBuilder()
+ .setId(MetricsUtils.VALUE_METRIC_ID)
+ .setWhat(atomName.hashCode())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setValueField(FieldMatcher.newBuilder()
+ .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
+ .setCondition(predicateName.hashCode())
+ .build());
+
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
+
+ // Create AppBreadcrumbReported Start/Stop events.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ // Wait for 2 min and 1 sec to capture at least 2 buckets
+ RunUtil.getDefault().sleep(2 * 60_000 + 10_000);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+
+ // Wait for the metrics to propagate to statsd.
+ RunUtil.getDefault().sleep(1_000);
+
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
+ StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
+ assertThat(valueData.getDataCount()).isEqualTo(1);
+
+ int bucketCount = valueData.getData(0).getBucketInfoCount();
+ // should have at least 2 buckets
+ assertThat(bucketCount).isAtLeast(2);
+ ValueMetricData data = valueData.getData(0);
+ int totalValue = 0;
+ for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
+ MetricsUtils.assertBucketTimePresent(bucketInfo);
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
+ totalValue += (int) bucketInfo.getValues(0).getValueLong();
+ }
+ // At most we lose one full min bucket
+ assertThat(totalValue).isGreaterThan(130_000 - 60_000);
+ }
+
+ // Test value metric with pulled atoms and across multiple buckets
+ public void testMultipleEventsPerBucket() throws Exception {
+ // Add AtomMatcher's.
+ final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
+ final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
+ final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP";
+
+ AtomMatcher startAtomMatcher =
+ MetricsUtils.startAtomMatcher(predicateTrueName.hashCode());
+ AtomMatcher stopAtomMatcher =
+ MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode());
+
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ builder.addAtomMatcher(startAtomMatcher);
+ builder.addAtomMatcher(stopAtomMatcher);
+ builder.addPredicate(Predicate.newBuilder()
+ .setId(predicateName.hashCode())
+ .setSimplePredicate(SimplePredicate.newBuilder()
+ .setStart(predicateTrueName.hashCode())
+ .setStop(predicateFalseName.hashCode())
+ .setCountNesting(false)
+ )
+ );
+
+ final String atomName = "SYSTEM_ELAPSED_REALTIME";
+ SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(
+ Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
+ builder.addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+
+ // Add ValueMetric.
+ builder.addValueMetric(ValueMetric.newBuilder()
+ .setId(MetricsUtils.VALUE_METRIC_ID)
+ .setWhat(atomName.hashCode())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setValueField(FieldMatcher.newBuilder()
+ .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
+ .setCondition(predicateName.hashCode())
+ .build());
+
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
+
+ final int NUM_EVENTS = 10;
+ final long GAP_INTERVAL = 10_000;
+ // Create AppBreadcrumbReported Start/Stop events.
+ for (int i = 0; i < NUM_EVENTS; i++) {
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(GAP_INTERVAL);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), 1);
+ RunUtil.getDefault().sleep(GAP_INTERVAL);
+ }
+
+ // Wait for the metrics to propagate to statsd.
+ RunUtil.getDefault().sleep(1_000);
+
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
+ StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
+ assertThat(valueData.getDataCount()).isEqualTo(1);
+
+ int bucketCount = valueData.getData(0).getBucketInfoCount();
+ // should have at least 2 buckets
+ assertThat(bucketCount).isAtLeast(2);
+ ValueMetricData data = valueData.getData(0);
+ int totalValue = 0;
+ for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
+ MetricsUtils.assertBucketTimePresent(bucketInfo);
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
+ totalValue += (int) bucketInfo.getValues(0).getValueLong();
+ }
+ // At most we lose one full min bucket
+ assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000);
+ }
+
+ // Test value metric with pulled atoms and across multiple buckets
+ public void testPullerAcrossBucketsWithActivation() throws Exception {
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+
+ // Add AtomMatcher's.
+ int activationAtomMatcherId = 1;
+ int activationAtomMatcherLabel = 1;
+ AtomMatcher activationAtomMatcher =
+ MetricsUtils.appBreadcrumbMatcherWithLabel(
+ activationAtomMatcherId, activationAtomMatcherLabel);
+ final String atomName = "SYSTEM_ELAPSED_REALTIME";
+ SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER);
+ builder.addAtomMatcher(activationAtomMatcher)
+ .addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(atomName.hashCode())
+ .setSimpleAtomMatcher(sam));
+
+ // Add ValueMetric.
+ builder.addValueMetric(ValueMetric.newBuilder()
+ .setId(MetricsUtils.VALUE_METRIC_ID)
+ .setWhat(atomName.hashCode())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setValueField(FieldMatcher.newBuilder()
+ .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder().setField(
+ SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER)))
+ .build());
+ // Add activation.
+ builder.addMetricActivation(MetricActivation.newBuilder()
+ .setMetricId(MetricsUtils.VALUE_METRIC_ID)
+ .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY)
+ .addEventActivation(EventActivation.newBuilder()
+ .setAtomMatcherId(activationAtomMatcherId)
+ .setTtlSeconds(5)));
+
+
+ // Upload config.
+ ConfigUtils.uploadConfig(getDevice(), builder);
+
+ // Wait for 1 min and 10 sec to capture at least 1 bucket
+ RunUtil.getDefault().sleep(60_000 + 10_000);
+
+ // Wait for the metrics to propagate to statsd.
+ RunUtil.getDefault().sleep(1_000);
+
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
+ LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.getValueMetrics().getDataList()).isEmpty();
+ // Skipped buckets are not added when metric is empty.
+ assertThat(metricReport.getValueMetrics().getSkippedList()).isEmpty();
+ }
public void testValueMetricWithConditionAndActivation() throws Exception {
final int conditionLabel = 2;
@@ -340,7 +374,8 @@
AtomMatcher whatMatcher =
MetricsUtils.unspecifiedAtomMatcher(whatMatcherId);
- StatsdConfig.Builder builder = createConfigBuilder()
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE)
.addAtomMatcher(conditionStartAtomMatcher)
.addAtomMatcher(conditionStopAtomMatcher)
.addAtomMatcher(whatMatcher)
@@ -352,14 +387,13 @@
.setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID)
.build();
Predicate predicate = Predicate.newBuilder()
- .setId(MetricsUtils.StringToId("Predicate"))
- .setSimplePredicate(simplePredicate)
- .build();
+ .setId(MetricsUtils.StringToId("Predicate"))
+ .setSimplePredicate(simplePredicate)
+ .build();
builder.addPredicate(predicate);
// Add ValueMetric.
- builder
- .addValueMetric(ValueMetric.newBuilder()
+ builder.addValueMetric(ValueMetric.newBuilder()
.setId(MetricsUtils.VALUE_METRIC_ID)
.setWhat(whatMatcher.getId())
.setBucket(TimeUnit.ONE_MINUTE)
@@ -380,66 +414,79 @@
)
);
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
// Activate the metric.
- doAppBreadcrumbReportedStart(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Set the condition to true.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Skipped due to unknown condition at start of bucket.
- doAppBreadcrumbReported(10);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 10);
+ RunUtil.getDefault().sleep(10);
// Skipped due to unknown condition at start of bucket.
- doAppBreadcrumbReported(200);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 200);
+ RunUtil.getDefault().sleep(10);
// Set the condition to false.
- doAppBreadcrumbReportedStop(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should not be counted because condition is false.
- doAppBreadcrumbReported(3_000);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 3_000);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Log an event that should not be counted.
- doAppBreadcrumbReported(40_000);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 40_000);
+ RunUtil.getDefault().sleep(10);
// Condition to true again.
- doAppBreadcrumbReportedStart(conditionLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), conditionLabel);
+ RunUtil.getDefault().sleep(10);
// Event should not be counted, metric is still not active.
- doAppBreadcrumbReported(500_000);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 500_000);
+ RunUtil.getDefault().sleep(10);
// Activate the metric.
- doAppBreadcrumbReportedStart(activationMatcherLabel);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel);
+ RunUtil.getDefault().sleep(10);
// Log an event that should be counted.
- doAppBreadcrumbReported(6_000_000);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 6_000_000);
+ RunUtil.getDefault().sleep(10);
// Let the metric deactivate.
- Thread.sleep(ttlSec * 1000);
+ RunUtil.getDefault().sleep(ttlSec * 1000);
// Log an event that should not be counted.
- doAppBreadcrumbReported(70_000_000);
- Thread.sleep(10);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 70_000_000);
+ RunUtil.getDefault().sleep(10);
// Wait for the metrics to propagate to statsd.
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
- StatsLogReport metricReport = getStatsLogReport();
+ StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
assertThat(metricReport.hasValueMetrics()).isTrue();
diff --git a/tests/src/android/cts/statsd/restricted/ReadRestrictedStatsPermissionTest.java b/tests/src/android/cts/statsd/restricted/ReadRestrictedStatsPermissionTest.java
new file mode 100644
index 0000000..8e563b9
--- /dev/null
+++ b/tests/src/android/cts/statsd/restricted/ReadRestrictedStatsPermissionTest.java
@@ -0,0 +1,44 @@
+package android.cts.statsd.restricted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
+
+/**
+ * Tests Suite for restricted stats permissions.
+ */
+public class ReadRestrictedStatsPermissionTest extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ public void testReadRestrictedStatsPermission() throws Exception {
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ ".RestrictedPermissionTests", "testReadRestrictedStatsPermission");
+ }
+}
diff --git a/tests/src/android/cts/statsd/subscriber/ShellSubscriberTest.java b/tests/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
index d3c142b..a79b2b0 100644
--- a/tests/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
+++ b/tests/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
@@ -16,12 +16,11 @@
package android.cts.statsd.subscriber;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import com.android.compatibility.common.util.CpuFeatures;
import com.android.internal.os.StatsdConfigProto;
+import com.android.os.AtomsProto;
import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.SystemUptime;
import com.android.os.ShellConfig;
import com.android.os.statsd.ShellDataProto;
import com.android.tradefed.device.CollectingByteOutputReceiver;
@@ -29,6 +28,8 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil;
import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.RunUtil;
+
import com.google.common.io.Files;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -37,18 +38,20 @@
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
-import android.cts.statsd.atom.AtomTestCase;
+
+import android.cts.statsdatom.lib.AtomTestUtils;
/**
* Statsd shell data subscription test.
*/
-public class ShellSubscriberTest extends AtomTestCase {
+public class ShellSubscriberTest extends DeviceTestCase {
private int sizetBytes;
public class ShellSubscriptionThread extends Thread {
String cmd;
CollectingByteOutputReceiver receiver;
int maxTimeoutForCommandSec;
+
public ShellSubscriptionThread(
String cmd,
CollectingByteOutputReceiver receiver,
@@ -57,7 +60,8 @@
this.receiver = receiver;
this.maxTimeoutForCommandSec = maxTimeoutForCommandSec;
}
- public void run () {
+
+ public void run() {
try {
getDevice().executeShellCommand(cmd, receiver, maxTimeoutForCommandSec,
/*maxTimeToOutputShellResponse=*/maxTimeoutForCommandSec, TimeUnit.SECONDS,
@@ -124,27 +128,28 @@
String cmd = "cat " + remotePath + " | cmd stats data-subscribe " + timeout;
String firstSubCmd =
- "cat " + remotePath + " | cmd stats data-subscribe " + firstSubTimeout;
+ "cat " + remotePath + " | cmd stats data-subscribe " + firstSubTimeout;
for (int i = 0; i < maxSubs; i++) {
// Run data-subscribe on a thread
receivers[i] = new CollectingByteOutputReceiver();
if (i == 0) {
shellThreads[i] =
- new ShellSubscriptionThread(firstSubCmd, receivers[i], firstSubTimeout);
+ new ShellSubscriptionThread(firstSubCmd, receivers[i], firstSubTimeout);
} else {
shellThreads[i] =
- new ShellSubscriptionThread(cmd, receivers[i], timeout);
+ new ShellSubscriptionThread(cmd, receivers[i], timeout);
}
shellThreads[i].start();
LogUtil.CLog.d("Starting new shell subscription.");
}
// Sleep 2 seconds to make sure all subscription clients are initialized before
// first pushed event
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
// Pushed event. arbitrary label = 1
- doAppBreadcrumbReported(1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 1);
// Make sure the last 19 threads die before moving to the next step.
// First subscription is still active due to its longer timeout that is used keep
@@ -163,26 +168,27 @@
// Run data-subscribe on a thread
receivers[i] = new CollectingByteOutputReceiver();
shellThreads[i] =
- new ShellSubscriptionThread(cmd, receivers[i], timeout);
+ new ShellSubscriptionThread(cmd, receivers[i], timeout);
shellThreads[i].start();
LogUtil.CLog.d("Starting new shell subscription.");
}
// Sleep 2 seconds to make sure all subscription clients are initialized before
// pushed event
- Thread.sleep(2000);
+ RunUtil.getDefault().sleep(2000);
// ShellSubscriber only allows 20 subscriptions at a time. This is the 21st which will
// be ignored
receivers[maxSubs] = new CollectingByteOutputReceiver();
shellThreads[maxSubs] =
- new ShellSubscriptionThread(cmd, receivers[maxSubs], timeout);
+ new ShellSubscriptionThread(cmd, receivers[maxSubs], timeout);
shellThreads[maxSubs].start();
// Sleep 1 seconds to ensure that the 21st subscription is rejected
- Thread.sleep(1000);
+ RunUtil.getDefault().sleep(1000);
// Pushed event. arbitrary label = 1
- doAppBreadcrumbReported(1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 1);
// Make sure all the threads die before moving to the next step
for (int i = 0; i <= maxSubs; i++) {
@@ -219,7 +225,7 @@
private ShellConfig.ShellSubscription createConfig() {
return ShellConfig.ShellSubscription.newBuilder()
.addPushed((StatsdConfigProto.SimpleAtomMatcher.newBuilder()
- .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER))
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER))
.build()).build();
}
@@ -260,15 +266,16 @@
String cmd = "cat " + remotePath + " | cmd stats data-subscribe " + timeout;
// Run data-subscribe on a thread
ShellSubscriptionThread shellThread =
- new ShellSubscriptionThread(cmd, receiver, timeout);
+ new ShellSubscriptionThread(cmd, receiver, timeout);
shellThread.start();
LogUtil.CLog.d("Starting new shell subscription.");
// Sleep a second to make sure subscription is initiated
- Thread.sleep(1000);
+ RunUtil.getDefault().sleep(1000);
// Pushed event. arbitrary label = 1
- doAppBreadcrumbReported(1);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 1);
// Wait for thread to die before returning
shellThread.join();
// Remove config from device if not already deleted
@@ -319,7 +326,7 @@
assertThat(data.getAtom(0).hasAppBreadcrumbReported()).isTrue();
assertThat(data.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(1);
assertThat(data.getAtom(0).getAppBreadcrumbReported().getState().getNumber())
- .isEqualTo(1);
+ .isEqualTo(1);
atomCount++;
startIndex += sizetBytes + dataLength;
}
diff --git a/tests/src/android/cts/statsd/uidmap/UidMapTests.java b/tests/src/android/cts/statsd/uidmap/UidMapTests.java
index 4ceefa7..595a987 100644
--- a/tests/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/tests/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -17,7 +17,12 @@
import static com.google.common.truth.Truth.assertThat;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.internal.os.StatsdConfigProto;
import com.android.os.AtomsProto;
@@ -25,18 +30,54 @@
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.UidMapping;
import com.android.os.StatsLog.UidMapping.PackageInfoSnapshot;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
-import java.util.List;
+import com.google.protobuf.ExtensionRegistry;
-public class UidMapTests extends DeviceAtomTestCase {
+public class UidMapTests extends DeviceTestCase implements IBuildReceiver {
+
+ private static final long DEVICE_SIDE_TEST_PKG_HASH =
+ Long.parseUnsignedLong("15694052924544098582");
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
// Tests that every report has at least one snapshot.
public void testUidSnapshotIncluded() throws Exception {
// There should be at least the test app installed during the test setup.
- createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
- ConfigMetricsReportList reports = getReportList();
+ ConfigMetricsReportList reports = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(reports.getReportsCount()).isGreaterThan(0);
for (ConfigMetricsReport report : reports.getReportsList()) {
@@ -64,21 +105,24 @@
// Tests that delta event included during app installation.
public void testChangeFromInstallation() throws Exception {
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
- createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
+ getDevice().uninstallPackage(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
// Install the package after the config is sent to statsd. The uid map is not guaranteed to
// be updated if there's no config in statsd.
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
final String result = getDevice().installPackage(
- buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), false, true);
+ buildHelper.getTestFile(MetricsUtils.DEVICE_SIDE_TEST_APK), false, true);
- Thread.sleep(WAIT_TIME_LONG);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
- ConfigMetricsReportList reports = getReportList();
+ ConfigMetricsReportList reports = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
- int uid = getUid();
+ int uid = DeviceUtils.getAppUid(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
for (ConfigMetricsReport report : reports.getReportsList()) {
LogUtil.CLog.d("Got the following report: \n" + report.toString());
if (hasMatchingChange(report.getUidMap(), uid, false)) {
@@ -91,18 +135,23 @@
// We check that a re-installation gives a change event (similar to an app upgrade).
public void testChangeFromReinstall() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- getDevice().installPackage(buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), false, true);
- createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
+ getDevice().installPackage(buildHelper.getTestFile(MetricsUtils.DEVICE_SIDE_TEST_APK),
+ false, true);
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
// Now enable re-installation.
- getDevice().installPackage(buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), true, true);
+ getDevice().installPackage(buildHelper.getTestFile(MetricsUtils.DEVICE_SIDE_TEST_APK), true,
+ true);
- Thread.sleep(WAIT_TIME_LONG);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
- ConfigMetricsReportList reports = getReportList();
+ ConfigMetricsReportList reports = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
- int uid = getUid();
+ int uid = DeviceUtils.getAppUid(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
for (ConfigMetricsReport report : reports.getReportsList()) {
LogUtil.CLog.d("Got the following report: \n" + report.toString());
if (hasMatchingChange(report.getUidMap(), uid, false)) {
@@ -114,14 +163,19 @@
public void testChangeFromUninstall() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- getDevice().installPackage(buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), true, true);
- createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
- int uid = getUid();
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ getDevice().installPackage(buildHelper.getTestFile(MetricsUtils.DEVICE_SIDE_TEST_APK), true,
+ true);
- Thread.sleep(WAIT_TIME_LONG);
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
+ int uid = DeviceUtils.getAppUid(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ getDevice().uninstallPackage(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
- ConfigMetricsReportList reports = getReportList();
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
+
+ ConfigMetricsReportList reports = ReportUtils.getReportList(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
diff --git a/tests/src/android/cts/statsd/validation/DirectoryValidationTest.java b/tests/src/android/cts/statsd/validation/DirectoryValidationTest.java
index 37ded0b..4fa5e2a 100644
--- a/tests/src/android/cts/statsd/validation/DirectoryValidationTest.java
+++ b/tests/src/android/cts/statsd/validation/DirectoryValidationTest.java
@@ -1,34 +1,64 @@
package android.cts.statsd.validation;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
/**
* Tests Suite for directories used by Statsd.
*/
-public class DirectoryValidationTest extends DeviceAtomTestCase {
+public class DirectoryValidationTest extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
public void testStatsActiveMetricDirectoryExists() throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".DirectoryTests", "testStatsActiveMetricDirectoryExists");
}
public void testStatsDataDirectoryExists() throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".DirectoryTests", "testStatsDataDirectoryExists");
}
public void testStatsMetadataDirectoryExists() throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".DirectoryTests", "testStatsMetadataDirectoryExists");
}
public void testStatsServiceDirectoryExists() throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".DirectoryTests", "testStatsServiceDirectoryExists");
}
public void testTrainInfoDirectoryExists() throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".DirectoryTests", "testTrainInfoDirectoryExists");
}
}
diff --git a/tests/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/tests/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index 1ca4c5c..6b61497 100644
--- a/tests/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/tests/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -18,20 +18,22 @@
import static com.google.common.truth.Truth.assertThat;
import android.cts.statsd.atom.ProcStateTestCase;
-import android.service.procstats.ProcessState;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import android.service.procstats.AggregatedProcessState;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.ProcessStateAggregated;
-import com.android.os.AtomsProto.ProcessStatsPackageProto;
import com.android.os.AtomsProto.ProcessStatsProto;
import com.android.os.AtomsProto.ProcessStatsStateProto;
-import com.android.os.StatsLog.DimensionsValue;
-import com.android.os.StatsLog.ValueBucketInfo;
-import com.android.os.StatsLog.ValueMetricData;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.util.RunUtil;
import java.util.List;
@@ -42,54 +44,61 @@
private static final String TAG = "Statsd.ProcStatsValidationTests";
+ private static final String sBackgroundServiceName = "StatsdCtsBackgroundService";
+
private static final int EXTRA_WAIT_TIME_MS = 1_000; // as buffer when proc state changing.
+ private static final String DUMP_PROCSTATS_CMD = "dumpsys procstats";
+
private void toggleScreenAndSleep(final long duration) throws Exception {
final long half = duration >> 1;
- Thread.sleep(half);
- turnScreenOff();
- Thread.sleep(half);
- turnScreenOn();
+ RunUtil.getDefault().sleep(half);
+ DeviceUtils.turnScreenOff(getDevice());
+ RunUtil.getDefault().sleep(half);
+ DeviceUtils.turnScreenOn(getDevice());
}
public void testProcessStateByPulling() throws Exception {
startProcStatsTesting();
clearProcStats();
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
// foreground service
executeForegroundService();
- Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+ RunUtil.getDefault().sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
// background
- executeBackgroundService(ACTION_BACKGROUND_SLEEP);
- Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+ DeviceUtils.executeBackgroundService(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ sBackgroundServiceName, ACTION_BACKGROUND_SLEEP);
+ RunUtil.getDefault().sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
// top
executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
- Thread.sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+ RunUtil.getDefault().sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
// Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
- executeBackgroundService(ACTION_END_IMMEDIATELY);
+ DeviceUtils.executeBackgroundService(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
+ sBackgroundServiceName, ACTION_END_IMMEDIATELY);
final int cacheTime = 2_000; // process should be in cached state for up to this long
- Thread.sleep(cacheTime);
+ RunUtil.getDefault().sleep(cacheTime);
// foreground
// overlay should take 2 sec to appear. So this makes it 4 sec in TOP
executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
- Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+ RunUtil.getDefault().sleep(EXTRA_WAIT_TIME_MS + 5_000);
- Thread.sleep(60_000);
- uninstallPackage();
+ RunUtil.getDefault().sleep(60_000);
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
stopProcStatsTesting();
commitProcStatsToDisk();
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
final String fileName = "PROCSTATSQ_PULL.pbtxt";
- StatsdConfig config = createValidationUtil().getConfig(fileName);
+ StatsdConfig config = ValidationTestUtil.getConfig(fileName, mCtsBuild);
LogUtil.CLog.d("Updating the following config:\n" + config.toString());
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT + 5_000);
+ ConfigUtils.uploadConfig(getDevice(), config.toBuilder());
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(),
+ AtomsProto.AppBreadcrumbReported.State.START.getNumber(), 1);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT + 5_000);
- List<Atom> statsdData = getGaugeMetricDataList();
+ List<Atom> statsdData = ReportUtils.getGaugeMetricAtoms(getDevice());
List<android.service.procstats.ProcessStatsProto> processStatsProtoList
= getAllProcStatsProtoForStatsd();
@@ -101,7 +110,8 @@
String statsdPkgName = "com.android.server.cts.device.statsd";
long rssAvgStatsd = 0;
for (Atom d : statsdData) {
- for (ProcessStatsProto proc : d.getProcStats().getProcStatsSection().getProcessStatsList()) {
+ for (ProcessStatsProto proc :
+ d.getProcStats().getProcStatsSection().getProcessStatsList()) {
if (proc.getProcess().equals(statsdPkgName)) {
LogUtil.CLog.d("Got proto from statsd:");
LogUtil.CLog.d(proc.toString());
@@ -116,7 +126,7 @@
}
long rssAvgProcstats = 0;
- for (android.service.procstats.ProcessStatsProto process: processStatsProtoList) {
+ for (android.service.procstats.ProcessStatsProto process : processStatsProtoList) {
if (process.getProcess().equals(statsdPkgName)) {
LogUtil.CLog.d("Got proto from procstats dumpsys:");
LogUtil.CLog.d(process.toString());
@@ -139,111 +149,112 @@
* Temporarily disable this test as the proc stats data being pulled into the statsd
* doesn't include the pkg part now.
*
- startProcStatsTesting();
- clearProcStats();
- Thread.sleep(WAIT_TIME_SHORT);
+ startProcStatsTesting();
+ clearProcStats();
+ RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
- // foreground service
- executeForegroundService();
- Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
- // background
- executeBackgroundService(ACTION_BACKGROUND_SLEEP);
- Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
- // top
- executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
- Thread.sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
- // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
- executeBackgroundService(ACTION_END_IMMEDIATELY);
- final int cacheTime = 2_000; // process should be in cached state for up to this long
- Thread.sleep(cacheTime);
- // foreground
- // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
- executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
- Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+ // foreground service
+ executeForegroundService();
+ RunUtil.getDefault().sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+ // background
+ executeBackgroundService(ACTION_BACKGROUND_SLEEP);
+ RunUtil.getDefault().sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+ // top
+ executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
+ RunUtil.getDefault().sleep(SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+ // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
+ executeBackgroundService(ACTION_END_IMMEDIATELY);
+ final int cacheTime = 2_000; // process should be in cached state for up to this long
+ RunUtil.getDefault().sleep(cacheTime);
+ // foreground
+ // overlay should take 2 sec to appear. So this makes it 4 sec in TOP
+ executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
+ RunUtil.getDefault().sleep(EXTRA_WAIT_TIME_MS + 5_000);
- Thread.sleep(60_000);
- uninstallPackage();
- stopProcStatsTesting();
- commitProcStatsToDisk();
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(60_000);
+ uninstallPackage();
+ stopProcStatsTesting();
+ commitProcStatsToDisk();
+ RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
- final String fileName = "PROCSTATSQ_PULL_PKG_PROC.pbtxt";
- StatsdConfig config = createValidationUtil().getConfig(fileName);
- LogUtil.CLog.d("Updating the following config:\n" + config.toString());
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_SHORT);
+ final String fileName = "PROCSTATSQ_PULL_PKG_PROC.pbtxt";
+ StatsdConfig config = createValidationUtil().getConfig(fileName);
+ LogUtil.CLog.d("Updating the following config:\n" + config.toString());
+ uploadConfig(config);
+ RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
+ setAppBreadcrumbPredicate();
+ RunUtil.getDefault().sleep(WAIT_TIME_SHORT);
- List<Atom> statsdData = getGaugeMetricDataList();
- assertThat(statsdData).isNotEmpty();
- assertThat(
- statsdData.get(0).getProcStatsPkgProc().getProcStatsSection()
- .getProcessStatsList()
- ).isNotEmpty();
+ List<Atom> statsdData = getGaugeMetricDataList();
+ assertThat(statsdData).isNotEmpty();
+ assertThat(
+ statsdData.get(0).getProcStatsPkgProc().getProcStatsSection()
+ .getProcessStatsList()
+ ).isNotEmpty();
- // We pull directly from ProcessStatsService, so not necessary to compare every field.
- // Make sure that 1. both capture statsd package 2. spot check some values are reasonable
- LogUtil.CLog.d("======================");
+ // We pull directly from ProcessStatsService, so not necessary to compare every field.
+ // Make sure that 1. both capture statsd package 2. spot check some values are reasonable
+ LogUtil.CLog.d("======================");
- String statsdPkgName = "com.android.server.cts.device.statsd";
- long rssAvgStatsd = 0;
- long durationStatsd = 0;
- for (Atom d : statsdData) {
- for (ProcessStatsPackageProto pkg : d.getProcStatsPkgProc().getProcStatsSection().getPackageStatsList()) {
- if (pkg.getPackage().equals(statsdPkgName)) {
- LogUtil.CLog.d("Got proto from statsd:");
- LogUtil.CLog.d(pkg.toString());
- for (ProcessStatsProto process : pkg.getProcessStatsList()) {
- for (ProcessStatsStateProto state : process.getStatesList()) {
- if (state.getProcessState()
- == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- durationStatsd = state.getDurationMillis();
- rssAvgStatsd = state.getRss().getAverage();
- }
- }
- }
- }
- assertThat(pkg.getServiceStatsCount()).isEqualTo(0L);
- assertThat(pkg.getAssociationStatsCount()).isEqualTo(0L);
- }
- }
+ String statsdPkgName = "com.android.server.cts.device.statsd";
+ long rssAvgStatsd = 0;
+ long durationStatsd = 0;
+ for (Atom d : statsdData) {
+ for (ProcessStatsPackageProto pkg : d.getProcStatsPkgProc().getProcStatsSection()
+ .getPackageStatsList()) {
+ if (pkg.getPackage().equals(statsdPkgName)) {
+ LogUtil.CLog.d("Got proto from statsd:");
+ LogUtil.CLog.d(pkg.toString());
+ for (ProcessStatsProto process : pkg.getProcessStatsList()) {
+ for (ProcessStatsStateProto state : process.getStatesList()) {
+ if (state.getProcessState()
+ == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ durationStatsd = state.getDurationMillis();
+ rssAvgStatsd = state.getRss().getAverage();
+ }
+ }
+ }
+ }
+ assertThat(pkg.getServiceStatsCount()).isEqualTo(0L);
+ assertThat(pkg.getAssociationStatsCount()).isEqualTo(0L);
+ }
+ }
- LogUtil.CLog.d("avg rss from statsd is " + rssAvgStatsd);
+ LogUtil.CLog.d("avg rss from statsd is " + rssAvgStatsd);
- List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
+ List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
- long pssAvgProcstats = 0;
- long ussAvgProcstats = 0;
- long rssAvgProcstats = 0;
- long durationProcstats = 0;
- int serviceStatsCount = 0;
- int associationStatsCount = 0;
- for (ProcessStatsPackageProto pkg : processStatsPackageProtoList) {
- if (pkg.getPackage().equals(statsdPkgName)) {
- LogUtil.CLog.d("Got proto from procstats dumpsys:");
- LogUtil.CLog.d(pkg.toString());
- for (ProcessStatsProto process : pkg.getProcessStatsList()) {
- for (ProcessStatsStateProto state : process.getStatesList()) {
- if (state.getProcessState()
- == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- durationProcstats = state.getDurationMillis();
- pssAvgProcstats = state.getPss().getAverage();
- ussAvgProcstats = state.getUss().getAverage();
- rssAvgProcstats = state.getRss().getAverage();
- }
- }
- }
- }
- serviceStatsCount += pkg.getServiceStatsCount();
- associationStatsCount += pkg.getAssociationStatsCount();
- }
- assertThat(serviceStatsCount).isGreaterThan(0);
- assertThat(associationStatsCount).isGreaterThan(0);
+ long pssAvgProcstats = 0;
+ long ussAvgProcstats = 0;
+ long rssAvgProcstats = 0;
+ long durationProcstats = 0;
+ int serviceStatsCount = 0;
+ int associationStatsCount = 0;
+ for (ProcessStatsPackageProto pkg : processStatsPackageProtoList) {
+ if (pkg.getPackage().equals(statsdPkgName)) {
+ LogUtil.CLog.d("Got proto from procstats dumpsys:");
+ LogUtil.CLog.d(pkg.toString());
+ for (ProcessStatsProto process : pkg.getProcessStatsList()) {
+ for (ProcessStatsStateProto state : process.getStatesList()) {
+ if (state.getProcessState()
+ == ProcessState.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ durationProcstats = state.getDurationMillis();
+ pssAvgProcstats = state.getPss().getAverage();
+ ussAvgProcstats = state.getUss().getAverage();
+ rssAvgProcstats = state.getRss().getAverage();
+ }
+ }
+ }
+ }
+ serviceStatsCount += pkg.getServiceStatsCount();
+ associationStatsCount += pkg.getAssociationStatsCount();
+ }
+ assertThat(serviceStatsCount).isGreaterThan(0);
+ assertThat(associationStatsCount).isGreaterThan(0);
- LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
- assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
- */
+ LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
+ assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
+ */
}
private boolean isPssProfilingDisabled() throws Exception {
@@ -254,4 +265,45 @@
final String dumpsys = device.executeShellCommand("dumpsys activity settings");
return (dumpsys.contains(stringToCompare));
}
+
+ protected void clearProcStats() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --clear");
+ }
+
+ private void startProcStatsTesting() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --start-testing");
+ }
+
+ private void stopProcStatsTesting() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --stop-testing");
+ }
+
+ private void commitProcStatsToDisk() throws Exception {
+ getDevice().executeShellCommand("dumpsys procstats --commit");
+ }
+
+ /*
+ * Get all processes' procstats statsd data in proto
+ */
+ protected List<android.service.procstats.ProcessStatsProto> getAllProcStatsProtoForStatsd()
+ throws Exception {
+ try {
+ android.service.procstats.ProcessStatsSectionProto sectionProto = MetricsUtils.getDump(
+ getDevice(),
+ android.service.procstats.ProcessStatsSectionProto.parser(),
+ String.join(" ", DUMP_PROCSTATS_CMD,
+ "--statsd"));
+ List<android.service.procstats.ProcessStatsProto> processStatsProtoList
+ = sectionProto.getProcessStatsList();
+ LogUtil.CLog.d("Got procstats:\n ");
+ for (android.service.procstats.ProcessStatsProto processStatsProto
+ : processStatsProtoList) {
+ LogUtil.CLog.d(processStatsProto.toString());
+ }
+ return processStatsProtoList;
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ LogUtil.CLog.e("Failed to dump procstats proto");
+ throw (e);
+ }
+ }
}
diff --git a/tests/src/android/cts/statsd/validation/StatsFrameworkInitializerTest.java b/tests/src/android/cts/statsd/validation/StatsFrameworkInitializerTest.java
index 0ea332e..2a67748 100644
--- a/tests/src/android/cts/statsd/validation/StatsFrameworkInitializerTest.java
+++ b/tests/src/android/cts/statsd/validation/StatsFrameworkInitializerTest.java
@@ -16,13 +16,43 @@
package android.cts.statsd.validation;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import static com.google.common.truth.Truth.assertThat;
-public class StatsFrameworkInitializerTest extends DeviceAtomTestCase {
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
+
+public class StatsFrameworkInitializerTest extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
public void testStatsFrameworkInitializer_failsWhenCalledOutsideOfSystemServiceRegistry()
throws Exception {
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE,
".StatsFrameworkInitializerTests", "testRegisterServiceWrappers_expectFail");
}
diff --git a/tests/src/android/cts/statsd/validation/ValidationTestUtil.java b/tests/src/android/cts/statsd/validation/ValidationTestUtil.java
index d3e5bad..cb3a41e 100644
--- a/tests/src/android/cts/statsd/validation/ValidationTestUtil.java
+++ b/tests/src/android/cts/statsd/validation/ValidationTestUtil.java
@@ -15,11 +15,10 @@
*/
package android.cts.statsd.validation;
-import android.cts.statsd.atom.BaseTestCase;
-
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.util.FileUtil;
import com.google.protobuf.TextFormat;
@@ -28,14 +27,14 @@
import java.io.File;
import java.io.IOException;
-public class ValidationTestUtil extends BaseTestCase {
+public class ValidationTestUtil {
private static final String TAG = "Statsd.ValidationTestUtil";
- public StatsdConfig getConfig(String fileName) throws IOException {
+ public static StatsdConfig getConfig(String fileName, IBuildInfo ctsBuild) throws IOException {
try {
// TODO: Ideally, we should use real metrics that are also pushed to the fleet.
- File configFile = getBuildHelper().getTestFile(fileName);
+ File configFile = getBuildHelper(ctsBuild).getTestFile(fileName);
String configStr = FileUtil.readStringFromFile(configFile);
StatsdConfig.Builder builder = StatsdConfig.newBuilder();
TextFormat.merge(configStr, builder);
@@ -47,4 +46,8 @@
}
return null;
}
+
+ private static CompatibilityBuildHelper getBuildHelper(IBuildInfo ctsBuild) {
+ return new CompatibilityBuildHelper(ctsBuild);
+ }
}
diff --git a/tests/src/android/cts/statsd/validation/ValidationTests.java b/tests/src/android/cts/statsd/validation/ValidationTests.java
index 3add66d..a3f0109 100644
--- a/tests/src/android/cts/statsd/validation/ValidationTests.java
+++ b/tests/src/android/cts/statsd/validation/ValidationTests.java
@@ -18,7 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import android.cts.statsd.atom.DeviceAtomTestCase;
+import android.cts.statsd.metric.MetricsUtils;
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
import android.os.BatteryPluggedStateEnum;
import android.os.BatteryStatsProto;
import android.os.UidProto;
@@ -47,9 +51,14 @@
import com.android.os.StatsLog.DurationMetricData;
import com.android.os.StatsLog.EventMetricData;
import com.android.os.StatsLog.StatsLogReport;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.RunUtil;
import com.google.common.collect.Range;
+import com.google.protobuf.ExtensionRegistry;
import java.util.Arrays;
import java.util.HashMap;
@@ -60,36 +69,53 @@
/**
* Side-by-side comparison between statsd and batterystats.
*/
-public class ValidationTests extends DeviceAtomTestCase {
+public class ValidationTests extends DeviceTestCase implements IBuildReceiver {
+
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_APK,
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, mCtsBuild);
+ RunUtil.getDefault().sleep(1000);
+ DeviceUtils.turnBatteryStatsAutoResetOff(
+ getDevice()); // Turn off Battery Stats auto resetting
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallTestApp(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
+ DeviceUtils.resetBatteryStatus(getDevice());
+ DeviceUtils.turnScreenOn(getDevice());
+ DeviceUtils.turnBatteryStatsAutoResetOn(getDevice());
+ super.tearDown();
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
private static final String TAG = "Statsd.ValidationTests";
private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final boolean ENABLE_LOAD_TEST = false;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- turnBatteryStatsAutoResetOff(); // Turn off Battery Stats auto resetting
- }
-
- @Override
- protected void tearDown() throws Exception {
- resetBatteryStatus(); // Undo any unplugDevice().
- turnScreenOn(); // Reset screen to on state
- turnBatteryStatsAutoResetOn(); // Turn Battery Stats auto resetting back on
- super.tearDown();
- }
-
public void testPartialWakelock() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
+ if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
resetBatteryStats();
- unplugDevice();
- flushBatteryStatsHandlers();
+ DeviceUtils.unplugDevice(getDevice());
+ DeviceUtils.flushBatteryStatsHandlers(getDevice());
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
// on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
- String aodState = getAodState();
- setAodState("0");
- turnScreenOff();
+ String aodState = DeviceUtils.getAodState(getDevice());
+ DeviceUtils.setAodState(getDevice(), "0");
+ DeviceUtils.turnScreenOff(getDevice());
final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
@@ -105,15 +131,17 @@
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(wakelockOn, wakelockOff);
- createAndUploadConfig(atomTag, true); // True: uses attribution.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockState");
+ ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, atomTag, true); // True: uses attribution.
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, ".AtomTests",
+ "testWakelockState");
// Sorted list of events in order in which they occurred.
- List<EventMetricData> data = getEventMetricDataList();
+ List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
//=================== verify that statsd is correct ===============//
// Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
+ AtomTestUtils.assertStatesOccurred(stateSet, data,
atom -> atom.getWakelockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
@@ -126,28 +154,30 @@
@RestrictedBuildTest
public void testPartialWakelockDuration() throws Exception {
- if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
+ if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
// getUid() needs shell command via ADB. turnScreenOff() sometimes let system go to suspend.
// ADB disconnection causes failure of getUid(). Move up here before turnScreenOff().
- final int EXPECTED_UID = getUid();
+ final int EXPECTED_UID = DeviceUtils.getAppUid(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
- turnScreenOn(); // To ensure that the ScreenOff later gets logged.
+ DeviceUtils.turnScreenOn(getDevice()); // To ensure that the ScreenOff later gets logged.
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
// on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
- String aodState = getAodState();
- setAodState("0");
+ String aodState = DeviceUtils.getAodState(getDevice());
+ DeviceUtils.setAodState(getDevice(), "0");
uploadWakelockDurationBatteryStatsConfig(TimeUnit.CTS);
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
resetBatteryStats();
- unplugDevice();
- turnScreenOff();
- flushBatteryStatsHandlers();
+ DeviceUtils.unplugDevice(getDevice());
+ DeviceUtils.turnScreenOff(getDevice());
+ DeviceUtils.flushBatteryStatsHandlers(getDevice());
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockState");
- Thread.sleep(WAIT_TIME_LONG); // Make sure the one second bucket has ended.
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, ".AtomTests",
+ "testWakelockState");
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG); // Make sure the one second bucket has ended.
final String EXPECTED_TAG = "StatsdPartialWakelock";
@@ -169,33 +199,35 @@
EXPECTED_UID, EXPECTED_TAG
).that(statsdDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));
- setAodState(aodState); // restores AOD to initial state.
+ DeviceUtils.setAodState(getDevice(), aodState); // restores AOD to initial state.
}
public void testPartialWakelockLoad() throws Exception {
if (!ENABLE_LOAD_TEST) return;
- turnScreenOn(); // To ensure that the ScreenOff later gets logged.
+ DeviceUtils.turnScreenOn(getDevice()); // To ensure that the ScreenOff later gets logged.
uploadWakelockDurationBatteryStatsConfig(TimeUnit.CTS);
- Thread.sleep(WAIT_TIME_SHORT);
+ RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
resetBatteryStats();
- unplugDevice();
- turnScreenOff();
+ DeviceUtils.unplugDevice(getDevice());
+ DeviceUtils.turnScreenOff(getDevice());
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockLoad");
+ DeviceUtils.runDeviceTests(getDevice(), MetricsUtils.DEVICE_SIDE_TEST_PACKAGE, ".AtomTests",
+ "testWakelockLoad");
// Give time for stuck wakelocks to increase duration.
- Thread.sleep(10_000);
+ RunUtil.getDefault().sleep(10_000);
final String EXPECTED_TAG = "StatsdPartialWakelock";
- final int EXPECTED_UID = getUid();
+ final int EXPECTED_UID = DeviceUtils.getAppUid(getDevice(),
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
final int NUM_THREADS = 16;
final int NUM_COUNT_PER_THREAD = 1000;
final int MAX_DURATION_MS = 15_000;
final int MIN_DURATION_MS = 1_000;
- BatteryStatsProto batterystatsProto = getBatteryStatsProto();
- HashMap<Integer, HashMap<Long, Long>> statsdWakelockData = getStatsdWakelockData();
+// BatteryStatsProto batterystatsProto = getBatteryStatsProto();
+// HashMap<Integer, HashMap<Long, Long>> statsdWakelockData = getStatsdWakelockData();
// TODO: this fails because we only have the hashes of the wakelock tags in statsd.
// If we want to run this test, we need to fix this.
@@ -256,7 +288,8 @@
// TODO: Refactor these into some utils class.
public HashMap<Integer, HashMap<Long, Long>> getStatsdWakelockData() throws Exception {
- StatsLogReport report = getStatsLogReport();
+ StatsLogReport report = ReportUtils.getStatsLogReport(getDevice(),
+ ExtensionRegistry.getEmptyRegistry());
CLog.d("Received the following stats log report: \n" + report.toString());
// Stores total duration of each wakelock across buckets.
@@ -607,13 +640,14 @@
.addPredicate(screenIsOffId)
.addPredicate(deviceIsUnpluggedId));
- StatsdConfig.Builder builder = createConfigBuilder();
+ StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder(
+ MetricsUtils.DEVICE_SIDE_TEST_PACKAGE);
builder.addDurationMetric(DurationMetric.newBuilder()
- .setId(metricId)
- .setWhat(partialWakelockIsOnId)
- .setCondition(screenOffBatteryOnId)
- .setDimensionsInWhat(dimensions)
- .setBucket(bucketsize))
+ .setId(metricId)
+ .setWhat(partialWakelockIsOnId)
+ .setCondition(screenOffBatteryOnId)
+ .setDimensionsInWhat(dimensions)
+ .setBucket(bucketsize))
.addAtomMatcher(wakelockAcquire)
.addAtomMatcher(wakelockChangeAcquire)
.addAtomMatcher(wakelockRelease)
@@ -639,6 +673,10 @@
.addPredicate(screenIsOff)
.addPredicate(screenOffBatteryOn);
- uploadConfig(builder);
+ ConfigUtils.uploadConfig(getDevice(), builder);
+ }
+
+ private void resetBatteryStats() throws Exception {
+ getDevice().executeShellCommand("dumpsys batterystats --reset");
}
}
diff --git a/tests/utils/tests/Android.bp b/tests/utils/tests/Android.bp
index 88476b1..58063ff 100644
--- a/tests/utils/tests/Android.bp
+++ b/tests/utils/tests/Android.bp
@@ -29,7 +29,7 @@
manifest: "AndroidManifest.xml",
static_libs: [
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
"statsdprotolite",
"StatsdTestUtils",
],
@@ -48,6 +48,10 @@
genrule {
name: "statslog-statsdtest-java-gen",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module statsdtest --javaPackage android.util --javaClass StatsdTestStatsLog",
+ cmd: "$(location stats-log-api-gen) " +
+ "--java $(out) " +
+ "--module statsdtest " +
+ "--javaPackage android.util " +
+ "--javaClass StatsdTestStatsLog",
out: ["android/util/StatsdTestStatsLog.java"],
}
diff --git a/tests/utils/tests/AndroidTest.xml b/tests/utils/tests/AndroidTest.xml
new file mode 100644
index 0000000..0438508
--- /dev/null
+++ b/tests/utils/tests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Config for StatsdTestUtilsTest.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-suite-tag" value="mts" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="StatsdTestUtilsTest.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.os.statsd.testutils.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+ </object>
+</configuration>