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