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