Android多屏幕支持-Android12

Android多屏幕支持-Android12

  • 1、概覽及相關文章
  • 2、屏幕窗口配置
    • 2.1 配置xml文件
    • 2.2 DisplayInfo#uniqueId 屏幕標識
    • 2.3 adb查看信息
  • 3、配置文件解析
    • 3.1 xml字段讀取
    • 3.2 簡要時序圖
  • 4、每屏幕焦點

android12-release


1、概覽及相關文章

AOSP > 文檔 > 心主題 > 多屏幕概覽

術語
在這些文章中,主屏幕和輔助屏幕的定義如下:

主(默認)屏幕的屏幕 IDDEFAULT_DISPLAY
輔助屏幕的屏幕 ID 不是 DEFAULT_DISPLAY

主題區域文章
開發和測試推薦做法
測試和開發環境
常見問題解答
相關文章集顯示
系統裝飾支持
輸入法支持
單篇文章多項恢復
Activity 啟動政策
鎖定屏幕
輸入路由
多區音頻

2、屏幕窗口配置

2.1 配置xml文件

/data/system/display_settings.xml 配置:

  • 模擬屏幕uniqueId 用于在名稱屬性中標識屏幕,對于模擬屏幕,此 ID 為 overlay:1
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="0" />
<displayname="overlay:1"shouldShowSystemDecors="true"shouldShowIme="true" />
</display-settings>
  • 內置屏幕uniqueId 對于內置屏幕,示例值可以是 “local:45354385242535243453”。另一種方式是使用硬件端口信息,并設置 identifier=“1” 以與 DisplayWindowSettingsProvider#IDENTIFIER_PORT 對應,然后更新 name 以使用 "port:<port_id>" 格式
<?xmlversion='1.0' encoding='utf-8' standalone='yes' ?>
<display-settings>
<config identifier="1" />
<displayname="port:12345"shouldShowSystemDecors="true"shouldShowIme="true" />
</display-settings>

2.2 DisplayInfo#uniqueId 屏幕標識

DisplayInfo#uniqueId,以添加穩定的標識符并區分本地、網絡和虛擬屏幕

屏幕類型格式
本地local:<stable-id>
網絡network:<mac-address>
虛擬virtual:<package-name-and-name>

2.3 adb查看信息

$ 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");}
}

在這里插入圖片描述

3、配置文件解析

3.1 xml字段讀取

  • 文件路徑: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"
  • FileData對象: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 */)等等
private static final class FileData {int mIdentifierType;final Map<String, SettingsEntry> mSettings = new HashMap<>();@Overridepublic String toString() {return "FileData{"+ "mIdentifierType=" + mIdentifierType+ ", mSettings=" + mSettings+ '}';}
}
  • DisplayWindowSettings.java有關顯示器的當前持久設置。提供顯示設置的策略,并將設置值的持久性和查找委派給提供的{@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 簡要時序圖

在這里插入圖片描述

4、每屏幕焦點

每個屏幕焦點

為了同時支持多個以單個屏幕為目標的輸入源,可以將 Android 10 配置為支持多個聚焦窗口,每個屏幕最多支持一個。當多個用戶同時與同一設備交互并使用不同的輸入方法或設備(例如 Android Automotive)時,此功能僅適用于特殊類型的設備。

強烈建議不要為常規設備啟用此功能,包括跨屏設備或用于類似桌面設備體驗的設備。這主要是出于安全方面的考慮,因為這樣做可能會導致用戶不確定哪個窗口具有輸入焦點

想象一下,用戶在文本輸入字段中輸入安全信息,也許是登錄某個銀行應用或者輸入包含敏感信息的文本。惡意應用可以創建一個虛擬的屏幕外屏幕用于執行 activity,也可以使用文本輸入字段執行 activity。合法 activity 和惡意 activity 均具有焦點,并且都顯示一個有效的輸入指示符(閃爍光標)。

不過,鍵盤(硬件或軟件)的輸入只能進入最頂層的 activity(最近啟動的應用)。通過創建隱藏的虛擬屏幕,即使在主設備屏幕上使用軟件鍵盤,惡意應用也可以獲取用戶輸入。

使用 com.android.internal.R.bool.config_perDisplayFocusEnabled 設置每屏幕焦點。

兼容性
**問題:**在 Android 9 及更低版本中,系統中一次最多只有一個窗口具有焦點。

**解決方案:**在極少數情況下,來自同一進程的兩個窗口都處于聚焦狀態,則系統僅向在 Z 軸順序中較高的窗口提供焦點。對于以 Android 10 為目標平臺的應用,目前已取消這一限制,此時預計這些應用可以支持同時聚焦多個窗口。

實現
WindowManagerService#mPerDisplayFocusEnabled 用于控制此功能的可用性。在 ActivityManager 中,系統現在使用的是 ActivityDisplay#getFocusedStack(),而不是利用變量進行全局跟蹤。ActivityDisplay#getFocusedStack() 根據 Z 軸順序確定焦點,而不是通過緩存值來確定。這樣一來,只有一個來源 WindowManager 需要跟蹤 activity 的 Z 軸順序。

如果必須要確定系統中最頂層的聚焦堆棧,ActivityStackSupervisor#getTopDisplayFocusedStack() 會采用類似的方法處理這些情況。系統將從上到下遍歷這些堆棧,搜索第一個符合條件的堆棧。

InputDispatcher 現在可以有多個聚焦窗口(每個屏幕一個)。如果某個輸入事件特定于屏幕,則該事件會被分派到相應屏幕中的聚焦窗口。否則,它會被分派到聚焦屏幕(即用戶最近與之交互的屏幕)中的聚焦窗口。

請參閱 InputDispatcher::mFocusedWindowHandlesByDisplay 和 InputDispatcher::setFocusedDisplay()。聚焦應用也會通過 NativeInputManager::setFocusedApplication()InputManagerService 中分別更新。

WindowManager 中,系統還會單獨跟蹤聚焦窗口。請參閱 DisplayContent#mCurrentFocusDisplayContent#mFocusedApp 以及各自的用途。相關的焦點跟蹤和更新方法已從 WindowManagerService 移至 DisplayContent

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36586.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36586.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36586.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何利用DeepBook自動做市商(AMM),發揮應用的最大價值

盡管Sui宣布DeepBook作為其首個本地流動性層&#xff0c;即中央限價單簿&#xff08;Central Limit Order Book&#xff0c;CLOB&#xff09;&#xff0c;但自動做市商&#xff08;Automated Market Maker&#xff0c;AMM&#xff09;平臺也可以在Sui上發揮作用。事實上&#x…

理解jvm之對象已死怎么判斷?

目錄 引用計數算法 什么是引用 可達性分析算法&#xff08;用的最多的&#xff09; 引用計數算法 定義&#xff1a;在對象中添加一個引用計數器&#xff0c;每當有一個地方引用它時&#xff0c;計數器值就加一&#xff1b;當引用失效時&#xff0c;計數器值就減一&#xff1…

文件批量改名高手:輕松刪除文件名,僅保留編號!

您是否經常需要對大量文件進行命名調整&#xff1f;是否為繁瑣的手動操作而感到厭煩&#xff1f;現在&#xff0c;我們的智能批量文件改名工具為您提供了一種簡單而高效的解決方案&#xff01;只需幾步操作&#xff0c;您就能輕松刪除原有的文件名&#xff0c;僅保留編號&#…

YOLO相關原理(文件結構、視頻檢測等)

超參數進化(hyperparameter evolution) 超參數進化是一種使用了genetic algorithm&#xff08;GA&#xff09;遺傳算法進行超參數優化的一種方法。 YOLOv5的文件結構 images文件夾內的文件和labels中的文件存在一一對應關系 激活函數&#xff1a;非線性處理單元 activation f…

c#學習路線

文章目錄 .net coreN層架構大項目實戰高性能互聯網項目架構c#高級編程各種主流框架分布式通信SSO單點登錄+權限管理系統實戰N層架構WEB安全ASP.NET MVCNoSQLORM框架c#6和c#7新語法VS插件分享項目管理三層項目實戰三層架構ASP.NET基礎數據庫和ASP.NETADO.NET計算機基礎計算機硬件…

C# 11 中的新增功能

本文內容 泛型屬性泛型數學支持數值 IntPtr 和 UIntPtr字符串內插中的換行符 顯示另外 11 個 C# 11 中增加了以下功能&#xff1a; 原始字符串字面量泛型數學支持泛型屬性UTF-8 字符串字面量字符串內插表達式中的換行符列表模式文件本地類型必需的成員自動默認結構常量 str…

【設計模式】MVC 模式

MVC 模式代表 Model-View-Controller&#xff08;模型-視圖-控制器&#xff09; 模式。這種模式用于應用程序的分層開發。 Model&#xff08;模型&#xff09; - 模型代表一個存取數據的對象或 JAVA POJO。它也可以帶有邏輯&#xff0c;在數據變化時更新控制器。View&#xff…

Linux6.37 Kubernetes 集群調度

文章目錄 計算機系統5G云計算第三章 LINUX Kubernetes 集群調度一、調度約束1.調度過程2.指定調度節點3.親和性1&#xff09;節點親和性2&#xff09;Pod 親和性3&#xff09;鍵值運算關系 4.污點(Taint) 和 容忍(Tolerations)1&#xff09;污點(Taint)2&#xff09;容忍(Toler…

centos搭建k8s

centos搭建k8s環境_centos k8s_進擊的Coders的博客-CSDN博客

VSCODE[配置ssh免密遠程登錄]

配置ssh免密遠程登錄 本文摘錄于&#xff1a;https://blog.csdn.net/qq_44571245/article/details/123031276只是做學習備份之用&#xff0c;絕無抄襲之意&#xff0c;有疑惑請聯系本人&#xff01; 這里要注意如下幾個地方: 1.要進入.ssh目錄創建文件: 2.是拷貝帶"ssh-…

微服務系列文章之 Springboot+Vue實現登錄注冊

一、springBoot 創建springBoot項目 分為三個包&#xff0c;分別為controller&#xff0c;service&#xff0c; dao以及resource目錄下的xml文件。 UserController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 …

如何利用 EMC 模型解決能源服務提供商的瓶頸

01. 什么是合同能源管理&#xff1f; 合同能源管理(EMC-Energy Management Contract) 是一種新型的市場化節能機制,其實質就是以減少的能源費用來支付節能項目全部成本的節能投資方式。&#xff1a;節能服務公司與用能單位以契約形式約定節能項目的節能目標&#xff0c;節能服…

(二)Node.js 基礎模塊

&#xff08;二&#xff09;Node.js 基礎模塊 1. fs文件系統模塊1.1 什么是fs文件系統模塊1.2 讀取指定文件中的內容1. fs.readFile()的語法格式2. fs.readFile()的示例代碼 1.3 向指定的文件中寫入內容1. fs.writeFile()的語法格式2. fs.writeFile()的實例代碼 1.4 __dirname …

正則表達式的使用

1、正則表達式-教程 正則表達式&#xff1a;文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a到z之間的字母&#xff09;和特殊字符&#xff08;稱為元字符&#xff09;。 正則表達式使用單個字符串來描述&#xff0c;匹配一系列匹配某個句法規則的字符串。 2、…

《論文閱讀13》Efficient Urban-scale Point Clouds Segmentationwith BEV Projection

一、論文 研究領域&#xff1a; 城市級3D語義分割論文&#xff1a;Efficient Urban-scale Point Clouds Segmentationwith BEV Projection清華大學&#xff0c;新疆大學2021.9.19論文github論文鏈接 二、論文概要 2.1主要思路 提出了城市級3D語義分割新的方法&#xff0c;將…

1.SpringMVC接收請求參數及數據回顯:前端url地址欄傳遞參數通過轉發顯示在網頁

1、SpringMVC 處理前端提交的數據 1.1 提交的域名和處理方法的參數不一致&#xff0c;使用注解解決 1.2 提交的域名和處理方法的參數不一致&#xff0c;使用注解解決 1.3 提交的是一個對象 2、前端url地址欄傳遞的是一個參數 請求地址url&#xff1a;http://localhost:8080/s…

測試開發工程師到底是做什么的?

一二三線互聯網公司對測試開發工程師的要求&#xff1a; 現在很多測試的同事對測試開發工程師的認識都有一定的誤差。 我最早在阿里的時候和測試開發工程師溝通的時候&#xff0c;發現阿里的測試開發工程師&#xff0c;他們基本上都分為兩種&#xff0c;一種是業務類型的&…

Python基礎教程: json序列化詳細用法介紹

前言 嗨嘍&#xff0c;大家好呀~這里是愛看美女的茜茜吶 Python內置的json模塊提供了非常完善的對象到JSON格式的轉換。 廢話不多說&#xff0c;我們先看看如何把Python對象變成一個JSON&#xff1a; d dict(nameKaven, age17, sexMale) print(json.dumps(d)) # {"na…

【Linux】環境變量

目錄 一、環境變量的概念二、 常見的環境變量1.查看環境變量的方法2.PATH3.HOME4.SHELL 三、環境變量的相關指令四、命令行參數 一、環境變量的概念 環境變量(environment variables)一般是指在操作系統中用來指定操作系統運行環境的一些參數 如&#xff1a;我們在編寫C/C代碼的…

Prometheus技術文檔-基本使用-配置文件全解!!!!!

簡介&#xff1a; Prometheus是一個開源的系統監控和告警系統&#xff0c;由Google的BorgMon監控系統發展而來。它主要用于監控和度量各種時間序列數據&#xff0c;比如系統性能、網絡延遲、應用程序錯誤等。Prometheus通過采集監控數據并存儲在時間序列數據庫中&#xff0c;…