blob: 105c2534c574017e9659abb3133f18222d58afe6 [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
Jeff Brown74da1092012-11-07 16:02:13 -080084 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
Jeff Brown89d55462012-09-19 11:33:42 -070093 public WifiDisplay[] getRememberedWifiDisplays() {
94 loadIfNeeded();
95 return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
96 }
97
98 public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) {
99 if (display != null) {
100 loadIfNeeded();
101
Jeff Brownc2b9ea62012-09-19 23:09:23 -0700102 String alias = null;
Jeff Brown89d55462012-09-19 11:33:42 -0700103 int index = findRememberedWifiDisplay(display.getDeviceAddress());
104 if (index >= 0) {
Jeff Brownc2b9ea62012-09-19 23:09:23 -0700105 alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
106 }
107 if (!Objects.equal(display.getDeviceAlias(), alias)) {
108 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias);
Jeff Brown89d55462012-09-19 11:33:42 -0700109 }
110 }
111 return display;
112 }
113
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
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
Jeff Brown89d55462012-09-19 11:33:42 -0700149 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
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
169 private void loadIfNeeded() {
170 if (!mLoaded) {
171 load();
172 mLoaded = true;
173 }
174 }
175
176 private void setDirty() {
177 mDirty = true;
178 }
179
180 private void clearState() {
181 mRememberedWifiDisplays.clear();
182 }
183
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
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
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
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
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}