RFC-0177:父項檢視畫面的焦點觀察器 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 讓父項檢視畫面瞭解焦點如何在其檢視區塊樹狀結構中移動的 API |
問題 | |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2022-06-06 |
審查日期 (年-月-日) | 2022-07-07 |
摘要
這份 RFC 提出了檢視畫面焦點的 API 設計,讓一般 UI 用戶端 可安全地在樹狀結構外使用,並說明焦點可觀察性方面的安全性限制。重點在於提供最少的資訊,並提供優質的開發人員體驗。
提振精神
為了透過多個元件建立使用者體驗 (圖形、輸入等),UI 用戶端會透過設定檢視區塊樹狀結構,將內容製作工作委派給其他 UI 用戶端,其中父項檢視區塊會管理一個或多個子項檢視區塊。Ermine 系統殼就是其中一個例子,Google 的智慧螢幕也是另一個例子。父項檢視區塊的一項重要責任,就是監控檢視區塊焦點狀態:
- 用於指出何時父項檢視畫面可透過程式輔助方式將檢視畫面焦點移至子項檢視畫面。
- 舉例來說,如果父項檢視畫面不在檢視畫面樹狀結構的焦點鏈中,則父項檢視畫面要求將焦點移至子項檢視畫面時會失敗 (https://fxbug.dev/42168713)。
- 如果檢視焦點已移至子檢視畫面,可用來識別目前哪個子項具有檢視焦點。
- 舉例來說,如果使用者透過觸控將焦點移至檢視畫面,父項檢視畫面可能會想使用焦點邊界裝飾該子項檢視畫面,並需要知道何時發生此情況,以及子項檢視畫面的身分。
焦點可能會在沒有父項檢視畫面參與的情況下變更 (使用者觸控、檢視畫面分離等)。父項檢視畫面必須持續瞭解檢視畫面焦點的移動方式,但必須遵守全域檢視畫面樹狀結構設定的資訊限制。
這份 RFC 建議採用「焦點觀察器」設計,可 (1) 讓父項檢視畫面正確回應檢視畫面焦點變更、(2) 安全地在樹狀結構外使用,以及 (3) 在最少資訊曝露的情況下改善 Fuchsia View 系統的安全性。
相關人員
協助人員:
審查者:sanjayc@google.com (Workstation)、quiche@google.com (HCI)、neelsa@google.com (HCI)、akbiggs@google.com (Flutter)
諮詢對象:shiveshganju@google.com、fmil@google.com、emircan@google.com、jsankey@google.com
社會化:
這項 RFC 已與受影響團隊的主管進行交流。
需求條件
- 盡量減少焦點觀察功能的資訊曝光
- 明確的安全機制,可取得觀察管道
- 將 SDK 納入「合作夥伴」等級或更高等級
- 簡便的開發人員體驗
設計
這個焦點觀察器的核心提案是下列 FIDL 通訊協定。
library fuchsia.ui.observation.focus;
using zx;
protocol ScopedProvider {
Watch() -> (ScopedResponse);
};
type ScopedResponse = table {
1: observation_end zx.time;
2: focused zx.koid;
};
名稱中的「Scoped」表示該通訊協定提供的焦點資訊,其範圍為 focus.ScopedProvider 用戶端的檢視區塊樹狀結構,或受其限制。focus.ScopedProvider 用戶端的檢視畫面是這個可觀察檢視區塊樹狀結構的根目錄。
observation_end
時間標示 Watch 期間的結束時間,讓用戶端知道何時傳回的焦點正確無誤。舉例來說,如果一系列焦點變更發生在單一 Watch 期間,並且回復為先前的焦點,這項功能就會讓用戶端能夠區分相同焦點值的不同傳回值。
focused
KOID 是檢視器參照 KOID,或是特殊哨兵值 ZX_KOID_INVALID
,用於指出檢視畫面焦點位於 focus.ScopedProvider 用戶端的檢視區塊樹狀結構之外。下文將進一步說明可能的值和語意。
檢視拓撲的範例
請考慮下列檢視圖,其中每個圓圈代表一個 View,而檢視畫面「U」是 focus.ScopedProvider 的用戶端。
將焦點可見範圍限制在檢視樹狀結構
focus.ScopedProvider 的用戶端對全域檢視區塊樹狀結構的瀏覽權限有限 (詳情請參閱「安全性考量」)。系統可以瞭解檢視焦點位於檢視樹狀結構中 (根目錄為 focus.ScopedProvider 用戶端的檢視畫面),或是檢視樹狀結構外部,但會刻意省略具體內容。
當焦點位於 focus.ScopedProvider 用戶端的檢視區塊樹狀結構之外時,系統會使用 ZX_KOID_INVALID
哨兵值,向用戶端通知這項非常一般化的事實。用戶端不會瞭解新 View 焦點的身份。
如果焦點保留在 focus.ScopedProvider 用戶端的檢視樹狀結構中,用戶端只會收到下列資訊:
- 如果焦點是 focus.ScopedProvider 用戶端的檢視畫面本身,則為用戶端檢視畫面的 KOID。
- 如果焦點是用戶端檢視畫面的直接子項檢視畫面,則為該直接子項檢視畫面的 KOID。
- 如果焦點是用於用戶端檢視畫面的間接子項檢視畫面,則為該間接子項檢視畫面的祖系的直接子項檢視畫面的 KOID。
父項檢視畫面需要知道何時可在子項之間移動焦點。當檢視畫面焦點位於檢視區塊樹狀結構時,這項功能就會生效。否則,對 fuchsia.ui.views.Focuser.RequestFocus() 的呼叫一律會失敗。
值得一提的是,focus.ScopedProvider 的資訊是透過管道傳播的快照,因此變更焦點的要求可能會與下一次快照更新競爭。舉例來說,如果祖系檢視區塊成功要求將焦點變更至此檢視區塊之外,則一個快照可能會指出焦點位於 focus.ScopedProvider 用戶端的檢視區塊樹狀結構中,而變更焦點至直接子項的請求可能會遭到拒絕。
在這個順序圖中,當焦點移至 U 時,U 會收到通知,並在焦點完全移出 U 的檢視區塊樹狀結構時再次收到通知。
回報給用戶端的焦點值
focused
是三個值類別之一,其中包含 ZX_KOID_INVALID
哨兵值。如果 focused
有效 (即不是哨兵),則檢視畫面可在自身和子檢視畫面之間任意移動焦點。
具體違規事項如下:
- 如果
focused
是ZX_KOID_INVALID
,表示焦點已離開這個檢視區塊樹狀結構。導致這種情況的原因有很多,舉例來說,U 中的檢視區塊樹狀結構可能會連結至全域檢視區塊樹狀結構,但祖系檢視區塊可能會將焦點移至 U 的兄弟檢視區塊。或者,U 可能已與全域檢視區塊樹狀結構中斷連線,也就是說,U 已無法再保留焦點。或者,U 的祖系本身可能已中斷連線,在這種情況下,該祖系的所有後代都無法保留焦點。請參閱重點項目政策。 - 如果是父項的檢視畫面參照 KOID,則父項檢視畫面本身具有焦點。這項用法與 fuchsia.ui.views.ViewRefFocused 相同,因此我們可以淘汰該通訊協定。
- 如果是有效的 KOID 或父項,則是直接子項的 ViewRef KOID。這個欄位只會提及直接子項,即使聚焦的檢視區塊是直接子項的後代。
在本例中,焦點已移至 U 下 V 的子項 X。焦點觀察器會回報 U 的直接子項,也就是 V。
摘要語意
如果在過去的監控期間內有多次焦點變更,這個 API 只會傳回最後的焦點。用戶端通常無法針對過去的焦點變更採取行動,因此 API 已簡化為只傳回「摘要」。
通常,如果掛起的 Get 用戶端透過 Watch 停用回呼,焦點變更會導致立即傳回至用戶端。不過,用戶端可能會在下次掛起的 get 作業中延遲停用,因此伺服器可能會在下次傳回時看到多個焦點變更。伺服器也可能會收到大量焦點變更,因此視執行緒或工作排程而定,在經過多次焦點變更後,可能會為已停用的掛起 get 服務。
在這些範例中,無論 Watch() 呼叫的具體時間為何,U 都會收到相同的通知。
狀態變更語意
Watch 呼叫會根據個別用戶端的狀態變更而觸發。
- 在連線後的第一次呼叫中,系統會立即傳回目前狀態。
- 在一個 Watch 呼叫傳回與下一個 Watch 呼叫開始之間,如果發生多個狀態變更,下一個 Watch 呼叫會立即傳回觀察到的最後一次變更。
在層級變更的情況下,只有在收到 Watch 呼叫後發生變更時,伺服器才會通知用戶端,並忽略收到 Watch 呼叫前發生的變更。用戶端會在該期間錯過焦點變更摘要,這不符合預期用途。
根據狀態變更進行設定會對伺服器實作造成更大的負擔,因為它需要追蹤每個觀察者管道的最後發布狀態。不過,由於狀態變更可在客戶的 Watch 呼叫和任何焦點變更之間,以有序的方式進行交換,因此可提供更直覺的開發人員體驗。舉例來說,Watch 呼叫停放在伺服器上後,在回呼處理之前,可能會在這個期間發生多次焦點變更,具體取決於伺服器的執行緒和實作細節。
實作
檢視畫面焦點與檢視畫面生命週期和檢視畫面拓樸的維護密切相關。Scenic 是 View 管理員元件,因此此通訊協定的實作內容應屬於 Scenic。
成效
焦點可能會經常變動,但實際上會以「人類規模」移動。因此,FIDL 呼叫頻率並未被視為問題。FIDL 酬載也非常輕,而且流程控管模式可避免管道塞滿。
人體工學
這個 API 致力於改善 DX,以取代先前的 API。簡化的錯誤處理、損失的摘要語意,以及不含容器資料類型,這些都應該會讓採用方式更容易。
演化
這個 API 的用途是在 OOT 存放區中使用,而伺服器實作項目則位於 Scenic 平台元件中。透過新增較新的掛起取得方法,API 將可安全地演進,並保留回溯相容性。當所有使用已淘汰方法的存放區都更新為較新的做法時,系統就會將已淘汰的方法標示為已刪除。
安全性考量
這個 API 會連結至 fuchsia.ui.composition.Flatland.ViewBoundProtocols 資料表,在檢視畫面建立時,將這個 API 的伺服器端點與與父項檢視畫面相關聯的特定 ViewRef 緊密連結。
API 用戶端無法要求深入自身檢視區塊樹狀結構或檢視區塊樹狀結構以外的詳細資訊。收到的檢視區塊參照 KOID 資訊會限定為自身和直接子項,進而提升檢視區塊的安全性。
偷取焦點
在較寬鬆的系統中,惡意檢視畫面只要要求,就能「竊取」其他檢視畫面的焦點。Fuchsia View 系統的焦點政策可透過定義焦點移動的情況和範圍來降低這種可能性:只有在祖系檢視畫面授予焦點時,檢視畫面才能移動焦點,且只能在其檢視畫面子樹內移動焦點,而非在其外部移動焦點。
這個焦點觀察器設計會遵循焦點政策的範圍方法,將可觀察性限制在觀察到的檢視區塊及其直接子項。
KOID 不是功能
另一項小幅改善是,焦點觀察器通訊協定會傳遞子檢視區塊參照的 KOID,而不是檢視區塊參照本身的副本。某些 UI 通訊協定會對檢視畫面參照進行操作,因此傳回 KOID 可降低濫用情況。舉例來說,如果 Ermine 的 focus.ScopedProvider 管道端點已委派至另一個元件 'C',則為安全的委派,因為 'C' 無法將 Ermine 或 Ermine 的任何子項檢視畫面模擬為 KeyboardListener 通訊協定。
一般用途是識別哪個檢視畫面獲得焦點,而檢視畫面的檢視畫面參照 KOID 就足夠了。請注意,要求獲得焦點功能需要使用即時檢視參照,而非僅使用檢視參照的 KOID。用戶端應自行維護子檢視區塊參照項目清單 (即透過 Flatland 通訊協定取得),這些檢視區塊參照項目可用於要求焦點。
隱私權注意事項
FocusChainListener 通訊協定可讓您全面掌握檢視區塊樹狀結構,甚至是根檢視區塊。這個焦點觀察器通訊協定會刻意限制可見範圍,其中可見檢視區塊樹狀結構的根目錄為用戶端檢視區塊本身。
ViewRefFocused 的範圍已限定為用戶端的檢視畫面。這個焦點觀察器通訊協定只會將用戶端的顯示範圍擴大到用戶端檢視畫面的直接子檢視畫面。
由於有這些因應措施,我們預期隱私權影響會降到最低。
測試
實作項目將包含單元測試和平台端整合測試。此外,與其他 SDK 可見的 FIDL 一樣,這項測試也會進行 CTF 測試。
說明文件
在 fuchsia.dev 中,您可以找到使用說明指南。
缺點、替代方案和未知事項
此 API 不符合觀察焦點的所有已知用途。不過,先前的社交化工作強化了為不同需求建立個別 API 的需求。後續的 RFC 將處理其他「焦點觀察器」API。
既有技術與參考資料
舊版 FocusChainListener 的問題
目前,您只能透過 fuchsia.ui.focus.FocusChainListener 通訊協定,在檢視區塊樹狀結構中觀察檢視畫面焦點移動情形。由於以下問題,已將其淘汰:
- 這會讓用戶端全域可見查看觀看焦點移動的位置,這會洩漏平台實作詳細資料。舉例來說,這個焦點鏈結會公開根場景中的所有檢視畫面,讓用戶端能夠對根場景的結構進行斷言,進而避免平台內部實作變更。
- 它會提供 fuchsia.ui.views.ViewRef 權杖 (由 Zircon 事件配對物件支援),讓較弱協定的用戶端可假裝成其他檢視畫面。