Merge "Add Ambient Brightness tracker API"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d3986bdc3
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.hardware.display;
|
||||
|
||||
parcelable AmbientBrightnessDayStats;
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2018 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 android.hardware.display;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
|
||||
* {@see DisplayManager.getAmbientBrightnessStats()}
|
||||
* TODO: Make this system API
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AmbientBrightnessDayStats implements Parcelable {
|
||||
|
||||
/** The localdate for which brightness stats are being tracked */
|
||||
private final LocalDate mLocalDate;
|
||||
|
||||
/** Ambient brightness values for creating bucket boundaries from */
|
||||
private final float[] mBucketBoundaries;
|
||||
|
||||
/** Stats of how much time (in seconds) was spent in each of the buckets */
|
||||
private final float[] mStats;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
|
||||
@NonNull float[] bucketBoundaries) {
|
||||
Preconditions.checkNotNull(localDate);
|
||||
Preconditions.checkNotNull(bucketBoundaries);
|
||||
int numBuckets = bucketBoundaries.length;
|
||||
if (numBuckets < 1) {
|
||||
throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
|
||||
}
|
||||
mLocalDate = localDate;
|
||||
mBucketBoundaries = bucketBoundaries;
|
||||
mStats = new float[numBuckets];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
|
||||
@NonNull float[] bucketBoundaries, @NonNull float[] stats) {
|
||||
Preconditions.checkNotNull(localDate);
|
||||
Preconditions.checkNotNull(bucketBoundaries);
|
||||
Preconditions.checkNotNull(stats);
|
||||
if (bucketBoundaries.length < 1) {
|
||||
throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
|
||||
}
|
||||
if (bucketBoundaries.length != stats.length) {
|
||||
throw new IllegalArgumentException("Bucket boundaries and stats must be of same size.");
|
||||
}
|
||||
mLocalDate = localDate;
|
||||
mBucketBoundaries = bucketBoundaries;
|
||||
mStats = stats;
|
||||
}
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return mLocalDate;
|
||||
}
|
||||
|
||||
public float[] getStats() {
|
||||
return mStats;
|
||||
}
|
||||
|
||||
public float[] getBucketBoundaries() {
|
||||
return mBucketBoundaries;
|
||||
}
|
||||
|
||||
private AmbientBrightnessDayStats(Parcel source) {
|
||||
mLocalDate = LocalDate.parse(source.readString());
|
||||
mBucketBoundaries = source.createFloatArray();
|
||||
mStats = source.createFloatArray();
|
||||
}
|
||||
|
||||
public static final Creator<AmbientBrightnessDayStats> CREATOR =
|
||||
new Creator<AmbientBrightnessDayStats>() {
|
||||
|
||||
@Override
|
||||
public AmbientBrightnessDayStats createFromParcel(Parcel source) {
|
||||
return new AmbientBrightnessDayStats(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmbientBrightnessDayStats[] newArray(int size) {
|
||||
return new AmbientBrightnessDayStats[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
|
||||
return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
|
||||
other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = result * prime + mLocalDate.hashCode();
|
||||
result = result * prime + Arrays.hashCode(mBucketBoundaries);
|
||||
result = result * prime + Arrays.hashCode(mStats);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder bucketBoundariesString = new StringBuilder();
|
||||
StringBuilder statsString = new StringBuilder();
|
||||
for (int i = 0; i < mBucketBoundaries.length; i++) {
|
||||
if (i != 0) {
|
||||
bucketBoundariesString.append(", ");
|
||||
statsString.append(", ");
|
||||
}
|
||||
bucketBoundariesString.append(mBucketBoundaries[i]);
|
||||
statsString.append(mStats[i]);
|
||||
}
|
||||
return new StringBuilder()
|
||||
.append(mLocalDate).append(" ")
|
||||
.append("{").append(bucketBoundariesString).append("} ")
|
||||
.append("{").append(statsString).append("}").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mLocalDate.toString());
|
||||
dest.writeFloatArray(mBucketBoundaries);
|
||||
dest.writeFloatArray(mStats);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void log(float ambientBrightness, float durationSec) {
|
||||
int bucketIndex = getBucketIndex(ambientBrightness);
|
||||
if (bucketIndex >= 0) {
|
||||
mStats[bucketIndex] += durationSec;
|
||||
}
|
||||
}
|
||||
|
||||
private int getBucketIndex(float ambientBrightness) {
|
||||
if (ambientBrightness < mBucketBoundaries[0]) {
|
||||
return -1;
|
||||
}
|
||||
int low = 0;
|
||||
int high = mBucketBoundaries.length - 1;
|
||||
while (low < high) {
|
||||
int mid = (low + high) / 2;
|
||||
if (mBucketBoundaries[mid] <= ambientBrightness
|
||||
&& ambientBrightness < mBucketBoundaries[mid + 1]) {
|
||||
return mid;
|
||||
} else if (mBucketBoundaries[mid] < ambientBrightness) {
|
||||
low = mid + 1;
|
||||
} else if (mBucketBoundaries[mid] > ambientBrightness) {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
return low;
|
||||
}
|
||||
}
|
||||
@@ -630,6 +630,16 @@ public final class DisplayManager {
|
||||
return mGlobal.getBrightnessEvents(mContext.getOpPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch {@link AmbientBrightnessDayStats}s.
|
||||
*
|
||||
* @hide until we make it a system api
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
|
||||
public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
|
||||
return mGlobal.getAmbientBrightnessStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the global display brightness configuration.
|
||||
*
|
||||
|
||||
@@ -525,6 +525,21 @@ public final class DisplayManagerGlobal {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves ambient brightness stats.
|
||||
*/
|
||||
public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
|
||||
try {
|
||||
ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
|
||||
if (stats == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return stats.getList();
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
|
||||
@Override
|
||||
public void onDisplayEvent(int displayId, int event) {
|
||||
|
||||
@@ -174,9 +174,9 @@ public abstract class DisplayManagerInternal {
|
||||
public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
|
||||
|
||||
/**
|
||||
* Persist brightness slider events.
|
||||
* Persist brightness slider events and ambient brightness stats.
|
||||
*/
|
||||
public abstract void persistBrightnessSliderEvents();
|
||||
public abstract void persistBrightnessTrackerState();
|
||||
|
||||
/**
|
||||
* Notifies the display manager that resource overlays have changed.
|
||||
|
||||
@@ -87,6 +87,9 @@ interface IDisplayManager {
|
||||
// Requires BRIGHTNESS_SLIDER_USAGE permission.
|
||||
ParceledListSlice getBrightnessEvents(String callingPackage);
|
||||
|
||||
// Requires ACCESS_AMBIENT_LIGHT_STATS permission.
|
||||
ParceledListSlice getAmbientBrightnessStats();
|
||||
|
||||
// Sets the global brightness configuration for a given user. Requires
|
||||
// CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not
|
||||
// the same as the calling user.
|
||||
|
||||
@@ -3029,6 +3029,13 @@
|
||||
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
|
||||
android:protectionLevel="signature|privileged|development" />
|
||||
|
||||
<!-- Allows an application to collect ambient light stats.
|
||||
<p>Not for use by third party applications.</p>
|
||||
TODO: Make a system API
|
||||
@hide -->
|
||||
<permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS"
|
||||
android:protectionLevel="signature|privileged|development" />
|
||||
|
||||
<!-- Allows an application to modify the display brightness configuration
|
||||
@hide
|
||||
@SystemApi -->
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.hardware.display;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AmbientBrightnessDayStatsTest {
|
||||
|
||||
@Test
|
||||
public void testAmbientBrightnessDayStatsAdd() {
|
||||
AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(LocalDate.now(),
|
||||
new float[]{0, 1, 10, 100});
|
||||
dayStats.log(0, 1);
|
||||
dayStats.log(0.5f, 1.5f);
|
||||
dayStats.log(50, 12.5f);
|
||||
dayStats.log(2000, 1.24f);
|
||||
dayStats.log(-10, 0.5f);
|
||||
assertEquals(2.5f, dayStats.getStats()[0], 0);
|
||||
assertEquals(0, dayStats.getStats()[1], 0);
|
||||
assertEquals(12.5f, dayStats.getStats()[2], 0);
|
||||
assertEquals(1.24f, dayStats.getStats()[3], 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbientBrightnessDayStatsEquals() {
|
||||
LocalDate today = LocalDate.now();
|
||||
AmbientBrightnessDayStats dayStats1 = new AmbientBrightnessDayStats(today,
|
||||
new float[]{0, 1, 10, 100});
|
||||
AmbientBrightnessDayStats dayStats2 = new AmbientBrightnessDayStats(today,
|
||||
new float[]{0, 1, 10, 100}, new float[4]);
|
||||
AmbientBrightnessDayStats dayStats3 = new AmbientBrightnessDayStats(today,
|
||||
new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 7});
|
||||
AmbientBrightnessDayStats dayStats4 = new AmbientBrightnessDayStats(today,
|
||||
new float[]{0, 1, 10, 100}, new float[]{1, 3, 5, 0});
|
||||
assertEquals(dayStats1, dayStats2);
|
||||
assertEquals(dayStats1.hashCode(), dayStats2.hashCode());
|
||||
assertNotEquals(dayStats1, dayStats3);
|
||||
assertNotEquals(dayStats1.hashCode(), dayStats3.hashCode());
|
||||
dayStats4.log(100, 7);
|
||||
assertEquals(dayStats3, dayStats4);
|
||||
assertEquals(dayStats3.hashCode(), dayStats4.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAmbientBrightnessDayStatsIncorrectInit() {
|
||||
try {
|
||||
new AmbientBrightnessDayStats(LocalDate.now(), new float[]{1, 10, 100},
|
||||
new float[]{1, 5, 6, 7});
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected
|
||||
}
|
||||
try {
|
||||
new AmbientBrightnessDayStats(LocalDate.now(), new float[]{});
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParcelUnparcelAmbientBrightnessDayStats() {
|
||||
LocalDate today = LocalDate.now();
|
||||
AmbientBrightnessDayStats stats = new AmbientBrightnessDayStats(today,
|
||||
new float[]{0, 1, 10, 100}, new float[]{1.3f, 2.6f, 5.8f, 10});
|
||||
// Parcel the data
|
||||
Parcel parcel = Parcel.obtain();
|
||||
stats.writeToParcel(parcel, 0);
|
||||
byte[] parceled = parcel.marshall();
|
||||
parcel.recycle();
|
||||
// Unparcel and check that it has not changed
|
||||
parcel = Parcel.obtain();
|
||||
parcel.unmarshall(parceled, 0, parceled.length);
|
||||
parcel.setDataPosition(0);
|
||||
AmbientBrightnessDayStats statsAgain = AmbientBrightnessDayStats.CREATOR.createFromParcel(
|
||||
parcel);
|
||||
assertEquals(stats, statsAgain);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright 2018 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.server.display;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.hardware.display.AmbientBrightnessDayStats;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class that stores stats of ambient brightness regions as histogram.
|
||||
*/
|
||||
public class AmbientBrightnessStatsTracker {
|
||||
|
||||
private static final String TAG = "AmbientBrightnessStatsTracker";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@VisibleForTesting
|
||||
static final float[] BUCKET_BOUNDARIES_FOR_NEW_STATS =
|
||||
{0, 0.1f, 0.3f, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000};
|
||||
@VisibleForTesting
|
||||
static final int MAX_DAYS_TO_TRACK = 7;
|
||||
|
||||
private final AmbientBrightnessStats mAmbientBrightnessStats;
|
||||
private final Timer mTimer;
|
||||
private final Injector mInjector;
|
||||
private final UserManager mUserManager;
|
||||
private float mCurrentAmbientBrightness;
|
||||
private @UserIdInt int mCurrentUserId;
|
||||
|
||||
public AmbientBrightnessStatsTracker(UserManager userManager, @Nullable Injector injector) {
|
||||
mUserManager = userManager;
|
||||
if (injector != null) {
|
||||
mInjector = injector;
|
||||
} else {
|
||||
mInjector = new Injector();
|
||||
}
|
||||
mAmbientBrightnessStats = new AmbientBrightnessStats();
|
||||
mTimer = new Timer(() -> mInjector.elapsedRealtimeMillis());
|
||||
mCurrentAmbientBrightness = -1;
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
mTimer.reset();
|
||||
mTimer.start();
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
if (mTimer.isRunning()) {
|
||||
mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
|
||||
mCurrentAmbientBrightness, mTimer.totalDurationSec());
|
||||
}
|
||||
mTimer.reset();
|
||||
mCurrentAmbientBrightness = -1;
|
||||
}
|
||||
|
||||
public synchronized void add(@UserIdInt int userId, float newAmbientBrightness) {
|
||||
if (mTimer.isRunning()) {
|
||||
if (userId == mCurrentUserId) {
|
||||
mAmbientBrightnessStats.log(mCurrentUserId, mInjector.getLocalDate(),
|
||||
mCurrentAmbientBrightness, mTimer.totalDurationSec());
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "User switched since last sensor event.");
|
||||
}
|
||||
mCurrentUserId = userId;
|
||||
}
|
||||
mTimer.reset();
|
||||
mTimer.start();
|
||||
mCurrentAmbientBrightness = newAmbientBrightness;
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.e(TAG, "Timer not running while trying to add brightness stats.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void writeStats(OutputStream stream) throws IOException {
|
||||
mAmbientBrightnessStats.writeToXML(stream);
|
||||
}
|
||||
|
||||
public synchronized void readStats(InputStream stream) throws IOException {
|
||||
mAmbientBrightnessStats.readFromXML(stream);
|
||||
}
|
||||
|
||||
public synchronized ArrayList<AmbientBrightnessDayStats> getUserStats(int userId) {
|
||||
return mAmbientBrightnessStats.getUserStats(userId);
|
||||
}
|
||||
|
||||
public synchronized void dump(PrintWriter pw) {
|
||||
pw.println("AmbientBrightnessStats:");
|
||||
pw.print(mAmbientBrightnessStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* AmbientBrightnessStats tracks ambient brightness stats across users over multiple days.
|
||||
* This class is not ThreadSafe.
|
||||
*/
|
||||
class AmbientBrightnessStats {
|
||||
|
||||
private static final String TAG_AMBIENT_BRIGHTNESS_STATS = "ambient-brightness-stats";
|
||||
private static final String TAG_AMBIENT_BRIGHTNESS_DAY_STATS =
|
||||
"ambient-brightness-day-stats";
|
||||
private static final String ATTR_USER = "user";
|
||||
private static final String ATTR_LOCAL_DATE = "local-date";
|
||||
private static final String ATTR_BUCKET_BOUNDARIES = "bucket-boundaries";
|
||||
private static final String ATTR_BUCKET_STATS = "bucket-stats";
|
||||
|
||||
private Map<Integer, Deque<AmbientBrightnessDayStats>> mStats;
|
||||
|
||||
public AmbientBrightnessStats() {
|
||||
mStats = new HashMap<>();
|
||||
}
|
||||
|
||||
public void log(@UserIdInt int userId, LocalDate localDate, float ambientBrightness,
|
||||
float durationSec) {
|
||||
Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(mStats, userId);
|
||||
AmbientBrightnessDayStats dayStats = getOrCreateDayStats(userStats, localDate);
|
||||
dayStats.log(ambientBrightness, durationSec);
|
||||
}
|
||||
|
||||
public ArrayList<AmbientBrightnessDayStats> getUserStats(@UserIdInt int userId) {
|
||||
if (mStats.containsKey(userId)) {
|
||||
return new ArrayList<>(mStats.get(userId));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToXML(OutputStream stream) throws IOException {
|
||||
XmlSerializer out = new FastXmlSerializer();
|
||||
out.setOutput(stream, StandardCharsets.UTF_8.name());
|
||||
out.startDocument(null, true);
|
||||
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||
|
||||
final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
|
||||
out.startTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
|
||||
for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
|
||||
for (AmbientBrightnessDayStats userDayStats : entry.getValue()) {
|
||||
int userSerialNumber = mInjector.getUserSerialNumber(mUserManager,
|
||||
entry.getKey());
|
||||
if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
|
||||
out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
|
||||
out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
|
||||
out.attribute(null, ATTR_LOCAL_DATE,
|
||||
userDayStats.getLocalDate().toString());
|
||||
StringBuilder bucketBoundariesValues = new StringBuilder();
|
||||
StringBuilder timeSpentValues = new StringBuilder();
|
||||
for (int i = 0; i < userDayStats.getBucketBoundaries().length; i++) {
|
||||
if (i > 0) {
|
||||
bucketBoundariesValues.append(",");
|
||||
timeSpentValues.append(",");
|
||||
}
|
||||
bucketBoundariesValues.append(userDayStats.getBucketBoundaries()[i]);
|
||||
timeSpentValues.append(userDayStats.getStats()[i]);
|
||||
}
|
||||
out.attribute(null, ATTR_BUCKET_BOUNDARIES,
|
||||
bucketBoundariesValues.toString());
|
||||
out.attribute(null, ATTR_BUCKET_STATS, timeSpentValues.toString());
|
||||
out.endTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.endTag(null, TAG_AMBIENT_BRIGHTNESS_STATS);
|
||||
out.endDocument();
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
public void readFromXML(InputStream stream) throws IOException {
|
||||
try {
|
||||
Map<Integer, Deque<AmbientBrightnessDayStats>> parsedStats = new HashMap<>();
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(stream, StandardCharsets.UTF_8.name());
|
||||
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
}
|
||||
String tag = parser.getName();
|
||||
if (!TAG_AMBIENT_BRIGHTNESS_STATS.equals(tag)) {
|
||||
throw new XmlPullParserException(
|
||||
"Ambient brightness stats not found in tracker file " + tag);
|
||||
}
|
||||
|
||||
final LocalDate cutOffDate = mInjector.getLocalDate().minusDays(MAX_DAYS_TO_TRACK);
|
||||
parser.next();
|
||||
int outerDepth = parser.getDepth();
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
tag = parser.getName();
|
||||
if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
|
||||
String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
|
||||
LocalDate localDate = LocalDate.parse(
|
||||
parser.getAttributeValue(null, ATTR_LOCAL_DATE));
|
||||
String[] bucketBoundaries = parser.getAttributeValue(null,
|
||||
ATTR_BUCKET_BOUNDARIES).split(",");
|
||||
String[] bucketStats = parser.getAttributeValue(null,
|
||||
ATTR_BUCKET_STATS).split(",");
|
||||
if (bucketBoundaries.length != bucketStats.length
|
||||
|| bucketBoundaries.length < 1) {
|
||||
throw new IOException("Invalid brightness stats string.");
|
||||
}
|
||||
float[] parsedBucketBoundaries = new float[bucketBoundaries.length];
|
||||
float[] parsedBucketStats = new float[bucketStats.length];
|
||||
for (int i = 0; i < bucketBoundaries.length; i++) {
|
||||
parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
|
||||
parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
|
||||
}
|
||||
int userId = mInjector.getUserId(mUserManager,
|
||||
Integer.parseInt(userSerialNumber));
|
||||
if (userId != -1 && localDate.isAfter(cutOffDate)) {
|
||||
Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
|
||||
parsedStats, userId);
|
||||
userStats.offer(
|
||||
new AmbientBrightnessDayStats(localDate,
|
||||
parsedBucketBoundaries, parsedBucketStats));
|
||||
}
|
||||
}
|
||||
}
|
||||
mStats = parsedStats;
|
||||
} catch (NullPointerException | NumberFormatException | XmlPullParserException |
|
||||
DateTimeParseException | IOException e) {
|
||||
throw new IOException("Failed to parse brightness stats file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Map.Entry<Integer, Deque<AmbientBrightnessDayStats>> entry : mStats.entrySet()) {
|
||||
for (AmbientBrightnessDayStats dayStats : entry.getValue()) {
|
||||
builder.append(" ");
|
||||
builder.append(entry.getKey()).append(" ");
|
||||
builder.append(dayStats).append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private Deque<AmbientBrightnessDayStats> getOrCreateUserStats(
|
||||
Map<Integer, Deque<AmbientBrightnessDayStats>> stats, @UserIdInt int userId) {
|
||||
if (!stats.containsKey(userId)) {
|
||||
stats.put(userId, new ArrayDeque<>());
|
||||
}
|
||||
return stats.get(userId);
|
||||
}
|
||||
|
||||
private AmbientBrightnessDayStats getOrCreateDayStats(
|
||||
Deque<AmbientBrightnessDayStats> userStats, LocalDate localDate) {
|
||||
AmbientBrightnessDayStats lastBrightnessStats = userStats.peekLast();
|
||||
if (lastBrightnessStats != null && lastBrightnessStats.getLocalDate().equals(
|
||||
localDate)) {
|
||||
return lastBrightnessStats;
|
||||
} else {
|
||||
AmbientBrightnessDayStats dayStats = new AmbientBrightnessDayStats(localDate,
|
||||
BUCKET_BOUNDARIES_FOR_NEW_STATS);
|
||||
if (userStats.size() == MAX_DAYS_TO_TRACK) {
|
||||
userStats.poll();
|
||||
}
|
||||
userStats.offer(dayStats);
|
||||
return dayStats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
interface Clock {
|
||||
long elapsedTimeMillis();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class Timer {
|
||||
|
||||
private final Clock clock;
|
||||
private long startTimeMillis;
|
||||
private boolean started;
|
||||
|
||||
public Timer(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
started = false;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (!started) {
|
||||
startTimeMillis = clock.elapsedTimeMillis();
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return started;
|
||||
}
|
||||
|
||||
public float totalDurationSec() {
|
||||
if (started) {
|
||||
return (float) ((clock.elapsedTimeMillis() - startTimeMillis) / 1000.0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class Injector {
|
||||
public long elapsedRealtimeMillis() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
public int getUserSerialNumber(UserManager userManager, int userId) {
|
||||
return userManager.getUserSerialNumber(userId);
|
||||
}
|
||||
|
||||
public int getUserId(UserManager userManager, int userSerialNumber) {
|
||||
return userManager.getUserHandle(userSerialNumber);
|
||||
}
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return LocalDate.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class BrightnessIdleJob extends JobService {
|
||||
Slog.d(BrightnessTracker.TAG, "Scheduled write of brightness events");
|
||||
}
|
||||
DisplayManagerInternal dmi = LocalServices.getService(DisplayManagerInternal.class);
|
||||
dmi.persistBrightnessSliderEvents();
|
||||
dmi.persistBrightnessTrackerState();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.display;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
@@ -28,8 +29,8 @@ import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.AmbientBrightnessDayStats;
|
||||
import android.hardware.display.BrightnessChangeEvent;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
@@ -81,6 +82,7 @@ public class BrightnessTracker {
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
private static final String EVENTS_FILE = "brightness_events.xml";
|
||||
private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
|
||||
private static final int MAX_EVENTS = 100;
|
||||
// Discard events when reading or writing that are older than this.
|
||||
private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
|
||||
@@ -116,6 +118,10 @@ public class BrightnessTracker {
|
||||
private final Runnable mEventsWriter = () -> writeEvents();
|
||||
private volatile boolean mWriteEventsScheduled;
|
||||
|
||||
private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
|
||||
private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats();
|
||||
private volatile boolean mWriteBrightnessStatsScheduled;
|
||||
|
||||
private UserManager mUserManager;
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
@@ -123,6 +129,7 @@ public class BrightnessTracker {
|
||||
// mBroadcastReceiver and mSensorListener should only be used on the mBgHandler thread.
|
||||
private BroadcastReceiver mBroadcastReceiver;
|
||||
private SensorListener mSensorListener;
|
||||
private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
|
||||
|
||||
// Lock held while collecting data related to brightness changes.
|
||||
private final Object mDataCollectionLock = new Object();
|
||||
@@ -160,12 +167,19 @@ public class BrightnessTracker {
|
||||
}
|
||||
mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
|
||||
mUserManager = mContext.getSystemService(UserManager.class);
|
||||
|
||||
try {
|
||||
final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
|
||||
mCurrentUserId = focusedStack.userId;
|
||||
} catch (RemoteException e) {
|
||||
// Really shouldn't be possible.
|
||||
return;
|
||||
}
|
||||
mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
|
||||
}
|
||||
|
||||
private void backgroundStart(float initialBrightness) {
|
||||
readEvents();
|
||||
readAmbientBrightnessStats();
|
||||
|
||||
mSensorListener = new SensorListener();
|
||||
|
||||
@@ -199,12 +213,20 @@ public class BrightnessTracker {
|
||||
mInjector.unregisterSensorListener(mContext, mSensorListener);
|
||||
mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
|
||||
mInjector.cancelIdleJob(mContext);
|
||||
mAmbientBrightnessStatsTracker.stop();
|
||||
|
||||
synchronized (mDataCollectionLock) {
|
||||
mStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onSwitchUser(@UserIdInt int newUserId) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
|
||||
}
|
||||
mCurrentUserId = newUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userId userId to fetch data for.
|
||||
* @param includePackage if false we will null out BrightnessChangeEvent.packageName
|
||||
@@ -231,8 +253,8 @@ public class BrightnessTracker {
|
||||
return new ParceledListSlice<>(out);
|
||||
}
|
||||
|
||||
public void persistEvents() {
|
||||
scheduleWriteEvents();
|
||||
public void persistBrightnessTrackerState() {
|
||||
scheduleWriteBrightnessTrackerState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,11 +354,15 @@ public class BrightnessTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleWriteEvents() {
|
||||
private void scheduleWriteBrightnessTrackerState() {
|
||||
if (!mWriteEventsScheduled) {
|
||||
mBgHandler.post(mEventsWriter);
|
||||
mWriteEventsScheduled = true;
|
||||
}
|
||||
if (!mWriteBrightnessStatsScheduled) {
|
||||
mBgHandler.post(mAmbientBrightnessStatsWriter);
|
||||
mWriteBrightnessStatsScheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEvents() {
|
||||
@@ -347,7 +373,7 @@ public class BrightnessTracker {
|
||||
return;
|
||||
}
|
||||
|
||||
final AtomicFile writeTo = mInjector.getFile();
|
||||
final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
|
||||
if (writeTo == null) {
|
||||
return;
|
||||
}
|
||||
@@ -371,12 +397,29 @@ public class BrightnessTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAmbientBrightnessStats() {
|
||||
mWriteBrightnessStatsScheduled = false;
|
||||
final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
|
||||
if (writeTo == null) {
|
||||
return;
|
||||
}
|
||||
FileOutputStream output = null;
|
||||
try {
|
||||
output = writeTo.startWrite();
|
||||
mAmbientBrightnessStatsTracker.writeStats(output);
|
||||
writeTo.finishWrite(output);
|
||||
} catch (IOException e) {
|
||||
writeTo.failWrite(output);
|
||||
Slog.e(TAG, "Failed to write ambient brightness stats.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void readEvents() {
|
||||
synchronized (mEventsLock) {
|
||||
// Read might prune events so mark as dirty.
|
||||
mEventsDirty = true;
|
||||
mEvents.clear();
|
||||
final AtomicFile readFrom = mInjector.getFile();
|
||||
final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
|
||||
if (readFrom != null && readFrom.exists()) {
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
@@ -392,6 +435,23 @@ public class BrightnessTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private void readAmbientBrightnessStats() {
|
||||
mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
|
||||
final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
|
||||
if (readFrom != null && readFrom.exists()) {
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
input = readFrom.openRead();
|
||||
mAmbientBrightnessStatsTracker.readStats(input);
|
||||
} catch (IOException e) {
|
||||
readFrom.delete();
|
||||
Slog.e(TAG, "Failed to read ambient brightness stats.", e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mEventsLock")
|
||||
void writeEventsLocked(OutputStream stream) throws IOException {
|
||||
@@ -581,6 +641,13 @@ public class BrightnessTracker {
|
||||
pw.println("}");
|
||||
}
|
||||
}
|
||||
if (mAmbientBrightnessStatsTracker != null) {
|
||||
mAmbientBrightnessStatsTracker.dump(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
|
||||
return new ParceledListSlice<>(mAmbientBrightnessStatsTracker.getUserStats(userId));
|
||||
}
|
||||
|
||||
// Not allowed to keep the SensorEvent so used to copy the data we care about.
|
||||
@@ -620,6 +687,10 @@ public class BrightnessTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private void recordAmbientBrightnessStats(SensorEvent event) {
|
||||
mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
|
||||
}
|
||||
|
||||
private void batteryLevelChanged(int level, int scale) {
|
||||
synchronized (mDataCollectionLock) {
|
||||
mLastBatteryLevel = (float) level / (float) scale;
|
||||
@@ -630,6 +701,7 @@ public class BrightnessTracker {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
recordSensorEvent(event);
|
||||
recordAmbientBrightnessStats(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -647,7 +719,7 @@ public class BrightnessTracker {
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_SHUTDOWN.equals(action)) {
|
||||
stop();
|
||||
scheduleWriteEvents();
|
||||
scheduleWriteBrightnessTrackerState();
|
||||
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
|
||||
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
|
||||
@@ -655,8 +727,10 @@ public class BrightnessTracker {
|
||||
batteryLevelChanged(level, scale);
|
||||
}
|
||||
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
|
||||
mAmbientBrightnessStatsTracker.stop();
|
||||
mInjector.unregisterSensorListener(mContext, mSensorListener);
|
||||
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
|
||||
mAmbientBrightnessStatsTracker.start();
|
||||
mInjector.registerSensorListener(mContext, mSensorListener,
|
||||
mInjector.getBackgroundHandler());
|
||||
}
|
||||
@@ -732,8 +806,8 @@ public class BrightnessTracker {
|
||||
return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
|
||||
}
|
||||
|
||||
public AtomicFile getFile() {
|
||||
return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
|
||||
public AtomicFile getFile(String filename) {
|
||||
return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
|
||||
}
|
||||
|
||||
public long currentTimeMillis() {
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.content.pm.ParceledListSlice;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.AmbientBrightnessDayStats;
|
||||
import android.hardware.display.BrightnessChangeEvent;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.hardware.display.DisplayManagerGlobal;
|
||||
@@ -359,6 +360,7 @@ public final class DisplayManagerService extends SystemService {
|
||||
mPersistentDataStore.getBrightnessConfiguration(userSerial);
|
||||
mDisplayPowerController.setBrightnessConfiguration(config);
|
||||
}
|
||||
mDisplayPowerController.onSwitchUser(newUserId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1834,6 +1836,23 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS,
|
||||
"Permission required to to access ambient light stats.");
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int userId = UserHandle.getUserId(callingUid);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mSyncRoot) {
|
||||
return mDisplayPowerController.getAmbientBrightnessStats(userId);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void setBrightnessConfigurationForUser(
|
||||
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
|
||||
@@ -2039,9 +2058,9 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persistBrightnessSliderEvents() {
|
||||
public void persistBrightnessTrackerState() {
|
||||
synchronized (mSyncRoot) {
|
||||
mDisplayPowerController.persistBrightnessSliderEvents();
|
||||
mDisplayPowerController.persistBrightnessTrackerState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.AmbientBrightnessDayStats;
|
||||
import android.hardware.display.BrightnessChangeEvent;
|
||||
import android.hardware.display.BrightnessConfiguration;
|
||||
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
|
||||
@@ -498,11 +499,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
|
||||
return mBrightnessTracker.getEvents(userId, includePackage);
|
||||
}
|
||||
|
||||
public void onSwitchUser(@UserIdInt int newUserId) {
|
||||
mBrightnessTracker.onSwitchUser(newUserId);
|
||||
}
|
||||
|
||||
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
|
||||
@UserIdInt int userId) {
|
||||
return mBrightnessTracker.getAmbientBrightnessStats(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the brightness slider events to disk.
|
||||
* Persist the brightness slider events and ambient brightness stats to disk.
|
||||
*/
|
||||
public void persistBrightnessSliderEvents() {
|
||||
mBrightnessTracker.persistEvents();
|
||||
public void persistBrightnessTrackerState() {
|
||||
mBrightnessTracker.persistBrightnessTrackerState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright 2018 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.server.display;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.hardware.display.AmbientBrightnessDayStats;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AmbientBrightnessStatsTrackerTest {
|
||||
|
||||
private TestInjector mTestInjector;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mTestInjector = new TestInjector();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessStatsTrackerOverSingleDay() {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
float[] expectedStats;
|
||||
// Test case where no user data
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertNull(userStats);
|
||||
// Test after adding some user data
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.stop();
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(1, userStats.size());
|
||||
assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 1;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
// Test after adding some more user data
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0.05f);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 0.2f);
|
||||
mTestInjector.incrementTime(1500);
|
||||
statsTracker.add(0, 50000);
|
||||
mTestInjector.incrementTime(2500);
|
||||
statsTracker.stop();
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(1, userStats.size());
|
||||
assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 2;
|
||||
expectedStats[1] = 1.5f;
|
||||
expectedStats[11] = 2.5f;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessStatsTrackerOverMultipleDays() {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
float[] expectedStats;
|
||||
// Add data for day 1
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0.05f);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 0.2f);
|
||||
mTestInjector.incrementTime(1500);
|
||||
statsTracker.add(0, 1);
|
||||
mTestInjector.incrementTime(2500);
|
||||
statsTracker.stop();
|
||||
// Add data for day 2
|
||||
mTestInjector.incrementDate(1);
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0);
|
||||
mTestInjector.incrementTime(3500);
|
||||
statsTracker.add(0, 5);
|
||||
mTestInjector.incrementTime(5000);
|
||||
statsTracker.stop();
|
||||
// Test that the data is tracked as expected
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(2, userStats.size());
|
||||
assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 1;
|
||||
expectedStats[1] = 1.5f;
|
||||
expectedStats[3] = 2.5f;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 3.5f;
|
||||
expectedStats[4] = 5;
|
||||
assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessStatsTrackerOverMultipleUsers() {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
float[] expectedStats;
|
||||
// Add data for user 1
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0.05f);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 0.2f);
|
||||
mTestInjector.incrementTime(1500);
|
||||
statsTracker.add(0, 1);
|
||||
mTestInjector.incrementTime(2500);
|
||||
statsTracker.stop();
|
||||
// Add data for user 2
|
||||
mTestInjector.incrementDate(1);
|
||||
statsTracker.start();
|
||||
statsTracker.add(1, 0);
|
||||
mTestInjector.incrementTime(3500);
|
||||
statsTracker.add(1, 5);
|
||||
mTestInjector.incrementTime(5000);
|
||||
statsTracker.stop();
|
||||
// Test that the data is tracked as expected
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(1, userStats.size());
|
||||
assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 1;
|
||||
expectedStats[1] = 1.5f;
|
||||
expectedStats[3] = 2.5f;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
userStats = statsTracker.getUserStats(1);
|
||||
assertEquals(1, userStats.size());
|
||||
assertEquals(mTestInjector.getLocalDate(), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 3.5f;
|
||||
expectedStats[4] = 5;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrightnessStatsTrackerOverMaxDays() {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
// Add 10 extra days of data over the buffer limit
|
||||
for (int i = 0; i < AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK + 10; i++) {
|
||||
mTestInjector.incrementDate(1);
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 10);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 20);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.stop();
|
||||
}
|
||||
// Assert that we are only tracking last "MAX_DAYS_TO_TRACK"
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK, userStats.size());
|
||||
LocalDate runningDate = mTestInjector.getLocalDate();
|
||||
for (int i = AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1; i >= 0; i--) {
|
||||
assertEquals(runningDate, userStats.get(i).getLocalDate());
|
||||
runningDate = runningDate.minusDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAmbientBrightnessStats() throws IOException {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
LocalDate date = mTestInjector.getLocalDate();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
String statsFile =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
|
||||
+ "<ambient-brightness-stats>\r\n"
|
||||
// Old stats that shouldn't be read
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\""
|
||||
+ date.minusDays(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK)
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
|
||||
+ "0.0,0.0,0.0\" />\r\n"
|
||||
// Valid stats that should get read
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\""
|
||||
+ date.minusDays(1)
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
|
||||
+ "0.0,0.0,0.0\" />\r\n"
|
||||
// Valid stats that should get read
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
|
||||
+ "0.0\" />\r\n"
|
||||
+ "</ambient-brightness-stats>";
|
||||
statsTracker.readStats(getInputStream(statsFile));
|
||||
userStats = statsTracker.getUserStats(0);
|
||||
assertEquals(2, userStats.size());
|
||||
assertEquals(new AmbientBrightnessDayStats(date.minusDays(1),
|
||||
new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
|
||||
new float[]{1.088f, 0, 0.726f, 0, 25.868f, 0, 0, 0, 0, 0}), userStats.get(0));
|
||||
assertEquals(new AmbientBrightnessDayStats(date,
|
||||
new float[]{0, 1, 3, 10, 30, 100, 300, 1000, 3000, 10000},
|
||||
new float[]{0, 0, 0, 0, 4.482f, 0, 0, 0, 0, 0}), userStats.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedReadAmbientBrightnessStatsWithException() {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
LocalDate date = mTestInjector.getLocalDate();
|
||||
String statsFile;
|
||||
// Test with parse error
|
||||
statsFile =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
|
||||
+ "<ambient-brightness-stats>\r\n"
|
||||
// Incorrect since bucket boundaries not parsable
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
|
||||
+ "\" bucket-boundaries=\"asdf,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
|
||||
+ "0.0,0.0,0.0\" />\r\n"
|
||||
+ "</ambient-brightness-stats>";
|
||||
try {
|
||||
statsTracker.readStats(getInputStream(statsFile));
|
||||
} catch (IOException e) {
|
||||
// Expected
|
||||
}
|
||||
assertNull(statsTracker.getUserStats(0));
|
||||
// Test with incorrect data (bucket boundaries length not equal to stats length)
|
||||
statsFile =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
|
||||
+ "<ambient-brightness-stats>\r\n"
|
||||
// Correct data
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\""
|
||||
+ date.minusDays(1)
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"0.0,0.0,0.0,0.0,4.482,0.0,0.0,0.0,0.0,"
|
||||
+ "0.0\" />\r\n"
|
||||
// Incorrect data
|
||||
+ "<ambient-brightness-day-stats user=\"10\" local-date=\"" + date
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,1000.0,"
|
||||
+ "3000.0,10000.0\" bucket-stats=\"1.088,0.0,0.726,0.0,25.868,0.0,0.0,"
|
||||
+ "0.0,0.0,0.0\" />\r\n"
|
||||
+ "</ambient-brightness-stats>";
|
||||
try {
|
||||
statsTracker.readStats(getInputStream(statsFile));
|
||||
} catch (Exception e) {
|
||||
// Expected
|
||||
}
|
||||
assertNull(statsTracker.getUserStats(0));
|
||||
// Test with missing attribute
|
||||
statsFile =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\r\n"
|
||||
+ "<ambient-brightness-stats>\r\n"
|
||||
+ "<ambientBrightnessDayStats user=\"10\" local-date=\"" + date
|
||||
+ "\" bucket-boundaries=\"0.0,1.0,3.0,10.0,30.0,100.0,300.0,1000.0,"
|
||||
+ "3000.0,10000.0\" />\r\n"
|
||||
+ "</ambient-brightness-stats>";
|
||||
try {
|
||||
statsTracker.readStats(getInputStream(statsFile));
|
||||
} catch (Exception e) {
|
||||
// Expected
|
||||
}
|
||||
assertNull(statsTracker.getUserStats(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteThenReadAmbientBrightnessStats() throws IOException {
|
||||
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
|
||||
ArrayList<AmbientBrightnessDayStats> userStats;
|
||||
float[] expectedStats;
|
||||
// Generate some dummy data
|
||||
// Data: very old which should not be read
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0.05f);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 0.2f);
|
||||
mTestInjector.incrementTime(1500);
|
||||
statsTracker.add(0, 1);
|
||||
mTestInjector.incrementTime(2500);
|
||||
statsTracker.stop();
|
||||
// Data: day 1 user 1
|
||||
mTestInjector.incrementDate(AmbientBrightnessStatsTracker.MAX_DAYS_TO_TRACK - 1);
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0.05f);
|
||||
mTestInjector.incrementTime(1000);
|
||||
statsTracker.add(0, 0.2f);
|
||||
mTestInjector.incrementTime(1500);
|
||||
statsTracker.add(0, 1);
|
||||
mTestInjector.incrementTime(2500);
|
||||
statsTracker.stop();
|
||||
// Data: day 1 user 2
|
||||
statsTracker.start();
|
||||
statsTracker.add(1, 0);
|
||||
mTestInjector.incrementTime(3500);
|
||||
statsTracker.add(1, 5);
|
||||
mTestInjector.incrementTime(5000);
|
||||
statsTracker.stop();
|
||||
// Data: day 2 user 1
|
||||
mTestInjector.incrementDate(1);
|
||||
statsTracker.start();
|
||||
statsTracker.add(0, 0);
|
||||
mTestInjector.incrementTime(3500);
|
||||
statsTracker.add(0, 50000);
|
||||
mTestInjector.incrementTime(5000);
|
||||
statsTracker.stop();
|
||||
// Write them
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
statsTracker.writeStats(baos);
|
||||
baos.flush();
|
||||
// Read them back and assert that it's the same
|
||||
ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
|
||||
AmbientBrightnessStatsTracker newStatsTracker = getTestStatsTracker();
|
||||
newStatsTracker.readStats(input);
|
||||
userStats = newStatsTracker.getUserStats(0);
|
||||
assertEquals(2, userStats.size());
|
||||
// Check day 1 user 1
|
||||
assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 1;
|
||||
expectedStats[1] = 1.5f;
|
||||
expectedStats[3] = 2.5f;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
// Check day 2 user 1
|
||||
assertEquals(mTestInjector.getLocalDate(), userStats.get(1).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 3.5f;
|
||||
expectedStats[11] = 5;
|
||||
assertArrayEquals(expectedStats, userStats.get(1).getStats(), 0);
|
||||
userStats = newStatsTracker.getUserStats(1);
|
||||
assertEquals(1, userStats.size());
|
||||
// Check day 1 user 2
|
||||
assertEquals(mTestInjector.getLocalDate().minusDays(1), userStats.get(0).getLocalDate());
|
||||
expectedStats = getEmptyStatsArray();
|
||||
expectedStats[0] = 3.5f;
|
||||
expectedStats[4] = 5;
|
||||
assertArrayEquals(expectedStats, userStats.get(0).getStats(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimer() {
|
||||
AmbientBrightnessStatsTracker.Timer timer = new AmbientBrightnessStatsTracker.Timer(
|
||||
() -> mTestInjector.elapsedRealtimeMillis());
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
mTestInjector.incrementTime(1000);
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
assertFalse(timer.isRunning());
|
||||
// Start timer
|
||||
timer.start();
|
||||
assertTrue(timer.isRunning());
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
mTestInjector.incrementTime(1000);
|
||||
assertTrue(timer.isRunning());
|
||||
assertEquals(1, timer.totalDurationSec(), 0);
|
||||
// Reset timer
|
||||
timer.reset();
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
assertFalse(timer.isRunning());
|
||||
// Start again
|
||||
timer.start();
|
||||
assertTrue(timer.isRunning());
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
mTestInjector.incrementTime(2000);
|
||||
assertTrue(timer.isRunning());
|
||||
assertEquals(2, timer.totalDurationSec(), 0);
|
||||
// Reset again
|
||||
timer.reset();
|
||||
assertEquals(0, timer.totalDurationSec(), 0);
|
||||
assertFalse(timer.isRunning());
|
||||
}
|
||||
|
||||
private class TestInjector extends AmbientBrightnessStatsTracker.Injector {
|
||||
|
||||
private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
|
||||
private LocalDate mLocalDate = LocalDate.now();
|
||||
|
||||
public void incrementTime(long timeMillis) {
|
||||
mElapsedRealtimeMillis += timeMillis;
|
||||
}
|
||||
|
||||
public void incrementDate(int numDays) {
|
||||
mLocalDate = mLocalDate.plusDays(numDays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long elapsedRealtimeMillis() {
|
||||
return mElapsedRealtimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserSerialNumber(UserManager userManager, int userId) {
|
||||
return userId + 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserId(UserManager userManager, int userSerialNumber) {
|
||||
return userSerialNumber - 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate getLocalDate() {
|
||||
return LocalDate.from(mLocalDate);
|
||||
}
|
||||
}
|
||||
|
||||
private AmbientBrightnessStatsTracker getTestStatsTracker() {
|
||||
return new AmbientBrightnessStatsTracker(
|
||||
InstrumentationRegistry.getContext().getSystemService(UserManager.class),
|
||||
mTestInjector);
|
||||
}
|
||||
|
||||
private float[] getEmptyStatsArray() {
|
||||
return new float[AmbientBrightnessStatsTracker.BUCKET_BOUNDARIES_FOR_NEW_STATS.length];
|
||||
}
|
||||
|
||||
private InputStream getInputStream(String data) {
|
||||
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
@@ -656,7 +656,7 @@ public class BrightnessTrackerTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicFile getFile() {
|
||||
public AtomicFile getFile(String filename) {
|
||||
// Don't have the test write / read from anywhere.
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user