blob: 89cef621a55d59de36c2ab08b4990248b43a97e7 [file] [log] [blame]
/*
* Copyright (C) 2012 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 com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.WifiDisplay;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Pair;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import libcore.io.IoUtils;
/**
* Manages persistent state recorded by the display manager service as an XML file.
* Caller must acquire lock on the data store before accessing it.
*
* File format:
* <code>
* &lt;display-manager-state>
* &lt;remembered-wifi-displays>
* &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
* &lt;remembered-wifi-displays>
* &lt;display-states>
* &lt;display unique-id="XXXXXXX">
* &lt;color-mode>0&lt;/color-mode>
* &lt;/display>
* &lt;/display-states>
* &lt;stable-device-values>
* &lt;stable-display-height>1920&lt;/stable-display-height>
* &lt;stable-display-width>1080&lt;/stable-display-width>
* &lt;/stable-device-values>
* &lt;brightness-configurations>
* &lt;brightness-configuration user-serial="0" package-name="com.example" timestamp="1234">
* &lt;brightness-curve description="some text">
* &lt;brightness-point lux="0" nits="13.25"/>
* &lt;brightness-point lux="20" nits="35.94"/>
* &lt;/brightness-curve>
* &lt;/brightness-configuration>
* &lt;/brightness-configurations>
* &lt;/display-manager-state>
* </code>
*
* TODO: refactor this to extract common code shared with the input manager's data store
*/
final class PersistentDataStore {
static final String TAG = "DisplayManager";
private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays";
private static final String TAG_WIFI_DISPLAY = "wifi-display";
private static final String ATTR_DEVICE_ADDRESS = "deviceAddress";
private static final String ATTR_DEVICE_NAME = "deviceName";
private static final String ATTR_DEVICE_ALIAS = "deviceAlias";
private static final String TAG_DISPLAY_STATES = "display-states";
private static final String TAG_DISPLAY = "display";
private static final String TAG_COLOR_MODE = "color-mode";
private static final String ATTR_UNIQUE_ID = "unique-id";
private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height";
private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width";
private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
private static final String ATTR_USER_SERIAL = "user-serial";
private static final String ATTR_PACKAGE_NAME = "package-name";
private static final String ATTR_TIME_STAMP = "timestamp";
private static final String ATTR_LUX = "lux";
private static final String ATTR_NITS = "nits";
private static final String ATTR_DESCRIPTION = "description";
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
// Display state by unique id.
private final HashMap<String, DisplayState> mDisplayStates =
new HashMap<String, DisplayState>();
// Display values which should be stable across the device's lifetime.
private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
// Brightness configuration by user
private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
// True if the data has been loaded.
private boolean mLoaded;
// True if there are changes to be saved.
private boolean mDirty;
// The interface for methods which should be replaced by the test harness.
private Injector mInjector;
public PersistentDataStore() {
this(new Injector());
}
@VisibleForTesting
PersistentDataStore(Injector injector) {
mInjector = injector;
}
public void saveIfNeeded() {
if (mDirty) {
save();
mDirty = false;
}
}
public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
loadIfNeeded();
int index = findRememberedWifiDisplay(deviceAddress);
if (index >= 0) {
return mRememberedWifiDisplays.get(index);
}
return null;
}
public WifiDisplay[] getRememberedWifiDisplays() {
loadIfNeeded();
return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
}
public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
if (display != null) {
loadIfNeeded();
String alias = null;
int index = findRememberedWifiDisplay(display.getDeviceAddress());
if (index >= 0) {
alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
}
if (!Objects.equals(display.getDeviceAlias(), alias)) {
return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(),
alias, display.isAvailable(), display.canConnect(), display.isRemembered());
}
}
return display;
}
public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
WifiDisplay[] results = displays;
if (results != null) {
int count = displays.length;
for (int i = 0; i < count; i++) {
WifiDisplay result = applyWifiDisplayAlias(displays[i]);
if (result != displays[i]) {
if (results == displays) {
results = new WifiDisplay[count];
System.arraycopy(displays, 0, results, 0, count);
}
results[i] = result;
}
}
}
return results;
}
public boolean rememberWifiDisplay(WifiDisplay display) {
loadIfNeeded();
int index = findRememberedWifiDisplay(display.getDeviceAddress());
if (index >= 0) {
WifiDisplay other = mRememberedWifiDisplays.get(index);
if (other.equals(display)) {
return false; // already remembered without change
}
mRememberedWifiDisplays.set(index, display);
} else {
mRememberedWifiDisplays.add(display);
}
setDirty();
return true;
}
public boolean forgetWifiDisplay(String deviceAddress) {
loadIfNeeded();
int index = findRememberedWifiDisplay(deviceAddress);
if (index >= 0) {
mRememberedWifiDisplays.remove(index);
setDirty();
return true;
}
return false;
}
private int findRememberedWifiDisplay(String deviceAddress) {
int count = mRememberedWifiDisplays.size();
for (int i = 0; i < count; i++) {
if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
return i;
}
}
return -1;
}
public int getColorMode(DisplayDevice device) {
if (!device.hasStableUniqueId()) {
return Display.COLOR_MODE_INVALID;
}
DisplayState state = getDisplayState(device.getUniqueId(), false);
if (state == null) {
return Display.COLOR_MODE_INVALID;
}
return state.getColorMode();
}
public boolean setColorMode(DisplayDevice device, int colorMode) {
if (!device.hasStableUniqueId()) {
return false;
}
DisplayState state = getDisplayState(device.getUniqueId(), true);
if (state.setColorMode(colorMode)) {
setDirty();
return true;
}
return false;
}
public Point getStableDisplaySize() {
loadIfNeeded();
return mStableDeviceValues.getDisplaySize();
}
public void setStableDisplaySize(Point size) {
loadIfNeeded();
if (mStableDeviceValues.setDisplaySize(size)) {
setDirty();
}
}
public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial,
@Nullable String packageName) {
loadIfNeeded();
if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
packageName)) {
setDirty();
}
}
public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
loadIfNeeded();
return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
}
private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
loadIfNeeded();
DisplayState state = mDisplayStates.get(uniqueId);
if (state == null && createIfAbsent) {
state = new DisplayState();
mDisplayStates.put(uniqueId, state);
setDirty();
}
return state;
}
public void loadIfNeeded() {
if (!mLoaded) {
load();
mLoaded = true;
}
}
private void setDirty() {
mDirty = true;
}
private void clearState() {
mRememberedWifiDisplays.clear();
}
private void load() {
clearState();
final InputStream is;
try {
is = mInjector.openRead();
} catch (FileNotFoundException ex) {
return;
}
XmlPullParser parser;
try {
parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
loadFromXml(parser);
} catch (IOException ex) {
Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
clearState();
} catch (XmlPullParserException ex) {
Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
clearState();
} finally {
IoUtils.closeQuietly(is);
}
}
private void save() {
final OutputStream os;
try {
os = mInjector.startWrite();
boolean success = false;
try {
XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
saveToXml(serializer);
serializer.flush();
success = true;
} finally {
mInjector.finishWrite(os, success);
}
} catch (IOException ex) {
Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
}
}
private void loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) {
loadRememberedWifiDisplaysFromXml(parser);
}
if (parser.getName().equals(TAG_DISPLAY_STATES)) {
loadDisplaysFromXml(parser);
}
if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) {
mStableDeviceValues.loadFromXml(parser);
}
if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
mBrightnessConfigurations.loadFromXml(parser);
}
}
}
private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(TAG_WIFI_DISPLAY)) {
String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS);
String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME);
String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS);
if (deviceAddress == null || deviceName == null) {
throw new XmlPullParserException(
"Missing deviceAddress or deviceName attribute on wifi-display.");
}
if (findRememberedWifiDisplay(deviceAddress) >= 0) {
throw new XmlPullParserException(
"Found duplicate wifi display device address.");
}
mRememberedWifiDisplays.add(
new WifiDisplay(deviceAddress, deviceName, deviceAlias,
false, false, false));
}
}
}
private void loadDisplaysFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(TAG_DISPLAY)) {
String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID);
if (uniqueId == null) {
throw new XmlPullParserException(
"Missing unique-id attribute on display.");
}
if (mDisplayStates.containsKey(uniqueId)) {
throw new XmlPullParserException("Found duplicate display.");
}
DisplayState state = new DisplayState();
state.loadFromXml(parser);
mDisplayStates.put(uniqueId, state);
}
}
}
private void saveToXml(XmlSerializer serializer) throws IOException {
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
for (WifiDisplay display : mRememberedWifiDisplays) {
serializer.startTag(null, TAG_WIFI_DISPLAY);
serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress());
serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName());
if (display.getDeviceAlias() != null) {
serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias());
}
serializer.endTag(null, TAG_WIFI_DISPLAY);
}
serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
serializer.startTag(null, TAG_DISPLAY_STATES);
for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
final String uniqueId = entry.getKey();
final DisplayState state = entry.getValue();
serializer.startTag(null, TAG_DISPLAY);
serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId);
state.saveToXml(serializer);
serializer.endTag(null, TAG_DISPLAY);
}
serializer.endTag(null, TAG_DISPLAY_STATES);
serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
mStableDeviceValues.saveToXml(serializer);
serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
mBrightnessConfigurations.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
serializer.endDocument();
}
public void dump(PrintWriter pw) {
pw.println("PersistentDataStore");
pw.println(" mLoaded=" + mLoaded);
pw.println(" mDirty=" + mDirty);
pw.println(" RememberedWifiDisplays:");
int i = 0;
for (WifiDisplay display : mRememberedWifiDisplays) {
pw.println(" " + i++ + ": " + display);
}
pw.println(" DisplayStates:");
i = 0;
for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
pw.println(" " + i++ + ": " + entry.getKey());
entry.getValue().dump(pw, " ");
}
pw.println(" StableDeviceValues:");
mStableDeviceValues.dump(pw, " ");
pw.println(" BrightnessConfigurations:");
mBrightnessConfigurations.dump(pw, " ");
}
private static final class DisplayState {
private int mColorMode;
public boolean setColorMode(int colorMode) {
if (colorMode == mColorMode) {
return false;
}
mColorMode = colorMode;
return true;
}
public int getColorMode() {
return mColorMode;
}
public void loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(TAG_COLOR_MODE)) {
String value = parser.nextText();
mColorMode = Integer.parseInt(value);
}
}
}
public void saveToXml(XmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_COLOR_MODE);
serializer.text(Integer.toString(mColorMode));
serializer.endTag(null, TAG_COLOR_MODE);
}
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "ColorMode=" + mColorMode);
}
}
private static final class StableDeviceValues {
private int mWidth;
private int mHeight;
private Point getDisplaySize() {
return new Point(mWidth, mHeight);
}
public boolean setDisplaySize(Point r) {
if (mWidth != r.x || mHeight != r.y) {
mWidth = r.x;
mHeight = r.y;
return true;
}
return false;
}
public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
switch (parser.getName()) {
case TAG_STABLE_DISPLAY_WIDTH:
mWidth = loadIntValue(parser);
break;
case TAG_STABLE_DISPLAY_HEIGHT:
mHeight = loadIntValue(parser);
break;
}
}
}
private static int loadIntValue(XmlPullParser parser)
throws IOException, XmlPullParserException {
try {
String value = parser.nextText();
return Integer.parseInt(value);
} catch (NumberFormatException nfe) {
return 0;
}
}
public void saveToXml(XmlSerializer serializer) throws IOException {
if (mWidth > 0 && mHeight > 0) {
serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
serializer.text(Integer.toString(mWidth));
serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH);
serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT);
serializer.text(Integer.toString(mHeight));
serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT);
}
}
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "StableDisplayWidth=" + mWidth);
pw.println(prefix + "StableDisplayHeight=" + mHeight);
}
}
private static final class BrightnessConfigurations {
// Maps from a user ID to the users' given brightness configuration
private SparseArray<BrightnessConfiguration> mConfigurations;
// Timestamp of time the configuration was set.
private SparseLongArray mTimeStamps;
// Package that set the configuration.
private SparseArray<String> mPackageNames;
public BrightnessConfigurations() {
mConfigurations = new SparseArray<>();
mTimeStamps = new SparseLongArray();
mPackageNames = new SparseArray<>();
}
private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c,
int userSerial, String packageName) {
BrightnessConfiguration currentConfig = mConfigurations.get(userSerial);
if (currentConfig != c && (currentConfig == null || !currentConfig.equals(c))) {
if (c != null) {
if (packageName == null) {
mPackageNames.remove(userSerial);
} else {
mPackageNames.put(userSerial, packageName);
}
mTimeStamps.put(userSerial, System.currentTimeMillis());
mConfigurations.put(userSerial, c);
} else {
mPackageNames.remove(userSerial);
mTimeStamps.delete(userSerial);
mConfigurations.remove(userSerial);
}
return true;
}
return false;
}
public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
return mConfigurations.get(userSerial);
}
public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
int userSerial;
try {
userSerial = Integer.parseInt(
parser.getAttributeValue(null, ATTR_USER_SERIAL));
} catch (NumberFormatException nfe) {
userSerial = -1;
Slog.e(TAG, "Failed to read in brightness configuration", nfe);
}
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP);
long timeStamp = -1;
if (timeStampString != null) {
try {
timeStamp = Long.parseLong(timeStampString);
} catch (NumberFormatException nfe) {
// Ignore we will just not restore the timestamp.
}
}
try {
BrightnessConfiguration config = loadConfigurationFromXml(parser);
if (userSerial >= 0 && config != null) {
mConfigurations.put(userSerial, config);
if (timeStamp != -1) {
mTimeStamps.put(userSerial, timeStamp);
}
if (packageName != null) {
mPackageNames.put(userSerial, packageName);
}
}
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to load brightness configuration!", iae);
}
}
}
}
private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
String description = null;
Pair<float[], float[]> curve = null;
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
curve = loadCurveFromXml(parser);
}
}
if (curve == null) {
return null;
}
final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
curve.first, curve.second);
builder.setDescription(description);
return builder.build();
}
private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
List<Float> luxLevels = new ArrayList<>();
List<Float> nitLevels = new ArrayList<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
}
}
final int N = luxLevels.size();
float[] lux = new float[N];
float[] nits = new float[N];
for (int i = 0; i < N; i++) {
lux[i] = luxLevels.get(i);
nits[i] = nitLevels.get(i);
}
return Pair.create(lux, nits);
}
private static float loadFloat(String val) {
try {
return Float.parseFloat(val);
} catch (NullPointerException | NumberFormatException e) {
Slog.e(TAG, "Failed to parse float loading brightness config", e);
return Float.NEGATIVE_INFINITY;
}
}
public void saveToXml(XmlSerializer serializer) throws IOException {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
final BrightnessConfiguration config = mConfigurations.valueAt(i);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
String packageName = mPackageNames.get(userSerial);
if (packageName != null) {
serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
}
long timestamp = mTimeStamps.get(userSerial, -1);
if (timestamp != -1) {
serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
}
saveConfigurationToXml(serializer, config);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
}
}
private static void saveConfigurationToXml(XmlSerializer serializer,
BrightnessConfiguration config) throws IOException {
serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
if (config.getDescription() != null) {
serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
}
final Pair<float[], float[]> curve = config.getCurve();
for (int i = 0; i < curve.first.length; i++) {
serializer.startTag(null, TAG_BRIGHTNESS_POINT);
serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
serializer.endTag(null, TAG_BRIGHTNESS_POINT);
}
serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
}
public void dump(final PrintWriter pw, final String prefix) {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
long time = mTimeStamps.get(userSerial, -1);
String packageName = mPackageNames.get(userSerial);
pw.println(prefix + "User " + userSerial + ":");
if (time != -1) {
pw.println(prefix + " set at: " + TimeUtils.formatForLogging(time));
}
if (packageName != null) {
pw.println(prefix + " set by: " + packageName);
}
pw.println(prefix + " " + mConfigurations.valueAt(i));
}
}
}
@VisibleForTesting
static class Injector {
private final AtomicFile mAtomicFile;
public Injector() {
mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"),
"display-state");
}
public InputStream openRead() throws FileNotFoundException {
return mAtomicFile.openRead();
}
public OutputStream startWrite() throws IOException {
return mAtomicFile.startWrite();
}
public void finishWrite(OutputStream os, boolean success) {
if (!(os instanceof FileOutputStream)) {
throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
}
FileOutputStream fos = (FileOutputStream) os;
if (success) {
mAtomicFile.finishWrite(fos);
} else {
mAtomicFile.failWrite(fos);
}
}
}
}