• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.internal.util.FastXmlSerializer;
20 import com.android.internal.util.XmlUtils;
21 
22 import org.xmlpull.v1.XmlPullParser;
23 import org.xmlpull.v1.XmlPullParserException;
24 import org.xmlpull.v1.XmlSerializer;
25 
26 import android.hardware.display.WifiDisplay;
27 import android.util.AtomicFile;
28 import android.util.Slog;
29 import android.util.Xml;
30 import android.view.Display;
31 
32 import java.io.BufferedInputStream;
33 import java.io.BufferedOutputStream;
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.PrintWriter;
40 import java.nio.charset.StandardCharsets;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.Map;
44 
45 import libcore.io.IoUtils;
46 import libcore.util.Objects;
47 
48 /**
49  * Manages persistent state recorded by the display manager service as an XML file.
50  * Caller must acquire lock on the data store before accessing it.
51  *
52  * File format:
53  * <code>
54  * &lt;display-manager-state>
55  *   &lt;remembered-wifi-displays>
56  *     &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
57  *   &lt;remembered-wifi-displays>
58  *   &lt;display-states>
59  *      &lt;display>
60  *          &lt;color-mode>0&lt;/color-mode>
61  *      &lt;/display>
62  *  &lt;/display-states>
63  * &lt;/display-manager-state>
64  * </code>
65  *
66  * TODO: refactor this to extract common code shared with the input manager's data store
67  */
68 final class PersistentDataStore {
69     static final String TAG = "DisplayManager";
70 
71     // Remembered Wifi display devices.
72     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
73 
74     // Display state by unique id.
75     private final HashMap<String, DisplayState> mDisplayStates =
76             new HashMap<String, DisplayState>();
77 
78     // The atomic file used to safely read or write the file.
79     private final AtomicFile mAtomicFile;
80 
81     // True if the data has been loaded.
82     private boolean mLoaded;
83 
84     // True if there are changes to be saved.
85     private boolean mDirty;
86 
PersistentDataStore()87     public PersistentDataStore() {
88         mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
89     }
90 
saveIfNeeded()91     public void saveIfNeeded() {
92         if (mDirty) {
93             save();
94             mDirty = false;
95         }
96     }
97 
getRememberedWifiDisplay(String deviceAddress)98     public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
99         loadIfNeeded();
100         int index = findRememberedWifiDisplay(deviceAddress);
101         if (index >= 0) {
102             return mRememberedWifiDisplays.get(index);
103         }
104         return null;
105     }
106 
getRememberedWifiDisplays()107     public WifiDisplay[] getRememberedWifiDisplays() {
108         loadIfNeeded();
109         return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
110     }
111 
applyWifiDisplayAlias(WifiDisplay display)112     public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
113         if (display != null) {
114             loadIfNeeded();
115 
116             String alias = null;
117             int index = findRememberedWifiDisplay(display.getDeviceAddress());
118             if (index >= 0) {
119                 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
120             }
121             if (!Objects.equal(display.getDeviceAlias(), alias)) {
122                 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(),
123                         alias, display.isAvailable(), display.canConnect(), display.isRemembered());
124             }
125         }
126         return display;
127     }
128 
applyWifiDisplayAliases(WifiDisplay[] displays)129     public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
130         WifiDisplay[] results = displays;
131         if (results != null) {
132             int count = displays.length;
133             for (int i = 0; i < count; i++) {
134                 WifiDisplay result = applyWifiDisplayAlias(displays[i]);
135                 if (result != displays[i]) {
136                     if (results == displays) {
137                         results = new WifiDisplay[count];
138                         System.arraycopy(displays, 0, results, 0, count);
139                     }
140                     results[i] = result;
141                 }
142             }
143         }
144         return results;
145     }
146 
rememberWifiDisplay(WifiDisplay display)147     public boolean rememberWifiDisplay(WifiDisplay display) {
148         loadIfNeeded();
149 
150         int index = findRememberedWifiDisplay(display.getDeviceAddress());
151         if (index >= 0) {
152             WifiDisplay other = mRememberedWifiDisplays.get(index);
153             if (other.equals(display)) {
154                 return false; // already remembered without change
155             }
156             mRememberedWifiDisplays.set(index, display);
157         } else {
158             mRememberedWifiDisplays.add(display);
159         }
160         setDirty();
161         return true;
162     }
163 
forgetWifiDisplay(String deviceAddress)164     public boolean forgetWifiDisplay(String deviceAddress) {
165         int index = findRememberedWifiDisplay(deviceAddress);
166         if (index >= 0) {
167             mRememberedWifiDisplays.remove(index);
168             setDirty();
169             return true;
170         }
171         return false;
172     }
173 
findRememberedWifiDisplay(String deviceAddress)174     private int findRememberedWifiDisplay(String deviceAddress) {
175         int count = mRememberedWifiDisplays.size();
176         for (int i = 0; i < count; i++) {
177             if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
178                 return i;
179             }
180         }
181         return -1;
182     }
183 
getColorMode(DisplayDevice device)184     public int getColorMode(DisplayDevice device) {
185         if (!device.hasStableUniqueId()) {
186             return Display.COLOR_MODE_DEFAULT;
187         }
188         DisplayState state = getDisplayState(device.getUniqueId(), false);
189         if (state == null) {
190             return Display.COLOR_MODE_DEFAULT;
191         }
192         return state.getColorMode();
193     }
194 
setColorMode(DisplayDevice device, int colorMode)195     public boolean setColorMode(DisplayDevice device, int colorMode) {
196         if (!device.hasStableUniqueId()) {
197             return false;
198         }
199         DisplayState state = getDisplayState(device.getUniqueId(), true);
200         if (state.setColorMode(colorMode)) {
201             setDirty();
202             return true;
203         }
204         return false;
205     }
206 
getDisplayState(String uniqueId, boolean createIfAbsent)207     private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
208         loadIfNeeded();
209         DisplayState state = mDisplayStates.get(uniqueId);
210         if (state == null && createIfAbsent) {
211             state = new DisplayState();
212             mDisplayStates.put(uniqueId, state);
213             setDirty();
214         }
215         return state;
216     }
217 
loadIfNeeded()218     public void loadIfNeeded() {
219         if (!mLoaded) {
220             load();
221             mLoaded = true;
222         }
223     }
224 
setDirty()225     private void setDirty() {
226         mDirty = true;
227     }
228 
clearState()229     private void clearState() {
230         mRememberedWifiDisplays.clear();
231     }
232 
load()233     private void load() {
234         clearState();
235 
236         final InputStream is;
237         try {
238             is = mAtomicFile.openRead();
239         } catch (FileNotFoundException ex) {
240             return;
241         }
242 
243         XmlPullParser parser;
244         try {
245             parser = Xml.newPullParser();
246             parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
247             loadFromXml(parser);
248         } catch (IOException ex) {
249             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
250             clearState();
251         } catch (XmlPullParserException ex) {
252             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
253             clearState();
254         } finally {
255             IoUtils.closeQuietly(is);
256         }
257     }
258 
save()259     private void save() {
260         final FileOutputStream os;
261         try {
262             os = mAtomicFile.startWrite();
263             boolean success = false;
264             try {
265                 XmlSerializer serializer = new FastXmlSerializer();
266                 serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
267                 saveToXml(serializer);
268                 serializer.flush();
269                 success = true;
270             } finally {
271                 if (success) {
272                     mAtomicFile.finishWrite(os);
273                 } else {
274                     mAtomicFile.failWrite(os);
275                 }
276             }
277         } catch (IOException ex) {
278             Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
279         }
280     }
281 
loadFromXml(XmlPullParser parser)282     private void loadFromXml(XmlPullParser parser)
283             throws IOException, XmlPullParserException {
284         XmlUtils.beginDocument(parser, "display-manager-state");
285         final int outerDepth = parser.getDepth();
286         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
287             if (parser.getName().equals("remembered-wifi-displays")) {
288                 loadRememberedWifiDisplaysFromXml(parser);
289             }
290             if (parser.getName().equals("display-states")) {
291                 loadDisplaysFromXml(parser);
292             }
293         }
294     }
295 
loadRememberedWifiDisplaysFromXml(XmlPullParser parser)296     private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
297             throws IOException, XmlPullParserException {
298         final int outerDepth = parser.getDepth();
299         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
300             if (parser.getName().equals("wifi-display")) {
301                 String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
302                 String deviceName = parser.getAttributeValue(null, "deviceName");
303                 String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
304                 if (deviceAddress == null || deviceName == null) {
305                     throw new XmlPullParserException(
306                             "Missing deviceAddress or deviceName attribute on wifi-display.");
307                 }
308                 if (findRememberedWifiDisplay(deviceAddress) >= 0) {
309                     throw new XmlPullParserException(
310                             "Found duplicate wifi display device address.");
311                 }
312 
313                 mRememberedWifiDisplays.add(
314                         new WifiDisplay(deviceAddress, deviceName, deviceAlias,
315                                 false, false, false));
316             }
317         }
318     }
319 
loadDisplaysFromXml(XmlPullParser parser)320     private void loadDisplaysFromXml(XmlPullParser parser)
321             throws IOException, XmlPullParserException {
322         final int outerDepth = parser.getDepth();
323         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
324             if (parser.getName().equals("display")) {
325                 String uniqueId = parser.getAttributeValue(null, "unique-id");
326                 if (uniqueId == null) {
327                     throw new XmlPullParserException(
328                             "Missing unique-id attribute on display.");
329                 }
330                 if (mDisplayStates.containsKey(uniqueId)) {
331                     throw new XmlPullParserException("Found duplicate display.");
332                 }
333 
334                 DisplayState state = new DisplayState();
335                 state.loadFromXml(parser);
336                 mDisplayStates.put(uniqueId, state);
337             }
338         }
339     }
340 
saveToXml(XmlSerializer serializer)341     private void saveToXml(XmlSerializer serializer) throws IOException {
342         serializer.startDocument(null, true);
343         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
344         serializer.startTag(null, "display-manager-state");
345         serializer.startTag(null, "remembered-wifi-displays");
346         for (WifiDisplay display : mRememberedWifiDisplays) {
347             serializer.startTag(null, "wifi-display");
348             serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
349             serializer.attribute(null, "deviceName", display.getDeviceName());
350             if (display.getDeviceAlias() != null) {
351                 serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
352             }
353             serializer.endTag(null, "wifi-display");
354         }
355         serializer.endTag(null, "remembered-wifi-displays");
356         serializer.startTag(null, "display-states");
357         for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
358             final String uniqueId = entry.getKey();
359             final DisplayState state = entry.getValue();
360             serializer.startTag(null, "display");
361             serializer.attribute(null, "unique-id", uniqueId);
362             state.saveToXml(serializer);
363             serializer.endTag(null, "display");
364         }
365         serializer.endTag(null, "display-states");
366         serializer.endTag(null, "display-manager-state");
367         serializer.endDocument();
368     }
369 
dump(PrintWriter pw)370     public void dump(PrintWriter pw) {
371         pw.println("PersistentDataStore");
372         pw.println("  mLoaded=" + mLoaded);
373         pw.println("  mDirty=" + mDirty);
374         pw.println("  RememberedWifiDisplays:");
375         int i = 0;
376         for (WifiDisplay display : mRememberedWifiDisplays) {
377             pw.println("    " + i++ + ": " + display);
378         }
379         pw.println("  DisplayStates:");
380         i = 0;
381         for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
382             pw.println("    " + i++ + ": " + entry.getKey());
383             entry.getValue().dump(pw, "      ");
384         }
385     }
386 
387     private static final class DisplayState {
388         private int mColorMode;
389 
setColorMode(int colorMode)390         public boolean setColorMode(int colorMode) {
391             if (colorMode == mColorMode) {
392                 return false;
393             }
394             mColorMode = colorMode;
395             return true;
396         }
397 
getColorMode()398         public int getColorMode() {
399             return mColorMode;
400         }
401 
loadFromXml(XmlPullParser parser)402         public void loadFromXml(XmlPullParser parser)
403                 throws IOException, XmlPullParserException {
404             final int outerDepth = parser.getDepth();
405 
406             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
407                 if (parser.getName().equals("color-mode")) {
408                     String value = parser.nextText();
409                     mColorMode = Integer.parseInt(value);
410                 }
411             }
412         }
413 
saveToXml(XmlSerializer serializer)414         public void saveToXml(XmlSerializer serializer) throws IOException {
415             serializer.startTag(null, "color-mode");
416             serializer.text(Integer.toString(mColorMode));
417             serializer.endTag(null, "color-mode");
418         }
419 
dump(final PrintWriter pw, final String prefix)420         private void dump(final PrintWriter pw, final String prefix) {
421             pw.println(prefix + "ColorMode=" + mColorMode);
422         }
423     }
424 }
425