blob: 207e8ef728eb9540cba1869a35b78196d2e75729 [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;
Yuncheol Heof6461442019-07-12 10:03:40 -070033import android.os.FileUtils;
Riddle Hsuf53da812018-08-15 22:00:27 +080034import android.provider.Settings;
Dianne Hackbornc652de82013-02-15 16:32:56 -080035import android.util.AtomicFile;
36import android.util.Slog;
37import android.util.Xml;
Garfield Tane0846042018-07-26 13:42:04 -070038import android.view.Display;
Andrii Kulian453347f2019-03-17 20:49:39 -070039import android.view.DisplayAddress;
Garfield Tane0846042018-07-26 13:42:04 -070040import android.view.DisplayInfo;
Garfield Tan90c90052018-10-08 12:29:41 -070041import android.view.Surface;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080042
Garfield Tane0846042018-07-26 13:42:04 -070043import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornc652de82013-02-15 16:32:56 -080044import com.android.internal.util.FastXmlSerializer;
45import com.android.internal.util.XmlUtils;
Garfield Tan90c90052018-10-08 12:29:41 -070046import com.android.server.policy.WindowManagerPolicy;
Riddle Hsuf53da812018-08-15 22:00:27 +080047import com.android.server.wm.DisplayContent.ForceScalingMode;
Garfield Tan90c90052018-10-08 12:29:41 -070048
49import org.xmlpull.v1.XmlPullParser;
50import org.xmlpull.v1.XmlPullParserException;
51import org.xmlpull.v1.XmlSerializer;
Dianne Hackbornc652de82013-02-15 16:32:56 -080052
53import java.io.File;
Dianne Hackbornc652de82013-02-15 16:32:56 -080054import java.io.FileNotFoundException;
55import java.io.FileOutputStream;
56import java.io.IOException;
Andrii Kulian453347f2019-03-17 20:49:39 -070057import java.io.InputStream;
58import java.io.OutputStream;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010059import java.nio.charset.StandardCharsets;
Dianne Hackbornc652de82013-02-15 16:32:56 -080060import java.util.HashMap;
61
62/**
63 * Current persistent settings about a display
64 */
Chilun8753ad32018-10-09 15:56:45 +080065class DisplayWindowSettings {
66 private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM;
Dianne Hackbornc652de82013-02-15 16:32:56 -080067
Yuncheol Heof6461442019-07-12 10:03:40 -070068 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
Andrii Kulian453347f2019-03-17 20:49:39 -070073 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
Garfield Tane0846042018-07-26 13:42:04 -070081 private final WindowManagerService mService;
Andrii Kulian453347f2019-03-17 20:49:39 -070082 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 {
96 InputStream openRead() throws IOException;
97 OutputStream startWrite() throws IOException;
98 void finishWrite(OutputStream os, boolean success);
99 }
Dianne Hackbornc652de82013-02-15 16:32:56 -0800100
Garfield Tane0846042018-07-26 13:42:04 -0700101 private static class Entry {
Garfield Tan90c90052018-10-08 12:29:41 -0700102 private final String mName;
103 private int mOverscanLeft;
104 private int mOverscanTop;
105 private int mOverscanRight;
106 private int mOverscanBottom;
107 private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
108 private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
109 private int mUserRotation = Surface.ROTATION_0;
Riddle Hsuf53da812018-08-15 22:00:27 +0800110 private int mForcedWidth;
111 private int mForcedHeight;
112 private int mForcedDensity;
113 private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO;
Chilun8753ad32018-10-09 15:56:45 +0800114 private int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED;
115 private boolean mShouldShowWithInsecureKeyguard = false;
116 private boolean mShouldShowSystemDecors = false;
117 private boolean mShouldShowIme = false;
Garfield Tan7fbca052019-02-19 10:45:35 -0800118 private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation =
119 FIXED_TO_USER_ROTATION_DEFAULT;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800120
Chilun8753ad32018-10-09 15:56:45 +0800121 private Entry(String name) {
122 mName = name;
Garfield Tan90c90052018-10-08 12:29:41 -0700123 }
124
Andrii Kulian453347f2019-03-17 20:49:39 -0700125 private Entry(String name, Entry copyFrom) {
126 this(name);
127 mOverscanLeft = copyFrom.mOverscanLeft;
128 mOverscanTop = copyFrom.mOverscanTop;
129 mOverscanRight = copyFrom.mOverscanRight;
130 mOverscanBottom = copyFrom.mOverscanBottom;
131 mWindowingMode = copyFrom.mWindowingMode;
132 mUserRotationMode = copyFrom.mUserRotationMode;
133 mUserRotation = copyFrom.mUserRotation;
134 mForcedWidth = copyFrom.mForcedWidth;
135 mForcedHeight = copyFrom.mForcedHeight;
136 mForcedDensity = copyFrom.mForcedDensity;
137 mForcedScalingMode = copyFrom.mForcedScalingMode;
138 mRemoveContentMode = copyFrom.mRemoveContentMode;
139 mShouldShowWithInsecureKeyguard = copyFrom.mShouldShowWithInsecureKeyguard;
140 mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors;
141 mShouldShowIme = copyFrom.mShouldShowIme;
142 mFixedToUserRotation = copyFrom.mFixedToUserRotation;
143 }
144
Riddle Hsuf53da812018-08-15 22:00:27 +0800145 /** @return {@code true} if all values are default. */
Garfield Tan90c90052018-10-08 12:29:41 -0700146 private boolean isEmpty() {
147 return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
148 && mOverscanBottom == 0
149 && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
150 && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
Riddle Hsuf53da812018-08-15 22:00:27 +0800151 && mUserRotation == Surface.ROTATION_0
152 && mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
Chilun8753ad32018-10-09 15:56:45 +0800153 && mForcedScalingMode == FORCE_SCALING_MODE_AUTO
154 && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
155 && !mShouldShowWithInsecureKeyguard
156 && !mShouldShowSystemDecors
Garfield Tanff362222018-11-14 17:52:32 -0800157 && !mShouldShowIme
Garfield Tan7fbca052019-02-19 10:45:35 -0800158 && mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800159 }
160 }
161
Chilun8753ad32018-10-09 15:56:45 +0800162 DisplayWindowSettings(WindowManagerService service) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700163 this(service, new AtomicFileStorage());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800164 }
165
Garfield Tane0846042018-07-26 13:42:04 -0700166 @VisibleForTesting
Andrii Kulian453347f2019-03-17 20:49:39 -0700167 DisplayWindowSettings(WindowManagerService service, SettingPersister storageImpl) {
Garfield Tane0846042018-07-26 13:42:04 -0700168 mService = service;
Andrii Kulian453347f2019-03-17 20:49:39 -0700169 mStorage = storageImpl;
Riddle Hsuf53da812018-08-15 22:00:27 +0800170 readSettings();
Garfield Tane0846042018-07-26 13:42:04 -0700171 }
172
Andrii Kulian453347f2019-03-17 20:49:39 -0700173 private @Nullable Entry getEntry(DisplayInfo displayInfo) {
174 final String identifier = getIdentifier(displayInfo);
Wale Ogunwale361ca212014-11-20 11:42:38 -0800175 Entry entry;
Andrii Kulian453347f2019-03-17 20:49:39 -0700176 // Try to get corresponding entry using preferred identifier for the current config.
177 if ((entry = mEntries.get(identifier)) != null) {
178 return entry;
Wale Ogunwale361ca212014-11-20 11:42:38 -0800179 }
Andrii Kulian453347f2019-03-17 20:49:39 -0700180 // Else, fall back to the display name.
181 if ((entry = mEntries.get(displayInfo.name)) != null) {
182 // Found an entry stored with old identifier - upgrade to the new type now.
183 return updateIdentifierForEntry(entry, displayInfo);
184 }
185 return null;
Garfield Tane0846042018-07-26 13:42:04 -0700186 }
187
Riddle Hsuf53da812018-08-15 22:00:27 +0800188 private Entry getOrCreateEntry(DisplayInfo displayInfo) {
189 final Entry entry = getEntry(displayInfo);
Andrii Kulian453347f2019-03-17 20:49:39 -0700190 return entry != null ? entry : new Entry(getIdentifier(displayInfo));
191 }
192
193 /**
194 * Upgrades the identifier of a legacy entry. Does it by copying the data from the old record
195 * and clearing the old key in memory. The entry will be written to storage next time when a
196 * setting changes.
197 */
198 private Entry updateIdentifierForEntry(Entry entry, DisplayInfo displayInfo) {
199 final Entry newEntry = new Entry(getIdentifier(displayInfo), entry);
200 removeEntry(displayInfo);
201 mEntries.put(newEntry.mName, newEntry);
202 return newEntry;
Garfield Tan90c90052018-10-08 12:29:41 -0700203 }
204
Riddle Hsuf53da812018-08-15 22:00:27 +0800205 void setOverscanLocked(DisplayInfo displayInfo, int left, int top, int right, int bottom) {
206 final Entry entry = getOrCreateEntry(displayInfo);
Garfield Tan90c90052018-10-08 12:29:41 -0700207 entry.mOverscanLeft = left;
208 entry.mOverscanTop = top;
209 entry.mOverscanRight = right;
210 entry.mOverscanBottom = bottom;
Riddle Hsuf53da812018-08-15 22:00:27 +0800211 writeSettingsIfNeeded(entry, displayInfo);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800212 }
213
Riddle Hsuf53da812018-08-15 22:00:27 +0800214 void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
215 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
216 final Entry entry = getOrCreateEntry(displayInfo);
217 entry.mUserRotationMode = rotationMode;
218 entry.mUserRotation = rotation;
219 writeSettingsIfNeeded(entry, displayInfo);
220 }
221
222 void setForcedSize(DisplayContent displayContent, int width, int height) {
223 if (displayContent.isDefaultDisplay) {
224 final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
225 Settings.Global.putString(mService.mContext.getContentResolver(),
226 Settings.Global.DISPLAY_SIZE_FORCED, sizeString);
227 return;
228 }
229
230 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
231 final Entry entry = getOrCreateEntry(displayInfo);
232 entry.mForcedWidth = width;
233 entry.mForcedHeight = height;
234 writeSettingsIfNeeded(entry, displayInfo);
235 }
236
237 void setForcedDensity(DisplayContent displayContent, int density, int userId) {
238 if (displayContent.isDefaultDisplay) {
239 final String densityString = density == 0 ? "" : Integer.toString(density);
240 Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
241 Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
242 return;
243 }
244
245 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
246 final Entry entry = getOrCreateEntry(displayInfo);
247 entry.mForcedDensity = density;
248 writeSettingsIfNeeded(entry, displayInfo);
249 }
250
251 void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
252 if (displayContent.isDefaultDisplay) {
253 Settings.Global.putInt(mService.mContext.getContentResolver(),
254 Settings.Global.DISPLAY_SCALING_FORCE, mode);
255 return;
256 }
257
258 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
259 final Entry entry = getOrCreateEntry(displayInfo);
260 entry.mForcedScalingMode = mode;
261 writeSettingsIfNeeded(entry, displayInfo);
262 }
263
Garfield Tan7fbca052019-02-19 10:45:35 -0800264 void setFixedToUserRotation(DisplayContent displayContent,
265 @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
Garfield Tanff362222018-11-14 17:52:32 -0800266 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
267 final Entry entry = getOrCreateEntry(displayInfo);
268 entry.mFixedToUserRotation = fixedToUserRotation;
269 writeSettingsIfNeeded(entry, displayInfo);
270 }
271
Riddle Hsuf53da812018-08-15 22:00:27 +0800272 private int getWindowingModeLocked(Entry entry, int displayId) {
Garfield Tan90c90052018-10-08 12:29:41 -0700273 int windowingMode = entry != null ? entry.mWindowingMode
Garfield Tane0846042018-07-26 13:42:04 -0700274 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
275 // This display used to be in freeform, but we don't support freeform anymore, so fall
276 // back to fullscreen.
277 if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
278 && !mService.mSupportsFreeformWindowManagement) {
279 return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
280 }
281 // No record is present so use default windowing mode policy.
282 if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
Andrii Kulian15cfb422018-11-07 13:38:49 -0800283 final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
284 && displayId != Display.DEFAULT_DISPLAY;
285 windowingMode = mService.mSupportsFreeformWindowManagement
286 && (mService.mIsPc || forceDesktopMode)
287 ? WindowConfiguration.WINDOWING_MODE_FREEFORM
288 : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
Garfield Tane0846042018-07-26 13:42:04 -0700289 }
290 return windowingMode;
291 }
292
Chilun8753ad32018-10-09 15:56:45 +0800293 int getWindowingModeLocked(DisplayContent dc) {
294 final DisplayInfo displayInfo = dc.getDisplayInfo();
295 final Entry entry = getEntry(displayInfo);
296 return getWindowingModeLocked(entry, dc.getDisplayId());
297 }
298
299 void setWindowingModeLocked(DisplayContent dc, int mode) {
300 final DisplayInfo displayInfo = dc.getDisplayInfo();
301 final Entry entry = getOrCreateEntry(displayInfo);
302 entry.mWindowingMode = mode;
303 dc.setWindowingMode(mode);
304 writeSettingsIfNeeded(entry, displayInfo);
305 }
306
307 int getRemoveContentModeLocked(DisplayContent dc) {
308 final DisplayInfo displayInfo = dc.getDisplayInfo();
309 final Entry entry = getEntry(displayInfo);
310 if (entry == null || entry.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
311 if (dc.isPrivate()) {
312 // For private displays by default content is destroyed on removal.
313 return REMOVE_CONTENT_MODE_DESTROY;
314 }
315 // For other displays by default content is moved to primary on removal.
316 return REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
317 }
318 return entry.mRemoveContentMode;
319 }
320
321 void setRemoveContentModeLocked(DisplayContent dc, int mode) {
322 final DisplayInfo displayInfo = dc.getDisplayInfo();
323 final Entry entry = getOrCreateEntry(displayInfo);
324 entry.mRemoveContentMode = mode;
325 writeSettingsIfNeeded(entry, displayInfo);
326 }
327
328 boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) {
329 final DisplayInfo displayInfo = dc.getDisplayInfo();
330 final Entry entry = getEntry(displayInfo);
331 if (entry == null) {
332 return false;
333 }
334 return entry.mShouldShowWithInsecureKeyguard;
335 }
336
337 void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) {
338 if (!dc.isPrivate() && shouldShow) {
339 Slog.e(TAG, "Public display can't be allowed to show content when locked");
340 return;
341 }
342
343 final DisplayInfo displayInfo = dc.getDisplayInfo();
344 final Entry entry = getOrCreateEntry(displayInfo);
345 entry.mShouldShowWithInsecureKeyguard = shouldShow;
346 writeSettingsIfNeeded(entry, displayInfo);
347 }
348
349 boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
350 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
351 // For default display should show system decors.
352 return true;
353 }
354
355 final DisplayInfo displayInfo = dc.getDisplayInfo();
356 final Entry entry = getEntry(displayInfo);
357 if (entry == null) {
358 return false;
359 }
360 return entry.mShouldShowSystemDecors;
361 }
362
363 void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) {
364 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
365 Slog.e(TAG, "Default display should show system decors");
366 return;
367 }
368
369 final DisplayInfo displayInfo = dc.getDisplayInfo();
370 final Entry entry = getOrCreateEntry(displayInfo);
371 entry.mShouldShowSystemDecors = shouldShow;
372 writeSettingsIfNeeded(entry, displayInfo);
373 }
374
375 boolean shouldShowImeLocked(DisplayContent dc) {
376 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
377 // For default display should shows IME.
378 return true;
379 }
380
381 final DisplayInfo displayInfo = dc.getDisplayInfo();
382 final Entry entry = getEntry(displayInfo);
383 if (entry == null) {
384 return false;
385 }
386 return entry.mShouldShowIme;
387 }
388
389 void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) {
390 if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
391 Slog.e(TAG, "Default display should show IME");
392 return;
393 }
394
395 final DisplayInfo displayInfo = dc.getDisplayInfo();
396 final Entry entry = getOrCreateEntry(displayInfo);
397 entry.mShouldShowIme = shouldShow;
398 writeSettingsIfNeeded(entry, displayInfo);
399 }
400
Riddle Hsuf53da812018-08-15 22:00:27 +0800401 void applySettingsToDisplayLocked(DisplayContent dc) {
Garfield Tan90c90052018-10-08 12:29:41 -0700402 final DisplayInfo displayInfo = dc.getDisplayInfo();
Tadashi G. Takaokafd911182018-12-20 18:19:57 +0900403 final Entry entry = getOrCreateEntry(displayInfo);
Garfield Tan90c90052018-10-08 12:29:41 -0700404
Riddle Hsuf53da812018-08-15 22:00:27 +0800405 // Setting windowing mode first, because it may override overscan values later.
406 dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
407
Riddle Hsuf53da812018-08-15 22:00:27 +0800408 displayInfo.overscanLeft = entry.mOverscanLeft;
409 displayInfo.overscanTop = entry.mOverscanTop;
410 displayInfo.overscanRight = entry.mOverscanRight;
411 displayInfo.overscanBottom = entry.mOverscanBottom;
412
Garfield Tanff362222018-11-14 17:52:32 -0800413 dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
414 entry.mUserRotation, entry.mFixedToUserRotation);
Riddle Hsuf53da812018-08-15 22:00:27 +0800415
416 if (entry.mForcedDensity != 0) {
417 dc.mBaseDisplayDensity = entry.mForcedDensity;
418 }
419 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
420 dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
421 dc.mBaseDisplayDensity);
422 }
423 dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
Garfield Tan90c90052018-10-08 12:29:41 -0700424 }
425
Garfield Tanb5910b42019-03-14 14:50:59 -0700426 /**
427 * Updates settings for the given display after system features are loaded into window manager
428 * service, e.g. if this device is PC and if this device supports freeform.
429 *
430 * @param dc the given display.
431 * @return {@code true} if any settings for this display has changed; {@code false} if nothing
432 * changed.
433 */
434 boolean updateSettingsForDisplay(DisplayContent dc) {
435 if (dc.getWindowingMode() != getWindowingModeLocked(dc)) {
436 // For the time being the only thing that may change is windowing mode, so just update
437 // that.
438 dc.setWindowingMode(getWindowingModeLocked(dc));
439 return true;
440 }
441 return false;
442 }
443
Riddle Hsuf53da812018-08-15 22:00:27 +0800444 private void readSettings() {
Andrii Kulian453347f2019-03-17 20:49:39 -0700445 InputStream stream;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800446 try {
Andrii Kulian453347f2019-03-17 20:49:39 -0700447 stream = mStorage.openRead();
448 } catch (IOException e) {
449 Slog.i(TAG, "No existing display settings, starting empty");
Dianne Hackbornc652de82013-02-15 16:32:56 -0800450 return;
451 }
452 boolean success = false;
453 try {
454 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100455 parser.setInput(stream, StandardCharsets.UTF_8.name());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800456 int type;
457 while ((type = parser.next()) != XmlPullParser.START_TAG
458 && type != XmlPullParser.END_DOCUMENT) {
Wale Ogunwale361ca212014-11-20 11:42:38 -0800459 // Do nothing.
Dianne Hackbornc652de82013-02-15 16:32:56 -0800460 }
461
462 if (type != XmlPullParser.START_TAG) {
463 throw new IllegalStateException("no start tag found");
464 }
465
466 int outerDepth = parser.getDepth();
467 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
468 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
469 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
470 continue;
471 }
472
473 String tagName = parser.getName();
474 if (tagName.equals("display")) {
475 readDisplay(parser);
Andrii Kulian453347f2019-03-17 20:49:39 -0700476 } else if (tagName.equals("config")) {
477 readConfig(parser);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800478 } else {
479 Slog.w(TAG, "Unknown element under <display-settings>: "
480 + parser.getName());
481 XmlUtils.skipCurrentTag(parser);
482 }
483 }
484 success = true;
485 } catch (IllegalStateException e) {
486 Slog.w(TAG, "Failed parsing " + e);
487 } catch (NullPointerException e) {
488 Slog.w(TAG, "Failed parsing " + e);
489 } catch (NumberFormatException e) {
490 Slog.w(TAG, "Failed parsing " + e);
491 } catch (XmlPullParserException e) {
492 Slog.w(TAG, "Failed parsing " + e);
493 } catch (IOException e) {
494 Slog.w(TAG, "Failed parsing " + e);
495 } catch (IndexOutOfBoundsException e) {
496 Slog.w(TAG, "Failed parsing " + e);
497 } finally {
498 if (!success) {
499 mEntries.clear();
500 }
501 try {
502 stream.close();
503 } catch (IOException e) {
504 }
505 }
506 }
507
508 private int getIntAttribute(XmlPullParser parser, String name) {
Garfield Tane0846042018-07-26 13:42:04 -0700509 return getIntAttribute(parser, name, 0 /* defaultValue */);
510 }
511
512 private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
Dianne Hackbornc652de82013-02-15 16:32:56 -0800513 try {
Chilun8753ad32018-10-09 15:56:45 +0800514 final String str = parser.getAttributeValue(null, name);
Garfield Tane0846042018-07-26 13:42:04 -0700515 return str != null ? Integer.parseInt(str) : defaultValue;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800516 } catch (NumberFormatException e) {
Garfield Tane0846042018-07-26 13:42:04 -0700517 return defaultValue;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800518 }
519 }
520
Chilun8753ad32018-10-09 15:56:45 +0800521 private boolean getBooleanAttribute(XmlPullParser parser, String name) {
522 return getBooleanAttribute(parser, name, false /* defaultValue */);
523 }
524
525 private boolean getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue) {
526 try {
527 final String str = parser.getAttributeValue(null, name);
528 return str != null ? Boolean.parseBoolean(str) : defaultValue;
529 } catch (NumberFormatException e) {
530 return defaultValue;
531 }
532 }
533
Dianne Hackbornc652de82013-02-15 16:32:56 -0800534 private void readDisplay(XmlPullParser parser) throws NumberFormatException,
535 XmlPullParserException, IOException {
536 String name = parser.getAttributeValue(null, "name");
537 if (name != null) {
538 Entry entry = new Entry(name);
Garfield Tan90c90052018-10-08 12:29:41 -0700539 entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
540 entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
541 entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
542 entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
543 entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
Garfield Tane0846042018-07-26 13:42:04 -0700544 WindowConfiguration.WINDOWING_MODE_UNDEFINED);
Garfield Tan90c90052018-10-08 12:29:41 -0700545 entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
546 WindowManagerPolicy.USER_ROTATION_FREE);
547 entry.mUserRotation = getIntAttribute(parser, "userRotation",
548 Surface.ROTATION_0);
Riddle Hsuf53da812018-08-15 22:00:27 +0800549 entry.mForcedWidth = getIntAttribute(parser, "forcedWidth");
550 entry.mForcedHeight = getIntAttribute(parser, "forcedHeight");
551 entry.mForcedDensity = getIntAttribute(parser, "forcedDensity");
552 entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode",
553 FORCE_SCALING_MODE_AUTO);
Chilun8753ad32018-10-09 15:56:45 +0800554 entry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode",
555 REMOVE_CONTENT_MODE_UNDEFINED);
556 entry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser,
557 "shouldShowWithInsecureKeyguard");
558 entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
559 entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
Garfield Tan7fbca052019-02-19 10:45:35 -0800560 entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
Dianne Hackbornc652de82013-02-15 16:32:56 -0800561 mEntries.put(name, entry);
562 }
563 XmlUtils.skipCurrentTag(parser);
564 }
565
Andrii Kulian453347f2019-03-17 20:49:39 -0700566 private void readConfig(XmlPullParser parser) throws NumberFormatException,
567 XmlPullParserException, IOException {
568 mIdentifier = getIntAttribute(parser, "identifier");
569 XmlUtils.skipCurrentTag(parser);
570 }
571
Riddle Hsuf53da812018-08-15 22:00:27 +0800572 private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700573 if (changedEntry.isEmpty() && !removeEntry(displayInfo)) {
574 // The entry didn't exist so nothing is changed and no need to update the file.
575 return;
Riddle Hsuf53da812018-08-15 22:00:27 +0800576 }
577
Andrii Kulian453347f2019-03-17 20:49:39 -0700578 mEntries.put(getIdentifier(displayInfo), changedEntry);
579 writeSettings();
580 }
581
582 private void writeSettings() {
583 OutputStream stream;
Dianne Hackbornc652de82013-02-15 16:32:56 -0800584 try {
Andrii Kulian453347f2019-03-17 20:49:39 -0700585 stream = mStorage.startWrite();
Dianne Hackbornc652de82013-02-15 16:32:56 -0800586 } catch (IOException e) {
587 Slog.w(TAG, "Failed to write display settings: " + e);
588 return;
589 }
590
591 try {
592 XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100593 out.setOutput(stream, StandardCharsets.UTF_8.name());
Dianne Hackbornc652de82013-02-15 16:32:56 -0800594 out.startDocument(null, true);
Andrii Kulian453347f2019-03-17 20:49:39 -0700595
Dianne Hackbornc652de82013-02-15 16:32:56 -0800596 out.startTag(null, "display-settings");
597
Andrii Kulian453347f2019-03-17 20:49:39 -0700598 out.startTag(null, "config");
599 out.attribute(null, "identifier", Integer.toString(mIdentifier));
600 out.endTag(null, "config");
601
Dianne Hackbornc652de82013-02-15 16:32:56 -0800602 for (Entry entry : mEntries.values()) {
603 out.startTag(null, "display");
Garfield Tan90c90052018-10-08 12:29:41 -0700604 out.attribute(null, "name", entry.mName);
605 if (entry.mOverscanLeft != 0) {
606 out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800607 }
Garfield Tan90c90052018-10-08 12:29:41 -0700608 if (entry.mOverscanTop != 0) {
609 out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800610 }
Garfield Tan90c90052018-10-08 12:29:41 -0700611 if (entry.mOverscanRight != 0) {
612 out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800613 }
Garfield Tan90c90052018-10-08 12:29:41 -0700614 if (entry.mOverscanBottom != 0) {
615 out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
Dianne Hackbornc652de82013-02-15 16:32:56 -0800616 }
Garfield Tan90c90052018-10-08 12:29:41 -0700617 if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
618 out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
619 }
620 if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
621 out.attribute(null, "userRotationMode",
622 Integer.toString(entry.mUserRotationMode));
623 }
624 if (entry.mUserRotation != Surface.ROTATION_0) {
625 out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
Garfield Tane0846042018-07-26 13:42:04 -0700626 }
Riddle Hsuf53da812018-08-15 22:00:27 +0800627 if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
628 out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth));
629 out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight));
630 }
631 if (entry.mForcedDensity != 0) {
632 out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity));
633 }
634 if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) {
635 out.attribute(null, "forcedScalingMode",
636 Integer.toString(entry.mForcedScalingMode));
637 }
Chilun8753ad32018-10-09 15:56:45 +0800638 if (entry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
639 out.attribute(null, "removeContentMode",
640 Integer.toString(entry.mRemoveContentMode));
641 }
642 if (entry.mShouldShowWithInsecureKeyguard) {
643 out.attribute(null, "shouldShowWithInsecureKeyguard",
644 Boolean.toString(entry.mShouldShowWithInsecureKeyguard));
645 }
646 if (entry.mShouldShowSystemDecors) {
647 out.attribute(null, "shouldShowSystemDecors",
648 Boolean.toString(entry.mShouldShowSystemDecors));
649 }
650 if (entry.mShouldShowIme) {
651 out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
652 }
Garfield Tan7fbca052019-02-19 10:45:35 -0800653 if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) {
Garfield Tanff362222018-11-14 17:52:32 -0800654 out.attribute(null, "fixedToUserRotation",
Garfield Tan7fbca052019-02-19 10:45:35 -0800655 Integer.toString(entry.mFixedToUserRotation));
Garfield Tanff362222018-11-14 17:52:32 -0800656 }
Dianne Hackbornc652de82013-02-15 16:32:56 -0800657 out.endTag(null, "display");
658 }
659
660 out.endTag(null, "display-settings");
661 out.endDocument();
Andrii Kulian453347f2019-03-17 20:49:39 -0700662 mStorage.finishWrite(stream, true /* success */);
Dianne Hackbornc652de82013-02-15 16:32:56 -0800663 } catch (IOException e) {
Andrii Kulian453347f2019-03-17 20:49:39 -0700664 Slog.w(TAG, "Failed to write display window settings.", e);
665 mStorage.finishWrite(stream, false /* success */);
666 }
667 }
668
669 /**
670 * Removes an entry from {@link #mEntries} cache. Looks up by new and previously used
671 * identifiers.
672 */
673 private boolean removeEntry(DisplayInfo displayInfo) {
674 // Remove entry based on primary identifier.
675 boolean removed = mEntries.remove(getIdentifier(displayInfo)) != null;
676 // Ensure that legacy entries are cleared as well.
677 removed |= mEntries.remove(displayInfo.uniqueId) != null;
678 removed |= mEntries.remove(displayInfo.name) != null;
679 return removed;
680 }
681
682 /** Gets the identifier of choice for the current config. */
683 private String getIdentifier(DisplayInfo displayInfo) {
684 if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) {
685 // Config suggests using port as identifier for physical displays.
686 if (displayInfo.address instanceof DisplayAddress.Physical) {
687 return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort();
688 }
689 }
690 return displayInfo.uniqueId;
691 }
692
693 private static class AtomicFileStorage implements SettingPersister {
694 private final AtomicFile mAtomicFile;
695
696 AtomicFileStorage() {
Yuncheol Heof6461442019-07-12 10:03:40 -0700697 final File folder = new File(Environment.getDataDirectory(), SYSTEM_DIRECTORY);
698 final File settingsFile = new File(folder, DISPLAY_SETTINGS_FILE_NAME);
699 // If display_settings.xml doesn't exist, try to copy the vendor's one instead
700 // in order to provide the vendor specific initialization.
701 if (!settingsFile.exists()) {
702 copyVendorSettings(settingsFile);
703 }
704 mAtomicFile = new AtomicFile(settingsFile, WM_DISPLAY_COMMIT_TAG);
705 }
706
707 private static void copyVendorSettings(File target) {
708 final File vendorFile = new File(Environment.getVendorDirectory(),
709 VENDOR_DISPLAY_SETTINGS_PATH);
710 if (vendorFile.canRead()) {
711 try {
712 FileUtils.copy(vendorFile, target);
713 } catch (IOException e) {
714 Slog.e(TAG, "Failed to copy vendor display_settings.xml");
715 }
716 }
Andrii Kulian453347f2019-03-17 20:49:39 -0700717 }
718
719 @Override
720 public InputStream openRead() throws FileNotFoundException {
721 return mAtomicFile.openRead();
722 }
723
724 @Override
725 public OutputStream startWrite() throws IOException {
726 return mAtomicFile.startWrite();
727 }
728
729 @Override
730 public void finishWrite(OutputStream os, boolean success) {
731 if (!(os instanceof FileOutputStream)) {
732 throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
733 }
734 FileOutputStream fos = (FileOutputStream) os;
735 if (success) {
736 mAtomicFile.finishWrite(fos);
737 } else {
738 mAtomicFile.failWrite(fos);
739 }
Dianne Hackbornc652de82013-02-15 16:32:56 -0800740 }
741 }
742}