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