blob: 34c8e22a9f1e0eff3084ef37d307f8141c813cd6 [file] [log] [blame]
Jeff Brown89d55462012-09-19 11:33:42 -07001/*
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
17package com.android.server.display;
18
19import com.android.internal.util.FastXmlSerializer;
20import com.android.internal.util.XmlUtils;
21
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24import org.xmlpull.v1.XmlSerializer;
25
Michael Wrighteedcbf12017-08-16 23:14:54 +010026import android.graphics.Point;
Jeff Brown89d55462012-09-19 11:33:42 -070027import android.hardware.display.WifiDisplay;
28import android.util.AtomicFile;
29import android.util.Slog;
30import android.util.Xml;
Michael Wright1c9977b2016-07-12 13:30:10 -070031import android.view.Display;
Jeff Brown89d55462012-09-19 11:33:42 -070032
33import java.io.BufferedInputStream;
34import java.io.BufferedOutputStream;
35import java.io.File;
36import java.io.FileNotFoundException;
37import java.io.FileOutputStream;
38import java.io.IOException;
39import java.io.InputStream;
Michael Wright1c9977b2016-07-12 13:30:10 -070040import java.io.PrintWriter;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010041import java.nio.charset.StandardCharsets;
Jeff Brown89d55462012-09-19 11:33:42 -070042import java.util.ArrayList;
Michael Wright1c9977b2016-07-12 13:30:10 -070043import java.util.HashMap;
44import java.util.Map;
Jeff Brown89d55462012-09-19 11:33:42 -070045
46import libcore.io.IoUtils;
47import libcore.util.Objects;
48
49/**
50 * Manages persistent state recorded by the display manager service as an XML file.
51 * Caller must acquire lock on the data store before accessing it.
52 *
53 * File format:
54 * <code>
55 * &lt;display-manager-state>
56 * &lt;remembered-wifi-displays>
57 * &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
Michael Wright1c9977b2016-07-12 13:30:10 -070058 * &lt;remembered-wifi-displays>
59 * &lt;display-states>
60 * &lt;display>
61 * &lt;color-mode>0&lt;/color-mode>
62 * &lt;/display>
63 * &lt;/display-states>
Michael Wrighteedcbf12017-08-16 23:14:54 +010064 * &lt;stable-device-values>
65 * &lt;stable-display-height>1920&lt;stable-display-height>
66 * &lt;stable-display-width>1080&lt;stable-display-width>
67 * &lt;/stable-device-values>
Michael Wright1c9977b2016-07-12 13:30:10 -070068 * &lt;/display-manager-state>
Jeff Brown89d55462012-09-19 11:33:42 -070069 * </code>
70 *
71 * TODO: refactor this to extract common code shared with the input manager's data store
72 */
73final class PersistentDataStore {
74 static final String TAG = "DisplayManager";
75
76 // Remembered Wifi display devices.
77 private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
78
Michael Wright1c9977b2016-07-12 13:30:10 -070079 // Display state by unique id.
80 private final HashMap<String, DisplayState> mDisplayStates =
81 new HashMap<String, DisplayState>();
82
Michael Wrighteedcbf12017-08-16 23:14:54 +010083 // Display values which should be stable across the device's lifetime.
84 private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
85
Jeff Brown89d55462012-09-19 11:33:42 -070086 // The atomic file used to safely read or write the file.
87 private final AtomicFile mAtomicFile;
88
89 // True if the data has been loaded.
90 private boolean mLoaded;
91
92 // True if there are changes to be saved.
93 private boolean mDirty;
94
95 public PersistentDataStore() {
96 mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
97 }
98
99 public void saveIfNeeded() {
100 if (mDirty) {
101 save();
102 mDirty = false;
103 }
104 }
105
Jeff Brown74da1092012-11-07 16:02:13 -0800106 public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
107 loadIfNeeded();
108 int index = findRememberedWifiDisplay(deviceAddress);
109 if (index >= 0) {
110 return mRememberedWifiDisplays.get(index);
111 }
112 return null;
113 }
114
Jeff Brown89d55462012-09-19 11:33:42 -0700115 public WifiDisplay[] getRememberedWifiDisplays() {
116 loadIfNeeded();
117 return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
118 }
119
120 public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
121 if (display != null) {
122 loadIfNeeded();
123
Jeff Brownc2b9ea62012-09-19 23:09:23 -0700124 String alias = null;
Jeff Brown89d55462012-09-19 11:33:42 -0700125 int index = findRememberedWifiDisplay(display.getDeviceAddress());
126 if (index >= 0) {
Jeff Brownc2b9ea62012-09-19 23:09:23 -0700127 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
128 }
129 if (!Objects.equal(display.getDeviceAlias(), alias)) {
Chong Zhang21f60392013-06-04 18:01:46 -0700130 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(),
Chong Zhangab87a632013-06-11 10:25:49 -0700131 alias, display.isAvailable(), display.canConnect(), display.isRemembered());
Jeff Brown89d55462012-09-19 11:33:42 -0700132 }
133 }
134 return display;
135 }
136
137 public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
138 WifiDisplay[] results = displays;
139 if (results != null) {
140 int count = displays.length;
141 for (int i = 0; i < count; i++) {
142 WifiDisplay result = applyWifiDisplayAlias(displays[i]);
143 if (result != displays[i]) {
144 if (results == displays) {
145 results = new WifiDisplay[count];
146 System.arraycopy(displays, 0, results, 0, count);
147 }
148 results[i] = result;
149 }
150 }
151 }
152 return results;
153 }
154
155 public boolean rememberWifiDisplay(WifiDisplay display) {
156 loadIfNeeded();
157
158 int index = findRememberedWifiDisplay(display.getDeviceAddress());
159 if (index >= 0) {
160 WifiDisplay other = mRememberedWifiDisplays.get(index);
161 if (other.equals(display)) {
162 return false; // already remembered without change
163 }
164 mRememberedWifiDisplays.set(index, display);
165 } else {
166 mRememberedWifiDisplays.add(display);
167 }
168 setDirty();
169 return true;
170 }
171
Jeff Brown89d55462012-09-19 11:33:42 -0700172 public boolean forgetWifiDisplay(String deviceAddress) {
Michael Wrighteedcbf12017-08-16 23:14:54 +0100173 loadIfNeeded();
Jeff Brown89d55462012-09-19 11:33:42 -0700174 int index = findRememberedWifiDisplay(deviceAddress);
175 if (index >= 0) {
176 mRememberedWifiDisplays.remove(index);
177 setDirty();
178 return true;
179 }
180 return false;
181 }
182
183 private int findRememberedWifiDisplay(String deviceAddress) {
184 int count = mRememberedWifiDisplays.size();
185 for (int i = 0; i < count; i++) {
186 if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
187 return i;
188 }
189 }
190 return -1;
191 }
192
Michael Wright1c9977b2016-07-12 13:30:10 -0700193 public int getColorMode(DisplayDevice device) {
194 if (!device.hasStableUniqueId()) {
Damien Bargiacchi4364bbf2016-11-01 21:44:20 -0700195 return Display.COLOR_MODE_INVALID;
Michael Wright1c9977b2016-07-12 13:30:10 -0700196 }
197 DisplayState state = getDisplayState(device.getUniqueId(), false);
198 if (state == null) {
Damien Bargiacchi4364bbf2016-11-01 21:44:20 -0700199 return Display.COLOR_MODE_INVALID;
Michael Wright1c9977b2016-07-12 13:30:10 -0700200 }
201 return state.getColorMode();
202 }
203
204 public boolean setColorMode(DisplayDevice device, int colorMode) {
205 if (!device.hasStableUniqueId()) {
206 return false;
207 }
208 DisplayState state = getDisplayState(device.getUniqueId(), true);
209 if (state.setColorMode(colorMode)) {
210 setDirty();
211 return true;
212 }
213 return false;
214 }
215
Michael Wrighteedcbf12017-08-16 23:14:54 +0100216 public Point getStableDisplaySize() {
217 loadIfNeeded();
218 return mStableDeviceValues.getDisplaySize();
219 }
220
221 public void setStableDisplaySize(Point size) {
222 loadIfNeeded();
223 if (mStableDeviceValues.setDisplaySize(size)) {
224 setDirty();
225 }
226 }
227
Michael Wright1c9977b2016-07-12 13:30:10 -0700228 private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
229 loadIfNeeded();
230 DisplayState state = mDisplayStates.get(uniqueId);
231 if (state == null && createIfAbsent) {
232 state = new DisplayState();
233 mDisplayStates.put(uniqueId, state);
234 setDirty();
235 }
236 return state;
237 }
238
239 public void loadIfNeeded() {
Jeff Brown89d55462012-09-19 11:33:42 -0700240 if (!mLoaded) {
241 load();
242 mLoaded = true;
243 }
244 }
245
246 private void setDirty() {
247 mDirty = true;
248 }
249
250 private void clearState() {
251 mRememberedWifiDisplays.clear();
252 }
253
254 private void load() {
255 clearState();
256
257 final InputStream is;
258 try {
259 is = mAtomicFile.openRead();
260 } catch (FileNotFoundException ex) {
261 return;
262 }
263
264 XmlPullParser parser;
265 try {
266 parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100267 parser.setInput(new BufferedInputStream(is), StandardCharsets.UTF_8.name());
Jeff Brown89d55462012-09-19 11:33:42 -0700268 loadFromXml(parser);
269 } catch (IOException ex) {
270 Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
271 clearState();
272 } catch (XmlPullParserException ex) {
273 Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
274 clearState();
275 } finally {
276 IoUtils.closeQuietly(is);
277 }
278 }
279
280 private void save() {
281 final FileOutputStream os;
282 try {
283 os = mAtomicFile.startWrite();
284 boolean success = false;
285 try {
286 XmlSerializer serializer = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100287 serializer.setOutput(new BufferedOutputStream(os), StandardCharsets.UTF_8.name());
Jeff Brown89d55462012-09-19 11:33:42 -0700288 saveToXml(serializer);
289 serializer.flush();
290 success = true;
291 } finally {
292 if (success) {
293 mAtomicFile.finishWrite(os);
294 } else {
295 mAtomicFile.failWrite(os);
296 }
297 }
298 } catch (IOException ex) {
299 Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
300 }
301 }
302
303 private void loadFromXml(XmlPullParser parser)
304 throws IOException, XmlPullParserException {
305 XmlUtils.beginDocument(parser, "display-manager-state");
306 final int outerDepth = parser.getDepth();
307 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
308 if (parser.getName().equals("remembered-wifi-displays")) {
309 loadRememberedWifiDisplaysFromXml(parser);
310 }
Michael Wright1c9977b2016-07-12 13:30:10 -0700311 if (parser.getName().equals("display-states")) {
312 loadDisplaysFromXml(parser);
313 }
Michael Wrighteedcbf12017-08-16 23:14:54 +0100314 if (parser.getName().equals("stable-device-values")) {
315 mStableDeviceValues.loadFromXml(parser);
316 }
Jeff Brown89d55462012-09-19 11:33:42 -0700317 }
318 }
319
320 private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
321 throws IOException, XmlPullParserException {
322 final int outerDepth = parser.getDepth();
323 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
324 if (parser.getName().equals("wifi-display")) {
325 String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
326 String deviceName = parser.getAttributeValue(null, "deviceName");
327 String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
328 if (deviceAddress == null || deviceName == null) {
329 throw new XmlPullParserException(
330 "Missing deviceAddress or deviceName attribute on wifi-display.");
331 }
332 if (findRememberedWifiDisplay(deviceAddress) >= 0) {
333 throw new XmlPullParserException(
334 "Found duplicate wifi display device address.");
335 }
336
337 mRememberedWifiDisplays.add(
Chong Zhangab87a632013-06-11 10:25:49 -0700338 new WifiDisplay(deviceAddress, deviceName, deviceAlias,
339 false, false, false));
Jeff Brown89d55462012-09-19 11:33:42 -0700340 }
341 }
342 }
343
Michael Wright1c9977b2016-07-12 13:30:10 -0700344 private void loadDisplaysFromXml(XmlPullParser parser)
345 throws IOException, XmlPullParserException {
346 final int outerDepth = parser.getDepth();
347 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
348 if (parser.getName().equals("display")) {
349 String uniqueId = parser.getAttributeValue(null, "unique-id");
350 if (uniqueId == null) {
351 throw new XmlPullParserException(
352 "Missing unique-id attribute on display.");
353 }
354 if (mDisplayStates.containsKey(uniqueId)) {
355 throw new XmlPullParserException("Found duplicate display.");
356 }
357
358 DisplayState state = new DisplayState();
359 state.loadFromXml(parser);
360 mDisplayStates.put(uniqueId, state);
361 }
362 }
363 }
364
Jeff Brown89d55462012-09-19 11:33:42 -0700365 private void saveToXml(XmlSerializer serializer) throws IOException {
366 serializer.startDocument(null, true);
367 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
368 serializer.startTag(null, "display-manager-state");
369 serializer.startTag(null, "remembered-wifi-displays");
370 for (WifiDisplay display : mRememberedWifiDisplays) {
371 serializer.startTag(null, "wifi-display");
372 serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
373 serializer.attribute(null, "deviceName", display.getDeviceName());
374 if (display.getDeviceAlias() != null) {
375 serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
376 }
377 serializer.endTag(null, "wifi-display");
378 }
379 serializer.endTag(null, "remembered-wifi-displays");
Michael Wright1c9977b2016-07-12 13:30:10 -0700380 serializer.startTag(null, "display-states");
381 for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
382 final String uniqueId = entry.getKey();
383 final DisplayState state = entry.getValue();
384 serializer.startTag(null, "display");
385 serializer.attribute(null, "unique-id", uniqueId);
386 state.saveToXml(serializer);
387 serializer.endTag(null, "display");
388 }
389 serializer.endTag(null, "display-states");
Michael Wrighteedcbf12017-08-16 23:14:54 +0100390 serializer.startTag(null, "stable-device-values");
391 mStableDeviceValues.saveToXml(serializer);
392 serializer.endTag(null, "stable-device-values");
Jeff Brown89d55462012-09-19 11:33:42 -0700393 serializer.endTag(null, "display-manager-state");
394 serializer.endDocument();
395 }
Michael Wright1c9977b2016-07-12 13:30:10 -0700396
397 public void dump(PrintWriter pw) {
398 pw.println("PersistentDataStore");
399 pw.println(" mLoaded=" + mLoaded);
400 pw.println(" mDirty=" + mDirty);
401 pw.println(" RememberedWifiDisplays:");
402 int i = 0;
403 for (WifiDisplay display : mRememberedWifiDisplays) {
404 pw.println(" " + i++ + ": " + display);
405 }
406 pw.println(" DisplayStates:");
407 i = 0;
408 for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
409 pw.println(" " + i++ + ": " + entry.getKey());
410 entry.getValue().dump(pw, " ");
411 }
Michael Wrighteedcbf12017-08-16 23:14:54 +0100412 pw.println(" StableDeviceValues:");
413 mStableDeviceValues.dump(pw, " ");
Michael Wright1c9977b2016-07-12 13:30:10 -0700414 }
415
416 private static final class DisplayState {
417 private int mColorMode;
418
419 public boolean setColorMode(int colorMode) {
420 if (colorMode == mColorMode) {
421 return false;
422 }
423 mColorMode = colorMode;
424 return true;
425 }
426
427 public int getColorMode() {
428 return mColorMode;
429 }
430
431 public void loadFromXml(XmlPullParser parser)
432 throws IOException, XmlPullParserException {
433 final int outerDepth = parser.getDepth();
434
435 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
436 if (parser.getName().equals("color-mode")) {
437 String value = parser.nextText();
438 mColorMode = Integer.parseInt(value);
439 }
440 }
441 }
442
443 public void saveToXml(XmlSerializer serializer) throws IOException {
444 serializer.startTag(null, "color-mode");
445 serializer.text(Integer.toString(mColorMode));
446 serializer.endTag(null, "color-mode");
447 }
448
Michael Wrighteedcbf12017-08-16 23:14:54 +0100449 public void dump(final PrintWriter pw, final String prefix) {
Michael Wright1c9977b2016-07-12 13:30:10 -0700450 pw.println(prefix + "ColorMode=" + mColorMode);
451 }
452 }
Michael Wrighteedcbf12017-08-16 23:14:54 +0100453
454 private static final class StableDeviceValues {
455 private int mWidth;
456 private int mHeight;
457
458 private Point getDisplaySize() {
459 return new Point(mWidth, mHeight);
460 }
461
462 public boolean setDisplaySize(Point r) {
463 if (mWidth != r.x || mHeight != r.y) {
464 mWidth = r.x;
465 mHeight = r.y;
466 return true;
467 }
468 return false;
469 }
470
471 public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
472 final int outerDepth = parser.getDepth();
473 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
474 switch (parser.getName()) {
475 case "stable-display-width":
476 mWidth = loadIntValue(parser);
477 break;
478 case "stable-display-height":
479 mHeight = loadIntValue(parser);
480 break;
481 }
482 }
483 }
484
485 private static int loadIntValue(XmlPullParser parser)
486 throws IOException, XmlPullParserException {
487 try {
488 String value = parser.nextText();
489 return Integer.parseInt(value);
490 } catch (NumberFormatException nfe) {
491 return 0;
492 }
493 }
494
495 public void saveToXml(XmlSerializer serializer) throws IOException {
496 if (mWidth > 0 && mHeight > 0) {
497 serializer.startTag(null, "stable-display-width");
498 serializer.text(Integer.toString(mWidth));
499 serializer.endTag(null, "stable-display-width");
500 serializer.startTag(null, "stable-display-height");
501 serializer.text(Integer.toString(mHeight));
502 serializer.endTag(null, "stable-display-height");
503 }
504 }
505
506 public void dump(final PrintWriter pw, final String prefix) {
507 pw.println(prefix + "StableDisplayWidth=" + mWidth);
508 pw.println(prefix + "StableDisplayHeight=" + mHeight);
509 }
510 }
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100511}