Loading services/core/java/com/android/server/location/LocationManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1385,7 +1385,7 @@ public class LocationManagerService extends ILocationManager.Stub implements ipw.println("Event Log:"); ipw.increaseIndent(); EVENT_LOG.iterate(manager.getName(), ipw::println); EVENT_LOG.iterate(ipw::println, manager.getName()); ipw.decreaseIndent(); return; } Loading services/core/java/com/android/server/location/eventlog/LocalEventLog.java +136 −161 Original line number Diff line number Diff line Loading @@ -16,217 +16,193 @@ package com.android.server.location.eventlog; import static java.lang.Integer.bitCount; import android.annotation.Nullable; import android.os.SystemClock; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import java.util.Iterator; import java.lang.reflect.Array; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.function.Consumer; import java.util.Objects; /** * An in-memory event log to support historical event information. * An in-memory event log to support historical event information. The log is of a constant size, * and new events will overwrite old events as the log fills up. * * @param <T> log event type */ public abstract class LocalEventLog { public class LocalEventLog<T> { private interface Log { // true if this is a filler element that should not be queried boolean isFiller(); long getTimeDeltaMs(); String getLogString(); boolean filter(@Nullable String filter); /** * Consumer of log events for iterating over the log. * * @param <T> log event type */ public interface LogConsumer<T> { /** Invoked with a time and a logEvent. */ void acceptLog(long time, T logEvent); } private static final class FillerEvent implements Log { // masks for the entries field. 1 bit is used to indicate whether this is a filler event or not, // and 31 bits to store the time delta. private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000; private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111; static final long MAX_TIME_DELTA = (1L << 32) - 1; private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK); private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK); private final int mTimeDelta; static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1; FillerEvent(long timeDelta) { Preconditions.checkArgument(timeDelta >= 0); mTimeDelta = (int) timeDelta; private static int countTrailingZeros(int i) { int c = 0; while (i != 0 && (i & 1) == 0) { c++; i = i >>> 1; } @Override public boolean isFiller() { return true; return c; } @Override public long getTimeDeltaMs() { return Integer.toUnsignedLong(mTimeDelta); private static int createEntry(boolean isFiller, int timeDelta) { Preconditions.checkArgument(timeDelta >= 0 && timeDelta <= MAX_TIME_DELTA); return (((isFiller ? 1 : 0) << IS_FILLER_OFFSET) & IS_FILLER_MASK) | ((timeDelta << TIME_DELTA_OFFSET) & TIME_DELTA_MASK); } @Override public String getLogString() { throw new AssertionError(); static int getTimeDelta(int entry) { return (entry & TIME_DELTA_MASK) >>> TIME_DELTA_OFFSET; } @Override public boolean filter(String filter) { return false; } static boolean isFiller(int entry) { return (entry & IS_FILLER_MASK) != 0; } /** * An abstraction of a log event to be implemented by subclasses. */ public abstract static class LogEvent implements Log { // circular buffer of log entries and events. each entry corrosponds to the log event at the // same index. the log entry holds the filler status and time delta according to the bit masks // above, and the log event is the log event. static final long MAX_TIME_DELTA = (1L << 32) - 1; @GuardedBy("this") final int[] mEntries; private final int mTimeDelta; protected LogEvent(long timeDelta) { Preconditions.checkArgument(timeDelta >= 0); mTimeDelta = (int) timeDelta; } @GuardedBy("this") final @Nullable T[] mLogEvents; @Override public final boolean isFiller() { return false; } @GuardedBy("this") int mLogSize; @Override public final long getTimeDeltaMs() { return Integer.toUnsignedLong(mTimeDelta); } @GuardedBy("this") int mLogEndIndex; @Override public boolean filter(String filter) { return false; } } // invalid if log is empty // circular buffer of log entries private final Log[] mLog; private int mLogSize; private int mLogEndIndex; @GuardedBy("this") long mStartTime; // invalid if log is empty private long mStartRealtimeMs; private long mLastLogRealtimeMs; @GuardedBy("this") long mLastLogTime; public LocalEventLog(int size) { @SuppressWarnings("unchecked") public LocalEventLog(int size, Class<T> clazz) { Preconditions.checkArgument(size > 0); mLog = new Log[size]; mEntries = new int[size]; mLogEvents = (T[]) Array.newInstance(clazz, size); mLogSize = 0; mLogEndIndex = 0; mStartRealtimeMs = -1; mLastLogRealtimeMs = -1; } /** * Should be overridden by subclasses to return a new immutable log event for the given * arguments (as passed into {@link #addLogEvent(int, Object...)}. */ protected abstract LogEvent createLogEvent(long timeDelta, int event, Object... args); /** * May be optionally overridden by subclasses if they wish to change how log event time is * formatted. */ protected String getTimePrefix(long timeMs) { return TimeUtils.logTimeOfDay(timeMs) + ": "; mStartTime = -1; mLastLogTime = -1; } /** * Call to add a new log event at the current time. The arguments provided here will be passed * into {@link #createLogEvent(long, int, Object...)} in addition to a time delta, and should be * used to construct an appropriate {@link LogEvent} object. */ public synchronized void addLogEvent(int event, Object... args) { long timeMs = SystemClock.elapsedRealtime(); /** Call to add a new log event at the given time. */ protected synchronized void addLog(long time, T logEvent) { Preconditions.checkArgument(logEvent != null); // calculate delta long delta = 0; if (!isEmpty()) { delta = timeMs - mLastLogRealtimeMs; delta = time - mLastLogTime; // if the delta is invalid, or if the delta is great enough using filler elements would // if the delta is negative, or if the delta is great enough using filler elements would // result in an empty log anyways, just clear the log and continue, otherwise insert // filler elements until we have a reasonable delta if (delta < 0 || (delta / FillerEvent.MAX_TIME_DELTA) >= mLog.length - 1) { if (delta < 0 || (delta / MAX_TIME_DELTA) >= mEntries.length - 1) { clear(); delta = 0; } else { while (delta >= LogEvent.MAX_TIME_DELTA) { long timeDelta = Math.min(FillerEvent.MAX_TIME_DELTA, delta); addLogEventInternal(new FillerEvent(timeDelta)); delta -= timeDelta; while (delta >= MAX_TIME_DELTA) { addLogEventInternal(true, MAX_TIME_DELTA, null); delta -= MAX_TIME_DELTA; } } } // for first log entry, set initial times if (isEmpty()) { mStartRealtimeMs = timeMs; mLastLogRealtimeMs = mStartRealtimeMs; mStartTime = time; mLastLogTime = mStartTime; } addLogEventInternal(createLogEvent(delta, event, args)); addLogEventInternal(false, (int) delta, logEvent); } private void addLogEventInternal(Log event) { Preconditions.checkState(mStartRealtimeMs != -1 && mLastLogRealtimeMs != -1); @GuardedBy("this") private void addLogEventInternal(boolean isFiller, int timeDelta, @Nullable T logEvent) { Preconditions.checkArgument(isFiller || logEvent != null); Preconditions.checkState(mStartTime != -1 && mLastLogTime != -1); if (mLogSize == mLog.length) { if (mLogSize == mEntries.length) { // if log is full, size will remain the same, but update the start time mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs(); mStartTime += getTimeDelta(mEntries[startIndex()]); } else { // otherwise add an item mLogSize++; } // set log and increment end index mLog[mLogEndIndex] = event; mEntries[mLogEndIndex] = createEntry(isFiller, timeDelta); mLogEvents[mLogEndIndex] = logEvent; mLogEndIndex = incrementIndex(mLogEndIndex); mLastLogRealtimeMs = mLastLogRealtimeMs + event.getTimeDeltaMs(); mLastLogTime = mLastLogTime + timeDelta; } /** Clears the log of all entries. */ public synchronized void clear() { // clear entries to allow gc Arrays.fill(mLogEvents, null); mLogEndIndex = 0; mLogSize = 0; mStartRealtimeMs = -1; mLastLogRealtimeMs = -1; mStartTime = -1; mLastLogTime = -1; } // checks if the log is empty (if empty, times are invalid) private synchronized boolean isEmpty() { @GuardedBy("this") private boolean isEmpty() { return mLogSize == 0; } /** Iterates over the event log, passing each log string to the given consumer. */ public synchronized void iterate(Consumer<String> consumer) { public synchronized void iterate(LogConsumer<? super T> consumer) { LogIterator it = new LogIterator(); while (it.hasNext()) { consumer.accept(it.next()); } } /** * Iterates over the event log, passing each filter-matching log string to the given * consumer. */ public synchronized void iterate(String filter, Consumer<String> consumer) { LogIterator it = new LogIterator(filter); while (it.hasNext()) { consumer.accept(it.next()); it.next(); consumer.acceptLog(it.getTime(), it.getLog()); } } // returns the index of the first element @GuardedBy("this") private int startIndex() { return wrapIndex(mLogEndIndex - mLogSize); } // returns the index after this one @GuardedBy("this") private int incrementIndex(int index) { if (index == -1) { return startIndex(); Loading @@ -238,69 +214,68 @@ public abstract class LocalEventLog { } // rolls over the given index if necessary @GuardedBy("this") private int wrapIndex(int index) { // java modulo will keep negative sign, we need to rollover return (index % mLog.length + mLog.length) % mLog.length; return (index % mEntries.length + mEntries.length) % mEntries.length; } private class LogIterator implements Iterator<String> { private final @Nullable String mFilter; private final long mSystemTimeDeltaMs; private class LogIterator { private long mCurrentRealtimeMs; private long mLogTime; private int mIndex; private int mCount; LogIterator() { this(null); } private long mCurrentTime; private T mCurrentLogEvent; LogIterator(@Nullable String filter) { mFilter = filter; mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime(); mCurrentRealtimeMs = mStartRealtimeMs; LogIterator() { synchronized (LocalEventLog.this) { mLogTime = mStartTime; mIndex = -1; mCount = -1; increment(); } } @Override public boolean hasNext() { synchronized (LocalEventLog.this) { return mCount < mLogSize; } } public String next() { public void next() { synchronized (LocalEventLog.this) { if (!hasNext()) { throw new NoSuchElementException(); } Log log = mLog[mIndex]; long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs; mCurrentTime = mLogTime + getTimeDelta(mEntries[mIndex]); mCurrentLogEvent = Objects.requireNonNull(mLogEvents[mIndex]); increment(); } } return getTimePrefix(timeMs) + log.getLogString(); public long getTime() { return mCurrentTime; } @Override public void remove() { throw new UnsupportedOperationException(); public T getLog() { return mCurrentLogEvent; } private void increment() { long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs(); @GuardedBy("LocalEventLog.this") private void increment(LogIterator this) { long nextDeltaMs = mIndex == -1 ? 0 : getTimeDelta(mEntries[mIndex]); do { mCurrentRealtimeMs += nextDeltaMs; mLogTime += nextDeltaMs; mIndex = incrementIndex(mIndex); if (++mCount < mLogSize) { nextDeltaMs = mLog[mIndex].getTimeDeltaMs(); nextDeltaMs = getTimeDelta(mEntries[mIndex]); } } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null && !mLog[mIndex].filter(mFilter)))); } while (mCount < mLogSize && isFiller(mEntries[mIndex])); } } } services/core/java/com/android/server/location/eventlog/LocationEventLog.java +90 −143 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/location/LocationManagerService.java +1 −1 Original line number Diff line number Diff line Loading @@ -1385,7 +1385,7 @@ public class LocationManagerService extends ILocationManager.Stub implements ipw.println("Event Log:"); ipw.increaseIndent(); EVENT_LOG.iterate(manager.getName(), ipw::println); EVENT_LOG.iterate(ipw::println, manager.getName()); ipw.decreaseIndent(); return; } Loading
services/core/java/com/android/server/location/eventlog/LocalEventLog.java +136 −161 Original line number Diff line number Diff line Loading @@ -16,217 +16,193 @@ package com.android.server.location.eventlog; import static java.lang.Integer.bitCount; import android.annotation.Nullable; import android.os.SystemClock; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import java.util.Iterator; import java.lang.reflect.Array; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.function.Consumer; import java.util.Objects; /** * An in-memory event log to support historical event information. * An in-memory event log to support historical event information. The log is of a constant size, * and new events will overwrite old events as the log fills up. * * @param <T> log event type */ public abstract class LocalEventLog { public class LocalEventLog<T> { private interface Log { // true if this is a filler element that should not be queried boolean isFiller(); long getTimeDeltaMs(); String getLogString(); boolean filter(@Nullable String filter); /** * Consumer of log events for iterating over the log. * * @param <T> log event type */ public interface LogConsumer<T> { /** Invoked with a time and a logEvent. */ void acceptLog(long time, T logEvent); } private static final class FillerEvent implements Log { // masks for the entries field. 1 bit is used to indicate whether this is a filler event or not, // and 31 bits to store the time delta. private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000; private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111; static final long MAX_TIME_DELTA = (1L << 32) - 1; private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK); private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK); private final int mTimeDelta; static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1; FillerEvent(long timeDelta) { Preconditions.checkArgument(timeDelta >= 0); mTimeDelta = (int) timeDelta; private static int countTrailingZeros(int i) { int c = 0; while (i != 0 && (i & 1) == 0) { c++; i = i >>> 1; } @Override public boolean isFiller() { return true; return c; } @Override public long getTimeDeltaMs() { return Integer.toUnsignedLong(mTimeDelta); private static int createEntry(boolean isFiller, int timeDelta) { Preconditions.checkArgument(timeDelta >= 0 && timeDelta <= MAX_TIME_DELTA); return (((isFiller ? 1 : 0) << IS_FILLER_OFFSET) & IS_FILLER_MASK) | ((timeDelta << TIME_DELTA_OFFSET) & TIME_DELTA_MASK); } @Override public String getLogString() { throw new AssertionError(); static int getTimeDelta(int entry) { return (entry & TIME_DELTA_MASK) >>> TIME_DELTA_OFFSET; } @Override public boolean filter(String filter) { return false; } static boolean isFiller(int entry) { return (entry & IS_FILLER_MASK) != 0; } /** * An abstraction of a log event to be implemented by subclasses. */ public abstract static class LogEvent implements Log { // circular buffer of log entries and events. each entry corrosponds to the log event at the // same index. the log entry holds the filler status and time delta according to the bit masks // above, and the log event is the log event. static final long MAX_TIME_DELTA = (1L << 32) - 1; @GuardedBy("this") final int[] mEntries; private final int mTimeDelta; protected LogEvent(long timeDelta) { Preconditions.checkArgument(timeDelta >= 0); mTimeDelta = (int) timeDelta; } @GuardedBy("this") final @Nullable T[] mLogEvents; @Override public final boolean isFiller() { return false; } @GuardedBy("this") int mLogSize; @Override public final long getTimeDeltaMs() { return Integer.toUnsignedLong(mTimeDelta); } @GuardedBy("this") int mLogEndIndex; @Override public boolean filter(String filter) { return false; } } // invalid if log is empty // circular buffer of log entries private final Log[] mLog; private int mLogSize; private int mLogEndIndex; @GuardedBy("this") long mStartTime; // invalid if log is empty private long mStartRealtimeMs; private long mLastLogRealtimeMs; @GuardedBy("this") long mLastLogTime; public LocalEventLog(int size) { @SuppressWarnings("unchecked") public LocalEventLog(int size, Class<T> clazz) { Preconditions.checkArgument(size > 0); mLog = new Log[size]; mEntries = new int[size]; mLogEvents = (T[]) Array.newInstance(clazz, size); mLogSize = 0; mLogEndIndex = 0; mStartRealtimeMs = -1; mLastLogRealtimeMs = -1; } /** * Should be overridden by subclasses to return a new immutable log event for the given * arguments (as passed into {@link #addLogEvent(int, Object...)}. */ protected abstract LogEvent createLogEvent(long timeDelta, int event, Object... args); /** * May be optionally overridden by subclasses if they wish to change how log event time is * formatted. */ protected String getTimePrefix(long timeMs) { return TimeUtils.logTimeOfDay(timeMs) + ": "; mStartTime = -1; mLastLogTime = -1; } /** * Call to add a new log event at the current time. The arguments provided here will be passed * into {@link #createLogEvent(long, int, Object...)} in addition to a time delta, and should be * used to construct an appropriate {@link LogEvent} object. */ public synchronized void addLogEvent(int event, Object... args) { long timeMs = SystemClock.elapsedRealtime(); /** Call to add a new log event at the given time. */ protected synchronized void addLog(long time, T logEvent) { Preconditions.checkArgument(logEvent != null); // calculate delta long delta = 0; if (!isEmpty()) { delta = timeMs - mLastLogRealtimeMs; delta = time - mLastLogTime; // if the delta is invalid, or if the delta is great enough using filler elements would // if the delta is negative, or if the delta is great enough using filler elements would // result in an empty log anyways, just clear the log and continue, otherwise insert // filler elements until we have a reasonable delta if (delta < 0 || (delta / FillerEvent.MAX_TIME_DELTA) >= mLog.length - 1) { if (delta < 0 || (delta / MAX_TIME_DELTA) >= mEntries.length - 1) { clear(); delta = 0; } else { while (delta >= LogEvent.MAX_TIME_DELTA) { long timeDelta = Math.min(FillerEvent.MAX_TIME_DELTA, delta); addLogEventInternal(new FillerEvent(timeDelta)); delta -= timeDelta; while (delta >= MAX_TIME_DELTA) { addLogEventInternal(true, MAX_TIME_DELTA, null); delta -= MAX_TIME_DELTA; } } } // for first log entry, set initial times if (isEmpty()) { mStartRealtimeMs = timeMs; mLastLogRealtimeMs = mStartRealtimeMs; mStartTime = time; mLastLogTime = mStartTime; } addLogEventInternal(createLogEvent(delta, event, args)); addLogEventInternal(false, (int) delta, logEvent); } private void addLogEventInternal(Log event) { Preconditions.checkState(mStartRealtimeMs != -1 && mLastLogRealtimeMs != -1); @GuardedBy("this") private void addLogEventInternal(boolean isFiller, int timeDelta, @Nullable T logEvent) { Preconditions.checkArgument(isFiller || logEvent != null); Preconditions.checkState(mStartTime != -1 && mLastLogTime != -1); if (mLogSize == mLog.length) { if (mLogSize == mEntries.length) { // if log is full, size will remain the same, but update the start time mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs(); mStartTime += getTimeDelta(mEntries[startIndex()]); } else { // otherwise add an item mLogSize++; } // set log and increment end index mLog[mLogEndIndex] = event; mEntries[mLogEndIndex] = createEntry(isFiller, timeDelta); mLogEvents[mLogEndIndex] = logEvent; mLogEndIndex = incrementIndex(mLogEndIndex); mLastLogRealtimeMs = mLastLogRealtimeMs + event.getTimeDeltaMs(); mLastLogTime = mLastLogTime + timeDelta; } /** Clears the log of all entries. */ public synchronized void clear() { // clear entries to allow gc Arrays.fill(mLogEvents, null); mLogEndIndex = 0; mLogSize = 0; mStartRealtimeMs = -1; mLastLogRealtimeMs = -1; mStartTime = -1; mLastLogTime = -1; } // checks if the log is empty (if empty, times are invalid) private synchronized boolean isEmpty() { @GuardedBy("this") private boolean isEmpty() { return mLogSize == 0; } /** Iterates over the event log, passing each log string to the given consumer. */ public synchronized void iterate(Consumer<String> consumer) { public synchronized void iterate(LogConsumer<? super T> consumer) { LogIterator it = new LogIterator(); while (it.hasNext()) { consumer.accept(it.next()); } } /** * Iterates over the event log, passing each filter-matching log string to the given * consumer. */ public synchronized void iterate(String filter, Consumer<String> consumer) { LogIterator it = new LogIterator(filter); while (it.hasNext()) { consumer.accept(it.next()); it.next(); consumer.acceptLog(it.getTime(), it.getLog()); } } // returns the index of the first element @GuardedBy("this") private int startIndex() { return wrapIndex(mLogEndIndex - mLogSize); } // returns the index after this one @GuardedBy("this") private int incrementIndex(int index) { if (index == -1) { return startIndex(); Loading @@ -238,69 +214,68 @@ public abstract class LocalEventLog { } // rolls over the given index if necessary @GuardedBy("this") private int wrapIndex(int index) { // java modulo will keep negative sign, we need to rollover return (index % mLog.length + mLog.length) % mLog.length; return (index % mEntries.length + mEntries.length) % mEntries.length; } private class LogIterator implements Iterator<String> { private final @Nullable String mFilter; private final long mSystemTimeDeltaMs; private class LogIterator { private long mCurrentRealtimeMs; private long mLogTime; private int mIndex; private int mCount; LogIterator() { this(null); } private long mCurrentTime; private T mCurrentLogEvent; LogIterator(@Nullable String filter) { mFilter = filter; mSystemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime(); mCurrentRealtimeMs = mStartRealtimeMs; LogIterator() { synchronized (LocalEventLog.this) { mLogTime = mStartTime; mIndex = -1; mCount = -1; increment(); } } @Override public boolean hasNext() { synchronized (LocalEventLog.this) { return mCount < mLogSize; } } public String next() { public void next() { synchronized (LocalEventLog.this) { if (!hasNext()) { throw new NoSuchElementException(); } Log log = mLog[mIndex]; long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs; mCurrentTime = mLogTime + getTimeDelta(mEntries[mIndex]); mCurrentLogEvent = Objects.requireNonNull(mLogEvents[mIndex]); increment(); } } return getTimePrefix(timeMs) + log.getLogString(); public long getTime() { return mCurrentTime; } @Override public void remove() { throw new UnsupportedOperationException(); public T getLog() { return mCurrentLogEvent; } private void increment() { long nextDeltaMs = mIndex == -1 ? 0 : mLog[mIndex].getTimeDeltaMs(); @GuardedBy("LocalEventLog.this") private void increment(LogIterator this) { long nextDeltaMs = mIndex == -1 ? 0 : getTimeDelta(mEntries[mIndex]); do { mCurrentRealtimeMs += nextDeltaMs; mLogTime += nextDeltaMs; mIndex = incrementIndex(mIndex); if (++mCount < mLogSize) { nextDeltaMs = mLog[mIndex].getTimeDeltaMs(); nextDeltaMs = getTimeDelta(mEntries[mIndex]); } } while (mCount < mLogSize && (mLog[mIndex].isFiller() || (mFilter != null && !mLog[mIndex].filter(mFilter)))); } while (mCount < mLogSize && isFiller(mEntries[mIndex])); } } }
services/core/java/com/android/server/location/eventlog/LocationEventLog.java +90 −143 File changed.Preview size limit exceeded, changes collapsed. Show changes