blob: a46fa13adf4ee9fb87e476495adb67e035ab2115 [file] [log] [blame]
Dianne Hackbornc652de82013-02-15 16:32:56 -08001/*
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
17package com.android.server.wm;
18
Chilun8753ad32018-10-09 15:56:45 +080019import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
20import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
21import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
22
Riddle Hsuf53da812018-08-15 22:00:27 +080023import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
24import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
Garfield Tan7fbca052019-02-19 10:45:35 -080025import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080026import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
27import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
28
Andrii Kulian453347f2019-03-17 20:49:39 -070029import android.annotation.IntDef;
30import android.annotation.Nullable;
Garfield Tane0846042018-07-26 13:42:04 -070031import android.app.WindowConfiguration;
Dianne Hackbornc652de82013-02-15 16:32:56 -080032import android.os.Environment;
Riddle Hsuf53da812018-08-15 22:00:27 +080033import android.provider.Settings;
Dianne Hackbornc652de82013-02-15 16:32:56 -080034import android.util.AtomicFile;
35import android.util.Slog;
36import android.util.Xml;
Garfield Tane0846042018-07-26 13:42:04 -070037import android.view.Display;
Andrii Kulian453347f2019-03-17 20:49:39 -070038import android.view.DisplayAddress;
Garfield Tane0846042018-07-26 13:42:04 -070039import android.view.DisplayInfo;
Garfield Tan90c90052018-10-08 12:29:41 -070040import android.view.Surface;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080041
Garfield Tane0846042018-07-26 13:42:04 -070042import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornc652de82013-02-15 16:32:56 -080043import com.android.internal.util.FastXmlSerializer;
44import com.android.internal.util.XmlUtils;
Garfield Tan90c90052018-10-08 12:29:41 -070045import com.android.server.policy.WindowManagerPolicy;
Riddle Hsuf53da812018-08-15 22:00:27 +080046import com.android.server.wm.DisplayContent.ForceScalingMode;
Garfield Tan90c90052018-10-08 12:29:41 -070047
48import org.xmlpull.v1.XmlPullParser;
49import org.xmlpull.v1.XmlPullParserException;
50import org.xmlpull.v1.XmlSerializer;
Dianne Hackbornc652de82013-02-15 16:32:56 -080051
52import java.io.File;
Dianne Hackbornc652de82013-02-15 16:32:56 -080053import java.io.FileNotFoundException;
54import java.io.FileOutputStream;
55import java.io.IOException;
Andrii Kulian453347f2019-03-17 20:49:39 -070056import java.io.InputStream;
57import java.io.OutputStream;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010058import java.nio.charset.StandardCharsets;
Dianne Hackbornc652de82013-02-15 16:32:56 -080059import java.util.HashMap;
60
61/**
62 * Current persistent settings about a display
63 */
Chilun8753ad32018-10-09 15:56:45 +080064class DisplayWindowSettings {
65 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM;
Dianne Hackbornc652de82013-02-15 16:32:56 -080066
Andrii Kulian453347f2019-03-17 20:49:39 -070067 private static final int IDENTIFIER_UNIQUE_ID = 0;
68 private static final int IDENTIFIER_PORT = 1;
69 @IntDef(prefix = { "IDENTIFIER_" }, value = {
70 IDENTIFIER_UNIQUE_ID,
71 IDENTIFIER_PORT,
72 })
73 @interface DisplayIdentifierType {}
74
Garfield Tane0846042018-07-26 13:42:04 -070075 private final WindowManagerService mService;
Andrii Kulian453347f2019-03-17 20:49:39 -070076 private final HashMap<String, Entry> mEntries = new HashMap<>();
77 private final SettingPersister mStorage;
78
79 /**
80 * The preferred type of a display identifier to use when storing and retrieving entries.
81 * {@link #getIdentifier(DisplayInfo)} must be used to get current preferred identifier for each
82 * display. It will fall back to using {@link #IDENTIFIER_UNIQUE_ID} if the currently selected
83 * one is not applicable to a particular display.
84 */
85 @DisplayIdentifierType
86 private int mIdentifier = IDENTIFIER_UNIQUE_ID;
87
88 /** Interface for persisting the display window settings. */
89 interface SettingPersister {
90 InputStream openRead() throws IOException;
91 OutputStream startWrite() throws IOException;
92 void finishWrite(OutputStream os, boolean success);
93 }
Dianne Hackbornc652de82013-02-15 16:32:56 -080094
Garfield Tane0846042018-07-26 13:42:04 -070095 private static class Entry {
Garfield Tan90c90052018-10-08 12:29:41 -070096 private final String mName;
97 private int mOverscanLeft;
98 private int mOverscanTop;
99 private int mOverscanRight;
100 private int mOverscanBottom;
101 private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
102 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
103 private int mUserRotation = Surface.ROTATION_0;
Riddle Hsuf53da812018-08-15 22:00:27 +0800104 private int mForcedWidth;
105 private int mForcedHeight;
106 private int mForcedDensity;
107 private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO;
Chilun8753ad32018-10-09 15:56:45 +0800108 private int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED;
109 private boolean mShouldShowWithInsecureKeyguard = false;
110 private boolean mShouldShowSystemDecors = false;
111 private boolean mShouldShowIme = false;
Garfield Tan7fbca052019-02-19 10:45:35 -0800112 private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation =
113 FIXED_TO_USER_ROTATION_DEFAULT;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800114
Chilun8753ad32018-10-09 15:56:45 +0800115 private Entry(String name) {
116 mName = name;
Garfield Tan90c90052018-10-08 12:29:41 -0700117 }
118
Andrii Kulian453347f2019-03-17 20:49:39 -0700119 private Entry(String name, Entry copyFrom) {
120 this(name);
121 mOverscanLeft = copyFrom.mOverscanLeft;
122 mOverscanTop = copyFrom.mOverscanTop;
123 mOverscanRight = copyFrom.mOverscanRight;
124 mOverscanBottom = copyFrom.mOverscanBottom;
125 mWindowingMode = copyFrom.mWindowingMode;
126 mUserRotationMode = copyFrom.mUserRotationMode;
127 mUserRotation = copyFrom.mUserRotation;
128 mForcedWidth = copyFrom.mForcedWidth;
129 mForcedHeight = copyFrom.mForcedHeight;
130 mForcedDensity = copyFrom.mForcedDensity;
131 mForcedScalingMode = copyFrom.mForcedScalingMode;
132 mRemoveContentMode = copyFrom.mRemoveContentMode;
133 mShouldShowWithInsecureKeyguard = copyFrom.mShouldShowWithInsecureKeyguard;
134 mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors;
135 mShouldShowIme = copyFrom.mShouldShowIme;
136 mFixedToUserRotation = copyFrom.mFixedToUserRotation;
137 }
138
Riddle Hsuf53da812018-08-15 22:00:27 +0800139 /** @return {@code true} if all values are default. */
Garfield Tan90c90052018-10-08 12:29:41 -0700140 private boolean isEmpty() {
141 return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
142 && mOverscanBottom == 0
143 && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
144 && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
Riddle Hsuf53da812018-08-15 22:00:27 +0800145 && mUserRotation == Surface.ROTATION_0
146 && mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
Chilun8753ad32018-10-09 15:56:45 +0800147 && mForcedScalingMode == FORCE_SCALING_MODE_AUTO
148 && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
149 && !mShouldShowWithInsecureKeyguard
150 && !mShouldShowSystemDecors
Garfield Tanff362222018-11-14 17:52:32 -0800151 && !mShouldShowIme
Garfield Tan7fbca052019-02-19 10:45:35 -0800152 && mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800153 }
154 }
155
Chilun8753ad32018-10-09 15:56:45 +0800156 DisplayWindowSettings(WindowManagerService service) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700157 this(service, new AtomicFileStorage());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800158 }
159
Garfield Tane0846042018-07-26 13:42:04 -0700160 @VisibleForTesting
Andrii Kulian453347f2019-03-17 20:49:39 -0700161 DisplayWindowSettings(WindowManagerService service, SettingPersister storageImpl) {
Garfield Tane0846042018-07-26 13:42:04 -0700162 mService = service;
Andrii Kulian453347f2019-03-17 20:49:39 -0700163 mStorage = storageImpl;
Riddle Hsuf53da812018-08-15 22:00:27 +0800164 readSettings();
Garfield Tane0846042018-07-26 13:42:04 -0700165 }
166
Andrii Kulian453347f2019-03-17 20:49:39 -0700167 private @Nullable Entry getEntry(DisplayInfo displayInfo) {
168 final String identifier = getIdentifier(displayInfo);
Wale Ogunwale361ca212014-11-20 11:42:38 -0800169 Entry entry;
Andrii Kulian453347f2019-03-17 20:49:39 -0700170 // Try to get corresponding entry using preferred identifier for the current config.
171 if ((entry = mEntries.get(identifier)) != null) {
172 return entry;
Wale Ogunwale361ca212014-11-20 11:42:38 -0800173 }
Andrii Kulian453347f2019-03-17 20:49:39 -0700174 // Else, fall back to the display name.
175 if ((entry = mEntries.get(displayInfo.name)) != null) {
176 // Found an entry stored with old identifier - upgrade to the new type now.
177 return updateIdentifierForEntry(entry, displayInfo);
178 }
179 return null;
Garfield Tane0846042018-07-26 13:42:04 -0700180 }
181
Riddle Hsuf53da812018-08-15 22:00:27 +0800182 private Entry getOrCreateEntry(DisplayInfo displayInfo) {
183 final Entry entry = getEntry(displayInfo);
Andrii Kulian453347f2019-03-17 20:49:39 -0700184 return entry != null ? entry : new Entry(getIdentifier(displayInfo));
185 }
186
187 /**
188 * Upgrades the identifier of a legacy entry. Does it by copying the data from the old record
189 * and clearing the old key in memory. The entry will be written to storage next time when a
190 * setting changes.
191 */
192 private Entry updateIdentifierForEntry(Entry entry, DisplayInfo displayInfo) {
193 final Entry newEntry = new Entry(getIdentifier(displayInfo), entry);
194 removeEntry(displayInfo);
195 mEntries.put(newEntry.mName, newEntry);
196 return newEntry;
Garfield Tan90c90052018-10-08 12:29:41 -0700197 }
198
Riddle Hsuf53da812018-08-15 22:00:27 +0800199 void setOverscanLocked(DisplayInfo displayInfo, int left, int top, int right, int bottom) {
200 final Entry entry = getOrCreateEntry(displayInfo);
Garfield Tan90c90052018-10-08 12:29:41 -0700201 entry.mOverscanLeft = left;
202 entry.mOverscanTop = top;
203 entry.mOverscanRight = right;
204 entry.mOverscanBottom = bottom;
Riddle Hsuf53da812018-08-15 22:00:27 +0800205 writeSettingsIfNeeded(entry, displayInfo);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800206 }
207
Riddle Hsuf53da812018-08-15 22:00:27 +0800208 void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
209 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
210 final Entry entry = getOrCreateEntry(displayInfo);
211 entry.mUserRotationMode = rotationMode;
212 entry.mUserRotation = rotation;
213 writeSettingsIfNeeded(entry, displayInfo);
214 }
215
216 void setForcedSize(DisplayContent displayContent, int width, int height) {
217 if (displayContent.isDefaultDisplay) {
218 final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
219 Settings.Global.putString(mService.mContext.getContentResolver(),
220 Settings.Global.DISPLAY_SIZE_FORCED, sizeString);
221 return;
222 }
223
224 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
225 final Entry entry = getOrCreateEntry(displayInfo);
226 entry.mForcedWidth = width;
227 entry.mForcedHeight = height;
228 writeSettingsIfNeeded(entry, displayInfo);
229 }
230
231 void setForcedDensity(DisplayContent displayContent, int density, int userId) {
232 if (displayContent.isDefaultDisplay) {
233 final String densityString = density == 0 ? "" : Integer.toString(density);
234 Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
235 Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
236 return;
237 }
238
239 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
240 final Entry entry = getOrCreateEntry(displayInfo);
241 entry.mForcedDensity = density;
242 writeSettingsIfNeeded(entry, displayInfo);
243 }
244
245 void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
246 if (displayContent.isDefaultDisplay) {
247 Settings.Global.putInt(mService.mContext.getContentResolver(),
248 Settings.Global.DISPLAY_SCALING_FORCE, mode);
249 return;
250 }
251
252 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
253 final Entry entry = getOrCreateEntry(displayInfo);
254 entry.mForcedScalingMode = mode;
255 writeSettingsIfNeeded(entry, displayInfo);
256 }
257
Garfield Tan7fbca052019-02-19 10:45:35 -0800258 void setFixedToUserRotation(DisplayContent displayContent,
259 @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800260 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
261 final Entry entry = getOrCreateEntry(displayInfo);
262 entry.mFixedToUserRotation = fixedToUserRotation;
263 writeSettingsIfNeeded(entry, displayInfo);
264 }
265
Riddle Hsuf53da812018-08-15 22:00:27 +0800266 private int getWindowingModeLocked(Entry entry, int displayId) {
Garfield Tan90c90052018-10-08 12:29:41 -0700267 int windowingMode = entry != null ? entry.mWindowingMode
Garfield Tane0846042018-07-26 13:42:04 -0700268 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
269 // This display used to be in freeform, but we don't support freeform anymore, so fall
270 // back to fullscreen.
271 if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
272 && !mService.mSupportsFreeformWindowManagement) {
273 return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
274 }
275 // No record is present so use default windowing mode policy.
276 if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
Andrii Kulian15cfb422018-11-07 13:38:49 -0800277 final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
278 && displayId != Display.DEFAULT_DISPLAY;
279 windowingMode = mService.mSupportsFreeformWindowManagement
280 && (mService.mIsPc || forceDesktopMode)
281 ? WindowConfiguration.WINDOWING_MODE_FREEFORM
282 : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
Garfield Tane0846042018-07-26 13:42:04 -0700283 }
284 return windowingMode;
285 }
286
Chilun8753ad32018-10-09 15:56:45 +0800287 int getWindowingModeLocked(DisplayContent dc) {
288 final DisplayInfo displayInfo = dc.getDisplayInfo();
289 final Entry entry = getEntry(displayInfo);
290 return getWindowingModeLocked(entry, dc.getDisplayId());
291 }
292
293 void setWindowingModeLocked(DisplayContent dc, int mode) {
294 final DisplayInfo displayInfo = dc.getDisplayInfo();
295 final Entry entry = getOrCreateEntry(displayInfo);
296 entry.mWindowingMode = mode;
297 dc.setWindowingMode(mode);
298 writeSettingsIfNeeded(entry, displayInfo);
299 }
300
301 int getRemoveContentModeLocked(DisplayContent dc) {
302 final DisplayInfo displayInfo = dc.getDisplayInfo();
303 final Entry entry = getEntry(displayInfo);
304 if (entry == null || entry.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
305 if (dc.isPrivate()) {
306 // For private displays by default content is destroyed on removal.
307 return REMOVE_CONTENT_MODE_DESTROY;
308 }
309 // For other displays by default content is moved to primary on removal.
310 return REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
311 }
312 return entry.mRemoveContentMode;
313 }
314
315 void setRemoveContentModeLocked(DisplayContent dc, int mode) {
316 final DisplayInfo displayInfo = dc.getDisplayInfo();
317 final Entry entry = getOrCreateEntry(displayInfo);
318 entry.mRemoveContentMode = mode;
319 writeSettingsIfNeeded(entry, displayInfo);
320 }
321
322 boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) {
323 final DisplayInfo displayInfo = dc.getDisplayInfo();
324 final Entry entry = getEntry(displayInfo);
325 if (entry == null) {
326 return false;
327 }
328 return entry.mShouldShowWithInsecureKeyguard;
329 }
330
331 void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) {
332 if (!dc.isPrivate() && shouldShow) {
333 Slog.e(TAG, "Public display can't be allowed to show content when locked");
334 return;
335 }
336
337 final DisplayInfo displayInfo = dc.getDisplayInfo();
338 final Entry entry = getOrCreateEntry(displayInfo);
339 entry.mShouldShowWithInsecureKeyguard = shouldShow;
340 writeSettingsIfNeeded(entry, displayInfo);
341 }
342
343 boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
344 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
345 // For default display should show system decors.
346 return true;
347 }
348
349 final DisplayInfo displayInfo = dc.getDisplayInfo();
350 final Entry entry = getEntry(displayInfo);
351 if (entry == null) {
352 return false;
353 }
354 return entry.mShouldShowSystemDecors;
355 }
356
357 void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) {
358 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
359 Slog.e(TAG, "Default display should show system decors");
360 return;
361 }
362
363 final DisplayInfo displayInfo = dc.getDisplayInfo();
364 final Entry entry = getOrCreateEntry(displayInfo);
365 entry.mShouldShowSystemDecors = shouldShow;
366 writeSettingsIfNeeded(entry, displayInfo);
367 }
368
369 boolean shouldShowImeLocked(DisplayContent dc) {
370 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
371 // For default display should shows IME.
372 return true;
373 }
374
375 final DisplayInfo displayInfo = dc.getDisplayInfo();
376 final Entry entry = getEntry(displayInfo);
377 if (entry == null) {
378 return false;
379 }
380 return entry.mShouldShowIme;
381 }
382
383 void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) {
384 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
385 Slog.e(TAG, "Default display should show IME");
386 return;
387 }
388
389 final DisplayInfo displayInfo = dc.getDisplayInfo();
390 final Entry entry = getOrCreateEntry(displayInfo);
391 entry.mShouldShowIme = shouldShow;
392 writeSettingsIfNeeded(entry, displayInfo);
393 }
394
Riddle Hsuf53da812018-08-15 22:00:27 +0800395 void applySettingsToDisplayLocked(DisplayContent dc) {
Garfield Tan90c90052018-10-08 12:29:41 -0700396 final DisplayInfo displayInfo = dc.getDisplayInfo();
Tadashi G. Takaokafd911182018-12-20 18:19:57 +0900397 final Entry entry = getOrCreateEntry(displayInfo);
Garfield Tan90c90052018-10-08 12:29:41 -0700398
Riddle Hsuf53da812018-08-15 22:00:27 +0800399 // Setting windowing mode first, because it may override overscan values later.
400 dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
401
Riddle Hsuf53da812018-08-15 22:00:27 +0800402 displayInfo.overscanLeft = entry.mOverscanLeft;
403 displayInfo.overscanTop = entry.mOverscanTop;
404 displayInfo.overscanRight = entry.mOverscanRight;
405 displayInfo.overscanBottom = entry.mOverscanBottom;
406
Garfield Tanff362222018-11-14 17:52:32 -0800407 dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
408 entry.mUserRotation, entry.mFixedToUserRotation);
Riddle Hsuf53da812018-08-15 22:00:27 +0800409
410 if (entry.mForcedDensity != 0) {
411 dc.mBaseDisplayDensity = entry.mForcedDensity;
412 }
413 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
414 dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
415 dc.mBaseDisplayDensity);
416 }
417 dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
Garfield Tan90c90052018-10-08 12:29:41 -0700418 }
419
Garfield Tanb5910b42019-03-14 14:50:59 -0700420 /**
421 * Updates settings for the given display after system features are loaded into window manager
422 * service, e.g. if this device is PC and if this device supports freeform.
423 *
424 * @param dc the given display.
425 * @return {@code true} if any settings for this display has changed; {@code false} if nothing
426 * changed.
427 */
428 boolean updateSettingsForDisplay(DisplayContent dc) {
429 if (dc.getWindowingMode() != getWindowingModeLocked(dc)) {
430 // For the time being the only thing that may change is windowing mode, so just update
431 // that.
432 dc.setWindowingMode(getWindowingModeLocked(dc));
433 return true;
434 }
435 return false;
436 }
437
Riddle Hsuf53da812018-08-15 22:00:27 +0800438 private void readSettings() {
Andrii Kulian453347f2019-03-17 20:49:39 -0700439 InputStream stream;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800440 try {
Andrii Kulian453347f2019-03-17 20:49:39 -0700441 stream = mStorage.openRead();
442 } catch (IOException e) {
443 Slog.i(TAG, "No existing display settings, starting empty");
Dianne Hackbornc652de82013-02-15 16:32:56 -0800444 return;
445 }
446 boolean success = false;
447 try {
448 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100449 parser.setInput(stream, StandardCharsets.UTF_8.name());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800450 int type;
451 while ((type = parser.next()) != XmlPullParser.START_TAG
452 && type != XmlPullParser.END_DOCUMENT) {
Wale Ogunwale361ca212014-11-20 11:42:38 -0800453 // Do nothing.
Dianne Hackbornc652de82013-02-15 16:32:56 -0800454 }
455
456 if (type != XmlPullParser.START_TAG) {
457 throw new IllegalStateException("no start tag found");
458 }
459
460 int outerDepth = parser.getDepth();
461 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
462 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
463 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
464 continue;
465 }
466
467 String tagName = parser.getName();
468 if (tagName.equals("display")) {
469 readDisplay(parser);
Andrii Kulian453347f2019-03-17 20:49:39 -0700470 } else if (tagName.equals("config")) {
471 readConfig(parser);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800472 } else {
473 Slog.w(TAG, "Unknown element under <display-settings>: "
474 + parser.getName());
475 XmlUtils.skipCurrentTag(parser);
476 }
477 }
478 success = true;
479 } catch (IllegalStateException e) {
480 Slog.w(TAG, "Failed parsing " + e);
481 } catch (NullPointerException e) {
482 Slog.w(TAG, "Failed parsing " + e);
483 } catch (NumberFormatException e) {
484 Slog.w(TAG, "Failed parsing " + e);
485 } catch (XmlPullParserException e) {
486 Slog.w(TAG, "Failed parsing " + e);
487 } catch (IOException e) {
488 Slog.w(TAG, "Failed parsing " + e);
489 } catch (IndexOutOfBoundsException e) {
490 Slog.w(TAG, "Failed parsing " + e);
491 } finally {
492 if (!success) {
493 mEntries.clear();
494 }
495 try {
496 stream.close();
497 } catch (IOException e) {
498 }
499 }
500 }
501
502 private int getIntAttribute(XmlPullParser parser, String name) {
Garfield Tane0846042018-07-26 13:42:04 -0700503 return getIntAttribute(parser, name, 0 /* defaultValue */);
504 }
505
506 private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
Dianne Hackbornc652de82013-02-15 16:32:56 -0800507 try {
Chilun8753ad32018-10-09 15:56:45 +0800508 final String str = parser.getAttributeValue(null, name);
Garfield Tane0846042018-07-26 13:42:04 -0700509 return str != null ? Integer.parseInt(str) : defaultValue;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800510 } catch (NumberFormatException e) {
Garfield Tane0846042018-07-26 13:42:04 -0700511 return defaultValue;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800512 }
513 }
514
Chilun8753ad32018-10-09 15:56:45 +0800515 private boolean getBooleanAttribute(XmlPullParser parser, String name) {
516 return getBooleanAttribute(parser, name, false /* defaultValue */);
517 }
518
519 private boolean getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue) {
520 try {
521 final String str = parser.getAttributeValue(null, name);
522 return str != null ? Boolean.parseBoolean(str) : defaultValue;
523 } catch (NumberFormatException e) {
524 return defaultValue;
525 }
526 }
527
Dianne Hackbornc652de82013-02-15 16:32:56 -0800528 private void readDisplay(XmlPullParser parser) throws NumberFormatException,
529 XmlPullParserException, IOException {
530 String name = parser.getAttributeValue(null, "name");
531 if (name != null) {
532 Entry entry = new Entry(name);
Garfield Tan90c90052018-10-08 12:29:41 -0700533 entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
534 entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
535 entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
536 entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
537 entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
Garfield Tane0846042018-07-26 13:42:04 -0700538 WindowConfiguration.WINDOWING_MODE_UNDEFINED);
Garfield Tan90c90052018-10-08 12:29:41 -0700539 entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
540 WindowManagerPolicy.USER_ROTATION_FREE);
541 entry.mUserRotation = getIntAttribute(parser, "userRotation",
542 Surface.ROTATION_0);
Riddle Hsuf53da812018-08-15 22:00:27 +0800543 entry.mForcedWidth = getIntAttribute(parser, "forcedWidth");
544 entry.mForcedHeight = getIntAttribute(parser, "forcedHeight");
545 entry.mForcedDensity = getIntAttribute(parser, "forcedDensity");
546 entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode",
547 FORCE_SCALING_MODE_AUTO);
Chilun8753ad32018-10-09 15:56:45 +0800548 entry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode",
549 REMOVE_CONTENT_MODE_UNDEFINED);
550 entry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser,
551 "shouldShowWithInsecureKeyguard");
552 entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
553 entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
Garfield Tan7fbca052019-02-19 10:45:35 -0800554 entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
Dianne Hackbornc652de82013-02-15 16:32:56 -0800555 mEntries.put(name, entry);
556 }
557 XmlUtils.skipCurrentTag(parser);
558 }
559
Andrii Kulian453347f2019-03-17 20:49:39 -0700560 private void readConfig(XmlPullParser parser) throws NumberFormatException,
561 XmlPullParserException, IOException {
562 mIdentifier = getIntAttribute(parser, "identifier");
563 XmlUtils.skipCurrentTag(parser);
564 }
565
Riddle Hsuf53da812018-08-15 22:00:27 +0800566 private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700567 if (changedEntry.isEmpty() && !removeEntry(displayInfo)) {
568 // The entry didn't exist so nothing is changed and no need to update the file.
569 return;
Riddle Hsuf53da812018-08-15 22:00:27 +0800570 }
571
Andrii Kulian453347f2019-03-17 20:49:39 -0700572 mEntries.put(getIdentifier(displayInfo), changedEntry);
573 writeSettings();
574 }
575
576 private void writeSettings() {
577 OutputStream stream;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800578 try {
Andrii Kulian453347f2019-03-17 20:49:39 -0700579 stream = mStorage.startWrite();
Dianne Hackbornc652de82013-02-15 16:32:56 -0800580 } catch (IOException e) {
581 Slog.w(TAG, "Failed to write display settings: " + e);
582 return;
583 }
584
585 try {
586 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100587 out.setOutput(stream, StandardCharsets.UTF_8.name());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800588 out.startDocument(null, true);
Andrii Kulian453347f2019-03-17 20:49:39 -0700589
Dianne Hackbornc652de82013-02-15 16:32:56 -0800590 out.startTag(null, "display-settings");
591
Andrii Kulian453347f2019-03-17 20:49:39 -0700592 out.startTag(null, "config");
593 out.attribute(null, "identifier", Integer.toString(mIdentifier));
594 out.endTag(null, "config");
595
Dianne Hackbornc652de82013-02-15 16:32:56 -0800596 for (Entry entry : mEntries.values()) {
597 out.startTag(null, "display");
Garfield Tan90c90052018-10-08 12:29:41 -0700598 out.attribute(null, "name", entry.mName);
599 if (entry.mOverscanLeft != 0) {
600 out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800601 }
Garfield Tan90c90052018-10-08 12:29:41 -0700602 if (entry.mOverscanTop != 0) {
603 out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800604 }
Garfield Tan90c90052018-10-08 12:29:41 -0700605 if (entry.mOverscanRight != 0) {
606 out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800607 }
Garfield Tan90c90052018-10-08 12:29:41 -0700608 if (entry.mOverscanBottom != 0) {
609 out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800610 }
Garfield Tan90c90052018-10-08 12:29:41 -0700611 if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
612 out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
613 }
614 if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
615 out.attribute(null, "userRotationMode",
616 Integer.toString(entry.mUserRotationMode));
617 }
618 if (entry.mUserRotation != Surface.ROTATION_0) {
619 out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
Garfield Tane0846042018-07-26 13:42:04 -0700620 }
Riddle Hsuf53da812018-08-15 22:00:27 +0800621 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
622 out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth));
623 out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight));
624 }
625 if (entry.mForcedDensity != 0) {
626 out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity));
627 }
628 if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) {
629 out.attribute(null, "forcedScalingMode",
630 Integer.toString(entry.mForcedScalingMode));
631 }
Chilun8753ad32018-10-09 15:56:45 +0800632 if (entry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
633 out.attribute(null, "removeContentMode",
634 Integer.toString(entry.mRemoveContentMode));
635 }
636 if (entry.mShouldShowWithInsecureKeyguard) {
637 out.attribute(null, "shouldShowWithInsecureKeyguard",
638 Boolean.toString(entry.mShouldShowWithInsecureKeyguard));
639 }
640 if (entry.mShouldShowSystemDecors) {
641 out.attribute(null, "shouldShowSystemDecors",
642 Boolean.toString(entry.mShouldShowSystemDecors));
643 }
644 if (entry.mShouldShowIme) {
645 out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
646 }
Garfield Tan7fbca052019-02-19 10:45:35 -0800647 if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) {
Garfield Tanff362222018-11-14 17:52:32 -0800648 out.attribute(null, "fixedToUserRotation",
Garfield Tan7fbca052019-02-19 10:45:35 -0800649 Integer.toString(entry.mFixedToUserRotation));
Garfield Tanff362222018-11-14 17:52:32 -0800650 }
Dianne Hackbornc652de82013-02-15 16:32:56 -0800651 out.endTag(null, "display");
652 }
653
654 out.endTag(null, "display-settings");
655 out.endDocument();
Andrii Kulian453347f2019-03-17 20:49:39 -0700656 mStorage.finishWrite(stream, true /* success */);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800657 } catch (IOException e) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700658 Slog.w(TAG, "Failed to write display window settings.", e);
659 mStorage.finishWrite(stream, false /* success */);
660 }
661 }
662
663 /**
664 * Removes an entry from {@link #mEntries} cache. Looks up by new and previously used
665 * identifiers.
666 */
667 private boolean removeEntry(DisplayInfo displayInfo) {
668 // Remove entry based on primary identifier.
669 boolean removed = mEntries.remove(getIdentifier(displayInfo)) != null;
670 // Ensure that legacy entries are cleared as well.
671 removed |= mEntries.remove(displayInfo.uniqueId) != null;
672 removed |= mEntries.remove(displayInfo.name) != null;
673 return removed;
674 }
675
676 /** Gets the identifier of choice for the current config. */
677 private String getIdentifier(DisplayInfo displayInfo) {
678 if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) {
679 // Config suggests using port as identifier for physical displays.
680 if (displayInfo.address instanceof DisplayAddress.Physical) {
681 return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort();
682 }
683 }
684 return displayInfo.uniqueId;
685 }
686
687 private static class AtomicFileStorage implements SettingPersister {
688 private final AtomicFile mAtomicFile;
689
690 AtomicFileStorage() {
691 final File folder = new File(Environment.getDataDirectory(), "system");
692 mAtomicFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
693 }
694
695 @Override
696 public InputStream openRead() throws FileNotFoundException {
697 return mAtomicFile.openRead();
698 }
699
700 @Override
701 public OutputStream startWrite() throws IOException {
702 return mAtomicFile.startWrite();
703 }
704
705 @Override
706 public void finishWrite(OutputStream os, boolean success) {
707 if (!(os instanceof FileOutputStream)) {
708 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
709 }
710 FileOutputStream fos = (FileOutputStream) os;
711 if (success) {
712 mAtomicFile.finishWrite(fos);
713 } else {
714 mAtomicFile.failWrite(fos);
715 }
Dianne Hackbornc652de82013-02-15 16:32:56 -0800716 }
717 }
718}