Android multi-screen support-Android12

android12-release


1. Overview and related articles

AOSP > Documentation > Heart Themes > Multiple Screen Overview

Terminology
In these articles, primary and secondary screens are defined as follows:

for primary (default) screen is 屏幕 IDfor DEFAULT_DISPLAY
secondary screen 屏幕 IDis notDEFAULT_DISPLAY

subject area article
development and testing Recommended Practices
Testing and Development Environments
FAQ
Collection of related articles Display
system decoration support
input method support
single article Multiple recovery
activities, startup policies,
lock screen,
input routing ,
multi-zone audio

2. Screen window configuration

2.1 Configuration xml file

/data/system/display_settings.xmlConfiguration:

  • Simulation Screen : uniqueIdUsed to identify the screen in the name attribute, for a simulation screen this ID is overlay:1.
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="0" />
<display
  name="overlay:1"
  shouldShowSystemDecors="true"
  shouldShowIme="true" />
</display-settings>
  • Built-in screens : uniqueIdFor built-in screens, a sample value could be "local:45354385242535243453". Another way is to use the hardware port information and set identifier="1" to correspond to DisplayWindowSettingsProvider#IDENTIFIER_PORT, then update name to use "port:<port_id>" 格式.
<?xmlversion='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="1" />
<display
  name="port:12345"
  shouldShowSystemDecors="true"
  shouldShowIme="true" />
</display-settings>

2.2 DisplayInfo#uniqueId screen identification

DisplayInfo#uniqueId to add a stable identifier and differentiate between local, network, and virtual screens

Screen type Format
local local:<stable-id>
network network:<mac-address>
virtual virtual:<package-name-and-name>

2.3 adb view information

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp#SurfaceFlinger::dumpDisplayIdentificationData

void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
    
    
    for (const auto& [token, display] : mDisplays) {
    
    
        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
        if (!displayId) {
    
    
            continue;
        }
        const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
        if (!hwcDisplayId) {
    
    
            continue;
        }

        StringAppendF(&result,
                      "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
                      *hwcDisplayId);
        uint8_t port;
        DisplayIdentificationData data;
        if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
    
    
            result.append("no identification data\n");
            continue;
        }

        if (!isEdid(data)) {
    
    
            result.append("unknown identification data\n");
            continue;
        }

        const auto edid = parseEdid(data);
        if (!edid) {
    
    
            result.append("invalid EDID\n");
            continue;
        }

        StringAppendF(&result, "port=%u pnpId=%s displayName=\"", port, edid->pnpId.data());
        result.append(edid->displayName.data(), edid->displayName.length());
        result.append("\"\n");
    }
}

Insert image description here

3. Configuration file analysis

3.1 xml field reading

  • File path: DATA_DISPLAY_SETTINGS_FILE_PATH = "system/display_settings.xml", VENDOR_DISPLAY_SETTINGS_FILE_PATH = "etc/display_settings.xml", Settings.Global.getString(resolver,DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH)( DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = "wm_display_settings_path")
  • FileDataObjects: fileData.mIdentifierType = getIntAttribute(parser, "identifier", IDENTIFIER_UNIQUE_ID), name = parser.getAttributeValue(null, "name"), shouldShowIme = getBooleanAttribute(parser, "shouldShowIme", null /* defaultValue */), settingsEntry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors", null /* defaultValue */)etc.
private static final class FileData {
     
     
  int mIdentifierType;
  final Map<String, SettingsEntry> mSettings = new HashMap<>();

  @Override
  public String toString() {
     
     
      return "FileData{"
              + "mIdentifierType=" + mIdentifierType
              + ", mSettings=" + mSettings
              + '}';
  }
}
  • DisplayWindowSettings.javaThe current persistent settings for the monitor. Provides a strategy for displaying settings and delegates persistence and lookup of setting values ​​to the provided{@link SettingsProvider}
@Nullable
private static FileData readSettings(ReadableSettingsStorage storage) {
    
    
    InputStream stream;
    try {
    
    
        stream = storage.openRead();
    } catch (IOException e) {
    
    
        Slog.i(TAG, "No existing display settings, starting empty");
        return null;
    }
    FileData fileData = new FileData();
    boolean success = false;
    try {
    
    
        TypedXmlPullParser parser = Xml.resolvePullParser(stream);
        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, fileData);
            } else if (tagName.equals("config")) {
    
    
                readConfig(parser, fileData);
            } 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 {
    
    
        try {
    
    
            stream.close();
        } catch (IOException ignored) {
    
    
        }
    }
    if (!success) {
    
    
        fileData.mSettings.clear();
    }
    return fileData;
}

private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) {
    
    
    return parser.getAttributeInt(null, name, defaultValue);
}

@Nullable
private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name,
        @Nullable Integer defaultValue) {
    
    
    try {
    
    
        return parser.getAttributeInt(null, name);
    } catch (Exception ignored) {
    
    
        return defaultValue;
    }
}

@Nullable
private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name,
        @Nullable Boolean defaultValue) {
    
    
    try {
    
    
        return parser.getAttributeBoolean(null, name);
    } catch (Exception ignored) {
    
    
        return defaultValue;
    }
}

private static void readDisplay(TypedXmlPullParser parser, FileData fileData)
        throws NumberFormatException, XmlPullParserException, IOException {
    
    
    String name = parser.getAttributeValue(null, "name");
    if (name != null) {
    
    
        SettingsEntry settingsEntry = new SettingsEntry();
        settingsEntry.mWindowingMode = getIntAttribute(parser, "windowingMode",
                WindowConfiguration.WINDOWING_MODE_UNDEFINED /* defaultValue */);
        settingsEntry.mUserRotationMode = getIntegerAttribute(parser, "userRotationMode",
                null /* defaultValue */);
        settingsEntry.mUserRotation = getIntegerAttribute(parser, "userRotation",
                null /* defaultValue */);
        settingsEntry.mForcedWidth = getIntAttribute(parser, "forcedWidth",
                0 /* defaultValue */);
        settingsEntry.mForcedHeight = getIntAttribute(parser, "forcedHeight",
                0 /* defaultValue */);
        settingsEntry.mForcedDensity = getIntAttribute(parser, "forcedDensity",
                0 /* defaultValue */);
        settingsEntry.mForcedScalingMode = getIntegerAttribute(parser, "forcedScalingMode",
                null /* defaultValue */);
        settingsEntry.mRemoveContentMode = getIntAttribute(parser, "removeContentMode",
                REMOVE_CONTENT_MODE_UNDEFINED /* defaultValue */);
        settingsEntry.mShouldShowWithInsecureKeyguard = getBooleanAttribute(parser,
                "shouldShowWithInsecureKeyguard", null /* defaultValue */);
        settingsEntry.mShouldShowSystemDecors = getBooleanAttribute(parser,
                "shouldShowSystemDecors", null /* defaultValue */);
        final Boolean shouldShowIme = getBooleanAttribute(parser, "shouldShowIme",
                null /* defaultValue */);
        if (shouldShowIme != null) {
    
    
            settingsEntry.mImePolicy = shouldShowIme ? DISPLAY_IME_POLICY_LOCAL
                    : DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
        } else {
    
    
            settingsEntry.mImePolicy = getIntegerAttribute(parser, "imePolicy",
                    null /* defaultValue */);
        }
        settingsEntry.mFixedToUserRotation = getIntegerAttribute(parser, "fixedToUserRotation",
                null /* defaultValue */);
        settingsEntry.mIgnoreOrientationRequest = getBooleanAttribute(parser,
                "ignoreOrientationRequest", null /* defaultValue */);
        settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
                "ignoreDisplayCutout", null /* defaultValue */);
        settingsEntry.mDontMoveToTop = getBooleanAttribute(parser,
                "dontMoveToTop", null /* defaultValue */);

        fileData.mSettings.put(name, settingsEntry);
    }
    XmlUtils.skipCurrentTag(parser);
}

private static void readConfig(TypedXmlPullParser parser, FileData fileData)
        throws NumberFormatException,
        XmlPullParserException, IOException {
    
    
    fileData.mIdentifierType = getIntAttribute(parser, "identifier",
            IDENTIFIER_UNIQUE_ID);
    XmlUtils.skipCurrentTag(parser);
}

3.2 Brief timing diagram

Insert image description here

4. Per-screen focus

focus per screen

To support multiple input sources targeting a single screen simultaneously, Android 10 can be configured to support multiple focus windows, up to one per screen. This feature only works on special types of devices when multiple users are interacting with the same device at the same time and using different input methods or devices (such as Android Automotive).

强烈建议不要为常规设备启用此功能,包括跨屏设备或用于类似桌面设备体验的设备。这主要是出于安全方面的考虑,因为这样做可能会导致用户不确定哪个窗口具有输入焦点

Imagine a user entering secure information into a text input field, perhaps logging into a banking app or entering text that contains sensitive information. A malicious application can create a virtual off-screen screen for executing activities, or it can use text input fields to execute activities. Both the legitimate and malicious activities have focus and both display a valid input indicator (blinking cursor).

However, keyboard (hardware or software) input can only enter the top-level activity (the most recently launched application). By creating a hidden virtual screen, a malicious app can obtain user input even when using the software keyboard on the main device screen.

Use to com.android.internal.R.bool.config_perDisplayFocusEnabledset per-screen focus.

兼容性
**Issue:** In Android 9 and lower, at most one window in the system has focus at a time.

**Solution:** In the rare case where two windows from the same process are both focused, the system provides focus only to the window higher in the Z-order. This restriction has been removed for apps targeting Android 10, and it is expected that these apps can support focusing multiple windows at the same time.

实现
WindowManagerService#mPerDisplayFocusEnabledUsed to control the availability of this feature. In ActivityManager, the system now uses ActivityDisplay#getFocusedStack()global tracking instead of utilizing variables. ActivityDisplay#getFocusedStack()Determine focus based on Z-axis order, not by cached values. This way, only one source WindowManagerneeds to track the activity's Z-order.

If it is necessary to determine the topmost focus stack in the system, ActivityStackSupervisor#getTopDisplayFocusedStack()a similar approach is used to handle these cases. The system will traverse these stacks from top to bottom, searching for the first stack that meets the criteria.

InputDispatcherIt is now possible to have multiple focus windows (one per screen). If an input event is specific to a screen, the event is dispatched to the focused window in that screen. Otherwise, it is dispatched to the focused window in the focused screen (that is, the screen the user most recently interacted with).

See InputDispatcher::mFocusedWindowHandlesByDisplay 和 InputDispatcher::setFocusedDisplay(). Spotlight apps are also updated individually via NativeInputManager::setFocusedApplication()in .InputManagerService

In WindowManager, the focused window is also tracked separately. See DisplayContent#mCurrentFocusand DisplayContent#mFocusedAppand their respective uses. The related focus tracking and update methods have been WindowManagerServicemoved from to DisplayContent.

Guess you like

Origin blog.csdn.net/qq_23452385/article/details/132262581