Loading core/java/com/android/internal/logging/LogBuilder.java +157 −9 Original line number Diff line number Diff line /* * 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; Loading @@ -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) { Loading @@ -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"); } Loading @@ -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. */ Loading @@ -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()); } } } } core/java/com/android/internal/logging/MetricsLogger.java +43 −5 Original line number Diff line number Diff line Loading @@ -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. * Loading @@ -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 { Loading @@ -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) Loading @@ -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()); } Loading @@ -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()); } } core/java/com/android/internal/logging/MetricsReader.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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(); } } core/java/com/android/internal/logging/legacy/CounterParser.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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); } } core/java/com/android/internal/logging/legacy/EventLogCollector.java 0 → 100644 +188 −0 Original line number Diff line number Diff line /* * 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); } } } } Loading
core/java/com/android/internal/logging/LogBuilder.java +157 −9 Original line number Diff line number Diff line /* * 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; Loading @@ -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) { Loading @@ -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"); } Loading @@ -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. */ Loading @@ -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()); } } } }
core/java/com/android/internal/logging/MetricsLogger.java +43 −5 Original line number Diff line number Diff line Loading @@ -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. * Loading @@ -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 { Loading @@ -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) Loading @@ -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()); } Loading @@ -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()); } }
core/java/com/android/internal/logging/MetricsReader.java 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * 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(); } }
core/java/com/android/internal/logging/legacy/CounterParser.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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); } }
core/java/com/android/internal/logging/legacy/EventLogCollector.java 0 → 100644 +188 −0 Original line number Diff line number Diff line /* * 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); } } } }