Merge "Refactor LocalEventLog for safety and split locations log"

This commit is contained in:
Soonil Nagarkar
2022-01-04 17:51:03 +00:00
committed by Android (Google) Code Review
2 changed files with 132 additions and 35 deletions

View File

@@ -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();
}
}
}
}

View File

@@ -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.
*/