summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/logging/LogBuilder.java166
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java48
-rw-r--r--core/java/com/android/internal/logging/MetricsReader.java64
-rw-r--r--core/java/com/android/internal/logging/legacy/CounterParser.java54
-rw-r--r--core/java/com/android/internal/logging/legacy/EventLogCollector.java188
-rw-r--r--core/java/com/android/internal/logging/legacy/HistogramParser.java35
-rw-r--r--core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java111
-rw-r--r--core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java80
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java72
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationAlertParser.java86
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java102
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationClickedParser.java70
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java76
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationKey.java60
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java42
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java56
-rw-r--r--core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java81
-rw-r--r--core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java66
-rw-r--r--core/java/com/android/internal/logging/legacy/StatusBarStateParser.java74
-rw-r--r--core/java/com/android/internal/logging/legacy/SysuiActionParser.java81
-rw-r--r--core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java49
-rw-r--r--core/java/com/android/internal/logging/legacy/SysuiQueryParser.java32
-rw-r--r--core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java58
-rwxr-xr-xcore/java/com/android/internal/logging/legacy/TagParser.java104
-rw-r--r--core/java/com/android/internal/logging/legacy/TronCounters.java72
-rw-r--r--core/java/com/android/internal/logging/legacy/TronLogger.java49
-rw-r--r--core/java/com/android/internal/logging/legacy/Util.java25
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java76
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java91
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java90
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java100
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java123
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java138
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java200
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java114
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java175
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java65
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java56
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java84
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java96
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java111
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java69
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java73
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java167
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java72
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java122
-rw-r--r--proto/src/metrics_constants.proto28
47 files changed, 4032 insertions, 19 deletions
diff --git a/core/java/com/android/internal/logging/LogBuilder.java b/core/java/com/android/internal/logging/LogBuilder.java
index 8e2e114be454..7eda3da70bba 100644
--- a/core/java/com/android/internal/logging/LogBuilder.java
+++ b/core/java/com/android/internal/logging/LogBuilder.java
@@ -1,6 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.internal.logging;
import android.util.EventLog;
+import android.util.Log;
import android.util.SparseArray;
import android.view.View;
@@ -14,16 +30,16 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
*/
public class LogBuilder {
-
+ private static final String TAG = "LogBuilder";
private SparseArray<Object> entries = new SparseArray();
public LogBuilder(int mainCategory) {
setCategory(mainCategory);
}
- public LogBuilder setView(View view) {
- entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VIEW, view.getId());
- return this;
+ /* Deserialize from the eventlog */
+ public LogBuilder(Object[] items) {
+ deserialize(items);
}
public LogBuilder setCategory(int category) {
@@ -36,16 +52,48 @@ public class LogBuilder {
return this;
}
+ public LogBuilder setSubtype(int subtype) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE, subtype);
+ return this;
+ }
+
+ public LogBuilder setTimestamp(long timestamp) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP, timestamp);
+ return this;
+ }
+
+ public LogBuilder setPackageName(String packageName) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME, packageName);
+ return this;
+ }
+
+ public LogBuilder setCounterName(String name) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME, name);
+ return this;
+ }
+
+ public LogBuilder setCounterBucket(int bucket) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET, bucket);
+ return this;
+ }
+
+ public LogBuilder setCounterBucket(long bucket) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET, bucket);
+ return this;
+ }
+
+ public LogBuilder setCounterValue(int value) {
+ entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_VALUE, value);
+ return this;
+ }
+
/**
* @param tag From your MetricsEvent enum.
* @param value One of Integer, Long, Float, String
* @return
*/
public LogBuilder addTaggedData(int tag, Object value) {
- if (!(value instanceof Integer ||
- value instanceof String ||
- value instanceof Long ||
- value instanceof Float)) {
+ if (isValidValue(value)) {
throw new IllegalArgumentException(
"Value must be loggable type - int, long, float, String");
}
@@ -53,6 +101,94 @@ public class LogBuilder {
return this;
}
+ public boolean isValidValue(Object value) {
+ return !(value instanceof Integer ||
+ value instanceof String ||
+ value instanceof Long ||
+ value instanceof Float);
+ }
+
+ public Object getTaggedData(int tag) {
+ return entries.get(tag);
+ }
+
+ public int getCategory() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY);
+ if (obj instanceof Integer) {
+ return (Integer) obj;
+ } else {
+ return MetricsEvent.VIEW_UNKNOWN;
+ }
+ }
+
+ public int getType() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE);
+ if (obj instanceof Integer) {
+ return (Integer) obj;
+ } else {
+ return MetricsEvent.TYPE_UNKNOWN;
+ }
+ }
+
+ public int getSubtype() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE);
+ if (obj instanceof Integer) {
+ return (Integer) obj;
+ } else {
+ return 0;
+ }
+ }
+
+ public long getTimestamp() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP);
+ if (obj instanceof Long) {
+ return (Long) obj;
+ } else {
+ return 0;
+ }
+ }
+
+ public String getPackageName() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME);
+ if (obj instanceof String) {
+ return (String) obj;
+ } else {
+ return null;
+ }
+ }
+
+ public String getCounterName() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME);
+ if (obj instanceof String) {
+ return (String) obj;
+ } else {
+ return null;
+ }
+ }
+
+ public long getCounterBucket() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
+ if (obj instanceof Number) {
+ return ((Number) obj).longValue();
+ } else {
+ return 0L;
+ }
+ }
+
+ public boolean isLongCounterBucket() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
+ return obj instanceof Long;
+ }
+
+ public int getCounterValue() {
+ Object obj = entries.get(MetricsEvent.RESERVED_FOR_LOGBUILDER_VALUE);
+ if (obj instanceof Integer) {
+ return (Integer) obj;
+ } else {
+ return 0;
+ }
+ }
+
/**
* Assemble logs into structure suitable for EventLog.
*/
@@ -64,5 +200,17 @@ public class LogBuilder {
}
return out;
}
-}
+ public void deserialize(Object[] items) {
+ int i = 0;
+ while(i < items.length) {
+ Object key = items[i++];
+ Object value = i < items.length ? items[i++] : null;
+ if (key instanceof Integer) {
+ entries.put((Integer) key, value);
+ } else {
+ Log.i(TAG, "Invalid key " + key.toString());
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 5eb39aed2c7b..16c2719c0584 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -17,12 +17,10 @@ package com.android.internal.logging;
import android.content.Context;
import android.os.Build;
-import android.util.EventLog;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
/**
* Log all the things.
*
@@ -38,6 +36,10 @@ public class MetricsLogger {
throw new IllegalArgumentException("Must define metric category");
}
EventLogTags.writeSysuiViewVisibility(category, 100);
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .serialize());
}
public static void hidden(Context context, int category) throws IllegalArgumentException {
@@ -45,6 +47,10 @@ public class MetricsLogger {
throw new IllegalArgumentException("Must define metric category");
}
EventLogTags.writeSysuiViewVisibility(category, 0);
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_CLOSE)
+ .serialize());
}
public static void visibility(Context context, int category, boolean visibile)
@@ -62,21 +68,38 @@ public class MetricsLogger {
}
public static void action(Context context, int category) {
- action(context, category, "");
+ EventLogTags.writeSysuiAction(category, "");
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .serialize());
}
public static void action(Context context, int category, int value) {
- action(context, category, Integer.toString(value));
+ EventLogTags.writeSysuiAction(category, Integer.toString(value));
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(value)
+ .serialize());
}
public static void action(Context context, int category, boolean value) {
- action(context, category, Boolean.toString(value));
+ EventLogTags.writeSysuiAction(category, Boolean.toString(value));
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(value ? 1 : 0)
+ .serialize());
}
public static void action(LogBuilder content) {
//EventLog.writeEvent(524292, content.serialize());
// Below would be the *right* way to do this, using the generated
// EventLogTags method, but that doesn't work.
+ if (content.getType() == MetricsEvent.TYPE_UNKNOWN) {
+ content.setType(MetricsEvent.TYPE_ACTION);
+ }
EventLogTags.writeSysuiMultiAction(content.serialize());
}
@@ -86,15 +109,30 @@ public class MetricsLogger {
throw new IllegalArgumentException("Must define metric category");
}
EventLogTags.writeSysuiAction(category, pkg);
+ EventLogTags.writeSysuiMultiAction(new LogBuilder(category)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setPackageName(pkg)
+ .serialize());
}
/** Add an integer value to the monotonically increasing counter with the given name. */
public static void count(Context context, String name, int value) {
EventLogTags.writeSysuiCount(name, value);
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+ .setCounterName(name)
+ .setCounterValue(value)
+ .serialize());
}
/** Increment the bucket with the integer label on the histogram with the given name. */
public static void histogram(Context context, String name, int bucket) {
EventLogTags.writeSysuiHistogram(name, bucket);
+ EventLogTags.writeSysuiMultiAction(
+ new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+ .setCounterName(name)
+ .setCounterBucket(bucket)
+ .setCounterValue(1)
+ .serialize());
}
}
diff --git a/core/java/com/android/internal/logging/MetricsReader.java b/core/java/com/android/internal/logging/MetricsReader.java
new file mode 100644
index 000000000000..c4fc963adaa6
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsReader.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging;
+
+import com.android.internal.logging.legacy.LegacyConversionLogger;
+import com.android.internal.logging.legacy.EventLogCollector;
+
+import java.util.Queue;
+
+/**
+ * Read platform logs.
+ */
+public class MetricsReader {
+ private EventLogCollector mReader;
+ private Queue<LogBuilder> mEventQueue;
+ private long mLastEventMs;
+ private long mCheckpointMs;
+
+ /** Open a new session and start reading logs.
+ *
+ * Starts reading from the oldest log not already read by this reader object.
+ * On first invocation starts from the oldest available log ion the system.
+ */
+ public void read(long startMs) {
+ EventLogCollector reader = EventLogCollector.getInstance();
+ LegacyConversionLogger logger = new LegacyConversionLogger();
+ mLastEventMs = reader.collect(logger, startMs);
+ mEventQueue = logger.getEvents();
+ }
+
+ public void checkpoint() {
+ read(0L);
+ mCheckpointMs = mLastEventMs;
+ mEventQueue = null;
+ }
+
+ public void reset() {
+ read(mCheckpointMs);
+ }
+
+ /* Does the current log session have another entry? */
+ public boolean hasNext() {
+ return mEventQueue == null ? false : !mEventQueue.isEmpty();
+ }
+
+ /* Next entry in the current log session. */
+ public LogBuilder next() {
+ return mEventQueue == null ? null : mEventQueue.remove();
+ }
+
+}
diff --git a/core/java/com/android/internal/logging/legacy/CounterParser.java b/core/java/com/android/internal/logging/legacy/CounterParser.java
new file mode 100644
index 000000000000..f318503db5e6
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/CounterParser.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+/**
+ * Parse the Android counter event logs.
+ * @hide
+ */
+public class CounterParser extends TagParser {
+ private static final String TAG = "CounterParser";
+ private static final int EVENTLOG_TAG = 524290;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 2) {
+ try {
+ String name = ((String) operands[0]);
+ int value = (Integer) operands[1];
+ logCount(logger, name, value);
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.d(TAG, "unexpected operand type", e);
+ }
+ }
+ } else if (debug) {
+ Log.d(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+
+ protected void logCount(TronLogger logger, String name, int value) {
+ logger.incrementBy(TronCounters.TRON_AOSP_PREFIX + name, value);
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/EventLogCollector.java b/core/java/com/android/internal/logging/legacy/EventLogCollector.java
new file mode 100644
index 000000000000..952ae2386abc
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/EventLogCollector.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Scan the event log for interaction metrics events.
+ * @hide
+ */
+public class EventLogCollector {
+ private static final String TAG = "EventLogCollector";
+
+ // TODO replace this with GoogleLogTags.TRON_HEARTBEAT
+ @VisibleForTesting
+ static final int TRON_HEARTBEAT = 208000;
+
+ private static EventLogCollector sInstance;
+
+ private final ArrayMap<Integer, TagParser> mTagParsers;
+ private int[] mInterestingTags;
+
+ private LogReader mLogReader;
+
+ private EventLogCollector() {
+ mTagParsers = new ArrayMap<>();
+ addParser(new SysuiViewVisibilityParser());
+ addParser(new SysuiActionParser());
+ addParser(new SysuiQueryParser());
+ addParser(new NotificationPanelRevealedParser());
+ addParser(new NotificationPanelHiddenParser());
+ addParser(new NotificationClickedParser());
+ addParser(new NotificationActionClickedParser());
+ addParser(new NotificationCanceledParser());
+ addParser(new NotificationVisibilityParser());
+ addParser(new NotificationAlertParser());
+ addParser(new NotificationExpansionParser());
+ addParser(new CounterParser());
+ addParser(new HistogramParser());
+ addParser(new LockscreenGestureParser());
+ addParser(new StatusBarStateParser());
+ addParser(new PowerScreenStateParser());
+ addParser(new SysuiMultiActionParser());
+
+ mLogReader = new LogReader();
+ }
+
+ public static EventLogCollector getInstance() {
+ if (sInstance == null) {
+ sInstance = new EventLogCollector();
+ }
+ return sInstance;
+ }
+
+ @VisibleForTesting
+ public void setLogReader(LogReader logReader) {
+ mLogReader = logReader;
+ }
+
+ private int[] getInterestingTags() {
+ if (mInterestingTags == null) {
+ mInterestingTags = new int[mTagParsers.size()];
+ for (int i = 0; i < mTagParsers.size(); i++) {
+ mInterestingTags[i] = mTagParsers.valueAt(i).getTag();
+ }
+ }
+ return mInterestingTags;
+ }
+
+ // I would customize ArrayMap to add put(TagParser), but ArrayMap is final.
+ @VisibleForTesting
+ void addParser(TagParser parser) {
+ mTagParsers.put(parser.getTag(), parser);
+ mInterestingTags = null;
+ }
+
+ public void collect(LegacyConversionLogger logger) {
+ collect(logger, 0L);
+ }
+
+ public long collect(TronLogger logger, long lastSeenEventMs) {
+ long lastEventMs = 0L;
+ final boolean debug = Util.debug();
+
+ if (debug) {
+ Log.d(TAG, "Eventlog Collection");
+ }
+ ArrayList<Event> events = new ArrayList<>();
+ try {
+ mLogReader.readEvents(getInterestingTags(), events);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (debug) {
+ Log.d(TAG, "read this many events: " + events.size());
+ }
+
+ for (Event event : events) {
+ final long millis = event.getTimeNanos() / 1000000;
+ if (millis > lastSeenEventMs) {
+ final int tag = event.getTag();
+ TagParser parser = mTagParsers.get(tag);
+ if (parser == null) {
+ if (debug) {
+ Log.d(TAG, "unknown tag: " + tag);
+ }
+ continue;
+ }
+ if (debug) {
+ Log.d(TAG, "parsing tag: " + tag);
+ }
+ parser.parseEvent(logger, event);
+ lastEventMs = Math.max(lastEventMs, millis);
+ } else {
+ if (debug) {
+ Log.d(TAG, "old event: " + millis + " < " + lastSeenEventMs);
+ }
+ }
+ }
+ return lastEventMs;
+ }
+
+ @VisibleForTesting
+ static class Event {
+ long mTimeNanos;
+ int mTag;
+ Object mData;
+
+ Event(long timeNanos, int tag, Object data) {
+ super();
+ mTimeNanos = timeNanos;
+ mTag = tag;
+ mData = data;
+ }
+
+ Event(EventLog.Event event) {
+ mTimeNanos = event.getTimeNanos();
+ mTag = event.getTag();
+ mData = event.getData();
+ }
+
+ public long getTimeNanos() {
+ return mTimeNanos;
+ }
+
+ public int getTag() {
+ return mTag;
+ }
+
+ public Object getData() {
+ return mData;
+ }
+ }
+
+ @VisibleForTesting
+ static class LogReader {
+ public void readEvents(int[] tags, Collection<Event> events) throws IOException {
+ // Testing in Android: the Static Final Class Strikes Back!
+ ArrayList<EventLog.Event> nativeEvents = new ArrayList<>();
+ EventLog.readEvents(tags, nativeEvents);
+ for (EventLog.Event nativeEvent : nativeEvents) {
+ Event event = new Event(nativeEvent);
+ events.add(event);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/HistogramParser.java b/core/java/com/android/internal/logging/legacy/HistogramParser.java
new file mode 100644
index 000000000000..bb7e75cce7f2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/HistogramParser.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Parse the Android histogram event logs.
+ * @hide
+ */
+public class HistogramParser extends CounterParser {
+ private static final String TAG = "HistogramParser";
+ private static final int EVENTLOG_TAG = 524291;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ protected void logCount(TronLogger logger, String name, int value) {
+ logger.incrementIntHistogram("tron_varz_" + name, value);
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java b/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
new file mode 100644
index 000000000000..7381ff08bbf2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.os.Bundle;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/** @hide */
+public class LegacyConversionLogger implements TronLogger {
+ public static final String VIEW_KEY = "view";
+ public static final String TYPE_KEY = "type";
+ public static final String EVENT_KEY = "data";
+
+ public static final int TYPE_COUNTER = 1;
+ public static final int TYPE_HISTOGRAM = 2;
+ public static final int TYPE_EVENT = 3;
+
+ private final Queue<LogBuilder> mQueue;
+ private HashMap<String, Boolean> mConfig;
+
+ public LegacyConversionLogger() {
+ mQueue = new LinkedList<>();
+ }
+
+ public Queue<LogBuilder> getEvents() {
+ return mQueue;
+ }
+
+ @Override
+ public void increment(String counterName) {
+ LogBuilder b = new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+ .setCounterName(counterName)
+ .setCounterValue(1);
+ mQueue.add(b);
+ }
+
+ @Override
+ public void incrementBy(String counterName, int value) {
+ LogBuilder b = new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+ .setCounterName(counterName)
+ .setCounterValue(value);
+ mQueue.add(b);
+ }
+
+ @Override
+ public void incrementIntHistogram(String counterName, int bucket) {
+ LogBuilder b = new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+ .setCounterName(counterName)
+ .setCounterBucket(bucket)
+ .setCounterValue(1);
+ mQueue.add(b);
+ }
+
+ @Override
+ public void incrementLongHistogram(String counterName, long bucket) {
+ LogBuilder b = new LogBuilder(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+ .setCounterName(counterName)
+ .setCounterBucket(bucket)
+ .setCounterValue(1);
+ mQueue.add(b);
+ }
+
+ @Override
+ public LogBuilder obtain() {
+ return new LogBuilder(MetricsEvent.VIEW_UNKNOWN);
+ }
+
+ @Override
+ public void dispose(LogBuilder proto) {
+ }
+
+ @Override
+ public void addEvent(LogBuilder proto) {
+ mQueue.add(proto);
+ }
+
+ @Override
+ public boolean getConfig(String configName) {
+ if (mConfig != null && mConfig.containsKey(configName)) {
+ return mConfig.get(configName);
+ }
+ return false;
+ }
+
+ @Override
+ public void setConfig(String configName, boolean newValue) {
+ if (mConfig == null) {
+ mConfig = new HashMap<>();
+ }
+ mConfig.put(configName, newValue);
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java b/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java
new file mode 100644
index 000000000000..6bede24d8316
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/LockscreenGestureParser.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class LockscreenGestureParser extends TagParser {
+ private static final String TAG = "LockscreenGestureParser";
+ private static final int EVENTLOG_TAG = 36021;
+
+ // source of truth is com.android.systemui.EventLogConstants
+ public static final int[] GESTURE_TYPE_MAP = {
+ MetricsEvent.VIEW_UNKNOWN, // there is no type 0
+ MetricsEvent.ACTION_LS_UNLOCK, // SYSUI_LOCKSCREEN_GESTURE_SWIPE_UP_UNLOCK = 1
+ MetricsEvent.ACTION_LS_SHADE, // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_FULL_SHADE = 2
+ MetricsEvent.ACTION_LS_HINT, // SYSUI_LOCKSCREEN_GESTURE_TAP_UNLOCK_HINT = 3
+ MetricsEvent.ACTION_LS_CAMERA, // SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA = 4
+ MetricsEvent.ACTION_LS_DIALER, // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER = 5
+ MetricsEvent.ACTION_LS_LOCK, // SYSUI_LOCKSCREEN_GESTURE_TAP_LOCK = 6
+ MetricsEvent.ACTION_LS_NOTE, // SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE = 7
+ MetricsEvent.ACTION_LS_QS, // SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_QS = 8
+ MetricsEvent.ACTION_SHADE_QS_PULL, // SYSUI_SHADE_GESTURE_SWIPE_DOWN_QS = 9
+ MetricsEvent.ACTION_SHADE_QS_TAP // SYSUI_TAP_TO_OPEN_QS = 10
+ };
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 1) {
+ try {
+ int type = ((Integer) operands[0]).intValue();
+ // ignore gesture length in operands[1]
+ // ignore gesture velocity in operands[2]
+
+ int category = MetricsEvent.VIEW_UNKNOWN;
+ if (type < GESTURE_TYPE_MAP.length) {
+ category = GESTURE_TYPE_MAP[type];
+ }
+ if (category != MetricsEvent.VIEW_UNKNOWN) {
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(category);
+ proto.setType(MetricsEvent.TYPE_ACTION);
+ proto.setTimestamp(eventTimeMs);
+ logger.addEvent(proto);
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java b/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java
new file mode 100644
index 000000000000..67b84e984e80
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationActionClickedParser.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification action button interaction event logs.
+ * @hide
+ */
+public class NotificationActionClickedParser extends TagParser {
+ private static final String TAG = "NotificationAction";
+ private static final int EVENTLOG_TAG = 27521;
+
+ private final NotificationKey mKey;
+
+ public NotificationActionClickedParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 1) {
+ try {
+ if (mKey.parse((String) operands[0])) {
+ int index = (Integer) operands[1];
+ parseTimes(operands, 2);
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION);
+ proto.setType(MetricsEvent.TYPE_ACTION);
+ proto.setSubtype(index);
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ filltimes(proto);
+ logger.addEvent(proto);
+ } else if (debug) {
+ Log.e(TAG, "unable to parse key.");
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java b/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java
new file mode 100644
index 000000000000..761197b19d66
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationAlertParser.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the new Android notification alert event logs.
+ * @hide
+ */
+public class NotificationAlertParser extends TagParser {
+ private static final String TAG = "NotificationAlertParser";
+ private static final int EVENTLOG_TAG = 27532;
+
+ @VisibleForTesting
+ static final int BUZZ = 0x00000001;
+ @VisibleForTesting
+ static final int BEEP = 0x00000002;
+ @VisibleForTesting
+ static final int BLINK = 0x00000004;
+
+ private final NotificationKey mKey;
+
+ public NotificationAlertParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 3) {
+ try {
+ final String keyString = (String) operands[0];
+ final boolean buzz = ((Integer) operands[1]) == 1;
+ final boolean beep = ((Integer) operands[2]) == 1;
+ final boolean blink = ((Integer) operands[3]) == 1;
+
+ if (mKey.parse(keyString)) {
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ALERT);
+ proto.setType(MetricsEvent.TYPE_OPEN);
+ proto.setSubtype((buzz ? BUZZ : 0) | (beep ? BEEP : 0) | (blink ? BLINK : 0));
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ filltimes(proto);
+ logger.addEvent(proto);
+ } else {
+ if (debug) {
+ Log.e(TAG, "unable to parse key: " + keyString);
+ }
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ return;
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java b/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java
new file mode 100644
index 000000000000..0cab1a859593
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationCanceledParser.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification cancellation event logs.
+ * @hide
+ */
+public class NotificationCanceledParser extends TagParser {
+ private static final String TAG = "NotificationCanceled";
+ private static final int EVENTLOG_TAG = 27530;
+
+ // from com.android.server.notification.NotificationManagerService
+ static final int REASON_DELEGATE_CLICK = 1;
+ static final int REASON_DELEGATE_CANCEL = 2;
+ static final int REASON_DELEGATE_CANCEL_ALL = 3;
+ static final int REASON_PACKAGE_BANNED = 7;
+ static final int REASON_LISTENER_CANCEL = 10;
+ static final int REASON_LISTENER_CANCEL_ALL = 11;
+
+ private final NotificationKey mKey;
+
+ public NotificationCanceledParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 1) {
+ try {
+ final String keyString = (String) operands[0];
+ final int reason = (Integer) operands[1];
+ parseTimes(operands, 2);
+
+ // handle old style log
+ // TODO: delete once M is launched
+ if (operands.length < 5) {
+ mSinceVisibleMillis = mSinceUpdateMillis;
+ mSinceUpdateMillis = 0;
+ }
+
+ boolean intentional = true;
+ switch (reason) {
+ case REASON_DELEGATE_CANCEL:
+ case REASON_DELEGATE_CANCEL_ALL:
+ case REASON_LISTENER_CANCEL:
+ case REASON_LISTENER_CANCEL_ALL:
+ case REASON_DELEGATE_CLICK:
+ case REASON_PACKAGE_BANNED:
+ break;
+ default:
+ intentional = false;
+ }
+
+ if (mKey.parse(keyString)) {
+ if (intentional) {
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ proto.setType(MetricsEvent.TYPE_DISMISS);
+ proto.setSubtype(reason);
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ filltimes(proto);
+ logger.addEvent(proto);
+ }
+ } else if (debug) {
+ Log.e(TAG, "unable to parse key: " + keyString);
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java b/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java
new file mode 100644
index 000000000000..eeae0a88feb8
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationClickedParser.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification interaction event logs.
+ * @hide
+ */
+public class NotificationClickedParser extends TagParser {
+ private static final String TAG = "NotificationClicked";
+ private static final int EVENTLOG_TAG = 27520;
+
+ private final NotificationKey mKey;
+
+ public NotificationClickedParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 0) {
+ try {
+ if (mKey.parse((String) operands[0])) {
+ parseTimes(operands, 1);
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ proto.setType(MetricsEvent.TYPE_ACTION);
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ filltimes(proto);
+ logger.addEvent(proto);
+ } else if (debug) {
+ Log.e(TAG, "unable to parse key.");
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java b/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java
new file mode 100644
index 000000000000..d44b8b1ae7a2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationExpansionParser.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification expansion event logs.
+ * @hide
+ */
+public class NotificationExpansionParser extends TagParser {
+ private static final String TAG = "NotificationExpansion";
+ private static final int EVENTLOG_TAG = 27511;
+
+ private final NotificationKey mKey;
+
+ public NotificationExpansionParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 2) {
+ try {
+ if (mKey.parse((String) operands[0])) {
+ boolean byUser = ((Integer) operands[1]) == 1;
+ boolean expanded = ((Integer) operands[2]) == 1;
+ parseTimes(operands, 3);
+
+ if (!byUser || !expanded) {
+ return;
+ }
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ proto.setType(MetricsEvent.TYPE_DETAIL);
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ filltimes(proto);
+ logger.addEvent(proto);
+ } else if (debug) {
+ Log.e(TAG, "unable to parse key.");
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationKey.java b/core/java/com/android/internal/logging/legacy/NotificationKey.java
new file mode 100644
index 000000000000..f8cac346fcec
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationKey.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+/**
+ * Parse Android notification keys
+ * @hide
+ */
+public class NotificationKey {
+
+ private static final String TAG = "NotificationKey";
+
+ public int mUser;
+ public String mPackageName;
+ public int mId;
+ public String mTag;
+ public int mUid;
+
+ public boolean parse(String key) {
+ if (key == null) {
+ return false;
+ }
+ boolean debug = Util.debug();
+ String[] parts = key.split("\\|");
+ if (parts.length == 5) {
+ try {
+ mUser = Integer.valueOf(parts[0]);
+ mPackageName = parts[1];
+ mId = Integer.valueOf(parts[2]);
+ mTag = parts[3].equals("null") ? "" : parts[3];
+ mUid = Integer.valueOf(parts[4]);
+ return true;
+ } catch (NumberFormatException e) {
+ if (debug) {
+ Log.w(TAG, "could not parse notification key.", e);
+ }
+ return false;
+ }
+ }
+ if (debug) {
+ Log.w(TAG, "wrong number of parts in notification key: " + key);
+ }
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java b/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java
new file mode 100644
index 000000000000..662295b5846d
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationPanelHiddenParser.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification panel visibility event logs.
+ * @hide
+ */
+public class NotificationPanelHiddenParser extends TagParser {
+ private static final String TAG = "NotificationPanelHidden";
+ private static final int EVENTLOG_TAG = 27501;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_PANEL);
+ proto.setType(MetricsEvent.TYPE_CLOSE);
+ proto.setTimestamp(eventTimeMs);
+ logger.addEvent(proto);
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java b/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java
new file mode 100644
index 000000000000..0566f0b64769
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationPanelRevealedParser.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android notification panel visibility event logs.
+ * @hide
+ */
+public class NotificationPanelRevealedParser extends TagParser {
+ private static final String TAG = "NotificationPanelRevea";
+ private static final int EVENTLOG_TAG = 27500;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 1) {
+ try {
+ int load = ((Integer) operands[0]).intValue();
+ //logger.incrementBy(TronCounters.TRON_NOTIFICATION_LOAD, load);
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ }
+
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_PANEL);
+ proto.setType(MetricsEvent.TYPE_OPEN);
+ proto.setTimestamp(eventTimeMs);
+ logger.addEvent(proto);
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java b/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java
new file mode 100644
index 000000000000..9185b91badc7
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/NotificationVisibilityParser.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the new Android notification visibility event logs.
+ * @hide
+ */
+public class NotificationVisibilityParser extends TagParser {
+ private static final String TAG = "NotificationVisibility";
+ private static final int EVENTLOG_TAG = 27531;
+
+ private final NotificationKey mKey;
+
+ public NotificationVisibilityParser() {
+ mKey = new NotificationKey();
+ }
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length > 1) {
+ try {
+ final String keyString = (String) operands[0];
+ final boolean visible = ((Integer) operands[1]) == 1;
+ parseTimes(operands, 2);
+ int index = 0;
+ if (operands.length > 5 && operands[5] instanceof Integer) {
+ index = (Integer) operands[5];
+ }
+
+ if (mKey.parse(keyString)) {
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.NOTIFICATION_ITEM);
+ proto.setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+ proto.setTimestamp(eventTimeMs);
+ proto.setPackageName(mKey.mPackageName);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_ID, mKey.mId);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_TAG, mKey.mTag);
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, index);
+ filltimes(proto);
+ logger.addEvent(proto);
+ } else {
+ if (debug) {
+ Log.e(TAG, "unable to parse key: " + keyString);
+ }
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ return;
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java b/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
new file mode 100644
index 000000000000..3bf0f9d43873
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class PowerScreenStateParser extends TagParser {
+ private static final String TAG = "PowerScreenStateParser";
+ private static final int EVENTLOG_TAG = 2728;
+
+ // source of truth is android.view.WindowManagerPolicy, why:
+ // 0: on
+ // 1: OFF_BECAUSE_OF_ADMIN
+ // 2: OFF_BECAUSE_OF_USER
+ // 3: OFF_BECAUSE_OF_TIMEOUT
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 2) {
+ try {
+ // (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
+ boolean state = (((Integer) operands[0]).intValue()) == 1;
+ int why = ((Integer) operands[1]).intValue();
+
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(MetricsEvent.SCREEN);
+ proto.setType(state ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+ proto.setTimestamp(eventTimeMs);
+ proto.setSubtype(why);
+ logger.addEvent(proto);
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java b/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java
new file mode 100644
index 000000000000..23abec47b636
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/StatusBarStateParser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android lockscreen gesture logs.
+ * @hide
+ */
+public class StatusBarStateParser extends TagParser {
+ private static final String TAG = "StatusBarStateParser";
+ private static final int EVENTLOG_TAG = 36004;
+
+ // source of truth is com.android.systemui.statusbar.StatusBarState
+ public static final int SHADE = 0;
+ public static final int KEYGUARD = 1;
+ public static final int SHADE_LOCKED = 2;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 6) {
+ try {
+ // [state, isShowing, isOccluded, isBouncerShowing, isSecure, isCurrentlyInsecure]
+ int state = ((Integer) operands[0]).intValue();
+ boolean isBouncerShowing = (((Integer) operands[3]).intValue()) == 1;
+ int isSecure = ((Integer) operands[4]).intValue();
+
+ int view = MetricsEvent.LOCKSCREEN;
+ int type = MetricsEvent.TYPE_OPEN;
+ if (state == SHADE) {
+ type = MetricsEvent.TYPE_CLOSE;
+ } else if (isBouncerShowing) {
+ view = MetricsEvent.BOUNCER;
+ }
+
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(view);
+ proto.setType(type);
+ proto.setTimestamp(eventTimeMs);
+ proto.setSubtype(isSecure);
+ logger.addEvent(proto);
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiActionParser.java b/core/java/com/android/internal/logging/legacy/SysuiActionParser.java
new file mode 100644
index 000000000000..7f91f92fc2d2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiActionParser.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android framework sysui action logs.
+ * @hide
+ */
+public class SysuiActionParser extends TagParser {
+ private static final String TAG = "SysuiActionParser";
+ private static final int EVENTLOG_TAG = 524288;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ try {
+ String packageName = null;
+ int subType = -1;
+ boolean hasSubType = false;
+ if (operands.length > 1) {
+ String arg = (String) operands[1];
+ if (arg.equals("true")) {
+ hasSubType = true;
+ subType = 1;
+ } else if (arg.equals("false")) {
+ hasSubType = true;
+ subType = 0;
+ } else if (arg.matches("^-?\\d+$")) {
+ try {
+ subType = Integer.valueOf(arg);
+ hasSubType = true;
+ } catch (NumberFormatException e) {
+ }
+ } else {
+ packageName = arg;
+ }
+ }
+ if (operands.length > 0) {
+ int category = ((Integer) operands[0]).intValue();
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(category);
+ proto.setType(MetricsEvent.TYPE_ACTION);
+ proto.setTimestamp(eventTimeMs);
+ if (packageName != null) {
+ proto.setPackageName(packageName);
+ }
+ if (hasSubType) {
+ proto.setSubtype(subType);
+ }
+ logger.addEvent(proto);
+ }
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java b/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
new file mode 100644
index 000000000000..f9b2f497a0dc
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * ...and one parser to rule them all.
+ *
+ * This should, at some point in the future, be the only parser.
+ * @hide
+ */
+public class SysuiMultiActionParser extends TagParser {
+ private static final String TAG = "SysuiMultiActionParser";
+ private static final int EVENTLOG_TAG = 524292;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ try {
+ logger.addEvent(new LogBuilder(operands).setTimestamp(eventTimeMs));
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java b/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java
new file mode 100644
index 000000000000..7b3c0a70d3c2
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiQueryParser.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Parse the Android framework sysui search query logs.
+ * For now just treat them like actions.
+ * @hide
+ */
+public class SysuiQueryParser extends SysuiActionParser {
+ private static final String TAG = "SysuiQueryParser";
+
+ private static final int EVENTLOG_TAG = 524289;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java b/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java
new file mode 100644
index 000000000000..5d5aec04116d
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/SysuiViewVisibilityParser.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Parse the Android framework sysui view visibility logs.
+ * @hide
+ */
+public class SysuiViewVisibilityParser extends TagParser {
+ private static final String TAG = "SysuiViewVisibility";
+ private static final int EVENTLOG_TAG = 524287;
+
+ @Override
+ public int getTag() {
+ return EVENTLOG_TAG;
+ }
+
+ @Override
+ public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
+ final boolean debug = Util.debug();
+ if (operands.length >= 2) {
+ try {
+ int category = ((Integer) operands[0]).intValue();
+ boolean visibility = ((Integer) operands[1]).intValue() != 0;
+
+ LogBuilder proto = logger.obtain();
+ proto.setCategory(category);
+ proto.setType(visibility ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
+ proto.setTimestamp(eventTimeMs);
+ logger.addEvent(proto);
+ } catch (ClassCastException e) {
+ if (debug) {
+ Log.e(TAG, "unexpected operand type: ", e);
+ }
+ }
+ } else if (debug) {
+ Log.w(TAG, "wrong number of operands: " + operands.length);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/TagParser.java b/core/java/com/android/internal/logging/legacy/TagParser.java
new file mode 100755
index 000000000000..c62d084f68e3
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TagParser.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Abstraction layer between EventLog static classes and the actual TagParsers.
+ * @hide
+ */
+public abstract class TagParser {
+ private static final String TAG = "TagParser";
+
+ protected int mSinceCreationMillis;
+ protected int mSinceUpdateMillis;
+ protected int mSinceVisibleMillis;
+
+ abstract int getTag();
+
+ @VisibleForTesting
+ abstract public void parseEvent(TronLogger logger, long eventTimeMs, Object[] objects);
+
+ /**
+ * Parse the event into the proto: return true if proto was modified.
+ */
+ public void parseEvent(TronLogger logger, EventLogCollector.Event event) {
+ final boolean debug = Util.debug();
+ Object data = event.getData();
+ Object[] objects;
+ if (data instanceof Object[]) {
+ objects = (Object[]) data;
+ for (int i = 0; i < objects.length; i++) {
+ if (objects[i] == null) {
+ if (debug) {
+ Log.d(TAG, "unexpected null value:" + event.getTag());
+ }
+ return;
+ }
+ }
+ } else {
+ // wrap scalar objects
+ objects = new Object[1];
+ objects[0] = data;
+ }
+
+ parseEvent(logger, event.getTimeNanos() / 1000000, objects);
+ }
+
+ protected void resetTimes() {
+ mSinceCreationMillis = 0;
+ mSinceUpdateMillis = 0;
+ mSinceVisibleMillis = 0;
+ }
+
+ public void parseTimes(Object[] operands, int index) {
+ resetTimes();
+
+ if (operands.length > index && operands[index] instanceof Integer) {
+ mSinceCreationMillis = (Integer) operands[index];
+ }
+
+ index++;
+ if (operands.length > index && operands[index] instanceof Integer) {
+ mSinceUpdateMillis = (Integer) operands[index];
+ }
+
+ index++;
+ if (operands.length > index && operands[index] instanceof Integer) {
+ mSinceVisibleMillis = (Integer) operands[index];
+ }
+ }
+
+ public void filltimes(LogBuilder proto) {
+ if (mSinceCreationMillis != 0) {
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS,
+ mSinceCreationMillis);
+ }
+ if (mSinceUpdateMillis != 0) {
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS,
+ mSinceUpdateMillis);
+ }
+ if (mSinceVisibleMillis != 0) {
+ proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS,
+ mSinceVisibleMillis);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/logging/legacy/TronCounters.java b/core/java/com/android/internal/logging/legacy/TronCounters.java
new file mode 100644
index 000000000000..e0828a20d6ad
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TronCounters.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Names of the counters that the Tron package defines.
+ *
+ * Other counter names may be generated by AOSP code.
+ * @hide
+ */
+public class TronCounters {
+
+ static final String TRON_COLLECTIONS = "tron_collections";
+
+ static final String TRON_COLLECTION_PERIOD = "tron_collection_period_minutes";
+
+ static final String TRON_EVENTLOG_LENGTH = "tron_eventlog_horizon";
+
+ static final String TRON_NOTE_DISMISS = "tron_note_dismiss";
+
+ static final String TRON_NOTE_DISMISS_BY_USER = "tron_note_dismiss_user";
+
+ static final String TRON_NOTE_DISMISS_BY_CLICK = "tron_note_dismiss_click";
+
+ static final String TRON_NOTE_DISMISS_BY_LISTENER = "tron_note_dismiss_listener";
+
+ static final String TRON_NOTE_DISMISS_BY_BAN = "tron_note_dismiss_ban";
+
+ static final String TRON_NOTE_REVEALED = "tron_note_revealed";
+
+ static final String TRON_NOTIFICATION_LOAD = "tron_notification_load";
+
+ static final String TRON_VIEW = "tron_view";
+
+ static final String TRON_ACTION = "tron_action";
+
+ static final String TRON_DETAIL = "tron_detail";
+
+ static final String TRON_NOTE_LIFETIME = "tron_note_lifetime";
+
+ /** Append the AOSP-generated name */
+ static final String TRON_AOSP_PREFIX = "tron_varz_";
+
+ static final String TRON_ACTION_BAD_INT = "tron_action_bad_int";
+
+ static final String TRON_NOTE_FRESHNESS = "tron_note_freshness";
+
+ static final String TRON_NOTE_BUZZ = "tron_note_buzz";
+
+ static final String TRON_NOTE_BEEP = "tron_note_beep";
+
+ static final String TRON_NOTE_BLINK = "tron_note_blink";
+
+ static final String TRON_DISABLE = "tron_disable";
+
+ static final String TRON_LAST_HEART_AGE = "tron_last_heart_minutes";
+
+ static final String TRON_HEARTS_SEEN = "tron_hearts_seen";
+}
diff --git a/core/java/com/android/internal/logging/legacy/TronLogger.java b/core/java/com/android/internal/logging/legacy/TronLogger.java
new file mode 100644
index 000000000000..dabe314d4565
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/TronLogger.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import com.android.internal.logging.LogBuilder;
+
+/**
+ * An entity that knows how to log events and counters.
+ */
+public interface TronLogger {
+ /** Add one to the named counter. */
+ void increment(String counterName);
+
+ /** Add an arbitrary value to the named counter. */
+ void incrementBy(String counterName, int value);
+
+ /** Increment a specified bucket on the named histogram by one. */
+ void incrementIntHistogram(String counterName, int bucket);
+
+ /** Increment the specified bucket on the named histogram by one. */
+ void incrementLongHistogram(String counterName, long bucket);
+
+ /** Obtain a SystemUiEvent proto, must release this with dispose() or addEvent(). */
+ LogBuilder obtain();
+
+ void dispose(LogBuilder proto);
+
+ /** Submit an event to be logged. Logger will dispose of proto. */
+ void addEvent(LogBuilder proto);
+
+ /** Get a config flag. */
+ boolean getConfig(String configName);
+
+ /** Set a config flag. */
+ void setConfig(String configName, boolean newValue);
+}
diff --git a/core/java/com/android/internal/logging/legacy/Util.java b/core/java/com/android/internal/logging/legacy/Util.java
new file mode 100644
index 000000000000..99f71caf5d29
--- /dev/null
+++ b/core/java/com/android/internal/logging/legacy/Util.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+/**
+ * Created by cwren on 11/21/16.
+ */
+public class Util {
+ public static boolean debug() {
+ return false;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
index e3f754ca8925..a3405591182d 100644
--- a/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
+++ b/core/tests/coretests/src/com/android/internal/logging/LogBuilderTest.java
@@ -1,5 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.internal.logging;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import junit.framework.TestCase;
public class LogBuilderTest extends TestCase {
@@ -15,6 +31,57 @@ public class LogBuilderTest extends TestCase {
assertEquals("two", out[3]);
}
+ public void testSerializeDeserialize() {
+ int category = 10;
+ int type = 11;
+ int subtype = 12;
+ long timestamp = 1484669007890L;
+ String packageName = "com.foo.bar";
+ String counterName = "sheep";
+ int bucket = 13;
+ int value = 14;
+
+ LogBuilder builder = new LogBuilder(category);
+ builder.setType(type);
+ builder.setSubtype(subtype);
+ builder.setTimestamp(timestamp);
+ builder.setPackageName(packageName);
+ builder.setCounterName(counterName);
+ builder.setCounterBucket(bucket);
+ builder.setCounterValue(value);
+ builder.addTaggedData(1, "one");
+ builder.addTaggedData(2, "two");
+
+ Object[] out = builder.serialize();
+ LogBuilder parsed = new LogBuilder(out);
+
+ assertEquals(category, parsed.getCategory());
+ assertEquals(type, parsed.getType());
+ assertEquals(subtype, parsed.getSubtype());
+ assertEquals(timestamp, parsed.getTimestamp());
+ assertEquals(packageName, parsed.getPackageName());
+ assertEquals(counterName, parsed.getCounterName());
+ assertEquals(bucket, parsed.getCounterBucket());
+ assertEquals(value, parsed.getCounterValue());
+ assertEquals("one", parsed.getTaggedData(1));
+ assertEquals("two", parsed.getTaggedData(2));
+ }
+
+ public void testIntBucket() {
+ LogBuilder builder = new LogBuilder(0);
+ builder.setCounterBucket(100);
+ assertEquals(100, builder.getCounterBucket());
+ assertEquals(false, builder.isLongCounterBucket());
+ }
+
+ public void testLongBucket() {
+ long longBucket = Long.MAX_VALUE;
+ LogBuilder builder = new LogBuilder(0);
+ builder.setCounterBucket(longBucket);
+ assertEquals(longBucket, builder.getCounterBucket());
+ assertEquals(true, builder.isLongCounterBucket());
+ }
+
public void testInvalidInputThrows() {
LogBuilder builder = new LogBuilder(0);
boolean threw = false;
@@ -24,7 +91,7 @@ public class LogBuilderTest extends TestCase {
threw = true;
}
assertTrue(threw);
- assertEquals(0, builder.serialize().length);
+ assertEquals(2, builder.serialize().length);
}
public void testValidInputTypes() {
@@ -40,4 +107,11 @@ public class LogBuilderTest extends TestCase {
assertEquals(123.0F, out[7]);
}
+ public void testCategoryDefault() {
+ LogBuilder builder = new LogBuilder(10);
+ Object[] out = builder.serialize();
+ assertEquals(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY, out[0]);
+ assertEquals(10, out[1]);
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java
new file mode 100644
index 000000000000..5a7766b816b7
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/CounterParserTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class CounterParserTest extends ParserTest {
+
+ public CounterParserTest() {
+ mParser = new CounterParser();
+ }
+
+ public void testGoodData() throws Throwable {
+ String name = "foo";
+ int value = 5;
+ Object[] objects = new Object[2];
+ objects[0] = name;
+ objects[1] = value;
+
+ validateGoodData(name, value, objects);
+ }
+
+ private void validateGoodData(String name, int value, Object[] objects) {
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, times(1)).incrementBy(mNameCaptor.capture(), mCountCaptor.capture());
+
+ assertEquals(TronCounters.TRON_AOSP_PREFIX + name, mNameCaptor.getValue());
+ assertEquals(value, mCountCaptor.getValue().intValue());
+ }
+
+ public void testMissingName() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testWrongTypes() throws Throwable {
+ String name = "foo";
+ int value = 5;
+ Object[] objects = new Object[2];
+ objects[0] = value;
+ objects[1] = name;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreMissingInput() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ String name = "foo";
+ int value = 5;
+ Object[] objects = new Object[3];
+ objects[0] = name;
+ objects[1] = value;
+ objects[2] = "foo";
+
+ validateGoodData(name, value, objects);
+ }
+
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java
new file mode 100644
index 000000000000..1bd9d83c5094
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/HistogramParserTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+public class HistogramParserTest extends ParserTest {
+
+ public HistogramParserTest() {
+ mParser = new HistogramParser();
+ }
+
+ public void testGoodData() throws Throwable {
+ String name = "foo";
+ int bucket = 5;
+ Object[] objects = new Object[2];
+ objects[0] = name;
+ objects[1] = bucket;
+
+ validateGoodData(name, bucket, objects);
+ }
+
+ private void validateGoodData(String name, int bucket, Object[] objects) {
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, times(1))
+ .incrementIntHistogram(mNameCaptor.capture(), mCountCaptor.capture());
+
+ assertEquals(TronCounters.TRON_AOSP_PREFIX + name, mNameCaptor.getValue());
+ assertEquals(bucket, mCountCaptor.getValue().intValue());
+ }
+
+ public void testMissingName() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testWrongTypes() throws Throwable {
+ String name = "foo";
+ int value = 5;
+ Object[] objects = new Object[2];
+ objects[0] = value;
+ objects[1] = name;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreMissingInput() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ String name = "foo";
+ int bucket = 5;
+ Object[] objects = new Object[3];
+ objects[0] = name;
+ objects[1] = bucket;
+ objects[2] = "foo";
+
+ validateGoodData(name, bucket, objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java
new file mode 100644
index 000000000000..0bff85075fce
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/LockscreenGestureParserTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class LockscreenGestureParserTest extends ParserTest {
+
+ public LockscreenGestureParserTest() {
+ mParser = new LockscreenGestureParser();
+ }
+
+ public void testSwipeUpUnlock() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_UNLOCK, 1, 359, 6382);
+ }
+
+ public void testSwipeToShade() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_SHADE, 2, 324, 0);
+ }
+
+ public void testTapLockHint() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_HINT, 3, 0, 0);
+ }
+
+ public void testCamera() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_CAMERA, 4, 223, 1756);
+ }
+
+ public void testDialer() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_DIALER, 5, 163, 861);
+ }
+
+ public void testTapToLock() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_LOCK, 6, 0, 0);
+ }
+
+ public void testTapOnNotification() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_NOTE, 7, 0, 0);
+ }
+
+ public void testLockscreenQuickSettings() throws Throwable {
+ validate(MetricsEvent.ACTION_LS_QS, 8, 284, 3824);
+ }
+
+ public void testShadePullQuickSettings() throws Throwable {
+ validate(MetricsEvent.ACTION_SHADE_QS_PULL, 9, 175, 3444);
+ }
+
+ public void testShadeTapQuickSettings() throws Throwable {
+ validate(MetricsEvent.ACTION_SHADE_QS_TAP, 10, 0, 0);
+ }
+
+ private void validate(int view, int type, int len, int vel) {
+ int t = 1000;
+ Object[] objects = new Object[3];
+ objects[0] = type;
+ objects[1] = len;
+ objects[2] = vel;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[4];
+ objects[0] = 1;
+ objects[1] = 0;
+ objects[2] = 0;
+ objects[3] = "foo";
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent((LogBuilder) anyObject());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java
new file mode 100644
index 000000000000..2119c25d05c2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationActionClickedParserTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationActionClickedParserTest extends ParserTest {
+
+ public NotificationActionClickedParserTest() {
+ mParser = new NotificationActionClickedParser();
+ }
+
+ public void testGoodData() throws Throwable {
+ int t = 1000;
+ int index = 1;
+ Object[] objects = new Object[2];
+ objects[0] = mKey;
+ objects[1] = index;
+
+ validateGoodData(t, "", index, objects);
+ }
+
+ public void testTagged() throws Throwable {
+ int t = 1000;
+ int index = 1;
+ Object[] objects = new Object[2];
+ objects[0] = mTaggedKey;
+ objects[1] = index;
+
+ validateGoodData(t, mTag, index, objects);
+ }
+
+ private LogBuilder validateGoodData(int t, String tag, int index, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ITEM_ACTION, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, tag);
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ assertEquals(index, proto.getSubtype());
+ return proto;
+ }
+
+ public void testMissingData() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testWrongType() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = 2;
+ objects[1] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testBadKey() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = "foo";
+ objects[1] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testMncTimestamps() throws Throwable {
+ int t = 1000;
+ int index = 1;
+ Object[] objects = new Object[5];
+ objects[0] = mKey;
+ objects[1] = index;
+ objects[2] = mSinceCreationMillis;
+ objects[3] = mSinceUpdateMillis;
+ objects[4] = mSinceVisibleMillis;
+
+ LogBuilder proto = validateGoodData(t, "", index, objects);
+ validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+ mSinceVisibleMillis);
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int index = 1;
+ Object[] objects = new Object[6];
+ objects[0] = mKey;
+ objects[1] = index;
+ objects[2] = mSinceCreationMillis;
+ objects[3] = mSinceUpdateMillis;
+ objects[4] = mSinceVisibleMillis;
+ objects[5] = "foo";
+
+ validateGoodData(t, "", index, objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java
new file mode 100644
index 000000000000..1e117eee097b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationAlertParserTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.mockito.ArgumentCaptor;
+
+public class NotificationAlertParserTest extends ParserTest {
+ protected ArgumentCaptor<Boolean> mConfigCaptor;
+
+ private final int mTime = 1000;
+
+ public NotificationAlertParserTest() {
+ mParser = new NotificationAlertParser();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mConfigCaptor = ArgumentCaptor.forClass(Boolean.class);
+ when(mLogger.getConfig(anyString())).thenReturn(false);
+ }
+
+ public void testBuzzOnly() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 1;
+ objects[2] = 0;
+ objects[3] = 0;
+
+ validateInteraction(true, false, false, objects);
+ }
+
+ public void testBeepOnly() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 0;
+ objects[2] = 1;
+ objects[3] = 0;
+
+ validateInteraction(false, true, false, objects);
+ }
+
+ public void testBlinkOnly() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 0;
+ objects[2] = 0;
+ objects[3] = 1;
+
+ validateInteraction(false, false, true, objects);
+ }
+
+ public void testBuzzBlink() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 1;
+ objects[2] = 0;
+ objects[3] = 1;
+
+ validateInteraction(true, false, true, objects);
+ }
+
+ public void testBeepBlink() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 0;
+ objects[2] = 1;
+ objects[3] = 1;
+
+ validateInteraction(false, true, true, objects);
+ }
+
+ public void testIgnoreExtraArgs() throws Throwable {
+ Object[] objects = new Object[5];
+ objects[0] = mTaggedKey;
+ objects[1] = 0;
+ objects[2] = 1;
+ objects[3] = 1;
+ objects[4] = "foo";
+
+ validateInteraction(false, true, true, objects);
+ }
+
+ private void validateInteraction(boolean buzz, boolean beep, boolean blink, Object[] objects) {
+ int flags = 0;
+ int counts = 0;
+ if (buzz) {
+ counts++;
+ flags |= NotificationAlertParser.BUZZ;
+ }
+ if (beep) {
+ counts++;
+ flags |= NotificationAlertParser.BEEP;
+ }
+ if (blink) {
+ counts++;
+ flags |= NotificationAlertParser.BLINK;
+ }
+
+ mParser.parseEvent(mLogger, mTime, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(mTime, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ALERT, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, mTag);
+ assertEquals(flags, proto.getSubtype());
+ assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java
new file mode 100644
index 000000000000..de1691953700
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationCanceledParserTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.List;
+
+public class NotificationCanceledParserTest extends ParserTest {
+
+ public NotificationCanceledParserTest() {
+ mParser = new NotificationCanceledParser();
+ }
+
+ public void testGoodProto() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[2];
+ objects[0] = mKey;
+ objects[1] = reason;
+
+ validateGoodData(t, "", reason, objects);
+ }
+
+ public void testTagged() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[2];
+ objects[0] = mTaggedKey;
+ objects[1] = reason;
+
+ validateGoodData(t, mTag, reason, objects);
+ }
+
+ private void validateGoodData(int t, String tag, int reason, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, tag);
+ assertEquals(MetricsEvent.TYPE_DISMISS, proto.getType());
+ assertEquals(reason, proto.getSubtype());
+ }
+
+ public void testLifetime() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = reason;
+ objects[2] = mSinceCreationMillis;
+
+ validateTimers(t, objects, mSinceCreationMillis, 0, 0);
+ }
+
+ public void testExposure() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[4];
+ objects[0] = mKey;
+ objects[1] = reason;
+ objects[2] = mSinceCreationMillis;
+ objects[3] = mSinceVisibleMillis;
+
+
+ validateTimers(t, objects, mSinceCreationMillis, 0, mSinceVisibleMillis);
+ }
+
+ public void testFreshness() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[5];
+ objects[0] = mKey;
+ objects[1] = reason;
+ objects[2] = mSinceCreationMillis;
+ objects[3] = mSinceUpdateMillis;
+ objects[4] = mSinceVisibleMillis;
+
+ validateTimers(t, objects, mSinceCreationMillis, mSinceUpdateMillis, mSinceVisibleMillis);
+ }
+
+ private void validateTimers(int t, Object[] objects, int life, int freshness, int exposure) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ validateNotificationTimes(proto, life, freshness, exposure);
+ }
+
+ public void verifyReason(int reason, boolean intentional, boolean important, String counter)
+ throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = mKey;
+ objects[1] = reason;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ if (intentional) {
+ verify(mLogger, times(1)).addEvent((LogBuilder) anyObject());
+ }
+ }
+
+ public void testDelegateCancel() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_DELEGATE_CANCEL,
+ true, true, TronCounters.TRON_NOTE_DISMISS_BY_USER);
+ }
+
+ public void testDelegateCancelAll() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_DELEGATE_CANCEL_ALL,
+ true, true, TronCounters.TRON_NOTE_DISMISS_BY_USER);
+ }
+
+ public void testListenerCancel() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_LISTENER_CANCEL,
+ false, true, TronCounters.TRON_NOTE_DISMISS_BY_LISTENER);
+ }
+
+ public void testListenerCancelAll() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_LISTENER_CANCEL_ALL,
+ false, true, TronCounters.TRON_NOTE_DISMISS_BY_LISTENER);
+ }
+
+ public void testDelegateClick() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_DELEGATE_CLICK,
+ true, true, TronCounters.TRON_NOTE_DISMISS_BY_CLICK);
+ }
+
+ public void testBanned() throws Throwable {
+ verifyReason(NotificationCanceledParser.REASON_PACKAGE_BANNED,
+ false, true, TronCounters.TRON_NOTE_DISMISS_BY_BAN);
+ }
+
+ public void testUnknownReason() throws Throwable {
+ verifyReason(1001010, false, false, TronCounters.TRON_NOTE_DISMISS_BY_BAN);
+ }
+
+ public void testMissingData() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testWrongType() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = 2;
+ objects[1] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testBadKey() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = "foo";
+ objects[1] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int reason = NotificationCanceledParser.REASON_DELEGATE_CANCEL;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = reason;
+ objects[2] = "foo";
+
+ validateGoodData(t, "", reason, objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java
new file mode 100644
index 000000000000..2f61dd7593c0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationClickedParserTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationClickedParserTest extends ParserTest {
+
+ public NotificationClickedParserTest() {
+ mParser = new NotificationClickedParser();
+ }
+
+ public void testGoodData() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[1];
+ objects[0] = mKey;
+
+ validateGoodData(t, "", objects);
+ }
+
+ public void testTagged() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[1];
+ objects[0] = mTaggedKey;
+
+ validateGoodData(t, mTag, objects);
+ }
+
+ private LogBuilder validateGoodData(int t, String tag, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, tag);
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ assertEquals(0, proto.getSubtype());
+ return proto;
+ }
+
+ public void testMissingKey() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testWrongType() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = 5;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testBadKey() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = "foo";
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testMncTimestamps() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[4];
+ objects[0] = mKey;
+ objects[1] = mSinceCreationMillis;
+ objects[2] = mSinceUpdateMillis;
+ objects[3] = mSinceVisibleMillis;
+
+ LogBuilder proto = validateGoodData(t, "", objects);
+ validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+ mSinceVisibleMillis);
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[5];
+ objects[0] = mKey;
+ objects[1] = mSinceCreationMillis;
+ objects[2] = mSinceUpdateMillis;
+ objects[3] = mSinceVisibleMillis;
+ objects[4] = "foo";
+
+ validateGoodData(t, "", objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java
new file mode 100644
index 000000000000..86b4a4536244
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationExpansionParserTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationExpansionParserTest extends ParserTest {
+
+ public NotificationExpansionParserTest() {
+ mParser = new NotificationExpansionParser();
+ }
+
+ public void testExpandedByUser() throws Throwable {
+ int t = 1000;
+ int byUser = 1;
+ int expanded = 1;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+
+ validateGoodData(t, "", objects);
+ }
+
+ public void testTagged() throws Throwable {
+ int t = 1000;
+ int byUser = 1;
+ int expanded = 1;
+ Object[] objects = new Object[3];
+ objects[0] = mTaggedKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+
+ validateGoodData(t, mTag, objects);
+ }
+
+ private LogBuilder validateGoodData(int t, String tag, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, tag);
+ assertEquals(MetricsEvent.TYPE_DETAIL, proto.getType());
+ return proto;
+ }
+
+ public void testAutoExpand() throws Throwable {
+ int t = 1000;
+ int byUser = 0;
+ int expanded = 1;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testCollapsedByUser() throws Throwable {
+ int t = 1000;
+ int byUser = 1;
+ int expanded = 0;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testAutoCollapsed() throws Throwable {
+ int t = 1000;
+ int byUser = 0;
+ int expanded = 0;
+ Object[] objects = new Object[3];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testMissingData() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testWrongType() throws Throwable {
+ Object[] objects = new Object[3];
+ objects[0] = 2;
+ objects[1] = 5;
+ objects[2] = 7;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testBadKey() throws Throwable {
+ Object[] objects = new Object[3];
+ objects[0] = "foo";
+ objects[1] = 5;
+ objects[2] = 2;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testMncTimestamps() throws Throwable {
+ int t = 1000;
+ int byUser = 1;
+ int expanded = 1;
+ Object[] objects = new Object[6];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+ objects[3] = mSinceCreationMillis;
+ objects[4] = mSinceUpdateMillis;
+ objects[5] = mSinceVisibleMillis;
+
+ LogBuilder proto = validateGoodData(t, "", objects);
+ validateNotificationTimes(proto, mSinceCreationMillis, mSinceUpdateMillis,
+ mSinceVisibleMillis);
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int byUser = 1;
+ int expanded = 1;
+ Object[] objects = new Object[7];
+ objects[0] = mKey;
+ objects[1] = byUser;
+ objects[2] = expanded;
+ objects[3] = mSinceCreationMillis;
+ objects[4] = mSinceUpdateMillis;
+ objects[5] = mSinceVisibleMillis;
+ objects[6] = "foo";
+
+ validateGoodData(t, "", objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java
new file mode 100644
index 000000000000..b50970044b2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationKeyTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import android.test.InstrumentationTestCase;
+
+public class NotificationKeyTest extends InstrumentationTestCase {
+
+ private final NotificationKey mKey;
+
+ public NotificationKeyTest() {
+ mKey = new NotificationKey();
+ }
+
+ public void testGoodKey() throws Throwable {
+ assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338|null|10090"));
+
+ assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+ assertEquals("", mKey.mTag);
+ assertEquals(31338, mKey.mId);
+ assertEquals(1, mKey.mUser);
+ assertEquals(10090, mKey.mUid);
+ }
+
+ public void testTaggedKey() throws Throwable {
+ assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338|foo|10090"));
+
+ assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+ assertEquals("foo", mKey.mTag);
+ assertEquals(31338, mKey.mId);
+ assertEquals(1, mKey.mUser);
+ assertEquals(10090, mKey.mUid);
+ }
+
+ public void testEmptyTag() throws Throwable {
+ assertTrue(mKey.parse("1|com.android.example.notificationshowcase|31338||10090"));
+
+ assertEquals("com.android.example.notificationshowcase", mKey.mPackageName);
+ assertEquals("", mKey.mTag);
+ assertEquals(31338, mKey.mId);
+ assertEquals(1, mKey.mUser);
+ assertEquals(10090, mKey.mUid);
+ }
+
+ public void testBadKeys() throws Throwable {
+ assertFalse(mKey.parse(null));
+ assertFalse(mKey.parse(""));
+ assertFalse(mKey.parse("foo")); // not a key
+ assertFalse(mKey.parse("1|com.android.example.notificationshowcase|31338|null"));
+ assertFalse(mKey.parse("bar|com.android.example.notificationshowcase|31338|null|10090"));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java
new file mode 100644
index 000000000000..3efc48fa828a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelHiddenParserTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationPanelHiddenParserTest extends ParserTest {
+
+ public NotificationPanelHiddenParserTest() {
+ mParser = new NotificationPanelHiddenParser();
+ }
+
+ public void testNoInput() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[0];
+
+ validateGoodData(t, objects);
+
+ }
+
+ public void testIgnoreExtraneousInput() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = "nothing to see here";
+
+ validateGoodData(0, objects);
+ }
+
+ private void validateGoodData(int t, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_CLOSE, proto.getType());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java
new file mode 100644
index 000000000000..34dda983cb50
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationPanelRevealedParserTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class NotificationPanelRevealedParserTest extends ParserTest {
+
+ public NotificationPanelRevealedParserTest() {
+ mParser = new NotificationPanelRevealedParser();
+ }
+
+ public void testLollipopInput() throws Throwable {
+ int t = 1000;
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+ }
+
+ public void testMncData() throws Throwable {
+ int t = 1000;
+ int n = 5;
+ Object[] objects = new Object[1];
+ objects[0] = Integer.valueOf(n);
+
+ validateMncData(t, n, objects);
+ }
+
+ private void validateMncData(int t, int n, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_PANEL, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+ }
+
+ public void testBadInput() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = "This is not the integer you're looking for.";
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, times(1)).addEvent((LogBuilder) anyObject());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int n = 5;
+ Object[] objects = new Object[2];
+ objects[0] = Integer.valueOf(n);
+ objects[1] = "foo";
+
+ validateMncData(t, n, objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java
new file mode 100644
index 000000000000..cc5421c0b422
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/NotificationVisibilityParserTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.mockito.ArgumentCaptor;
+
+public class NotificationVisibilityParserTest extends ParserTest {
+ private final int mCreationTime = 23124;
+ private final int mUpdateTime = 3412;
+ private final int mTime = 1000;
+
+ public NotificationVisibilityParserTest() {
+ mParser = new NotificationVisibilityParser();
+ }
+
+ public void testReveal() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 1;
+ objects[2] = mCreationTime;
+ objects[3] = mUpdateTime;
+
+ validateInteraction(true, mUpdateTime, 0, objects);
+ }
+
+ public void testHide() throws Throwable {
+ Object[] objects = new Object[4];
+ objects[0] = mTaggedKey;
+ objects[1] = 0;
+ objects[2] = mCreationTime;
+ objects[3] = mUpdateTime;
+
+ validateInteraction(false, mUpdateTime, 0, objects);
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ Object[] objects = new Object[5];
+ objects[0] = mTaggedKey;
+ objects[1] = 1;
+ objects[2] = mCreationTime;
+ objects[3] = mUpdateTime;
+ objects[4] = "foo";
+
+ validateInteraction(true, mUpdateTime, 0, objects);
+ }
+
+ public void testMarshmallowIndexData() throws Throwable {
+ Object[] objects = new Object[6];
+ objects[0] = mTaggedKey;
+ objects[1] = 1;
+ objects[2] = mCreationTime;
+ objects[3] = mUpdateTime;
+ objects[4] = 0;
+ objects[5] = 3;
+
+ validateInteraction(true, mUpdateTime, 3, objects);
+ }
+
+ private void validateInteraction(boolean visible, int freshness, int index, Object[] objects) {
+ mParser.parseEvent(mLogger, mTime, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(mTime, proto.getTimestamp());
+ assertEquals(MetricsEvent.NOTIFICATION_ITEM, proto.getCategory());
+ assertEquals(mKeyPackage, proto.getPackageName());
+ validateNotificationIdAndTag(proto, mId, mTag);
+ validateNotificationTimes(proto, mCreationTime, mUpdateTime);
+ assertEquals(index, proto.getTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX));
+ assertEquals(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE, proto.getType());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
new file mode 100644
index 000000000000..2e5c6d2a40b0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import junit.framework.TestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.OngoingStubbing;
+
+/**
+ * Common functions and temporaries for parser tests.
+ */
+public class ParserTest extends TestCase {
+ @Mock
+ protected TronLogger mLogger;
+
+ protected TagParser mParser;
+
+ protected LogBuilder[] mProto;
+ protected ArgumentCaptor<LogBuilder> mProtoCaptor;
+ protected ArgumentCaptor<String> mNameCaptor;
+ protected ArgumentCaptor<Integer> mCountCaptor;
+ protected String mKey = "0|com.android.example.notificationshowcase|31338|null|10090";
+ protected String mTaggedKey = "0|com.android.example.notificationshowcase|31338|badger|10090";
+ protected String mKeyPackage = "com.android.example.notificationshowcase";
+ protected String mTag = "badger";
+ protected int mId = 31338;
+ protected int mSinceCreationMillis = 5000;
+ protected int mSinceUpdateMillis = 1012;
+ protected int mSinceVisibleMillis = 323;
+
+
+ public ParserTest() {
+ mProto = new LogBuilder[5];
+ for (int i = 0; i < mProto.length; i++) {
+ mProto[i] = new LogBuilder(MetricsEvent.VIEW_UNKNOWN);
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ mProtoCaptor = ArgumentCaptor.forClass(LogBuilder.class);
+ mNameCaptor = ArgumentCaptor.forClass(String.class);
+ mCountCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ OngoingStubbing<LogBuilder> stub = when(mLogger.obtain()).thenReturn(mProto[0]);
+ for (int i = 1; i < mProto.length; i++) {
+ stub.thenReturn(mProto[i]);
+ }
+ doNothing().when(mLogger).addEvent(any(LogBuilder.class));
+ doNothing().when(mLogger).incrementBy(anyString(), anyInt());
+ }
+
+ protected void validateNotificationTimes(LogBuilder proto, int life, int freshness,
+ int exposure) {
+ validateNotificationTimes(proto, life, freshness);
+ if (exposure != 0) {
+ assertEquals(exposure,
+ proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+ } else {
+ assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
+ }
+ }
+
+ protected void validateNotificationTimes(LogBuilder proto, int life, int freshness) {
+ if (life != 0) {
+ assertEquals(life,
+ proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+ } else {
+ assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
+ }
+ if (freshness != 0) {
+ assertEquals(freshness,
+ proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+ } else {
+ assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
+ }
+ }
+
+ protected void validateNotificationIdAndTag(LogBuilder proto, int id, String tag) {
+ assertEquals(tag, proto.getTaggedData(MetricsEvent.NOTIFICATION_TAG));
+ assertEquals(id, proto.getTaggedData(MetricsEvent.NOTIFICATION_ID));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
new file mode 100644
index 000000000000..be918cd91921
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class PowerScreenStateParserTest extends ParserTest {
+
+ public PowerScreenStateParserTest() {
+ mParser = new PowerScreenStateParser();
+ }
+
+ public void testScreenOn() throws Throwable {
+ validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0");
+ }
+
+ public void testTimeout() throws Throwable {
+ validate(MetricsEvent.TYPE_CLOSE, 3, "0,3,0,0");
+ }
+
+ public void testUser() throws Throwable {
+ validate(MetricsEvent.TYPE_CLOSE, 2, "0,2,0,0");
+ }
+
+ public void testAdmin() throws Throwable {
+ validate(MetricsEvent.TYPE_CLOSE, 1, "0,1,0,0");
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0,5");
+ }
+
+ private void validate(int type, int subType, String log) {
+ String[] parts = log.split(",");
+ int t = 1000;
+ Object[] objects = new Object[parts.length];
+ for (int i = 0; i < parts.length; i++) {
+ objects[i] = Integer.valueOf(parts[i]);
+ }
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(type, proto.getType());
+ assertEquals(MetricsEvent.SCREEN, proto.getCategory());
+ assertEquals(subType, proto.getSubtype());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java
new file mode 100644
index 000000000000..906ec0403d86
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/StatusBarStateParserTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class StatusBarStateParserTest extends ParserTest {
+
+ public StatusBarStateParserTest() {
+ mParser = new StatusBarStateParser();
+ }
+
+ public void testLockScreen() throws Throwable {
+ validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 1, "1,1,0,0,1,0");
+ }
+
+ public void testBounce() throws Throwable {
+ validate(MetricsEvent.BOUNCER, MetricsEvent.TYPE_OPEN, 1, "1,1,0,1,1,0");
+ }
+
+ public void testUnlock() throws Throwable {
+ validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_CLOSE, 1, "0,0,0,0,1,0");
+ }
+
+ public void testSecure() throws Throwable {
+ validate(MetricsEvent.BOUNCER, MetricsEvent.TYPE_OPEN, 1, "2,1,0,1,1,0");
+ }
+
+ public void testInsecure() throws Throwable {
+ validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 0, "1,1,0,0,0,0");
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ validate(MetricsEvent.LOCKSCREEN, MetricsEvent.TYPE_OPEN, 0, "1,1,0,0,0,0,5");
+ }
+
+ private void validate(int view, int type, int subType, String log) {
+ String[] parts = log.split(",");
+ int t = 1000;
+ Object[] objects = new Object[parts.length];
+ for (int i = 0; i < parts.length; i++) {
+ objects[i] = Integer.valueOf(parts[i]);
+ }
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(type, proto.getType());
+ assertEquals(subType, proto.getSubtype());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java
new file mode 100644
index 000000000000..111909f67912
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiActionParserTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class SysuiActionParserTest extends ParserTest {
+
+ public SysuiActionParserTest() {
+ mParser = new SysuiActionParser();
+ }
+
+ public void testGoodDatal() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[1];
+ objects[0] = view;
+
+ validateGoodData(t, view, objects);
+ }
+
+ private void validateGoodData(int t, int view, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ }
+
+ public void testGoodDataWithPackage() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ String packageName = "com.foo";
+ Object[] objects = new Object[2];
+ objects[0] = view;
+ objects[1] = packageName;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(packageName, proto.getPackageName());
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ }
+
+ public void testGoodDataWithTrue() throws Throwable {
+ validateSubType(Boolean.toString(true), 1);
+ }
+
+ public void testGoodDataWithFalse() throws Throwable {
+ validateSubType(Boolean.toString(false), 0);
+ }
+
+ public void testGoodDataWithIntZero() throws Throwable {
+ validateSubType(Integer.toString(0), 0);
+ }
+
+ public void testGoodDataWithIntONe() throws Throwable {
+ validateSubType(Integer.toString(1), 1);
+ }
+
+ public void testGoodDataWithIntTwo() throws Throwable {
+ validateSubType(Integer.toString(2), 2);
+ }
+
+ public void testGoodDataWithNegativeInt() throws Throwable {
+ validateSubType(Integer.toString(-1), -1);
+ }
+
+ public void testGoodDataWithIntLarge() throws Throwable {
+ validateSubType(Integer.toString(120312), 120312);
+ }
+
+ public void testGoodDataWithNegativeIntLarge() throws Throwable {
+ validateSubType(Integer.toString(-120312), -120312);
+ }
+
+ private void validateSubType(String arg, int expectedValue) {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[2];
+ objects[0] = view;
+ objects[1] = arg;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(expectedValue, proto.getSubtype());
+ assertNull(proto.getPackageName());
+ assertEquals(MetricsEvent.TYPE_ACTION, proto.getType());
+ }
+
+ public void testIgnoreMissingInput() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreWrongInputs() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = "nothing to see here";
+ objects[1] = 10;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreStringViewInput() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = "this is not the input you are looking for";
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[2];
+ objects[0] = view;
+ objects[1] = "foo";
+
+ validateGoodData(t, view, objects);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
new file mode 100644
index 000000000000..7853f775c200
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class SysuiMultiActionParserTest extends ParserTest {
+
+ public SysuiMultiActionParserTest() {
+ mParser = new SysuiMultiActionParser();
+ }
+
+ public void testParseAllFields() {
+ int category = 10;
+ int type = 11;
+ int subtype = 12;
+ long timestamp = 1484669007890L;
+ String packageName = "com.foo.bar";
+ String counterName = "sheep";
+ int bucket = 13;
+ int value = 14;
+ LogBuilder builder = new LogBuilder(category);
+ builder.setType(type);
+ builder.setSubtype(subtype);
+ builder.setPackageName(packageName);
+ builder.setCounterName(counterName);
+ builder.setCounterBucket(bucket);
+ builder.setCounterValue(value);
+ builder.addTaggedData(1, "one");
+ builder.addTaggedData(2, "two");
+ Object[] out = builder.serialize();
+ int t = 1000;
+
+ mParser.parseEvent(mLogger, timestamp, out);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(category, proto.getCategory());
+ assertEquals(type, proto.getType());
+ assertEquals(subtype, proto.getSubtype());
+ assertEquals(timestamp, proto.getTimestamp());
+ assertEquals(packageName, proto.getPackageName());
+ assertEquals(counterName, proto.getCounterName());
+ assertEquals(bucket, proto.getCounterBucket());
+ assertEquals(value, proto.getCounterValue());
+ assertEquals("one", proto.getTaggedData(1));
+ assertEquals("two", proto.getTaggedData(2));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java
new file mode 100644
index 000000000000..1291508663cc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiViewVisibilityParserTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.logging.legacy;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import com.android.internal.logging.LogBuilder;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class SysuiViewVisibilityParserTest extends ParserTest {
+
+ public SysuiViewVisibilityParserTest() {
+ mParser = new SysuiViewVisibilityParser();
+ }
+
+ public void testViewReveal() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[2];
+ objects[0] = view;
+ objects[1] = 100;
+
+ validateViewReveal(t, view, objects);
+ }
+
+ private void validateViewReveal(int t, int view, Object[] objects) {
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(t, proto.getTimestamp());
+ assertEquals(view, proto.getCategory());
+ assertEquals(MetricsEvent.TYPE_OPEN, proto.getType());
+ }
+
+ public void testViewHidden() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[2];
+ objects[0] = view;
+ objects[1] = 0;
+
+ mParser.parseEvent(mLogger, t, objects);
+
+ verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
+
+ LogBuilder proto = mProtoCaptor.getValue();
+ assertEquals(MetricsEvent.TYPE_CLOSE, proto.getType());
+ }
+
+ public void testIgnoreMissingInput() throws Throwable {
+ Object[] objects = new Object[0];
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreStringInARgOne() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = "nothing to see here";
+ objects[1] = 100;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreStringInArgTwo() throws Throwable {
+ Object[] objects = new Object[2];
+ objects[0] = 100;
+ objects[1] = "nothing to see here";
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testOneInput() throws Throwable {
+ Object[] objects = new Object[1];
+ objects[0] = 100;
+
+ mParser.parseEvent(mLogger, 0, objects);
+
+ verify(mLogger, never()).addEvent((LogBuilder) anyObject());
+ verify(mLogger, never()).incrementBy(anyString(), anyInt());
+ }
+
+ public void testIgnoreUnexpectedData() throws Throwable {
+ int t = 1000;
+ int view = 10;
+ Object[] objects = new Object[3];
+ objects[0] = view;
+ objects[1] = 100;
+ objects[2] = "foo";
+
+ validateViewReveal(t, view, objects);
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 690eb883be24..62ea9e335107 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3173,9 +3173,9 @@ message MetricsEvent {
// These values should never appear in log outputs - they are reserved for
// internal Tron use.
- RESERVED_FOR_LOGBUILDER_VIEW = 757;
- RESERVED_FOR_LOGBUILDER_CATEGORY = 758;
- RESERVED_FOR_LOGBUILDER_TYPE = 759;
+ RESERVED_FOR_LOGBUILDER_CATEGORY = 757;
+ RESERVED_FOR_LOGBUILDER_TYPE = 758;
+ RESERVED_FOR_LOGBUILDER_SUBTYPE = 759;
// ACTION: "Do not show again" was enabled in the support disclaimer and the
// user accepted
@@ -3275,16 +3275,36 @@ message MetricsEvent {
// OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
+
// OPEN: Settings > Apps > Default Apps > Default auto-fill app
DEFAULT_AUTO_FILL_PICKER = 792;
- // ---- End O Constants, all O constants go above this line ----
+ // These values should never appear in log outputs - they are reserved for
+ // internal Tron use.
+ NOTIFICATION_SINCE_CREATE_MILLIS = 793;
+ NOTIFICATION_SINCE_VISIBLE_MILLIS = 794;
+ NOTIFICATION_SINCE_UPDATE_MILLIS = 795;
+ NOTIFICATION_ID = 796;
+ NOTIFICATION_TAG = 797;
+ NOTIFICATION_SHADE_INDEX = 798;
+ RESERVED_FOR_LOGBUILDER_NAME = 799;
// OPEN: QS NFC tile shown
// ACTION: QS NFC tile tapped
// CATEGORY: QUICK_SETTINGS
QS_NFC = 800;
+ // These values should never appear in log outputs - they are reserved for
+ // internal Tron use.
+ RESERVED_FOR_LOGBUILDER_BUCKET = 801;
+ RESERVED_FOR_LOGBUILDER_VALUE = 802;
+ RESERVED_FOR_LOGBUILDER_COUNTER = 803;
+ RESERVED_FOR_LOGBUILDER_HISTOGRAM = 804;
+ RESERVED_FOR_LOGBUILDER_TIMESTAMP = 805;
+ RESERVED_FOR_LOGBUILDER_PACKAGENAME = 806;
+
+ // ---- End O Constants, all O constants go above this line ----
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}