diff options
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 } |