Refactor LocalEventLog for safety and split locations log
Locations being received and delivered are much spammier operations than most other location logs. Split these into a separate log so they do not overwrite other events, and output the combined log. Test: manual Change-Id: I86561f46d6b84307d4849b3dd455a5e56f8bc8f3
This commit is contained in:
@@ -21,26 +21,23 @@ import static java.lang.Integer.bitCount;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 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 class LocalEventLog<T> {
|
||||
|
||||
/**
|
||||
* Consumer of log events for iterating over the log.
|
||||
*
|
||||
* @param <T> log event type
|
||||
*/
|
||||
/** Consumer of log events for iterating over the log. */
|
||||
public interface LogConsumer<T> {
|
||||
/** Invoked with a time and a logEvent. */
|
||||
void acceptLog(long time, T logEvent);
|
||||
@@ -48,12 +45,13 @@ public class LocalEventLog<T> {
|
||||
|
||||
// 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 IS_FILLER_MASK = 0b10000000000000000000000000000000;
|
||||
private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111;
|
||||
|
||||
private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK);
|
||||
private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK);
|
||||
|
||||
@VisibleForTesting
|
||||
static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1;
|
||||
|
||||
private static int countTrailingZeros(int i) {
|
||||
@@ -79,7 +77,7 @@ public class LocalEventLog<T> {
|
||||
return (entry & IS_FILLER_MASK) != 0;
|
||||
}
|
||||
|
||||
// circular buffer of log entries and events. each entry corrosponds to the log event at the
|
||||
// circular buffer of log entries and events. each entry corresponds 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.
|
||||
|
||||
@@ -103,6 +101,9 @@ public class LocalEventLog<T> {
|
||||
@GuardedBy("this")
|
||||
long mLastLogTime;
|
||||
|
||||
@GuardedBy("this")
|
||||
long mModificationCount;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LocalEventLog(int size, Class<T> clazz) {
|
||||
Preconditions.checkArgument(size > 0);
|
||||
@@ -143,6 +144,7 @@ public class LocalEventLog<T> {
|
||||
if (isEmpty()) {
|
||||
mStartTime = time;
|
||||
mLastLogTime = mStartTime;
|
||||
mModificationCount++;
|
||||
}
|
||||
|
||||
addLogEventInternal(false, (int) delta, logEvent);
|
||||
@@ -156,6 +158,7 @@ public class LocalEventLog<T> {
|
||||
if (mLogSize == mEntries.length) {
|
||||
// if log is full, size will remain the same, but update the start time
|
||||
mStartTime += getTimeDelta(mEntries[startIndex()]);
|
||||
mModificationCount++;
|
||||
} else {
|
||||
// otherwise add an item
|
||||
mLogSize++;
|
||||
@@ -170,11 +173,12 @@ public class LocalEventLog<T> {
|
||||
|
||||
/** Clears the log of all entries. */
|
||||
public synchronized void clear() {
|
||||
// clear entries to allow gc
|
||||
// clear entries to aid gc
|
||||
Arrays.fill(mLogEvents, null);
|
||||
|
||||
mLogEndIndex = 0;
|
||||
mLogSize = 0;
|
||||
mModificationCount++;
|
||||
|
||||
mStartTime = -1;
|
||||
mLastLogTime = -1;
|
||||
@@ -186,7 +190,10 @@ public class LocalEventLog<T> {
|
||||
return mLogSize == 0;
|
||||
}
|
||||
|
||||
/** Iterates over the event log, passing each log string to the given consumer. */
|
||||
/**
|
||||
* Iterates over the event log, passing each log event to the given consumer. Locks the log
|
||||
* while executing so that {@link ConcurrentModificationException}s cannot occur.
|
||||
*/
|
||||
public synchronized void iterate(LogConsumer<? super T> consumer) {
|
||||
LogIterator it = new LogIterator();
|
||||
while (it.hasNext()) {
|
||||
@@ -195,15 +202,53 @@ public class LocalEventLog<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all the given event logs in time order, passing each log event to the given
|
||||
* consumer. It is the caller's responsibility to ensure that {@link
|
||||
* ConcurrentModificationException}s cannot occur, whether through locking or other means.
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> void iterate(LogConsumer<? super T> consumer, LocalEventLog<T>... logs) {
|
||||
ArrayList<LocalEventLog<T>.LogIterator> its = new ArrayList<>(logs.length);
|
||||
for (LocalEventLog<T> log : logs) {
|
||||
LocalEventLog<T>.LogIterator it = log.new LogIterator();
|
||||
if (it.hasNext()) {
|
||||
its.add(it);
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
LocalEventLog<T>.LogIterator next = null;
|
||||
for (LocalEventLog<T>.LogIterator it : its) {
|
||||
if (it != null && (next == null || it.getTime() < next.getTime())) {
|
||||
next = it;
|
||||
}
|
||||
}
|
||||
|
||||
if (next == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumer.acceptLog(next.getTime(), next.getLog());
|
||||
|
||||
if (next.hasNext()) {
|
||||
next.next();
|
||||
} else {
|
||||
its.remove(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns the index of the first element
|
||||
@GuardedBy("this")
|
||||
private int startIndex() {
|
||||
int startIndex() {
|
||||
return wrapIndex(mLogEndIndex - mLogSize);
|
||||
}
|
||||
|
||||
// returns the index after this one
|
||||
@GuardedBy("this")
|
||||
private int incrementIndex(int index) {
|
||||
int incrementIndex(int index) {
|
||||
if (index == -1) {
|
||||
return startIndex();
|
||||
} else if (index >= 0) {
|
||||
@@ -215,12 +260,15 @@ public class LocalEventLog<T> {
|
||||
|
||||
// rolls over the given index if necessary
|
||||
@GuardedBy("this")
|
||||
private int wrapIndex(int index) {
|
||||
int wrapIndex(int index) {
|
||||
// java modulo will keep negative sign, we need to rollover
|
||||
return (index % mEntries.length + mEntries.length) % mEntries.length;
|
||||
}
|
||||
|
||||
private class LogIterator {
|
||||
/** Iterator over log times and events. */
|
||||
protected final class LogIterator {
|
||||
|
||||
private final long mModificationCount;
|
||||
|
||||
private long mLogTime;
|
||||
private int mIndex;
|
||||
@@ -229,8 +277,10 @@ public class LocalEventLog<T> {
|
||||
private long mCurrentTime;
|
||||
private T mCurrentLogEvent;
|
||||
|
||||
LogIterator() {
|
||||
public LogIterator() {
|
||||
synchronized (LocalEventLog.this) {
|
||||
mModificationCount = LocalEventLog.this.mModificationCount;
|
||||
|
||||
mLogTime = mStartTime;
|
||||
mIndex = -1;
|
||||
mCount = -1;
|
||||
@@ -241,6 +291,7 @@ public class LocalEventLog<T> {
|
||||
|
||||
public boolean hasNext() {
|
||||
synchronized (LocalEventLog.this) {
|
||||
checkModifications();
|
||||
return mCount < mLogSize;
|
||||
}
|
||||
}
|
||||
@@ -277,5 +328,12 @@ public class LocalEventLog<T> {
|
||||
}
|
||||
} while (mCount < mLogSize && isFiller(mEntries[mIndex]));
|
||||
}
|
||||
|
||||
@GuardedBy("LocalEventLog.this")
|
||||
private void checkModifications() {
|
||||
if (mModificationCount != LocalEventLog.this.mModificationCount) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,16 +52,28 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
if (D) {
|
||||
return 600;
|
||||
} else {
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getLocationsLogSize() {
|
||||
if (D) {
|
||||
return 200;
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mAggregateStats")
|
||||
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
|
||||
|
||||
public LocationEventLog() {
|
||||
@GuardedBy("this")
|
||||
private final LocationsEventLog mLocationsLog;
|
||||
|
||||
private LocationEventLog() {
|
||||
super(getLogSize(), Object.class);
|
||||
mAggregateStats = new ArrayMap<>(4);
|
||||
mLocationsLog = new LocationsEventLog(getLocationsLogSize());
|
||||
}
|
||||
|
||||
/** Copies out all aggregated stats. */
|
||||
@@ -95,39 +107,39 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
|
||||
/** Logs a user switched event. */
|
||||
public void logUserSwitched(int userIdFrom, int userIdTo) {
|
||||
addLogEvent(new UserSwitchedEvent(userIdFrom, userIdTo));
|
||||
addLog(new UserSwitchedEvent(userIdFrom, userIdTo));
|
||||
}
|
||||
|
||||
/** Logs a location enabled/disabled event. */
|
||||
public void logLocationEnabled(int userId, boolean enabled) {
|
||||
addLogEvent(new LocationEnabledEvent(userId, enabled));
|
||||
addLog(new LocationEnabledEvent(userId, enabled));
|
||||
}
|
||||
|
||||
/** Logs a location enabled/disabled event. */
|
||||
public void logAdasLocationEnabled(int userId, boolean enabled) {
|
||||
addLogEvent(new LocationAdasEnabledEvent(userId, enabled));
|
||||
addLog(new LocationAdasEnabledEvent(userId, enabled));
|
||||
}
|
||||
|
||||
/** Logs a location provider enabled/disabled event. */
|
||||
public void logProviderEnabled(String provider, int userId, boolean enabled) {
|
||||
addLogEvent(new ProviderEnabledEvent(provider, userId, enabled));
|
||||
addLog(new ProviderEnabledEvent(provider, userId, enabled));
|
||||
}
|
||||
|
||||
/** Logs a location provider being replaced/unreplaced by a mock provider. */
|
||||
public void logProviderMocked(String provider, boolean mocked) {
|
||||
addLogEvent(new ProviderMockedEvent(provider, mocked));
|
||||
addLog(new ProviderMockedEvent(provider, mocked));
|
||||
}
|
||||
|
||||
/** Logs a new client registration for a location provider. */
|
||||
public void logProviderClientRegistered(String provider, CallerIdentity identity,
|
||||
LocationRequest request) {
|
||||
addLogEvent(new ProviderClientRegisterEvent(provider, true, identity, request));
|
||||
addLog(new ProviderClientRegisterEvent(provider, true, identity, request));
|
||||
getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
|
||||
}
|
||||
|
||||
/** Logs a client unregistration for a location provider. */
|
||||
public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
|
||||
addLogEvent(new ProviderClientRegisterEvent(provider, false, identity, null));
|
||||
addLog(new ProviderClientRegisterEvent(provider, false, identity, null));
|
||||
getAggregateStats(provider, identity).markRequestRemoved();
|
||||
}
|
||||
|
||||
@@ -144,7 +156,7 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
/** Logs a client for a location provider entering the foreground state. */
|
||||
public void logProviderClientForeground(String provider, CallerIdentity identity) {
|
||||
if (D) {
|
||||
addLogEvent(new ProviderClientForegroundEvent(provider, true, identity));
|
||||
addLog(new ProviderClientForegroundEvent(provider, true, identity));
|
||||
}
|
||||
getAggregateStats(provider, identity).markRequestForeground();
|
||||
}
|
||||
@@ -152,7 +164,7 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
/** Logs a client for a location provider leaving the foreground state. */
|
||||
public void logProviderClientBackground(String provider, CallerIdentity identity) {
|
||||
if (D) {
|
||||
addLogEvent(new ProviderClientForegroundEvent(provider, false, identity));
|
||||
addLog(new ProviderClientForegroundEvent(provider, false, identity));
|
||||
}
|
||||
getAggregateStats(provider, identity).markRequestBackground();
|
||||
}
|
||||
@@ -160,32 +172,34 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
/** Logs a client for a location provider entering the permitted state. */
|
||||
public void logProviderClientPermitted(String provider, CallerIdentity identity) {
|
||||
if (D) {
|
||||
addLogEvent(new ProviderClientPermittedEvent(provider, true, identity));
|
||||
addLog(new ProviderClientPermittedEvent(provider, true, identity));
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs a client for a location provider leaving the permitted state. */
|
||||
public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
|
||||
if (D) {
|
||||
addLogEvent(new ProviderClientPermittedEvent(provider, false, identity));
|
||||
addLog(new ProviderClientPermittedEvent(provider, false, identity));
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs a change to the provider request for a location provider. */
|
||||
public void logProviderUpdateRequest(String provider, ProviderRequest request) {
|
||||
addLogEvent(new ProviderUpdateEvent(provider, request));
|
||||
addLog(new ProviderUpdateEvent(provider, request));
|
||||
}
|
||||
|
||||
/** Logs a new incoming location for a location provider. */
|
||||
public void logProviderReceivedLocations(String provider, int numLocations) {
|
||||
addLogEvent(new ProviderReceiveLocationEvent(provider, numLocations));
|
||||
synchronized (this) {
|
||||
mLocationsLog.logProviderReceivedLocations(provider, numLocations);
|
||||
}
|
||||
}
|
||||
|
||||
/** Logs a location deliver for a client of a location provider. */
|
||||
public void logProviderDeliveredLocations(String provider, int numLocations,
|
||||
CallerIdentity identity) {
|
||||
if (D) {
|
||||
addLogEvent(new ProviderDeliverLocationEvent(provider, numLocations, identity));
|
||||
synchronized (this) {
|
||||
mLocationsLog.logProviderDeliveredLocations(provider, numLocations, identity);
|
||||
}
|
||||
getAggregateStats(provider, identity).markLocationDelivered();
|
||||
}
|
||||
@@ -193,19 +207,24 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
/** Logs that a provider has entered or exited stationary throttling. */
|
||||
public void logProviderStationaryThrottled(String provider, boolean throttled,
|
||||
ProviderRequest request) {
|
||||
addLogEvent(new ProviderStationaryThrottledEvent(provider, throttled, request));
|
||||
addLog(new ProviderStationaryThrottledEvent(provider, throttled, request));
|
||||
}
|
||||
|
||||
/** Logs that the location power save mode has changed. */
|
||||
public void logLocationPowerSaveMode(
|
||||
@LocationPowerSaveMode int locationPowerSaveMode) {
|
||||
addLogEvent(new LocationPowerSaveModeEvent(locationPowerSaveMode));
|
||||
addLog(new LocationPowerSaveModeEvent(locationPowerSaveMode));
|
||||
}
|
||||
|
||||
private void addLogEvent(Object logEvent) {
|
||||
private void addLog(Object logEvent) {
|
||||
addLog(SystemClock.elapsedRealtime(), logEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void iterate(LogConsumer<? super Object> consumer) {
|
||||
iterate(consumer, this, mLocationsLog);
|
||||
}
|
||||
|
||||
public void iterate(Consumer<String> consumer) {
|
||||
iterate(consumer, null);
|
||||
}
|
||||
@@ -488,6 +507,26 @@ public class LocationEventLog extends LocalEventLog<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LocationsEventLog extends LocalEventLog<Object> {
|
||||
|
||||
LocationsEventLog(int size) {
|
||||
super(size, Object.class);
|
||||
}
|
||||
|
||||
public void logProviderReceivedLocations(String provider, int numLocations) {
|
||||
addLog(new ProviderReceiveLocationEvent(provider, numLocations));
|
||||
}
|
||||
|
||||
public void logProviderDeliveredLocations(String provider, int numLocations,
|
||||
CallerIdentity identity) {
|
||||
addLog(new ProviderDeliverLocationEvent(provider, numLocations, identity));
|
||||
}
|
||||
|
||||
private void addLog(Object logEvent) {
|
||||
this.addLog(SystemClock.elapsedRealtime(), logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate statistics for a single package under a single provider.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user