Make ReferenceWithHistory output easier to use

Make ReferenceWithHistory easier to use by outputting a timestamp for
each set() operation, and providing a stable "set id" for historic
entries. This means consecutive dump() calls will assign the same ID to
the same historic entry, enabling easier comparison.

This doesn't change the contract of the storage, just the dump() output.

Test: treehugger
Bug: 149014708
Change-Id: I28919a25b09b70910873c40562b51fa6664103b0
This commit is contained in:
Neil Fuller
2021-02-02 17:13:19 +00:00
parent 92ee65806f
commit d7c3a8903c

View File

@@ -19,8 +19,11 @@ package com.android.server.timezonedetector;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
import java.time.Duration;
import java.util.ArrayDeque;
/**
@@ -49,18 +52,15 @@ import java.util.ArrayDeque;
*/
public final class ReferenceWithHistory<V> {
private static final Object NULL_MARKER = "{null marker}";
/** The maximum number of references to store. */
private final int mMaxHistorySize;
/**
* The history storage. Note that ArrayDeque doesn't support {@code null} so this stores Object
* and not V. Use {@link #packNullIfRequired(Object)} and {@link #unpackNullIfRequired(Object)}
* to convert to / from the storage object.
*/
/** The number of times {@link #set(Object)} has been called. */
private int mSetCount;
/** The history storage. */
@Nullable
private ArrayDeque<Object> mValues;
private ArrayDeque<TimestampedValue<V>> mValues;
/**
* Creates an instance that records, at most, the specified number of values.
@@ -78,8 +78,8 @@ public final class ReferenceWithHistory<V> {
if (mValues == null || mValues.isEmpty()) {
return null;
}
Object value = mValues.getFirst();
return unpackNullIfRequired(value);
TimestampedValue<V> valueHolder = mValues.getFirst();
return valueHolder.getValue();
}
/**
@@ -98,8 +98,10 @@ public final class ReferenceWithHistory<V> {
V previous = get();
Object nullSafeValue = packNullIfRequired(newValue);
mValues.addFirst(nullSafeValue);
TimestampedValue<V> valueHolder =
new TimestampedValue<>(SystemClock.elapsedRealtime(), newValue);
mValues.addFirst(valueHolder);
mSetCount++;
return previous;
}
@@ -110,10 +112,13 @@ public final class ReferenceWithHistory<V> {
if (mValues == null) {
ipw.println("{Empty}");
} else {
int i = 0;
for (Object value : mValues) {
ipw.println(i + ": " + unpackNullIfRequired(value));
i++;
int i = mSetCount;
for (TimestampedValue<V> valueHolder : mValues) {
ipw.print(--i);
ipw.print("@");
ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
ipw.print(": ");
ipw.println(valueHolder.getValue());
}
}
ipw.flush();
@@ -130,23 +135,4 @@ public final class ReferenceWithHistory<V> {
public String toString() {
return String.valueOf(get());
}
/**
* Turns a non-nullable Object into a nullable value. See also
* {@link #packNullIfRequired(Object)}.
*/
@SuppressWarnings("unchecked")
@Nullable
private V unpackNullIfRequired(@NonNull Object value) {
return value == NULL_MARKER ? null : (V) value;
}
/**
* Turns a nullable value into a non-nullable Object. See also
* {@link #unpackNullIfRequired(Object)}.
*/
@NonNull
private Object packNullIfRequired(@Nullable V value) {
return value == null ? NULL_MARKER : value;
}
}