/*
|
* Copyright (C) 2013 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wm;
|
|
import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
|
import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
|
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
|
|
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
|
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
|
import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
|
import android.annotation.IntDef;
|
import android.annotation.Nullable;
|
import android.app.WindowConfiguration;
|
import android.os.Environment;
|
import android.os.SystemProperties;
|
import android.provider.Settings;
|
import android.util.AtomicFile;
|
import android.util.Slog;
|
import android.util.Xml;
|
import android.view.Display;
|
import android.view.DisplayAddress;
|
import android.view.DisplayInfo;
|
import android.view.Surface;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.util.FastXmlSerializer;
|
import com.android.internal.util.XmlUtils;
|
import com.android.server.policy.WindowManagerPolicy;
|
import com.android.server.wm.DisplayContent.ForceScalingMode;
|
|
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlSerializer;
|
|
import java.io.File;
|
import java.io.FileNotFoundException;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.OutputStream;
|
import java.nio.charset.StandardCharsets;
|
import java.util.HashMap;
|
|
/**
|
* Current persistent settings about a display
|
*/
|
class DisplayWindowSettings {
|
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM;
|
|
private static final int IDENTIFIER_UNIQUE_ID = 0;
|
private static final int IDENTIFIER_PORT = 1;
|
@IntDef(prefix = { "IDENTIFIER_" }, value = {
|
IDENTIFIER_UNIQUE_ID,
|
IDENTIFIER_PORT,
|
})
|
@interface DisplayIdentifierType {}
|
|
private final WindowManagerService mService;
|
private final HashMap<String, Entry> mEntries = new HashMap<>();
|
private final SettingPersister mStorage;
|
|
/**
|
* The preferred type of a display identifier to use when storing and retrieving entries.
|
* {@link #getIdentifier(DisplayInfo)} must be used to get current preferred identifier for each
|
* display. It will fall back to using {@link #IDENTIFIER_UNIQUE_ID} if the currently selected
|
* one is not applicable to a particular display.
|
*/
|
@DisplayIdentifierType
|
private int mIdentifier = IDENTIFIER_UNIQUE_ID;
|
|
/** Interface for persisting the display window settings. */
|
interface SettingPersister {
|
InputStream openRead() throws IOException;
|
OutputStream startWrite() throws IOException;
|
void finishWrite(OutputStream os, boolean success);
|
}
|
|
private static class Entry {
|
private final String mName;
|
private int mOverscanLeft;
|
private int mOverscanTop;
|
private int mOverscanRight;
|
private int mOverscanBottom;
|
private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
|
private int mUserRotation = Surface.ROTATION_0;
|
private int mForcedWidth;
|
private int mForcedHeight;
|
private int mForcedDensity;
|
private int mForcedScalingMode = FORCE_SCALING_MODE_AUTO;
|
private int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED;
|
private boolean mShouldShowWithInsecureKeyguard = false;
|
private boolean mShouldShowSystemDecors = false;
|
private boolean mShouldShowIme = false;
|
private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation =
|
FIXED_TO_USER_ROTATION_DEFAULT;
|
|
private Entry(String name) {
|
mName = name;
|
}
|
|
private Entry(String name, Entry copyFrom) {
|
this(name);
|
mOverscanLeft = copyFrom.mOverscanLeft;
|
mOverscanTop = copyFrom.mOverscanTop;
|
mOverscanRight = copyFrom.mOverscanRight;
|
mOverscanBottom = copyFrom.mOverscanBottom;
|
mWindowingMode = copyFrom.mWindowingMode;
|
mUserRotationMode = copyFrom.mUserRotationMode;
|
mUserRotation = copyFrom.mUserRotation;
|
mForcedWidth = copyFrom.mForcedWidth;
|
mForcedHeight = copyFrom.mForcedHeight;
|
mForcedDensity = copyFrom.mForcedDensity;
|
mForcedScalingMode = copyFrom.mForcedScalingMode;
|
mRemoveContentMode = copyFrom.mRemoveContentMode;
|
mShouldShowWithInsecureKeyguard = copyFrom.mShouldShowWithInsecureKeyguard;
|
mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors;
|
mShouldShowIme = copyFrom.mShouldShowIme;
|
mFixedToUserRotation = copyFrom.mFixedToUserRotation;
|
}
|
|
/** @return {@code true} if all values are default. */
|
private boolean isEmpty() {
|
return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
|
&& mOverscanBottom == 0
|
&& mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
|
&& mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
|
&& mUserRotation == Surface.ROTATION_0
|
&& mForcedWidth == 0 && mForcedHeight == 0 && mForcedDensity == 0
|
&& mForcedScalingMode == FORCE_SCALING_MODE_AUTO
|
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
|
&& !mShouldShowWithInsecureKeyguard
|
&& !mShouldShowSystemDecors
|
&& !mShouldShowIme
|
&& mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT;
|
}
|
}
|
|
DisplayWindowSettings(WindowManagerService service) {
|
this(service, new AtomicFileStorage());
|
}
|
|
@VisibleForTesting
|
DisplayWindowSettings(WindowManagerService service, SettingPersister storageImpl) {
|
mService = service;
|
mStorage = storageImpl;
|
readSettings();
|
}
|
|
private @Nullable Entry getEntry(DisplayInfo displayInfo) {
|
final String identifier = getIdentifier(displayInfo);
|
Entry entry;
|
// Try to get corresponding entry using preferred identifier for the current config.
|
if ((entry = mEntries.get(identifier)) != null) {
|
return entry;
|
}
|
// Else, fall back to the display name.
|
if ((entry = mEntries.get(displayInfo.name)) != null) {
|
// Found an entry stored with old identifier - upgrade to the new type now.
|
return updateIdentifierForEntry(entry, displayInfo);
|
}
|
return null;
|
}
|
|
private Entry getOrCreateEntry(DisplayInfo displayInfo) {
|
final Entry entry = getEntry(displayInfo);
|
return entry != null ? entry : new Entry(getIdentifier(displayInfo));
|
}
|
|
/**
|
* Upgrades the identifier of a legacy entry. Does it by copying the data from the old record
|
* and clearing the old key in memory. The entry will be written to storage next time when a
|
* setting changes.
|
*/
|
private Entry updateIdentifierForEntry(Entry entry, DisplayInfo displayInfo) {
|
final Entry newEntry = new Entry(getIdentifier(displayInfo), entry);
|
removeEntry(displayInfo);
|
mEntries.put(newEntry.mName, newEntry);
|
return newEntry;
|
}
|
|
void setOverscanLocked(DisplayInfo displayInfo, int left, int top, int right, int bottom) {
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mOverscanLeft = left;
|
entry.mOverscanTop = top;
|
entry.mOverscanRight = right;
|
entry.mOverscanBottom = bottom;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) {
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mUserRotationMode = rotationMode;
|
entry.mUserRotation = rotation;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void setForcedSize(DisplayContent displayContent, int width, int height) {
|
if (displayContent.isDefaultDisplay) {
|
final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
|
Settings.Global.putString(mService.mContext.getContentResolver(),
|
Settings.Global.DISPLAY_SIZE_FORCED, sizeString);
|
SystemProperties.set("persist.display.size_force", sizeString);
|
return;
|
}
|
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mForcedWidth = width;
|
entry.mForcedHeight = height;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void setForcedDensity(DisplayContent displayContent, int density, int userId) {
|
if (displayContent.isDefaultDisplay) {
|
final String densityString = density == 0 ? "" : Integer.toString(density);
|
Settings.Secure.putStringForUser(mService.mContext.getContentResolver(),
|
Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId);
|
return;
|
}
|
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mForcedDensity = density;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) {
|
if (displayContent.isDefaultDisplay) {
|
Settings.Global.putInt(mService.mContext.getContentResolver(),
|
Settings.Global.DISPLAY_SCALING_FORCE, mode);
|
return;
|
}
|
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mForcedScalingMode = mode;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void setFixedToUserRotation(DisplayContent displayContent,
|
@DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
|
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mFixedToUserRotation = fixedToUserRotation;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
private int getWindowingModeLocked(Entry entry, int displayId) {
|
int windowingMode = entry != null ? entry.mWindowingMode
|
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
// This display used to be in freeform, but we don't support freeform anymore, so fall
|
// back to fullscreen.
|
if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
|
&& !mService.mSupportsFreeformWindowManagement) {
|
return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
}
|
// No record is present so use default windowing mode policy.
|
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
|
final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
|
&& displayId != Display.DEFAULT_DISPLAY;
|
windowingMode = mService.mSupportsFreeformWindowManagement
|
&& (mService.mIsPc || forceDesktopMode)
|
? WindowConfiguration.WINDOWING_MODE_FREEFORM
|
: WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
}
|
return windowingMode;
|
}
|
|
int getWindowingModeLocked(DisplayContent dc) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getEntry(displayInfo);
|
return getWindowingModeLocked(entry, dc.getDisplayId());
|
}
|
|
void setWindowingModeLocked(DisplayContent dc, int mode) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mWindowingMode = mode;
|
dc.setWindowingMode(mode);
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
int getRemoveContentModeLocked(DisplayContent dc) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getEntry(displayInfo);
|
if (entry == null || entry.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
|
if (dc.isPrivate()) {
|
// For private displays by default content is destroyed on removal.
|
return REMOVE_CONTENT_MODE_DESTROY;
|
}
|
// For other displays by default content is moved to primary on removal.
|
return REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
|
}
|
return entry.mRemoveContentMode;
|
}
|
|
void setRemoveContentModeLocked(DisplayContent dc, int mode) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mRemoveContentMode = mode;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getEntry(displayInfo);
|
if (entry == null) {
|
return false;
|
}
|
return entry.mShouldShowWithInsecureKeyguard;
|
}
|
|
void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) {
|
if (!dc.isPrivate() && shouldShow) {
|
Slog.e(TAG, "Public display can't be allowed to show content when locked");
|
return;
|
}
|
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mShouldShowWithInsecureKeyguard = shouldShow;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
|
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
|
// For default display should show system decors.
|
return true;
|
}
|
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getEntry(displayInfo);
|
if (entry == null) {
|
return false;
|
}
|
return entry.mShouldShowSystemDecors;
|
}
|
|
void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) {
|
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
|
Slog.e(TAG, "Default display should show system decors");
|
return;
|
}
|
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mShouldShowSystemDecors = shouldShow;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
boolean shouldShowImeLocked(DisplayContent dc) {
|
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
|
// For default display should shows IME.
|
return true;
|
}
|
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getEntry(displayInfo);
|
if (entry == null) {
|
return false;
|
}
|
return entry.mShouldShowIme;
|
}
|
|
void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) {
|
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY && !shouldShow) {
|
Slog.e(TAG, "Default display should show IME");
|
return;
|
}
|
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
entry.mShouldShowIme = shouldShow;
|
writeSettingsIfNeeded(entry, displayInfo);
|
}
|
|
void applySettingsToDisplayLocked(DisplayContent dc) {
|
final DisplayInfo displayInfo = dc.getDisplayInfo();
|
final Entry entry = getOrCreateEntry(displayInfo);
|
|
// Setting windowing mode first, because it may override overscan values later.
|
dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
|
|
displayInfo.overscanLeft = entry.mOverscanLeft;
|
displayInfo.overscanTop = entry.mOverscanTop;
|
displayInfo.overscanRight = entry.mOverscanRight;
|
displayInfo.overscanBottom = entry.mOverscanBottom;
|
|
dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
|
entry.mUserRotation, entry.mFixedToUserRotation);
|
|
if (entry.mForcedDensity != 0) {
|
dc.mBaseDisplayDensity = entry.mForcedDensity;
|
}
|
if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
|
dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
|
dc.mBaseDisplayDensity);
|
}
|
dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
|
}
|
|
/**
|
* Updates settings for the given display after system features are loaded into window manager
|
* service, e.g. if this device is PC and if this device supports freeform.
|
*
|
* @param dc the given display.
|
* @return {@code true} if any settings for this display has changed; {@code false} if nothing
|
* changed.
|
*/
|
boolean updateSettingsForDisplay(DisplayContent dc) {
|
if (dc.getWindowingMode() != getWindowingModeLocked(dc)) {
|
// For the time being the only thing that may change is windowing mode, so just update
|
// that.
|
dc.setWindowingMode(getWindowingModeLocked(dc));
|
return true;
|
}
|
return false;
|
}
|
|
private void readSettings() {
|
InputStream stream;
|
try {
|
stream = mStorage.openRead();
|
} catch (IOException e) {
|
Slog.i(TAG, "No existing display settings, starting empty");
|
return;
|
}
|
boolean success = false;
|
try {
|
XmlPullParser parser = Xml.newPullParser();
|
parser.setInput(stream, StandardCharsets.UTF_8.name());
|
int type;
|
while ((type = parser.next()) != XmlPullParser.START_TAG
|
&& type != XmlPullParser.END_DOCUMENT) {
|
// Do nothing.
|
}
|
|
if (type != XmlPullParser.START_TAG) {
|
throw new IllegalStateException("no start tag found");
|
}
|
|
int outerDepth = parser.getDepth();
|
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
continue;
|
}
|
|
String tagName = parser.getName();
|
if (tagName.equals("display")) {
|
readDisplay(parser);
|
} else if (tagName.equals("config")) {
|
readConfig(parser);
|
} else {
|
Slog.w(TAG, "Unknown element under <display-settings>: "
|
+ parser.getName());
|
XmlUtils.skipCurrentTag(parser);
|
}
|
}
|
success = true;
|
} catch (IllegalStateException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} catch (NullPointerException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} catch (NumberFormatException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} catch (XmlPullParserException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} catch (IOException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} catch (IndexOutOfBoundsException e) {
|
Slog.w(TAG, "Failed parsing " + e);
|
} finally {
|
if (!success) {
|
mEntries.clear();
|
}
|
try {
|
stream.close();
|
} catch (IOException e) {
|
}
|
}
|
}
|
|
private int getIntAttribute(XmlPullParser parser, String name) {
|
return getIntAttribute(parser, name, 0 /* defaultValue */);
|
}
|
|
private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
|
try {
|
final String str = parser.getAttributeValue(null, name);
|
return str != null ? Integer.parseInt(str) : defaultValue;
|
} catch (NumberFormatException e) {
|
return defaultValue;
|
}
|
}
|
|
private boolean getBooleanAttribute(XmlPullParser parser, String name) {
|
return getBooleanAttribute(parser, name, false /* defaultValue */);
|
}
|
|
private boolean getBooleanAttribute(XmlPullParser parser, String name, boolean defaultValue) {
|
try {
|
final String str = parser.getAttributeValue(null, name);
|
return str != null ? Boolean.parseBoolean(str) : defaultValue;
|
} catch (NumberFormatException e) {
|
return defaultValue;
|
}
|
}
|
|
private void readDisplay(XmlPullParser parser) throws NumberFormatException,
|
XmlPullParserException, IOException {
|
String name = parser.getAttributeValue(null, "name");
|
if (name != null) {
|
Entry entry = new Entry(name);
|
entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
|
entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
|
entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
|
entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
|
entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
|
WindowConfiguration.WINDOWING_MODE_UNDEFINED);
|
entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
|
WindowManagerPolicy.USER_ROTATION_FREE);
|
entry.mUserRotation = getIntAttribute(parser, "userRotation",
|
Surface.ROTATION_0);
|
entry.mForcedWidth = getIntAttribute(parser, "forcedWidth");
|
entry.mForcedHeight = getIntAttribute(parser, "forcedHeight");
|
entry.mForcedDensity = getIntAttribute(parser, "forcedDensity");
|
entry.mForcedScalingMode = getIntAttribute(parser, "forcedScalingMode",
|
FORCE_SCALING_MODE_AUTO);
|
entry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode",
|
REMOVE_CONTENT_MODE_UNDEFINED);
|
entry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser,
|
"shouldShowWithInsecureKeyguard");
|
entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
|
entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
|
entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
|
mEntries.put(name, entry);
|
}
|
XmlUtils.skipCurrentTag(parser);
|
}
|
|
private void readConfig(XmlPullParser parser) throws NumberFormatException,
|
XmlPullParserException, IOException {
|
mIdentifier = getIntAttribute(parser, "identifier");
|
XmlUtils.skipCurrentTag(parser);
|
}
|
|
private void writeSettingsIfNeeded(Entry changedEntry, DisplayInfo displayInfo) {
|
if (changedEntry.isEmpty() && !removeEntry(displayInfo)) {
|
// The entry didn't exist so nothing is changed and no need to update the file.
|
return;
|
}
|
|
mEntries.put(getIdentifier(displayInfo), changedEntry);
|
writeSettings();
|
}
|
|
private void writeSettings() {
|
OutputStream stream;
|
try {
|
stream = mStorage.startWrite();
|
} catch (IOException e) {
|
Slog.w(TAG, "Failed to write display settings: " + e);
|
return;
|
}
|
|
try {
|
XmlSerializer out = new FastXmlSerializer();
|
out.setOutput(stream, StandardCharsets.UTF_8.name());
|
out.startDocument(null, true);
|
|
out.startTag(null, "display-settings");
|
|
out.startTag(null, "config");
|
out.attribute(null, "identifier", Integer.toString(mIdentifier));
|
out.endTag(null, "config");
|
|
for (Entry entry : mEntries.values()) {
|
out.startTag(null, "display");
|
out.attribute(null, "name", entry.mName);
|
if (entry.mOverscanLeft != 0) {
|
out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
|
}
|
if (entry.mOverscanTop != 0) {
|
out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
|
}
|
if (entry.mOverscanRight != 0) {
|
out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
|
}
|
if (entry.mOverscanBottom != 0) {
|
out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
|
}
|
if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
|
out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
|
}
|
if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
|
out.attribute(null, "userRotationMode",
|
Integer.toString(entry.mUserRotationMode));
|
}
|
if (entry.mUserRotation != Surface.ROTATION_0) {
|
out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
|
}
|
if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
|
out.attribute(null, "forcedWidth", Integer.toString(entry.mForcedWidth));
|
out.attribute(null, "forcedHeight", Integer.toString(entry.mForcedHeight));
|
}
|
if (entry.mForcedDensity != 0) {
|
out.attribute(null, "forcedDensity", Integer.toString(entry.mForcedDensity));
|
}
|
if (entry.mForcedScalingMode != FORCE_SCALING_MODE_AUTO) {
|
out.attribute(null, "forcedScalingMode",
|
Integer.toString(entry.mForcedScalingMode));
|
}
|
if (entry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
|
out.attribute(null, "removeContentMode",
|
Integer.toString(entry.mRemoveContentMode));
|
}
|
if (entry.mShouldShowWithInsecureKeyguard) {
|
out.attribute(null, "shouldShowWithInsecureKeyguard",
|
Boolean.toString(entry.mShouldShowWithInsecureKeyguard));
|
}
|
if (entry.mShouldShowSystemDecors) {
|
out.attribute(null, "shouldShowSystemDecors",
|
Boolean.toString(entry.mShouldShowSystemDecors));
|
}
|
if (entry.mShouldShowIme) {
|
out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
|
}
|
if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) {
|
out.attribute(null, "fixedToUserRotation",
|
Integer.toString(entry.mFixedToUserRotation));
|
}
|
out.endTag(null, "display");
|
}
|
|
out.endTag(null, "display-settings");
|
out.endDocument();
|
mStorage.finishWrite(stream, true /* success */);
|
} catch (IOException e) {
|
Slog.w(TAG, "Failed to write display window settings.", e);
|
mStorage.finishWrite(stream, false /* success */);
|
}
|
}
|
|
/**
|
* Removes an entry from {@link #mEntries} cache. Looks up by new and previously used
|
* identifiers.
|
*/
|
private boolean removeEntry(DisplayInfo displayInfo) {
|
// Remove entry based on primary identifier.
|
boolean removed = mEntries.remove(getIdentifier(displayInfo)) != null;
|
// Ensure that legacy entries are cleared as well.
|
removed |= mEntries.remove(displayInfo.uniqueId) != null;
|
removed |= mEntries.remove(displayInfo.name) != null;
|
return removed;
|
}
|
|
/** Gets the identifier of choice for the current config. */
|
private String getIdentifier(DisplayInfo displayInfo) {
|
if (mIdentifier == IDENTIFIER_PORT && displayInfo.address != null) {
|
// Config suggests using port as identifier for physical displays.
|
if (displayInfo.address instanceof DisplayAddress.Physical) {
|
return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort();
|
}
|
}
|
return displayInfo.uniqueId;
|
}
|
|
private static class AtomicFileStorage implements SettingPersister {
|
private final AtomicFile mAtomicFile;
|
|
AtomicFileStorage() {
|
final File folder = new File(Environment.getDataDirectory(), "system");
|
mAtomicFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
|
}
|
|
@Override
|
public InputStream openRead() throws FileNotFoundException {
|
return mAtomicFile.openRead();
|
}
|
|
@Override
|
public OutputStream startWrite() throws IOException {
|
return mAtomicFile.startWrite();
|
}
|
|
@Override
|
public void finishWrite(OutputStream os, boolean success) {
|
if (!(os instanceof FileOutputStream)) {
|
throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
|
}
|
FileOutputStream fos = (FileOutputStream) os;
|
if (success) {
|
mAtomicFile.finishWrite(fos);
|
} else {
|
mAtomicFile.failWrite(fos);
|
}
|
}
|
}
|
}
|