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 * <display-manager-state> 50 * <remembered-wifi-displays> 51 * <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" /> 52 * >remembered-wifi-displays> 53 * >/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}