• 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 
31 import java.io.BufferedInputStream;
32 import java.io.BufferedOutputStream;
33 import java.io.File;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.ArrayList;
39 
40 import libcore.io.IoUtils;
41 import libcore.util.Objects;
42 
43 /**
44  * Manages persistent state recorded by the display manager service as an XML file.
45  * Caller must acquire lock on the data store before accessing it.
46  *
47  * File format:
48  * <code>
49  * &lt;display-manager-state>
50  *   &lt;remembered-wifi-displays>
51  *     &lt;wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
52  *   &gt;remembered-wifi-displays>
53  * &gt;/display-manager-state>
54  * </code>
55  *
56  * TODO: refactor this to extract common code shared with the input manager's data store
57  */
58 final class PersistentDataStore {
59     static final String TAG = "DisplayManager";
60 
61     // Remembered Wifi display devices.
62     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
63 
64     // The atomic file used to safely read or write the file.
65     private final AtomicFile mAtomicFile;
66 
67     // True if the data has been loaded.
68     private boolean mLoaded;
69 
70     // True if there are changes to be saved.
71     private boolean mDirty;
72 
PersistentDataStore()73     public PersistentDataStore() {
74         mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
75     }
76 
saveIfNeeded()77     public void saveIfNeeded() {
78         if (mDirty) {
79             save();
80             mDirty = false;
81         }
82     }
83 
getRememberedWifiDisplay(String deviceAddress)84     public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
85         loadIfNeeded();
86         int index = findRememberedWifiDisplay(deviceAddress);
87         if (index >= 0) {
88             return mRememberedWifiDisplays.get(index);
89         }
90         return null;
91     }
92 
getRememberedWifiDisplays()93     public WifiDisplay[] getRememberedWifiDisplays() {
94         loadIfNeeded();
95         return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
96     }
97 
applyWifiDisplayAlias(WifiDisplay display)98     public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
99         if (display != null) {
100             loadIfNeeded();
101 
102             String alias = null;
103             int index = findRememberedWifiDisplay(display.getDeviceAddress());
104             if (index >= 0) {
105                 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
106             }
107             if (!Objects.equal(display.getDeviceAlias(), alias)) {
108                 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias);
109             }
110         }
111         return display;
112     }
113 
applyWifiDisplayAliases(WifiDisplay[] displays)114     public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
115         WifiDisplay[] results = displays;
116         if (results != null) {
117             int count = displays.length;
118             for (int i = 0; i < count; i++) {
119                 WifiDisplay result = applyWifiDisplayAlias(displays[i]);
120                 if (result != displays[i]) {
121                     if (results == displays) {
122                         results = new WifiDisplay[count];
123                         System.arraycopy(displays, 0, results, 0, count);
124                     }
125                     results[i] = result;
126                 }
127             }
128         }
129         return results;
130     }
131 
rememberWifiDisplay(WifiDisplay display)132     public boolean rememberWifiDisplay(WifiDisplay display) {
133         loadIfNeeded();
134 
135         int index = findRememberedWifiDisplay(display.getDeviceAddress());
136         if (index >= 0) {
137             WifiDisplay other = mRememberedWifiDisplays.get(index);
138             if (other.equals(display)) {
139                 return false; // already remembered without change
140             }
141             mRememberedWifiDisplays.set(index, display);
142         } else {
143             mRememberedWifiDisplays.add(display);
144         }
145         setDirty();
146         return true;
147     }
148 
forgetWifiDisplay(String deviceAddress)149     public boolean forgetWifiDisplay(String deviceAddress) {
150         int index = findRememberedWifiDisplay(deviceAddress);
151         if (index >= 0) {
152             mRememberedWifiDisplays.remove(index);
153             setDirty();
154             return true;
155         }
156         return false;
157     }
158 
findRememberedWifiDisplay(String deviceAddress)159     private int findRememberedWifiDisplay(String deviceAddress) {
160         int count = mRememberedWifiDisplays.size();
161         for (int i = 0; i < count; i++) {
162             if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
163                 return i;
164             }
165         }
166         return -1;
167     }
168 
loadIfNeeded()169     private void loadIfNeeded() {
170         if (!mLoaded) {
171             load();
172             mLoaded = true;
173         }
174     }
175 
setDirty()176     private void setDirty() {
177         mDirty = true;
178     }
179 
clearState()180     private void clearState() {
181         mRememberedWifiDisplays.clear();
182     }
183 
load()184     private void load() {
185         clearState();
186 
187         final InputStream is;
188         try {
189             is = mAtomicFile.openRead();
190         } catch (FileNotFoundException ex) {
191             return;
192         }
193 
194         XmlPullParser parser;
195         try {
196             parser = Xml.newPullParser();
197             parser.setInput(new BufferedInputStream(is), null);
198             loadFromXml(parser);
199         } catch (IOException ex) {
200             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
201             clearState();
202         } catch (XmlPullParserException ex) {
203             Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
204             clearState();
205         } finally {
206             IoUtils.closeQuietly(is);
207         }
208     }
209 
save()210     private void save() {
211         final FileOutputStream os;
212         try {
213             os = mAtomicFile.startWrite();
214             boolean success = false;
215             try {
216                 XmlSerializer serializer = new FastXmlSerializer();
217                 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
218                 saveToXml(serializer);
219                 serializer.flush();
220                 success = true;
221             } finally {
222                 if (success) {
223                     mAtomicFile.finishWrite(os);
224                 } else {
225                     mAtomicFile.failWrite(os);
226                 }
227             }
228         } catch (IOException ex) {
229             Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
230         }
231     }
232 
loadFromXml(XmlPullParser parser)233     private void loadFromXml(XmlPullParser parser)
234             throws IOException, XmlPullParserException {
235         XmlUtils.beginDocument(parser, "display-manager-state");
236         final int outerDepth = parser.getDepth();
237         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
238             if (parser.getName().equals("remembered-wifi-displays")) {
239                 loadRememberedWifiDisplaysFromXml(parser);
240             }
241         }
242     }
243 
loadRememberedWifiDisplaysFromXml(XmlPullParser parser)244     private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
245             throws IOException, XmlPullParserException {
246         final int outerDepth = parser.getDepth();
247         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
248             if (parser.getName().equals("wifi-display")) {
249                 String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
250                 String deviceName = parser.getAttributeValue(null, "deviceName");
251                 String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
252                 if (deviceAddress == null || deviceName == null) {
253                     throw new XmlPullParserException(
254                             "Missing deviceAddress or deviceName attribute on wifi-display.");
255                 }
256                 if (findRememberedWifiDisplay(deviceAddress) >= 0) {
257                     throw new XmlPullParserException(
258                             "Found duplicate wifi display device address.");
259                 }
260 
261                 mRememberedWifiDisplays.add(
262                         new WifiDisplay(deviceAddress, deviceName, deviceAlias));
263             }
264         }
265     }
266 
saveToXml(XmlSerializer serializer)267     private void saveToXml(XmlSerializer serializer) throws IOException {
268         serializer.startDocument(null, true);
269         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
270         serializer.startTag(null, "display-manager-state");
271         serializer.startTag(null, "remembered-wifi-displays");
272         for (WifiDisplay display : mRememberedWifiDisplays) {
273             serializer.startTag(null, "wifi-display");
274             serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
275             serializer.attribute(null, "deviceName", display.getDeviceName());
276             if (display.getDeviceAlias() != null) {
277                 serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
278             }
279             serializer.endTag(null, "wifi-display");
280         }
281         serializer.endTag(null, "remembered-wifi-displays");
282         serializer.endTag(null, "display-manager-state");
283         serializer.endDocument();
284     }
285 }