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(),
    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 }