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, int displayId)251 private int getWindowingModeLocked(Entry entry, int displayId) { 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 final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays 263 && displayId != Display.DEFAULT_DISPLAY; 264 windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement 265 && (mService.mIsPc || forceDesktopMode) 266 ? WindowConfiguration.WINDOWING_MODE_FREEFORM 267 : WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 268 } 269 return windowingMode; 270 } 271 getWindowingModeLocked(DisplayContent dc)272 int getWindowingModeLocked(DisplayContent dc) { 273 final DisplayInfo displayInfo = dc.getDisplayInfo(); 274 final Entry entry = getEntry(displayInfo); 275 return getWindowingModeLocked(entry, dc.getDisplayId()); 276 } 277 setWindowingModeLocked(DisplayContent dc, int mode)278 void setWindowingModeLocked(DisplayContent dc, int mode) { 279 final DisplayInfo displayInfo = dc.getDisplayInfo(); 280 final Entry entry = getOrCreateEntry(displayInfo); 281 entry.mWindowingMode = mode; 282 dc.setWindowingMode(mode); 283 writeSettingsIfNeeded(entry, displayInfo); 284 } 285 getRemoveContentModeLocked(DisplayContent dc)286 int getRemoveContentModeLocked(DisplayContent dc) { 287 final DisplayInfo displayInfo = dc.getDisplayInfo(); 288 final Entry entry = getEntry(displayInfo); 289 if (entry == null || entry.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) { 290 if (dc.isPrivate()) { 291 // For private displays by default content is destroyed on removal. 292 return REMOVE_CONTENT_MODE_DESTROY; 293 } 294 // For other displays by default content is moved to primary on removal. 295 return REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; 296 } 297 return entry.mRemoveContentMode; 298 } 299 setRemoveContentModeLocked(DisplayContent dc, int mode)300 void setRemoveContentModeLocked(DisplayContent dc, int mode) { 301 final DisplayInfo displayInfo = dc.getDisplayInfo(); 302 final Entry entry = getOrCreateEntry(displayInfo); 303 entry.mRemoveContentMode = mode; 304 writeSettingsIfNeeded(entry, displayInfo); 305 } 306 shouldShowWithInsecureKeyguardLocked(DisplayContent dc)307 boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) { 308 final DisplayInfo displayInfo = dc.getDisplayInfo(); 309 final Entry entry = getEntry(displayInfo); 310 if (entry == null) { 311 return false; 312 } 313 return entry.mShouldShowWithInsecureKeyguard; 314 } 315 setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow)316 void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) { 317 if (!dc.isPrivate() && shouldShow) { 318 Slog.e(TAG, "Public display can't be allowed to show content when locked"); 319 return; 320 } 321 322 final DisplayInfo displayInfo = dc.getDisplayInfo(); 323 final Entry entry = getOrCreateEntry(displayInfo); 324 entry.mShouldShowWithInsecureKeyguard = shouldShow; 325 writeSettingsIfNeeded(entry, displayInfo); 326 } 327 shouldShowSystemDecorsLocked(DisplayContent dc)328 boolean shouldShowSystemDecorsLocked(DisplayContent dc) { 329 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { 330 // For default display should show system decors. 331 return true; 332 } 333 334 final DisplayInfo displayInfo = dc.getDisplayInfo(); 335 final Entry entry = getEntry(displayInfo); 336 if (entry == null) { 337 return false; 338 } 339 return entry.mShouldShowSystemDecors; 340 } 341 setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow)342 void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) { 343 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) { 344 Slog.e(TAG, "Default display should show system decors"); 345 return; 346 } 347 348 final DisplayInfo displayInfo = dc.getDisplayInfo(); 349 final Entry entry = getOrCreateEntry(displayInfo); 350 entry.mShouldShowSystemDecors = shouldShow; 351 writeSettingsIfNeeded(entry, displayInfo); 352 } 353 shouldShowImeLocked(DisplayContent dc)354 boolean shouldShowImeLocked(DisplayContent dc) { 355 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { 356 // For default display should shows IME. 357 return true; 358 } 359 360 final DisplayInfo displayInfo = dc.getDisplayInfo(); 361 final Entry entry = getEntry(displayInfo); 362 if (entry == null) { 363 return false; 364 } 365 return entry.mShouldShowIme; 366 } 367 setShouldShowImeLocked(DisplayContent dc, boolean shouldShow)368 void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) { 369 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) { 370 Slog.e(TAG, "Default display should show IME"); 371 return; 372 } 373 374 final DisplayInfo displayInfo = dc.getDisplayInfo(); 375 final Entry entry = getOrCreateEntry(displayInfo); 376 entry.mShouldShowIme = shouldShow; 377 writeSettingsIfNeeded(entry, displayInfo); 378 } 379 applySettingsToDisplayLocked(DisplayContent dc)380 void applySettingsToDisplayLocked(DisplayContent dc) { 381 final DisplayInfo displayInfo = dc.getDisplayInfo(); 382 final Entry entry = getOrCreateEntry(displayInfo); 383 384 // Setting windowing mode first, because it may override overscan values later. 385 dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId())); 386 387 dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, 388 entry.mUserRotation, entry.mFixedToUserRotation); 389 390 if (entry.mForcedDensity != 0) { 391 dc.mBaseDisplayDensity = entry.mForcedDensity; 392 } 393 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) { 394 dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight, 395 dc.mBaseDisplayDensity); 396 } 397 dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED; 398 } 399 400 /** 401 * Updates settings for the given display after system features are loaded into window manager 402 * service, e.g. if this device is PC and if this device supports freeform. 403 * 404 * @param dc the given display. 405 * @return {@code true} if any settings for this display has changed; {@code false} if nothing 406 * changed. 407 */ updateSettingsForDisplay(DisplayContent dc)408 boolean updateSettingsForDisplay(DisplayContent dc) { 409 if (dc.getWindowingMode() != getWindowingModeLocked(dc)) { 410 // For the time being the only thing that may change is windowing mode, so just update 411 // that. 412 dc.setWindowingMode(getWindowingModeLocked(dc)); 413 return true; 414 } 415 return false; 416 } 417 readSettings()418 private void readSettings() { 419 InputStream stream; 420 try { 421 stream = mStorage.openRead(); 422 } catch (IOException e) { 423 Slog.i(TAG, "No existing display settings, starting empty"); 424 return; 425 } 426 boolean success = false; 427 try { 428 XmlPullParser parser = Xml.newPullParser(); 429 parser.setInput(stream, StandardCharsets.UTF_8.name()); 430 int type; 431 while ((type = parser.next()) != XmlPullParser.START_TAG 432 && type != XmlPullParser.END_DOCUMENT) { 433 // Do nothing. 434 } 435 436 if (type != XmlPullParser.START_TAG) { 437 throw new IllegalStateException("no start tag found"); 438 } 439 440 int outerDepth = parser.getDepth(); 441 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 442 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 443 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 444 continue; 445 } 446 447 String tagName = parser.getName(); 448 if (tagName.equals("display")) { 449 readDisplay(parser); 450 } else if (tagName.equals("config")) { 451 readConfig(parser); 452 } else { 453 Slog.w(TAG, "Unknown element under <display-settings>: " 454 + parser.getName()); 455 XmlUtils.skipCurrentTag(parser); 456 } 457 } 458 success = true; 459 } catch (IllegalStateException e) { 460 Slog.w(TAG, "Failed parsing " + e); 461 } catch (NullPointerException e) { 462 Slog.w(TAG, "Failed parsing " + e); 463 } catch (NumberFormatException e) { 464 Slog.w(TAG, "Failed parsing " + e); 465 } catch (XmlPullParserException e) { 466 Slog.w(TAG, "Failed parsing " + e); 467 } catch (IOException e) { 468 Slog.w(TAG, "Failed parsing " + e); 469 } catch (IndexOutOfBoundsException e) { 470 Slog.w(TAG, "Failed parsing " + e); 471 } finally { 472 if (!success) { 473 mEntries.clear(); 474 } 475 try { 476 stream.close(); 477 } catch (IOException e) { 478 } 479 } 480 } 481 getIntAttribute(XmlPullParser parser, String name)482 private int getIntAttribute(XmlPullParser parser, String name) { 483 return getIntAttribute(parser, name, 0 /* defaultValue */); 484 } 485 getIntAttribute(XmlPullParser parser, String name, int defaultValue)486 private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) { 487 try { 488 final String str = parser.getAttributeValue(null, name); 489 return str != null ? Integer.parseInt(str) : defaultValue; 490 } catch (NumberFormatException e) { 491 return defaultValue; 492 } 493 } 494 getBooleanAttribute(XmlPullParser parser, String name)495 private boolean getBooleanAttribute(XmlPullParser parser, String name) { 496 return getBooleanAttribute(parser, name, false /* defaultValue */); 497 } 498 getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue)499 private boolean getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue) { 500 try { 501 final String str = parser.getAttributeValue(null, name); 502 return str != null ? Boolean.parseBoolean(str) : defaultValue; 503 } catch (NumberFormatException e) { 504 return defaultValue; 505 } 506 } 507 readDisplay(XmlPullParser parser)508 private void readDisplay(XmlPullParser parser) throws NumberFormatException, 509 XmlPullParserException, IOException { 510 String name = parser.getAttributeValue(null, "name"); 511 if (name != null) { 512 Entry entry = new Entry(name); 513 entry.mWindowingMode = getIntAttribute(parser, "windowingMode", 514 WindowConfiguration.WINDOWING_MODE_UNDEFINED); 515 entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode", 516 WindowManagerPolicy.USER_ROTATION_FREE); 517 entry.mUserRotation = getIntAttribute(parser, "userRotation", 518 Surface.ROTATION_0); 519 entry.mForcedWidth = getIntAttribute(parser, "forcedWidth"); 520 entry.mForcedHeight = getIntAttribute(parser, "forcedHeight"); 521 entry.mForcedDensity = getIntAttribute(parser, "forcedDensity"); 522 entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode", 523 FORCE_SCALING_MODE_AUTO); 524 entry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode", 525 REMOVE_CONTENT_MODE_UNDEFINED); 526 entry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser, 527 "shouldShowWithInsecureKeyguard"); 528 entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); 529 entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); 530 entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation"); 531 mEntries.put(name, entry); 532 } 533 XmlUtils.skipCurrentTag(parser); 534 } 535 readConfig(XmlPullParser parser)536 private void readConfig(XmlPullParser parser) throws NumberFormatException, 537 XmlPullParserException, IOException { 538 mIdentifier = getIntAttribute(parser, "identifier"); 539 XmlUtils.skipCurrentTag(parser); 540 } 541 writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo)542 private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) { 543 if (changedEntry.isEmpty() && !removeEntry(displayInfo)) { 544 // The entry didn't exist so nothing is changed and no need to update the file. 545 return; 546 } 547 548 mEntries.put(getIdentifier(displayInfo), changedEntry); 549 writeSettings(); 550 } 551 writeSettings()552 private void writeSettings() { 553 OutputStream stream; 554 try { 555 stream = mStorage.startWrite(); 556 } catch (IOException e) { 557 Slog.w(TAG, "Failed to write display settings: " + e); 558 return; 559 } 560 561 try { 562 XmlSerializer out = new FastXmlSerializer(); 563 out.setOutput(stream, StandardCharsets.UTF_8.name()); 564 out.startDocument(null, true); 565 566 out.startTag(null, "display-settings"); 567 568 out.startTag(null, "config"); 569 out.attribute(null, "identifier", Integer.toString(mIdentifier)); 570 out.endTag(null, "config"); 571 572 for (Entry entry : mEntries.values()) { 573 out.startTag(null, "display"); 574 out.attribute(null, "name", entry.mName); 575 if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { 576 out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode)); 577 } 578 if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) { 579 out.attribute(null, "userRotationMode", 580 Integer.toString(entry.mUserRotationMode)); 581 } 582 if (entry.mUserRotation != Surface.ROTATION_0) { 583 out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation)); 584 } 585 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) { 586 out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth)); 587 out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight)); 588 } 589 if (entry.mForcedDensity != 0) { 590 out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity)); 591 } 592 if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) { 593 out.attribute(null, "forcedScalingMode", 594 Integer.toString(entry.mForcedScalingMode)); 595 } 596 if (entry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { 597 out.attribute(null, "removeContentMode", 598 Integer.toString(entry.mRemoveContentMode)); 599 } 600 if (entry.mShouldShowWithInsecureKeyguard) { 601 out.attribute(null, "shouldShowWithInsecureKeyguard", 602 Boolean.toString(entry.mShouldShowWithInsecureKeyguard)); 603 } 604 if (entry.mShouldShowSystemDecors) { 605 out.attribute(null, "shouldShowSystemDecors", 606 Boolean.toString(entry.mShouldShowSystemDecors)); 607 } 608 if (entry.mShouldShowIme) { 609 out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme)); 610 } 611 if (entry.mFixedToUserRotation != IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT) { 612 out.attribute(null, "fixedToUserRotation", 613 Integer.toString(entry.mFixedToUserRotation)); 614 } 615 out.endTag(null, "display"); 616 } 617 618 out.endTag(null, "display-settings"); 619 out.endDocument(); 620 mStorage.finishWrite(stream, true /* success */); 621 } catch (IOException e) { 622 Slog.w(TAG, "Failed to write display window settings.", e); 623 mStorage.finishWrite(stream, false /* success */); 624 } 625 } 626 627 /** 628 * Removes an entry from {@link #mEntries} cache. Looks up by new and previously used 629 * identifiers. 630 */ removeEntry(DisplayInfo displayInfo)631 private boolean removeEntry(DisplayInfo displayInfo) { 632 // Remove entry based on primary identifier. 633 boolean removed = mEntries.remove(getIdentifier(displayInfo)) != null; 634 // Ensure that legacy entries are cleared as well. 635 removed |= mEntries.remove(displayInfo.uniqueId) != null; 636 removed |= mEntries.remove(displayInfo.name) != null; 637 return removed; 638 } 639 640 /** Gets the identifier of choice for the current config. */ getIdentifier(DisplayInfo displayInfo)641 private String getIdentifier(DisplayInfo displayInfo) { 642 if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) { 643 // Config suggests using port as identifier for physical displays. 644 if (displayInfo.address instanceof DisplayAddress.Physical) { 645 byte port = ((DisplayAddress.Physical) displayInfo.address).getPort(); 646 return "port:" + Byte.toUnsignedInt(port); 647 } 648 } 649 return displayInfo.uniqueId; 650 } 651 652 private static class AtomicFileStorage implements SettingPersister { 653 private final AtomicFile mAtomicFile; 654 AtomicFileStorage()655 AtomicFileStorage() { 656 final File folder = new File(Environment.getDataDirectory(), SYSTEM_DIRECTORY); 657 final File settingsFile = new File(folder, DISPLAY_SETTINGS_FILE_NAME); 658 // If display_settings.xml doesn't exist, try to copy the vendor's one instead 659 // in order to provide the vendor specific initialization. 660 if (!settingsFile.exists()) { 661 copyVendorSettings(settingsFile); 662 } 663 mAtomicFile = new AtomicFile(settingsFile, WM_DISPLAY_COMMIT_TAG); 664 } 665 copyVendorSettings(File target)666 private static void copyVendorSettings(File target) { 667 final File vendorFile = new File(Environment.getVendorDirectory(), 668 VENDOR_DISPLAY_SETTINGS_PATH); 669 if (vendorFile.canRead()) { 670 try { 671 FileUtils.copy(vendorFile, target); 672 } catch (IOException e) { 673 Slog.e(TAG, "Failed to copy vendor display_settings.xml"); 674 } 675 } 676 } 677 678 @Override openRead()679 public InputStream openRead() throws FileNotFoundException { 680 return mAtomicFile.openRead(); 681 } 682 683 @Override startWrite()684 public OutputStream startWrite() throws IOException { 685 return mAtomicFile.startWrite(); 686 } 687 688 @Override finishWrite(OutputStream os, boolean success)689 public void finishWrite(OutputStream os, boolean success) { 690 if (!(os instanceof FileOutputStream)) { 691 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os); 692 } 693 FileOutputStream fos = (FileOutputStream) os; 694 if (success) { 695 mAtomicFile.finishWrite(fos); 696 } else { 697 mAtomicFile.failWrite(fos); 698 } 699 } 700 } 701 } 702