PersistentDataStore.java revision c2b9ea624148df80945afad4198fe686a0ab8dca
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
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
93            String alias = null;
94            int index = findRememberedWifiDisplay(display.getDeviceAddress());
95            if (index >= 0) {
96                alias = mRememberedWifiDisplays.get(index).getDeviceAlias();
97            }
98            if (!Objects.equal(display.getDeviceAlias(), alias)) {
99                return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias);
100            }
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}