1 /*
2  * Copyright (C) 2013 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.wm;
18 
19 import android.graphics.Rect;
20 import android.os.Environment;
21 import android.util.AtomicFile;
22 import android.util.Slog;
23 import android.util.Xml;
24 import com.android.internal.util.FastXmlSerializer;
25 import com.android.internal.util.XmlUtils;
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 import org.xmlpull.v1.XmlSerializer;
29 
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.FileNotFoundException;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.nio.charset.StandardCharsets;
36 import java.util.HashMap;
37 
38 /**
39  * Current persistent settings about a display
40  */
41 public class DisplaySettings {
42     private static final String TAG = WindowManagerService.TAG;
43 
44     private final AtomicFile mFile;
45     private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
46 
47     public static class Entry {
48         public final String name;
49         public int overscanLeft;
50         public int overscanTop;
51         public int overscanRight;
52         public int overscanBottom;
53 
Entry(String _name)54         public Entry(String _name) {
55             name = _name;
56         }
57     }
58 
DisplaySettings()59     public DisplaySettings() {
60         File dataDir = Environment.getDataDirectory();
61         File systemDir = new File(dataDir, "system");
62         mFile = new AtomicFile(new File(systemDir, "display_settings.xml"));
63     }
64 
getOverscanLocked(String name, String uniqueId, Rect outRect)65     public void getOverscanLocked(String name, String uniqueId, Rect outRect) {
66         // Try to get the entry with the unique if possible.
67         // Else, fall back on the display name.
68         Entry entry;
69         if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
70             entry = mEntries.get(name);
71         }
72         if (entry != null) {
73             outRect.left = entry.overscanLeft;
74             outRect.top = entry.overscanTop;
75             outRect.right = entry.overscanRight;
76             outRect.bottom = entry.overscanBottom;
77         } else {
78             outRect.set(0, 0, 0, 0);
79         }
80     }
81 
setOverscanLocked(String name, int left, int top, int right, int bottom)82     public void setOverscanLocked(String name, int left, int top, int right, int bottom) {
83         if (left == 0 && top == 0 && right == 0 && bottom == 0) {
84             // Right now all we are storing is overscan; if there is no overscan,
85             // we have no need for the entry.
86             mEntries.remove(name);
87             return;
88         }
89         Entry entry = mEntries.get(name);
90         if (entry == null) {
91             entry = new Entry(name);
92             mEntries.put(name, entry);
93         }
94         entry.overscanLeft = left;
95         entry.overscanTop = top;
96         entry.overscanRight = right;
97         entry.overscanBottom = bottom;
98     }
99 
readSettingsLocked()100     public void readSettingsLocked() {
101         FileInputStream stream;
102         try {
103             stream = mFile.openRead();
104         } catch (FileNotFoundException e) {
105             Slog.i(TAG, "No existing display settings " + mFile.getBaseFile()
106                     + "; starting empty");
107             return;
108         }
109         boolean success = false;
110         try {
111             XmlPullParser parser = Xml.newPullParser();
112             parser.setInput(stream, StandardCharsets.UTF_8.name());
113             int type;
114             while ((type = parser.next()) != XmlPullParser.START_TAG
115                     && type != XmlPullParser.END_DOCUMENT) {
116                 // Do nothing.
117             }
118 
119             if (type != XmlPullParser.START_TAG) {
120                 throw new IllegalStateException("no start tag found");
121             }
122 
123             int outerDepth = parser.getDepth();
124             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
125                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
126                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
127                     continue;
128                 }
129 
130                 String tagName = parser.getName();
131                 if (tagName.equals("display")) {
132                     readDisplay(parser);
133                 } else {
134                     Slog.w(TAG, "Unknown element under <display-settings>: "
135                             + parser.getName());
136                     XmlUtils.skipCurrentTag(parser);
137                 }
138             }
139             success = true;
140         } catch (IllegalStateException e) {
141             Slog.w(TAG, "Failed parsing " + e);
142         } catch (NullPointerException e) {
143             Slog.w(TAG, "Failed parsing " + e);
144         } catch (NumberFormatException e) {
145             Slog.w(TAG, "Failed parsing " + e);
146         } catch (XmlPullParserException e) {
147             Slog.w(TAG, "Failed parsing " + e);
148         } catch (IOException e) {
149             Slog.w(TAG, "Failed parsing " + e);
150         } catch (IndexOutOfBoundsException e) {
151             Slog.w(TAG, "Failed parsing " + e);
152         } finally {
153             if (!success) {
154                 mEntries.clear();
155             }
156             try {
157                 stream.close();
158             } catch (IOException e) {
159             }
160         }
161     }
162 
getIntAttribute(XmlPullParser parser, String name)163     private int getIntAttribute(XmlPullParser parser, String name) {
164         try {
165             String str = parser.getAttributeValue(null, name);
166             return str != null ? Integer.parseInt(str) : 0;
167         } catch (NumberFormatException e) {
168             return 0;
169         }
170     }
171 
readDisplay(XmlPullParser parser)172     private void readDisplay(XmlPullParser parser) throws NumberFormatException,
173             XmlPullParserException, IOException {
174         String name = parser.getAttributeValue(null, "name");
175         if (name != null) {
176             Entry entry = new Entry(name);
177             entry.overscanLeft = getIntAttribute(parser, "overscanLeft");
178             entry.overscanTop = getIntAttribute(parser, "overscanTop");
179             entry.overscanRight = getIntAttribute(parser, "overscanRight");
180             entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
181             mEntries.put(name, entry);
182         }
183         XmlUtils.skipCurrentTag(parser);
184     }
185 
writeSettingsLocked()186     public void writeSettingsLocked() {
187         FileOutputStream stream;
188         try {
189             stream = mFile.startWrite();
190         } catch (IOException e) {
191             Slog.w(TAG, "Failed to write display settings: " + e);
192             return;
193         }
194 
195         try {
196             XmlSerializer out = new FastXmlSerializer();
197             out.setOutput(stream, StandardCharsets.UTF_8.name());
198             out.startDocument(null, true);
199             out.startTag(null, "display-settings");
200 
201             for (Entry entry : mEntries.values()) {
202                 out.startTag(null, "display");
203                 out.attribute(null, "name", entry.name);
204                 if (entry.overscanLeft != 0) {
205                     out.attribute(null, "overscanLeft", Integer.toString(entry.overscanLeft));
206                 }
207                 if (entry.overscanTop != 0) {
208                     out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop));
209                 }
210                 if (entry.overscanRight != 0) {
211                     out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight));
212                 }
213                 if (entry.overscanBottom != 0) {
214                     out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
215                 }
216                 out.endTag(null, "display");
217             }
218 
219             out.endTag(null, "display-settings");
220             out.endDocument();
221             mFile.finishWrite(stream);
222         } catch (IOException e) {
223             Slog.w(TAG, "Failed to write display settings, restoring backup.", e);
224             mFile.failWrite(stream);
225         }
226     }
227 }
228