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 17 package com.android.server.display; 18 19 import com.android.internal.util.FastXmlSerializer; 20 import com.android.internal.util.XmlUtils; 21 22 import org.xmlpull.v1.XmlPullParser; 23 import org.xmlpull.v1.XmlPullParserException; 24 import org.xmlpull.v1.XmlSerializer; 25 26 import android.hardware.display.WifiDisplay; 27 import android.util.AtomicFile; 28 import android.util.Slog; 29 import android.util.Xml; 30 31 import java.io.BufferedInputStream; 32 import java.io.BufferedOutputStream; 33 import java.io.File; 34 import java.io.FileNotFoundException; 35 import java.io.FileOutputStream; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.util.ArrayList; 39 40 import libcore.io.IoUtils; 41 import 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 */ 58 final 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 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 93 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 102 String alias = null; 103 int index = findRememberedWifiDisplay(display.getDeviceAddress()); 104 if (index >= 0) { 105 alias = mRememberedWifiDisplays.get(index).getDeviceAlias(); 106 } 107 if (!Objects.equal(display.getDeviceAlias(), alias)) { 108 return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias); 109 } 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 149 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 }