blob: 3a6e1a6948a4ebc1af47ac68b1412ebd5ca6d512 [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
26import android.hardware.display.WifiDisplay;
27import android.util.AtomicFile;
28import android.util.Slog;
29import android.util.Xml;
30
31import java.io.BufferedInputStream;
32import java.io.BufferedOutputStream;
33import java.io.File;
34import java.io.FileNotFoundException;
35import java.io.FileOutputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.util.ArrayList;
39
40import libcore.io.IoUtils;
41import 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 */
58final 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
73 public PersistentDataStore() {
74 mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
75 }
76
77 public void saveIfNeeded() {
78 if (mDirty) {
79 save();
80 mDirty = false;
81 }
82 }
83
84 public WifiDisplay[] getRememberedWifiDisplays() {
85 loadIfNeeded();
86 return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
87 }
88
89 public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
90 if (display != null) {
91 loadIfNeeded();
92
Jeff Brownc2b9ea62012-09-19 23:09:23 -070093 String alias = null;
Jeff Brown89d55462012-09-19 11:33:42 -070094 int index = findRememberedWifiDisplay(display.getDeviceAddress());
95 if (index >= 0) {
Jeff Brownc2b9ea62012-09-19 23:09:23 -070096 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
97 }
98 if (!Objects.equal(display.getDeviceAlias(), alias)) {
99 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias);
Jeff Brown89d55462012-09-19 11:33:42 -0700100 }
101 }
102 return display;
103 }
104
105 public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) {
106 WifiDisplay[] results = displays;
107 if (results != null) {
108 int count = displays.length;
109 for (int i = 0; i < count; i++) {
110 WifiDisplay result = applyWifiDisplayAlias(displays[i]);
111 if (result != displays[i]) {
112 if (results == displays) {
113 results = new WifiDisplay[count];
114 System.arraycopy(displays, 0, results, 0, count);
115 }
116 results[i] = result;
117 }
118 }
119 }
120 return results;
121 }
122
123 public boolean rememberWifiDisplay(WifiDisplay display) {
124 loadIfNeeded();
125
126 int index = findRememberedWifiDisplay(display.getDeviceAddress());
127 if (index >= 0) {
128 WifiDisplay other = mRememberedWifiDisplays.get(index);
129 if (other.equals(display)) {
130 return false; // already remembered without change
131 }
132 mRememberedWifiDisplays.set(index, display);
133 } else {
134 mRememberedWifiDisplays.add(display);
135 }
136 setDirty();
137 return true;
138 }
139
140 public boolean renameWifiDisplay(String deviceAddress, String alias) {
141 int index = findRememberedWifiDisplay(deviceAddress);
142 if (index >= 0) {
143 WifiDisplay display = mRememberedWifiDisplays.get(index);
144 if (Objects.equal(display.getDeviceAlias(), alias)) {
145 return false; // already has this alias
146 }
147 WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress,
148 display.getDeviceName(), alias);
149 mRememberedWifiDisplays.set(index, renamedDisplay);
150 setDirty();
151 return true;
152 }
153 return false;
154 }
155
156 public boolean forgetWifiDisplay(String deviceAddress) {
157 int index = findRememberedWifiDisplay(deviceAddress);
158 if (index >= 0) {
159 mRememberedWifiDisplays.remove(index);
160 setDirty();
161 return true;
162 }
163 return false;
164 }
165
166 private int findRememberedWifiDisplay(String deviceAddress) {
167 int count = mRememberedWifiDisplays.size();
168 for (int i = 0; i < count; i++) {
169 if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) {
170 return i;
171 }
172 }
173 return -1;
174 }
175
176 private void loadIfNeeded() {
177 if (!mLoaded) {
178 load();
179 mLoaded = true;
180 }
181 }
182
183 private void setDirty() {
184 mDirty = true;
185 }
186
187 private void clearState() {
188 mRememberedWifiDisplays.clear();
189 }
190
191 private void load() {
192 clearState();
193
194 final InputStream is;
195 try {
196 is = mAtomicFile.openRead();
197 } catch (FileNotFoundException ex) {
198 return;
199 }
200
201 XmlPullParser parser;
202 try {
203 parser = Xml.newPullParser();
204 parser.setInput(new BufferedInputStream(is), null);
205 loadFromXml(parser);
206 } catch (IOException ex) {
207 Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
208 clearState();
209 } catch (XmlPullParserException ex) {
210 Slog.w(TAG, "Failed to load display manager persistent store data.", ex);
211 clearState();
212 } finally {
213 IoUtils.closeQuietly(is);
214 }
215 }
216
217 private void save() {
218 final FileOutputStream os;
219 try {
220 os = mAtomicFile.startWrite();
221 boolean success = false;
222 try {
223 XmlSerializer serializer = new FastXmlSerializer();
224 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
225 saveToXml(serializer);
226 serializer.flush();
227 success = true;
228 } finally {
229 if (success) {
230 mAtomicFile.finishWrite(os);
231 } else {
232 mAtomicFile.failWrite(os);
233 }
234 }
235 } catch (IOException ex) {
236 Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
237 }
238 }
239
240 private void loadFromXml(XmlPullParser parser)
241 throws IOException, XmlPullParserException {
242 XmlUtils.beginDocument(parser, "display-manager-state");
243 final int outerDepth = parser.getDepth();
244 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
245 if (parser.getName().equals("remembered-wifi-displays")) {
246 loadRememberedWifiDisplaysFromXml(parser);
247 }
248 }
249 }
250
251 private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
252 throws IOException, XmlPullParserException {
253 final int outerDepth = parser.getDepth();
254 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
255 if (parser.getName().equals("wifi-display")) {
256 String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
257 String deviceName = parser.getAttributeValue(null, "deviceName");
258 String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
259 if (deviceAddress == null || deviceName == null) {
260 throw new XmlPullParserException(
261 "Missing deviceAddress or deviceName attribute on wifi-display.");
262 }
263 if (findRememberedWifiDisplay(deviceAddress) >= 0) {
264 throw new XmlPullParserException(
265 "Found duplicate wifi display device address.");
266 }
267
268 mRememberedWifiDisplays.add(
269 new WifiDisplay(deviceAddress, deviceName, deviceAlias));
270 }
271 }
272 }
273
274 private void saveToXml(XmlSerializer serializer) throws IOException {
275 serializer.startDocument(null, true);
276 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
277 serializer.startTag(null, "display-manager-state");
278 serializer.startTag(null, "remembered-wifi-displays");
279 for (WifiDisplay display : mRememberedWifiDisplays) {
280 serializer.startTag(null, "wifi-display");
281 serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
282 serializer.attribute(null, "deviceName", display.getDeviceName());
283 if (display.getDeviceAlias() != null) {
284 serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
285 }
286 serializer.endTag(null, "wifi-display");
287 }
288 serializer.endTag(null, "remembered-wifi-displays");
289 serializer.endTag(null, "display-manager-state");
290 serializer.endDocument();
291 }
292}