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 android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; 20 import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; 21 import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; 22 23 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO; 24 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED; 25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 27 28 import android.annotation.IntDef; 29 import android.annotation.Nullable; 30 import android.app.WindowConfiguration; 31 import android.os.Environment; 32 import android.os.FileUtils; 33 import android.provider.Settings; 34 import android.util.AtomicFile; 35 import android.util.Slog; 36 import android.util.Xml; 37 import android.view.Display; 38 import android.view.DisplayAddress; 39 import android.view.DisplayInfo; 40 import android.view.IWindowManager; 41 import android.view.Surface; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.util.FastXmlSerializer; 45 import com.android.internal.util.XmlUtils; 46 import com.android.server.policy.WindowManagerPolicy; 47 import com.android.server.wm.DisplayContent.ForceScalingMode; 48 49 import org.xmlpull.v1.XmlPullParser; 50 import org.xmlpull.v1.XmlPullParserException; 51 import org.xmlpull.v1.XmlSerializer; 52 53 import java.io.File; 54 import java.io.FileNotFoundException; 55 import java.io.FileOutputStream; 56 import java.io.IOException; 57 import java.io.InputStream; 58 import java.io.OutputStream; 59 import java.nio.charset.StandardCharsets; 60 import java.util.HashMap; 61 62 /** 63 * Current persistent settings about a display 64 */ 65 class DisplayWindowSettings { 66 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM; 67 68 private static final String SYSTEM_DIRECTORY = "system"; 69 private static final String DISPLAY_SETTINGS_FILE_NAME = "display_settings.xml"; 70 private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/" + DISPLAY_SETTINGS_FILE_NAME; 71 private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays"; 72 73 private static final int IDENTIFIER_UNIQUE_ID = 0; 74 private static final int IDENTIFIER_PORT = 1; 75 @IntDef(prefix = { "IDENTIFIER_" }, value = { 76 IDENTIFIER_UNIQUE_ID, 77 IDENTIFIER_PORT, 78 }) 79 @interface DisplayIdentifierType {} 80 81 private final WindowManagerService mService; 82 private final HashMap<String, Entry> mEntries = new HashMap<>(); 83 private final SettingPersister mStorage; 84 85 /** 86 * The preferred type of a display identifier to use when storing and retrieving entries. 87 * {@link #getIdentifier(DisplayInfo)} must be used to get current preferred identifier for each 88 * display. It will fall back to using {@link #IDENTIFIER_UNIQUE_ID} if the currently selected 89 * one is not applicable to a particular display. 90 */ 91 @DisplayIdentifierType 92 private int mIdentifier = IDENTIFIER_UNIQUE_ID; 93 94 /** Interface for persisting the display window settings. */ 95 interface SettingPersister { openRead()96 InputStream openRead() throws IOException; startWrite()97 OutputStream startWrite() throws IOException; finishWrite(OutputStream os, boolean success)98 void finishWrite(OutputStream os, boolean success); 99 } 100 101 private static class Entry { 102 private final String mName; 103 private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED; 104 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; 105 private int mUserRotation = Surface.ROTATION_0; 106 private int mForcedWidth; 107 private int mForcedHeight; 108 private int mForcedDensity; 109 private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO; 110 private int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED; 111 private boolean mShouldShowWithInsecureKeyguard = false; 112 private boolean mShouldShowSystemDecors = false; 113 private boolean mShouldShowIme = false; 114 private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; 115 Entry(String name)116 private Entry(String name) { 117 mName = name; 118 } 119 Entry(String name, Entry copyFrom)120 private Entry(String name, Entry copyFrom) { 121 this(name); 122 mWindowingMode = copyFrom.mWindowingMode; 123 mUserRotationMode = copyFrom.mUserRotationMode; 124 mUserRotation = copyFrom.mUserRotation; 125 mForcedWidth = copyFrom.mForcedWidth; 126 mForcedHeight = copyFrom.mForcedHeight; 127 mForcedDensity = copyFrom.mForcedDensity; 128 mForcedScalingMode = copyFrom.mForcedScalingMode; 129 mRemoveContentMode = copyFrom.mRemoveContentMode; 130 mShouldShowWithInsecureKeyguard = copyFrom.mShouldShowWithInsecureKeyguard; 131 mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors; 132 mShouldShowIme = copyFrom.mShouldShowIme; 133 mFixedToUserRotation = copyFrom.mFixedToUserRotation; 134 } 135 136 /** @return {@code true} if all values are default. */ isEmpty()137 private boolean isEmpty() { 138 return mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED 139 && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE 140 && mUserRotation == Surface.ROTATION_0 141 && mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0 142 && mForcedScalingMode == FORCE_SCALING_MODE_AUTO 143 && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED 144 && !mShouldShowWithInsecureKeyguard 145 && !mShouldShowSystemDecors 146 && !mShouldShowIme 147 && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; 148 } 149 } 150 DisplayWindowSettings(WindowManagerService service)151 DisplayWindowSettings(WindowManagerService service) { 152 this(service, new AtomicFileStorage()); 153 } 154 155 @VisibleForTesting DisplayWindowSettings(WindowManagerService service, SettingPersister storageImpl)156 DisplayWindowSettings(WindowManagerService service, SettingPersister storageImpl) { 157 mService = service; 158 mStorage = storageImpl; 159 readSettings(); 160 } 161 getEntry(DisplayInfo displayInfo)162 private @Nullable Entry getEntry(DisplayInfo displayInfo) { 163 final String identifier = getIdentifier(displayInfo); 164 Entry entry; 165 // Try to get corresponding entry using preferred identifier for the current config. 166 if ((entry = mEntries.get(identifier)) != null) { 167 return entry; 168 } 169 // Else, fall back to the display name. 170 if ((entry = mEntries.get(displayInfo.name)) != null) { 171 // Found an entry stored with old identifier - upgrade to the new type now. 172 return updateIdentifierForEntry(entry, displayInfo); 173 } 174 return null; 175 } 176 getOrCreateEntry(DisplayInfo displayInfo)177 private Entry getOrCreateEntry(DisplayInfo displayInfo) { 178 final Entry entry = getEntry(displayInfo); 179 return entry != null ? entry : new Entry(getIdentifier(displayInfo)); 180 } 181 182 /** 183 * Upgrades the identifier of a legacy entry. Does it by copying the data from the old record 184 * and clearing the old key in memory. The entry will be written to storage next time when a 185 * setting changes. 186 */ updateIdentifierForEntry(Entry entry, DisplayInfo displayInfo)187 private Entry updateIdentifierForEntry(Entry entry, DisplayInfo displayInfo) { 188 final Entry newEntry = new Entry(getIdentifier(displayInfo), entry); 189 removeEntry(displayInfo); 190 mEntries.put(newEntry.mName, newEntry); 191 return newEntry; 192 } 193 setUserRotation(DisplayContent displayContent, int rotationMode, int rotation)194 void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) { 195 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 196 final Entry entry = getOrCreateEntry(displayInfo); 197 entry.mUserRotationMode = rotationMode; 198 entry.mUserRotation = rotation; 199 writeSettingsIfNeeded(entry, displayInfo); 200 } 201 setForcedSize(DisplayContent displayContent, int width, int height)202 void setForcedSize(DisplayContent displayContent, int width, int height) { 203 if (displayContent.isDefaultDisplay) { 204 final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height); 205 Settings.Global.putString(mService.mContext.getContentResolver(), 206 Settings.Global.DISPLAY_SIZE_FORCED, sizeString); 207 return; 208 } 209 210 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 211 final Entry entry = getOrCreateEntry(displayInfo); 212 entry.mForcedWidth = width; 213 entry.mForcedHeight = height; 214 writeSettingsIfNeeded(entry, displayInfo); 215 } 216 setForcedDensity(DisplayContent displayContent, int density, int userId)217 void setForcedDensity(DisplayContent displayContent, int density, int userId) { 218 if (displayContent.isDefaultDisplay) { 219 final String densityString = density == 0 ? "" : Integer.toString(density); 220 Settings.Secure.putStringForUser(mService.mContext.getContentResolver(), 221 Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId); 222 return; 223 } 224 225 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 226 final Entry entry = getOrCreateEntry(displayInfo); 227 entry.mForcedDensity = density; 228 writeSettingsIfNeeded(entry, displayInfo); 229 } 230 setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode)231 void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) { 232 if (displayContent.isDefaultDisplay) { 233 Settings.Global.putInt(mService.mContext.getContentResolver(), 234 Settings.Global.DISPLAY_SCALING_FORCE, mode); 235 return; 236 } 237 238 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 239 final Entry entry = getOrCreateEntry(displayInfo); 240 entry.mForcedScalingMode = mode; 241 writeSettingsIfNeeded(entry, displayInfo); 242 } 243 setFixedToUserRotation(DisplayContent displayContent, int fixedToUserRotation)244 void setFixedToUserRotation(DisplayContent displayContent, int fixedToUserRotation) { 245 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 246 final Entry entry = getOrCreateEntry(displayInfo); 247 entry.mFixedToUserRotation = fixedToUserRotation; 248 writeSettingsIfNeeded(entry, displayInfo); 249 } 250 getWindowingModeLocked(Entry entry, DisplayContent dc)251 private int getWindowingModeLocked(Entry entry, DisplayContent dc) { 252 int windowingMode = entry != null ? entry.mWindowingMode 253 : WindowConfiguration.WINDOWING_MODE_UNDEFINED; 254 // This display used to be in freeform, but we don't support freeform anymore, so fall 255 // back to fullscreen. 256 if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM 257 && !mService.mAtmService.mSupportsFreeformWindowManagement) { 258 return WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 259 } 260 // No record is present so use default windowing mode policy. 261 if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { 262 windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement 263 && (mService.mIsPc || dc.forceDesktopMode()) 264 ? WindowConfiguration.WINDOWING_MODE_FREEFORM 265 : WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 266 } 267 return windowingMode; 268 } 269 getWindowingModeLocked(DisplayContent dc)270 int getWindowingModeLocked(DisplayContent dc) { 271 final DisplayInfo displayInfo = dc.getDisplayInfo(); 272 final Entry entry = getEntry(displayInfo); 273 return getWindowingModeLocked(entry, dc); 274 } 275 setWindowingModeLocked(DisplayContent dc, int mode)276 void setWindowingModeLocked(DisplayContent dc, int mode) { 277 final DisplayInfo displayInfo = dc.getDisplayInfo(); 278 final Entry entry = getOrCreateEntry(displayInfo); 279 entry.mWindowingMode = mode; 280 dc.setWindowingMode(mode); 281 writeSettingsIfNeeded(entry, displayInfo); 282 } 283 getRemoveContentModeLocked(DisplayContent dc)284 int getRemoveContentModeLocked(DisplayContent dc) { 285 final DisplayInfo displayInfo = dc.getDisplayInfo(); 286 final Entry entry = getEntry(displayInfo); 287 if (entry == null || entry.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) { 288 if (dc.isPrivate()) { 289 // For private displays by default content is destroyed on removal. 290 return REMOVE_CONTENT_MODE_DESTROY; 291 } 292 // For other displays by default content is moved to primary on removal. 293 return REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; 294 } 295 return entry.mRemoveContentMode; 296 } 297 setRemoveContentModeLocked(DisplayContent dc, int mode)298 void setRemoveContentModeLocked(DisplayContent dc, int mode) { 299 final DisplayInfo displayInfo = dc.getDisplayInfo(); 300 final Entry entry = getOrCreateEntry(displayInfo); 301 entry.mRemoveContentMode = mode; 302 writeSettingsIfNeeded(entry, displayInfo); 303 } 304 shouldShowWithInsecureKeyguardLocked(DisplayContent dc)305 boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) { 306 final DisplayInfo displayInfo = dc.getDisplayInfo(); 307 final Entry entry = getEntry(displayInfo); 308 if (entry == null) { 309 return false; 310 } 311 return entry.mShouldShowWithInsecureKeyguard; 312 } 313 setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow)314 void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) { 315 if (!dc.isPrivate() && shouldShow) { 316 Slog.e(TAG, "Public display can't be allowed to show content when locked"); 317 return; 318 } 319 320 final DisplayInfo displayInfo = dc.getDisplayInfo(); 321 final Entry entry = getOrCreateEntry(displayInfo); 322 entry.mShouldShowWithInsecureKeyguard = shouldShow; 323 writeSettingsIfNeeded(entry, displayInfo); 324 } 325 shouldShowSystemDecorsLocked(DisplayContent dc)326 boolean shouldShowSystemDecorsLocked(DisplayContent dc) { 327 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { 328 // For default display should show system decors. 329 return true; 330 } 331 332 final DisplayInfo displayInfo = dc.getDisplayInfo(); 333 final Entry entry = getEntry(displayInfo); 334 if (entry == null) { 335 return false; 336 } 337 return entry.mShouldShowSystemDecors; 338 } 339 setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow)340 void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) { 341 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) { 342 Slog.e(TAG, "Default display should show system decors"); 343 return; 344 } 345 346 final DisplayInfo displayInfo = dc.getDisplayInfo(); 347 final Entry entry = getOrCreateEntry(displayInfo); 348 entry.mShouldShowSystemDecors = shouldShow; 349 writeSettingsIfNeeded(entry, displayInfo); 350 } 351 shouldShowImeLocked(DisplayContent dc)352 boolean shouldShowImeLocked(DisplayContent dc) { 353 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { 354 // For default display should shows IME. 355 return true; 356 } 357 358 final DisplayInfo displayInfo = dc.getDisplayInfo(); 359 final Entry entry = getEntry(displayInfo); 360 if (entry == null) { 361 return false; 362 } 363 return entry.mShouldShowIme; 364 } 365 setShouldShowImeLocked(DisplayContent dc, boolean shouldShow)366 void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) { 367 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) { 368 Slog.e(TAG, "Default display should show IME"); 369 return; 370 } 371 372 final DisplayInfo displayInfo = dc.getDisplayInfo(); 373 final Entry entry = getOrCreateEntry(displayInfo); 374 entry.mShouldShowIme = shouldShow; 375 writeSettingsIfNeeded(entry, displayInfo); 376 } 377 applySettingsToDisplayLocked(DisplayContent dc)378 void applySettingsToDisplayLocked(DisplayContent dc) { 379 final DisplayInfo displayInfo = dc.getDisplayInfo(); 380 final Entry entry = getOrCreateEntry(displayInfo); 381 382 // Setting windowing mode first, because it may override overscan values later. 383 dc.setWindowingMode(getWindowingModeLocked(entry, dc)); 384 385 dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, 386 entry.mUserRotation, entry.mFixedToUserRotation); 387 388 if (entry.mForcedDensity != 0) { 389 dc.mBaseDisplayDensity = entry.mForcedDensity; 390 } 391 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) { 392 dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight, 393 dc.mBaseDisplayDensity); 394 } 395 dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED; 396 } 397 398 /** 399 * Updates settings for the given display after system features are loaded into window manager 400 * service, e.g. if this device is PC and if this device supports freeform. 401 * 402 * @param dc the given display. 403 * @return {@code true} if any settings for this display has changed; {@code false} if nothing 404 * changed. 405 */ updateSettingsForDisplay(DisplayContent dc)406 boolean updateSettingsForDisplay(DisplayContent dc) { 407 if (dc.getWindowingMode() != getWindowingModeLocked(dc)) { 408 // For the time being the only thing that may change is windowing mode, so just update 409 // that. 410 dc.setWindowingMode(getWindowingModeLocked(dc)); 411 return true; 412 } 413 return false; 414 } 415 readSettings()416 private void readSettings() { 417 InputStream stream; 418 try { 419 stream = mStorage.openRead(); 420 } catch (IOException e) { 421 Slog.i(TAG, "No existing display settings, starting empty"); 422 return; 423 } 424 boolean success = false; 425 try { 426 XmlPullParser parser = Xml.newPullParser(); 427 parser.setInput(stream, StandardCharsets.UTF_8.name()); 428 int type; 429 while ((type = parser.next()) != XmlPullParser.START_TAG 430 && type != XmlPullParser.END_DOCUMENT) { 431 // Do nothing. 432 } 433 434 if (type != XmlPullParser.START_TAG) { 435 throw new IllegalStateException("no start tag found"); 436 } 437 438 int outerDepth = parser.getDepth(); 439 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 440 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 441 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 442 continue; 443 } 444 445 String tagName = parser.getName(); 446 if (tagName.equals("display")) { 447 readDisplay(parser); 448 } else if (tagName.equals("config")) { 449 readConfig(parser); 450 } else { 451 Slog.w(TAG, "Unknown element under <display-settings>: " 452 + parser.getName()); 453 XmlUtils.skipCurrentTag(parser); 454 } 455 } 456 success = true; 457 } catch (IllegalStateException e) { 458 Slog.w(TAG, "Failed parsing " + e); 459 } catch (NullPointerException e) { 460 Slog.w(TAG, "Failed parsing " + e); 461 } catch (NumberFormatException e) { 462 Slog.w(TAG, "Failed parsing " + e); 463 } catch (XmlPullParserException e) { 464 Slog.w(TAG, "Failed parsing " + e); 465 } catch (IOException e) { 466 Slog.w(TAG, "Failed parsing " + e); 467 } catch (IndexOutOfBoundsException e) { 468 Slog.w(TAG, "Failed parsing " + e); 469 } finally { 470 if (!success) { 471 mEntries.clear(); 472 } 473 try { 474 stream.close(); 475 } catch (IOException e) { 476 } 477 } 478 } 479 getIntAttribute(XmlPullParser parser, String name)480 private int getIntAttribute(XmlPullParser parser, String name) { 481 return getIntAttribute(parser, name, 0 /* defaultValue */); 482 } 483 getIntAttribute(XmlPullParser parser, String name, int defaultValue)484 private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) { 485 try { 486 final String str = parser.getAttributeValue(null, name); 487 return str != null ? Integer.parseInt(str) : defaultValue; 488 } catch (NumberFormatException e) { 489 return defaultValue; 490 } 491 } 492 getBooleanAttribute(XmlPullParser parser, String name)493 private boolean getBooleanAttribute(XmlPullParser parser, String name) { 494 return getBooleanAttribute(parser, name, false /* defaultValue */); 495 } 496 getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue)497 private boolean getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue) { 498 try { 499 final String str = parser.getAttributeValue(null, name); 500 return str != null ? Boolean.parseBoolean(str) : defaultValue; 501 } catch (NumberFormatException e) { 502 return defaultValue; 503 } 504 } 505 readDisplay(XmlPullParser parser)506 private void readDisplay(XmlPullParser parser) throws NumberFormatException, 507 XmlPullParserException, IOException { 508 String name = parser.getAttributeValue(null, "name"); 509 if (name != null) { 510 Entry entry = new Entry(name); 511 entry.mWindowingMode = getIntAttribute(parser, "windowingMode", 512 WindowConfiguration.WINDOWING_MODE_UNDEFINED); 513 entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode", 514 WindowManagerPolicy.USER_ROTATION_FREE); 515 entry.mUserRotation = getIntAttribute(parser, "userRotation", 516 Surface.ROTATION_0); 517 entry.mForcedWidth = getIntAttribute(parser, "forcedWidth"); 518 entry.mForcedHeight = getIntAttribute(parser, "forcedHeight"); 519 entry.mForcedDensity = getIntAttribute(parser, "forcedDensity"); 520 entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode", 521 FORCE_SCALING_MODE_AUTO); 522 entry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode", 523 REMOVE_CONTENT_MODE_UNDEFINED); 524 entry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser, 525 "shouldShowWithInsecureKeyguard"); 526 entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); 527 entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); 528 entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation"); 529 mEntries.put(name, entry); 530 } 531 XmlUtils.skipCurrentTag(parser); 532 } 533 readConfig(XmlPullParser parser)534 private void readConfig(XmlPullParser parser) throws NumberFormatException, 535 XmlPullParserException, IOException { 536 mIdentifier = getIntAttribute(parser, "identifier"); 537 XmlUtils.skipCurrentTag(parser); 538 } 539 writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo)540 private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) { 541 if (changedEntry.isEmpty() && !removeEntry(displayInfo)) { 542 // The entry didn't exist so nothing is changed and no need to update the file. 543 return; 544 } 545 546 mEntries.put(getIdentifier(displayInfo), changedEntry); 547 writeSettings(); 548 } 549 writeSettings()550 private void writeSettings() { 551 OutputStream stream; 552 try { 553 stream = mStorage.startWrite(); 554 } catch (IOException e) { 555 Slog.w(TAG, "Failed to write display settings: " + e); 556 return; 557 } 558 559 try { 560 XmlSerializer out = new FastXmlSerializer(); 561 out.setOutput(stream, StandardCharsets.UTF_8.name()); 562 out.startDocument(null, true); 563 564 out.startTag(null, "display-settings"); 565 566 out.startTag(null, "config"); 567 out.attribute(null, "identifier", Integer.toString(mIdentifier)); 568 out.endTag(null, "config"); 569 570 for (Entry entry : mEntries.values()) { 571 out.startTag(null, "display"); 572 out.attribute(null, "name", entry.mName); 573 if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { 574 out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode)); 575 } 576 if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) { 577 out.attribute(null, "userRotationMode", 578 Integer.toString(entry.mUserRotationMode)); 579 } 580 if (entry.mUserRotation != Surface.ROTATION_0) { 581 out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation)); 582 } 583 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) { 584 out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth)); 585 out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight)); 586 } 587 if (entry.mForcedDensity != 0) { 588 out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity)); 589 } 590 if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) { 591 out.attribute(null, "forcedScalingMode", 592 Integer.toString(entry.mForcedScalingMode)); 593 } 594 if (entry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { 595 out.attribute(null, "removeContentMode", 596 Integer.toString(entry.mRemoveContentMode)); 597 } 598 if (entry.mShouldShowWithInsecureKeyguard) { 599 out.attribute(null, "shouldShowWithInsecureKeyguard", 600 Boolean.toString(entry.mShouldShowWithInsecureKeyguard)); 601 } 602 if (entry.mShouldShowSystemDecors) { 603 out.attribute(null, "shouldShowSystemDecors", 604 Boolean.toString(entry.mShouldShowSystemDecors)); 605 } 606 if (entry.mShouldShowIme) { 607 out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme)); 608 } 609 if (entry.mFixedToUserRotation != IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT) { 610 out.attribute(null, "fixedToUserRotation", 611 Integer.toString(entry.mFixedToUserRotation)); 612 } 613 out.endTag(null, "display"); 614 } 615 616 out.endTag(null, "display-settings"); 617 out.endDocument(); 618 mStorage.finishWrite(stream, true /* success */); 619 } catch (IOException e) { 620 Slog.w(TAG, "Failed to write display window settings.", e); 621 mStorage.finishWrite(stream, false /* success */); 622 } 623 } 624 625 /** 626 * Removes an entry from {@link #mEntries} cache. Looks up by new and previously used 627 * identifiers. 628 */ removeEntry(DisplayInfo displayInfo)629 private boolean removeEntry(DisplayInfo displayInfo) { 630 // Remove entry based on primary identifier. 631 boolean removed = mEntries.remove(getIdentifier(displayInfo)) != null; 632 // Ensure that legacy entries are cleared as well. 633 removed |= mEntries.remove(displayInfo.uniqueId) != null; 634 removed |= mEntries.remove(displayInfo.name) != null; 635 return removed; 636 } 637 638 /** Gets the identifier of choice for the current config. */ getIdentifier(DisplayInfo displayInfo)639 private String getIdentifier(DisplayInfo displayInfo) { 640 if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) { 641 // Config suggests using port as identifier for physical displays. 642 if (displayInfo.address instanceof DisplayAddress.Physical) { 643 byte port = ((DisplayAddress.Physical) displayInfo.address).getPort(); 644 return "port:" + Byte.toUnsignedInt(port); 645 } 646 } 647 return displayInfo.uniqueId; 648 } 649 650 private static class AtomicFileStorage implements SettingPersister { 651 private final AtomicFile mAtomicFile; 652 AtomicFileStorage()653 AtomicFileStorage() { 654 final File folder = new File(Environment.getDataDirectory(), SYSTEM_DIRECTORY); 655 final File settingsFile = new File(folder, DISPLAY_SETTINGS_FILE_NAME); 656 // If display_settings.xml doesn't exist, try to copy the vendor's one instead 657 // in order to provide the vendor specific initialization. 658 if (!settingsFile.exists()) { 659 copyVendorSettings(settingsFile); 660 } 661 mAtomicFile = new AtomicFile(settingsFile, WM_DISPLAY_COMMIT_TAG); 662 } 663 copyVendorSettings(File target)664 private static void copyVendorSettings(File target) { 665 final File vendorFile = new File(Environment.getVendorDirectory(), 666 VENDOR_DISPLAY_SETTINGS_PATH); 667 if (vendorFile.canRead()) { 668 try { 669 FileUtils.copy(vendorFile, target); 670 } catch (IOException e) { 671 Slog.e(TAG, "Failed to copy vendor display_settings.xml"); 672 } 673 } 674 } 675 676 @Override openRead()677 public InputStream openRead() throws FileNotFoundException { 678 return mAtomicFile.openRead(); 679 } 680 681 @Override startWrite()682 public OutputStream startWrite() throws IOException { 683 return mAtomicFile.startWrite(); 684 } 685 686 @Override finishWrite(OutputStream os, boolean success)687 public void finishWrite(OutputStream os, boolean success) { 688 if (!(os instanceof FileOutputStream)) { 689 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os); 690 } 691 FileOutputStream fos = (FileOutputStream) os; 692 if (success) { 693 mAtomicFile.finishWrite(fos); 694 } else { 695 mAtomicFile.failWrite(fos); 696 } 697 } 698 } 699 } 700