1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import android.annotation.Nullable;
20 import android.graphics.Point;
21 import android.hardware.display.BrightnessConfiguration;
22 import android.hardware.display.WifiDisplay;
23 import android.util.AtomicFile;
24 import android.util.Slog;
25 import android.util.SparseArray;
26 import android.util.SparseLongArray;
27 import android.util.TimeUtils;
28 import android.util.Xml;
29 import android.view.Display;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.internal.util.FastXmlSerializer;
33 import com.android.internal.util.XmlUtils;
34 
35 import libcore.io.IoUtils;
36 
37 import org.xmlpull.v1.XmlPullParser;
38 import org.xmlpull.v1.XmlPullParserException;
39 import org.xmlpull.v1.XmlSerializer;
40 
41 import java.io.BufferedInputStream;
42 import java.io.BufferedOutputStream;
43 import java.io.File;
44 import java.io.FileNotFoundException;
45 import java.io.FileOutputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49 import java.io.PrintWriter;
50 import java.nio.charset.StandardCharsets;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.Map;
54 import java.util.Objects;
55 
56 /**
57  * Manages persistent state recorded by the display manager service as an XML file.
58  * Caller must acquire lock on the data store before accessing it.
59  *
60  * File format:
61  * <code>
62  * &lt;display-manager-state>
63  *   &lt;remembered-wifi-displays>
64  *     &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
65  *   &lt;remembered-wifi-displays>
66  *   &lt;display-states>
67  *      &lt;display unique-id="XXXXXXX">
68  *          &lt;color-mode>0&lt;/color-mode>
69  *      &lt;/display>
70  *  &lt;/display-states>
71  *  &lt;stable-device-values>
72  *      &lt;stable-display-height>1920&lt;/stable-display-height>
73  *      &lt;stable-display-width>1080&lt;/stable-display-width>
74  *  &lt;/stable-device-values>
75  *  &lt;brightness-configurations>
76  *      &lt;brightness-configuration user-serial="0" package-name="com.example" timestamp="1234">
77  *          &lt;brightness-curve description="some text">
78  *              &lt;brightness-point lux="0" nits="13.25"/>
79  *              &lt;brightness-point lux="20" nits="35.94"/>
80  *          &lt;/brightness-curve>
81  *      &lt;/brightness-configuration>
82  *  &lt;/brightness-configurations>
83  * &lt;/display-manager-state>
84  * </code>
85  *
86  * TODO: refactor this to extract common code shared with the input manager's data store
87  */
88 final class PersistentDataStore {
89     static final String TAG = "DisplayManager";
90 
91     private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
92 
93     private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays";
94     private static final String TAG_WIFI_DISPLAY = "wifi-display";
95     private static final String ATTR_DEVICE_ADDRESS = "deviceAddress";
96     private static final String ATTR_DEVICE_NAME = "deviceName";
97     private static final String ATTR_DEVICE_ALIAS = "deviceAlias";
98 
99     private static final String TAG_DISPLAY_STATES = "display-states";
100     private static final String TAG_DISPLAY = "display";
101     private static final String TAG_COLOR_MODE = "color-mode";
102     private static final String ATTR_UNIQUE_ID = "unique-id";
103 
104     private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
105     private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height";
106     private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width";
107 
108     private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
109     private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
110     private static final String ATTR_USER_SERIAL = "user-serial";
111     private static final String ATTR_PACKAGE_NAME = "package-name";
112     private static final String ATTR_TIME_STAMP = "timestamp";
113 
114     // Remembered Wifi display devices.
115     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
116 
117     // Display state by unique id.
118     private final HashMap<String, DisplayState> mDisplayStates =
119             new HashMap<String, DisplayState>();
120 
121     // Display values which should be stable across the device's lifetime.
122     private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
123 
124     // Brightness configuration by user
125     private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
126 
127     // True if the data has been loaded.
128     private boolean mLoaded;
129 
130     // True if there are changes to be saved.
131     private boolean mDirty;
132 
133     // The interface for methods which should be replaced by the test harness.
134     private Injector mInjector;
135 
PersistentDataStore()136     public PersistentDataStore() {
137         this(new Injector());
138     }
139 
140     @VisibleForTesting
PersistentDataStore(Injector injector)141     PersistentDataStore(Injector injector) {
142         mInjector = injector;
143     }
144 
saveIfNeeded()145     public void saveIfNeeded() {
146         if (mDirty) {
147             save();
148             mDirty = false;
149         }
150     }
151 
getRememberedWifiDisplay(String deviceAddress)152     public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
153         loadIfNeeded();
154         int index = findRememberedWifiDisplay(deviceAddress);
155         if (index >= 0) {
156             return mRememberedWifiDisplays.get(index);
157         }
158         return null;
159     }
160 
getRememberedWifiDisplays()161     public WifiDisplay[] getRememberedWifiDisplays() {
162         loadIfNeeded();
163         return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
164     }
165 
applyWifiDisplayAlias(WifiDisplay display)166     public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
167         if (display != null) {
168             loadIfNeeded();
169 
170             String alias = null;
171             int index = findRememberedWifiDisplay(display.getDeviceAddress());
172             if (index >= 0) {
173                 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
174             }
175             if (!Objects.equals(display.getDeviceAlias(), alias)) {
176                 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(),
177                         alias, display.isAvailable(), display.canConnect(), display.isRemembered());
178             }
179         }
180         return display;
181     }
182 
applyWifiDisplayAliases(WifiDisplay[] displays)183     public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
184         WifiDisplay[] results = displays;
185         if (results != null) {
186             int count = displays.length;
187             for (int i = 0; i < count; i++) {
188                 WifiDisplay result = applyWifiDisplayAlias(displays[i]);
189                 if (result != displays[i]) {
190                     if (results == displays) {
191                         results = new WifiDisplay[count];
192                         System.arraycopy(displays, 0, results, 0, count);
193                     }
194                     results[i] = result;
195                 }
196             }
197         }
198         return results;
199     }
200 
rememberWifiDisplay(WifiDisplay display)201     public boolean rememberWifiDisplay(WifiDisplay display) {
202         loadIfNeeded();
203 
204         int index = findRememberedWifiDisplay(display.getDeviceAddress());
205         if (index >= 0) {
206             WifiDisplay other = mRememberedWifiDisplays.get(index);
207             if (other.equals(display)) {
208                 return false; // already remembered without change
209             }
210             mRememberedWifiDisplays.set(index, display);
211         } else {
212             mRememberedWifiDisplays.add(display);
213         }
214         setDirty();
215         return true;
216     }
217 
forgetWifiDisplay(String deviceAddress)218     public boolean forgetWifiDisplay(String deviceAddress) {
219 		loadIfNeeded();
220         int index = findRememberedWifiDisplay(deviceAddress);
221         if (index >= 0) {
222             mRememberedWifiDisplays.remove(index);
223             setDirty();
224             return true;
225         }
226         return false;
227     }
228 
findRememberedWifiDisplay(String deviceAddress)229     private int findRememberedWifiDisplay(String deviceAddress) {
230         int count = mRememberedWifiDisplays.size();
231         for (int i = 0; i < count; i++) {
232             if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
233                 return i;
234             }
235         }
236         return -1;
237     }
238 
getColorMode(DisplayDevice device)239     public int getColorMode(DisplayDevice device) {
240         if (!device.hasStableUniqueId()) {
241             return Display.COLOR_MODE_INVALID;
242         }
243         DisplayState state = getDisplayState(device.getUniqueId(), false);
244         if (state == null) {
245             return Display.COLOR_MODE_INVALID;
246         }
247         return state.getColorMode();
248     }
249 
setColorMode(DisplayDevice device, int colorMode)250     public boolean setColorMode(DisplayDevice device, int colorMode) {
251         if (!device.hasStableUniqueId()) {
252             return false;
253         }
254         DisplayState state = getDisplayState(device.getUniqueId(), true);
255         if (state.setColorMode(colorMode)) {
256             setDirty();
257             return true;
258         }
259         return false;
260     }
261 
getStableDisplaySize()262 	public Point getStableDisplaySize() {
263 		loadIfNeeded();
264 		return mStableDeviceValues.getDisplaySize();
265 	}
266 
setStableDisplaySize(Point size)267 	public void setStableDisplaySize(Point size) {
268 		loadIfNeeded();
269 		if (mStableDeviceValues.setDisplaySize(size)) {
270 			setDirty();
271 		}
272 	}
273 
setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, @Nullable String packageName)274     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial,
275             @Nullable String packageName) {
276         loadIfNeeded();
277         if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
278                 packageName)) {
279             setDirty();
280         }
281     }
282 
getBrightnessConfiguration(int userSerial)283     public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
284         loadIfNeeded();
285         return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
286     }
287 
getDisplayState(String uniqueId, boolean createIfAbsent)288     private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
289         loadIfNeeded();
290         DisplayState state = mDisplayStates.get(uniqueId);
291         if (state == null && createIfAbsent) {
292             state = new DisplayState();
293             mDisplayStates.put(uniqueId, state);
294             setDirty();
295         }
296         return state;
297     }
298 
loadIfNeeded()299     public void loadIfNeeded() {
300         if (!mLoaded) {
301             load();
302             mLoaded = true;
303         }
304     }
305 
setDirty()306     private void setDirty() {
307         mDirty = true;
308     }
309 
clearState()310     private void clearState() {
311         mRememberedWifiDisplays.clear();
312     }
313 
load()314     private void load() {
315         clearState();
316 
317         final InputStream is;
318         try {
319             is = mInjector.openRead();
320         } catch (FileNotFoundException ex) {
321             return;
322         }
323 
324         XmlPullParser parser;
325         try {
326             parser = Xml.newPullParser();
327             parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
328             loadFromXml(parser);
329         } catch (IOException ex) {
330             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
331             clearState();
332         } catch (XmlPullParserException ex) {
333             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
334             clearState();
335         } finally {
336             IoUtils.closeQuietly(is);
337         }
338     }
339 
save()340     private void save() {
341         final OutputStream os;
342         try {
343             os = mInjector.startWrite();
344             boolean success = false;
345             try {
346                 XmlSerializer serializer = new FastXmlSerializer();
347                 serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
348                 saveToXml(serializer);
349                 serializer.flush();
350                 success = true;
351             } finally {
352                 mInjector.finishWrite(os, success);
353             }
354         } catch (IOException ex) {
355             Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
356         }
357     }
358 
loadFromXml(XmlPullParser parser)359     private void loadFromXml(XmlPullParser parser)
360             throws IOException, XmlPullParserException {
361         XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
362         final int outerDepth = parser.getDepth();
363         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
364             if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) {
365                 loadRememberedWifiDisplaysFromXml(parser);
366             }
367             if (parser.getName().equals(TAG_DISPLAY_STATES)) {
368                 loadDisplaysFromXml(parser);
369             }
370             if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) {
371                 mStableDeviceValues.loadFromXml(parser);
372             }
373             if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
374                 mBrightnessConfigurations.loadFromXml(parser);
375             }
376         }
377     }
378 
loadRememberedWifiDisplaysFromXml(XmlPullParser parser)379     private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
380             throws IOException, XmlPullParserException {
381         final int outerDepth = parser.getDepth();
382         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
383             if (parser.getName().equals(TAG_WIFI_DISPLAY)) {
384                 String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS);
385                 String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME);
386                 String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS);
387                 if (deviceAddress == null || deviceName == null) {
388                     throw new XmlPullParserException(
389                             "Missing deviceAddress or deviceName attribute on wifi-display.");
390                 }
391                 if (findRememberedWifiDisplay(deviceAddress) >= 0) {
392                     throw new XmlPullParserException(
393                             "Found duplicate wifi display device address.");
394                 }
395 
396                 mRememberedWifiDisplays.add(
397                         new WifiDisplay(deviceAddress, deviceName, deviceAlias,
398                                 false, false, false));
399             }
400         }
401     }
402 
loadDisplaysFromXml(XmlPullParser parser)403     private void loadDisplaysFromXml(XmlPullParser parser)
404             throws IOException, XmlPullParserException {
405         final int outerDepth = parser.getDepth();
406         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
407             if (parser.getName().equals(TAG_DISPLAY)) {
408                 String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID);
409                 if (uniqueId == null) {
410                     throw new XmlPullParserException(
411                             "Missing unique-id attribute on display.");
412                 }
413                 if (mDisplayStates.containsKey(uniqueId)) {
414                     throw new XmlPullParserException("Found duplicate display.");
415                 }
416 
417                 DisplayState state = new DisplayState();
418                 state.loadFromXml(parser);
419                 mDisplayStates.put(uniqueId, state);
420             }
421         }
422     }
423 
saveToXml(XmlSerializer serializer)424     private void saveToXml(XmlSerializer serializer) throws IOException {
425         serializer.startDocument(null, true);
426         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
427         serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
428         serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
429         for (WifiDisplay display : mRememberedWifiDisplays) {
430             serializer.startTag(null, TAG_WIFI_DISPLAY);
431             serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress());
432             serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName());
433             if (display.getDeviceAlias() != null) {
434                 serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias());
435             }
436             serializer.endTag(null, TAG_WIFI_DISPLAY);
437         }
438         serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
439         serializer.startTag(null, TAG_DISPLAY_STATES);
440         for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
441             final String uniqueId = entry.getKey();
442             final DisplayState state = entry.getValue();
443             serializer.startTag(null, TAG_DISPLAY);
444             serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId);
445             state.saveToXml(serializer);
446             serializer.endTag(null, TAG_DISPLAY);
447         }
448         serializer.endTag(null, TAG_DISPLAY_STATES);
449         serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
450         mStableDeviceValues.saveToXml(serializer);
451         serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
452         serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
453         mBrightnessConfigurations.saveToXml(serializer);
454         serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
455         serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
456         serializer.endDocument();
457     }
458 
dump(PrintWriter pw)459     public void dump(PrintWriter pw) {
460         pw.println("PersistentDataStore");
461         pw.println("  mLoaded=" + mLoaded);
462         pw.println("  mDirty=" + mDirty);
463         pw.println("  RememberedWifiDisplays:");
464         int i = 0;
465         for (WifiDisplay display : mRememberedWifiDisplays) {
466             pw.println("    " + i++ + ": " + display);
467         }
468         pw.println("  DisplayStates:");
469         i = 0;
470         for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
471             pw.println("    " + i++ + ": " + entry.getKey());
472             entry.getValue().dump(pw, "      ");
473         }
474         pw.println("  StableDeviceValues:");
475         mStableDeviceValues.dump(pw, "      ");
476         pw.println("  BrightnessConfigurations:");
477         mBrightnessConfigurations.dump(pw, "      ");
478     }
479 
480     private static final class DisplayState {
481         private int mColorMode;
482 
setColorMode(int colorMode)483         public boolean setColorMode(int colorMode) {
484             if (colorMode == mColorMode) {
485                 return false;
486             }
487             mColorMode = colorMode;
488             return true;
489         }
490 
getColorMode()491         public int getColorMode() {
492             return mColorMode;
493         }
494 
loadFromXml(XmlPullParser parser)495         public void loadFromXml(XmlPullParser parser)
496                 throws IOException, XmlPullParserException {
497             final int outerDepth = parser.getDepth();
498 
499             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
500                 if (parser.getName().equals(TAG_COLOR_MODE)) {
501                     String value = parser.nextText();
502                     mColorMode = Integer.parseInt(value);
503                 }
504             }
505         }
506 
saveToXml(XmlSerializer serializer)507         public void saveToXml(XmlSerializer serializer) throws IOException {
508             serializer.startTag(null, TAG_COLOR_MODE);
509             serializer.text(Integer.toString(mColorMode));
510             serializer.endTag(null, TAG_COLOR_MODE);
511         }
512 
dump(final PrintWriter pw, final String prefix)513         public void dump(final PrintWriter pw, final String prefix) {
514             pw.println(prefix + "ColorMode=" + mColorMode);
515         }
516     }
517 
518     private static final class StableDeviceValues {
519         private int mWidth;
520         private int mHeight;
521 
getDisplaySize()522         private Point getDisplaySize() {
523             return new Point(mWidth, mHeight);
524         }
525 
setDisplaySize(Point r)526         public boolean setDisplaySize(Point r) {
527             if (mWidth != r.x || mHeight != r.y) {
528                 mWidth = r.x;
529                 mHeight = r.y;
530                 return true;
531             }
532             return false;
533         }
534 
loadFromXml(XmlPullParser parser)535         public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
536             final int outerDepth = parser.getDepth();
537             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
538                 switch (parser.getName()) {
539                     case TAG_STABLE_DISPLAY_WIDTH:
540                         mWidth = loadIntValue(parser);
541                         break;
542                     case TAG_STABLE_DISPLAY_HEIGHT:
543                         mHeight = loadIntValue(parser);
544                         break;
545                 }
546             }
547         }
548 
loadIntValue(XmlPullParser parser)549         private static int loadIntValue(XmlPullParser parser)
550             throws IOException, XmlPullParserException {
551             try {
552                 String value = parser.nextText();
553                 return Integer.parseInt(value);
554             } catch (NumberFormatException nfe) {
555                 return 0;
556             }
557         }
558 
saveToXml(XmlSerializer serializer)559         public void saveToXml(XmlSerializer serializer) throws IOException {
560             if (mWidth > 0 && mHeight > 0) {
561                 serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
562                 serializer.text(Integer.toString(mWidth));
563                 serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH);
564                 serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT);
565                 serializer.text(Integer.toString(mHeight));
566                 serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT);
567             }
568         }
569 
dump(final PrintWriter pw, final String prefix)570         public void dump(final PrintWriter pw, final String prefix) {
571             pw.println(prefix + "StableDisplayWidth=" + mWidth);
572             pw.println(prefix + "StableDisplayHeight=" + mHeight);
573         }
574     }
575 
576     private static final class BrightnessConfigurations {
577         // Maps from a user ID to the users' given brightness configuration
578         private SparseArray<BrightnessConfiguration> mConfigurations;
579         // Timestamp of time the configuration was set.
580         private SparseLongArray mTimeStamps;
581         // Package that set the configuration.
582         private SparseArray<String> mPackageNames;
583 
BrightnessConfigurations()584         public BrightnessConfigurations() {
585             mConfigurations = new SparseArray<>();
586             mTimeStamps = new SparseLongArray();
587             mPackageNames = new SparseArray<>();
588         }
589 
setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial, String packageName)590         private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c,
591                 int userSerial, String packageName) {
592             BrightnessConfiguration currentConfig = mConfigurations.get(userSerial);
593             if (currentConfig != c && (currentConfig == null || !currentConfig.equals(c))) {
594                 if (c != null) {
595                     if (packageName == null) {
596                         mPackageNames.remove(userSerial);
597                     } else {
598                         mPackageNames.put(userSerial, packageName);
599                     }
600                     mTimeStamps.put(userSerial, System.currentTimeMillis());
601                     mConfigurations.put(userSerial, c);
602                 } else {
603                     mPackageNames.remove(userSerial);
604                     mTimeStamps.delete(userSerial);
605                     mConfigurations.remove(userSerial);
606                 }
607                 return true;
608             }
609             return false;
610         }
611 
getBrightnessConfiguration(int userSerial)612         public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
613             return mConfigurations.get(userSerial);
614         }
615 
loadFromXml(XmlPullParser parser)616         public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
617             final int outerDepth = parser.getDepth();
618             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
619                 if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
620                     int userSerial;
621                     try {
622                         userSerial = Integer.parseInt(
623                                 parser.getAttributeValue(null, ATTR_USER_SERIAL));
624                     } catch (NumberFormatException nfe) {
625                         userSerial = -1;
626                         Slog.e(TAG, "Failed to read in brightness configuration", nfe);
627                     }
628 
629                     String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
630                     String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP);
631                     long timeStamp = -1;
632                     if (timeStampString != null) {
633                         try {
634                             timeStamp = Long.parseLong(timeStampString);
635                         } catch (NumberFormatException nfe) {
636                             // Ignore we will just not restore the timestamp.
637                         }
638                     }
639 
640                     try {
641                         BrightnessConfiguration config =
642                                 BrightnessConfiguration.loadFromXml(parser);
643                         if (userSerial >= 0 && config != null) {
644                             mConfigurations.put(userSerial, config);
645                             if (timeStamp != -1) {
646                                 mTimeStamps.put(userSerial, timeStamp);
647                             }
648                             if (packageName != null) {
649                                 mPackageNames.put(userSerial, packageName);
650                             }
651                         }
652                     } catch (IllegalArgumentException iae) {
653                         Slog.e(TAG, "Failed to load brightness configuration!", iae);
654                     }
655                 }
656             }
657         }
658 
saveToXml(XmlSerializer serializer)659         public void saveToXml(XmlSerializer serializer) throws IOException {
660             for (int i = 0; i < mConfigurations.size(); i++) {
661                 final int userSerial = mConfigurations.keyAt(i);
662                 final BrightnessConfiguration config = mConfigurations.valueAt(i);
663 
664                 serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
665                 serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
666                 String packageName = mPackageNames.get(userSerial);
667                 if (packageName != null) {
668                     serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
669                 }
670                 long timestamp = mTimeStamps.get(userSerial, -1);
671                 if (timestamp != -1) {
672                     serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
673                 }
674                 config.saveToXml(serializer);
675                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
676             }
677         }
678 
dump(final PrintWriter pw, final String prefix)679         public void dump(final PrintWriter pw, final String prefix) {
680             for (int i = 0; i < mConfigurations.size(); i++) {
681                 final int userSerial = mConfigurations.keyAt(i);
682                 long time = mTimeStamps.get(userSerial, -1);
683                 String packageName = mPackageNames.get(userSerial);
684                 pw.println(prefix + "User " + userSerial + ":");
685                 if (time != -1) {
686                     pw.println(prefix + "  set at: " + TimeUtils.formatForLogging(time));
687                 }
688                 if (packageName != null) {
689                     pw.println(prefix + "  set by: " + packageName);
690                 }
691                 pw.println(prefix + "  " + mConfigurations.valueAt(i));
692             }
693         }
694     }
695 
696     @VisibleForTesting
697     static class Injector {
698         private final AtomicFile mAtomicFile;
699 
Injector()700         public Injector() {
701             mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"),
702                     "display-state");
703         }
704 
openRead()705         public InputStream openRead() throws FileNotFoundException {
706             return mAtomicFile.openRead();
707         }
708 
startWrite()709         public OutputStream startWrite() throws IOException {
710             return mAtomicFile.startWrite();
711         }
712 
finishWrite(OutputStream os, boolean success)713         public void finishWrite(OutputStream os, boolean success) {
714             if (!(os instanceof FileOutputStream)) {
715                 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
716             }
717             FileOutputStream fos = (FileOutputStream) os;
718             if (success) {
719                 mAtomicFile.finishWrite(fos);
720             } else {
721                 mAtomicFile.failWrite(fos);
722             }
723         }
724     }
725 }
726