The SystemUI of various products depend on and build against the WM Shell library. To ensure that we don't inadvertently build dependencies between the Shell library and one particular product (ie. handheld SysUI), we deliberately separate the initialization of the WM Shell component from the SysUI component when set up through Dagger.
TLDR Initialize everything as needed in the WM component scope and export only well defined interfaces to SysUI.
There are more details in the Dagger docs, but the general overview of the SysUI/Shell initialization flow is such:
GlobalModule
and its included modules)WMComponent
includes WMShellModule
which includes WMShellBaseModule
(common to all SysUI)TvWMComponent
includes TvWMShellModule
which includes WMShellBaseModule
WMComponent
to SysUIComponent
via the SysUIComponent#Builder
, then builds the SysUI scoped componentsWMShell
is the SystemUI “service” (in the SysUI scope) that initializes with the app after the SystemUI part of the dependency graph has been created. It contains the binding code between the interfaces provided by the Shell and the rest of SystemUI.More detail can be found in go/wm-sysui-dagger.
Within the same process, the WM Shell components can be running on a different thread than the main SysUI thread (disabled on certain products). This introduces challenges where we have to be careful about how SysUI calls into the Shell and vice versa.
As a result, we enforce explicit interfaces between SysUI and Shell components, and the implementations of the interfaces on each side need to post to the right thread before it calls into other code.
For example, you might have:
Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently necessary to maintain proper threading and logic isolation.