Home | History | Annotate | Download | only in display
      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  * &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  */
     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 }