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