Merge "Adding some Shell documentation" into tm-qpr-dev am: 5ff3c0e94c

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19230676

Change-Id: I06610ee9728720bd2fe7e230bf9b1734c2960b15
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Winson Chung
2022-07-12 16:46:45 +00:00
committed by Automerger Merge Worker
10 changed files with 468 additions and 13 deletions

View File

@@ -1,13 +0,0 @@
The dagger modules in this directory can be included by the host SysUI using the Shell library for
explicity injection of Shell components. Apps using this library are not required to use these
dagger modules for setup, but it is recommended for them to include them as needed.
The modules are currently inherited as such:
+- WMShellBaseModule (common shell features across SysUI)
|
+- WMShellModule (handheld)
|
+- TvPipModule (tv pip)
|
+- TvWMShellModule (tv)

View File

@@ -0,0 +1,18 @@
# Window Manager Shell Readme
The following docs present more detail about the implementation of the WMShell library (in no
particular order):
1) [What is the Shell](overview.md)
2) [Integration with SystemUI & Launcher](sysui.md)
3) [Usage of Dagger](dagger.md)
4) [Threading model in the Shell](threading.md)
5) [Making changes in the Shell](changes.md)
6) [Extending the Shell for Products/OEMs](extending.md)
7) [Debugging in the Shell](debugging.md)
8) [Testing in the Shell](testing.md)
Todo
- Per-feature docs
- Feature flagging
- Best practices

View File

@@ -0,0 +1,73 @@
# Making changes in the Shell
---
## Code reviews
In addition to the individual reviewers who are most familiar with the changes you are making,
please also add [wm-code-reviewers@google.com](http://g/wm-code-reviewers) to keep other WM folks
in the loop.
## Adding new code
### Internal Shell utility classes
If the new component is used only within the WMShell library, then there are no special
considerations, go ahead and add it (in the `com.android.wm.shell.common` package for example)
and make sure the appropriate [unit tests](testing.md) are added.
### Internal Shell components
If the new component is to be used by other components/features within the Shell library, then
you can create an appropriate package for this component to add your new code. The current
pattern is to have a single `<Component name>Controller` that handles the initialization of the
component.
As mentioned in the [Dagger usage](dagger.md) docs, you need to determine whether it should go into:
- `WMShellBaseModule` for components that other base & product components will depend on
- or `WMShellModule`, `TvWmShellModule`, etc. for product specific components that no base
components depend on
### SysUI accessible components
In addition to doing the above, you will also need to provide an interface for calling to SysUI
from the Shell and vice versa. The current pattern is to have a parallel `Optional<Component name>`
interface that the `<Component name>Controller` implements and handles on the main Shell thread.
In addition, because components accessible to SysUI injection are explicitly listed, you'll have to
add an appropriate method in `WMComponent` to get the interface and update the `Builder` in
`SysUIComponent` to take the interface so it can be injected in SysUI code. The binding between
the two is done in `SystemUIFactory#init()` which will need to be updated as well.
### Launcher accessible components
Because Launcher is not a part of SystemUI and is a separate process, exposing controllers to
Launcher requires a new AIDL interface to be created and implemented by the controller. The
implementation of the stub interface in the controller otherwise behaves similar to the interface
to SysUI where it posts the work to the main Shell thread.
### Component initialization
To initialize the component:
- On the Shell side, update `ShellInitImpl` to get a signal to initialize when the SysUI is started
- On the SysUI side, update `WMShell` to setup any bindings for the component that depend on
SysUI code
### General Do's & Dont's
Do:
- Do add unit tests for all new components
- Do keep controllers simple and break them down as needed
Don't:
- **Don't** do initialization in the constructor, only do initialization in the init callbacks.
Otherwise it complicates the building of the dependency graph.
- **Don't** create dependencies from base-module components on specific features (the base module
is intended for use with all products)
- Try adding a mechanism to register and listen for changes from the base module component instead
- **Don't** add blocking synchronous calls in the SysUI interface between Shell & SysUI
- Try adding a push-mechanism to share data, or an async callback to request data
### Exposing shared code for use in Launcher
Launcher doesn't currently build against the Shell library, but needs to have access to some shared
AIDL interfaces and constants. Currently, all AIDL files, and classes under the
`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
Launcher uses.
If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
[Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp) file under the
`wm_shell_util-sources` filegroup.

View File

@@ -0,0 +1,50 @@
# Usage of Dagger in the Shell library
---
## Dependencies
Dagger is not required to use the Shell library, but it has a lot of obvious benefits:
- Not having to worry about how to instantiate all the dependencies of a class, especially as
dependencies evolve (ie. product controller depends on base controller)
- Can create boundaries within the same app to encourage better code modularity
As such, the Shell also tries to provide some reasonable out-of-the-box modules for use with Dagger.
## Modules
All the Dagger related code in the Shell can be found in the `com.android.wm.shell.dagger` package,
this is intentional as it keeps the "magic" in a single location. The explicit nature of how
components in the shell are provided is as a result a bit more verbose, but it makes it easy for
developers to jump into a few select files and understand how different components are provided
(especially as products override components).
The module dependency tree looks a bit like:
- [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java)
(provides threading-related components)
- [WMShellBaseModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java)
(provides components that are likely common to all products, ie. DisplayController,
Transactions, etc.)
- [WMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java)
(phone/tablet specific components only)
- [TvPipModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java)
(PIP specific components for TV)
- [TvWMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java)
(TV specific components only)
- etc.
Ideally features could be abstracted out into their own modules and included as needed by each
product.
## Overriding base components
In some rare cases, there are base components that can change behavior depending on which
product it runs on. If there are hooks that can be added to the component, that is the
preferable approach.
The alternative is to use the [@DynamicOverride](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java)
annotation to allow the product module to provide an implementation that the base module can
reference. This is most useful if the existence of the entire component is controlled by the
product and the override implementation is optional (there is a default implementation). More
details can be found in the class's javadoc.

View File

@@ -0,0 +1,69 @@
# Debugging in the Shell
---
## Logging & ProtoLogs
The interactions in the Shell can be pretty complicated, so having good logging is crucial to
debugging problems that arise (especially in dogfood). The Shell uses the same efficient Protolog
mechanism as WM Core, which can be enabled at runtime on debug devices.
**TLDR**&nbsp; Don’t use Logs or Slogs except for error cases, Protologs are much more flexible,
easy to add and easy to use
### Adding a new ProtoLog
Update `ShellProtoLogGroup` to include a new log group (ie. NEW_FEATURE) for the content you want to
log. ProtoLog log calls mirror Log.v/d/e(), and take a format message and arguments:
```java
ProtoLog.v(NEW_FEATURE, "Test log w/ params: %d %s", 1, “a”)
```
This code itself will not compile by itself, but the `protologtool` will preprocess the file when
building to check the log state (is enabled) before printing the print format style log.
**Notes**
- ProtoLogs currently only work from soong builds (ie. via make/mp). We need to reimplement the
tool for use with SysUI-studio
- Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with
traces in Winscope)
### Enabling ProtoLog command line logging
Run these commands to enable protologs for both WM Core and WM Shell to print to logcat.
```shell
adb shell wm logging enable-text NEW_FEATURE
adb shell wm logging disable-text NEW_FEATURE
```
## Winscope Tracing
The Winscope tool is extremely useful in determining what is happening on-screen in both
WindowManager and SurfaceFlinger. Follow [go/winscope](http://go/winscope-help) to learn how to
use the tool.
In addition, there is limited preliminary support for Winscope tracing componetns in the Shell,
which involves adding trace fields to [wm_shell_trace.proto](frameworks/base/libs/WindowManager/Shell/proto/wm_shell_trace.proto)
file and ensure it is updated as a part of `WMShell#writeToProto`.
Tracing can be started via the shell command (to be added to the Winscope tool as needed):
```shell
adb shell cmd statusbar tracing start
adb shell cmd statusbar tracing stop
```
## Dumps
Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
part of dumping the SystemUI service. Dumping the Shell specific data can be done by specifying the
WMShell SysUI service:
```shell
adb shell dumpsys activity service SystemUIService WMShell
```
If information should be added to the dump, make updates to:
- `WMShell` if you are dumping SysUI state
- `ShellCommandHandler` if you are dumping Shell state
## Debugging in Android Studio
If you are using the [go/sysui-studio](http://go/sysui-studio) project, then you can debug Shell
code directly from Android Studio like any other app.

View File

@@ -0,0 +1,13 @@
# Extending the Shell for Products/OEMs
---
## General Do's & Dont's
Do:
- &nbsp;
Don't
- **Don't** override classes provided by WMShellBaseModule, it makes it difficult to make
simple changes to the Shell library base modules which are shared by all products
- If possible add mechanisms to modify the base class behavior

View File

@@ -0,0 +1,58 @@
# What is the WindowManager Shell
---
## Motivation
The primary motivation for the WindowManager Shell (WMShell) library is to effectively scale
WindowManager by making it easy&trade; and safe to create windowing features to fit the needs of
various Android products and form factors.
To achieve this, WindowManager separates the policy of managing windows (WMCore) from the
presentation of surfaces (WMShell) and provides a minimal interface boundary for the two to
communicate.
## Who is using the library?
Currently, the WMShell library is used to drive the windowing experience on handheld
(phones & tablets), TV, Auto, Arc++, and Wear to varying degrees.
## Where does the code live
The core WMShell library code is currently located in the [frameworks/base/libs/WindowManager/Shell](frameworks/base/libs/WindowManager/Shell)
directory and is included as a part dependency of the host SystemUI apk.
## How do I build the Shell library
The library can be built directly by running (using [go/makepush](http://go/makepush)):
```shell
mp :WindowManager-Shell
```
But this is mainly useful for inspecting the contents of the library or verifying it builds. The
various targets can be found in the Shell library's [Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp)
file.
Normally, you would build it as a part of the host SystemUI, for example via commandline:
```shell
# Phone SystemUI variant
mp sysuig
# Building Shell & SysUI changes along w/ framework changes
mp core services sysuig
```
Or preferably, if you are making WMShell/SysUI only changes (no other framework changes), then
building via [go/sysui-studio](http://go/sysui-studio) allows for very quick iteration (one click
build and push of SysUI in < 30s).
If you are making framework changes and are using `aidegen` to set up your platform IDE, make sure
to include the appropriate directories to build, for example:
```shell
# frameworks/base will include base/libs/WindowManager/Shell and base/packages/SystemUI
aidegen frameworks/base \
vendor/<oem>/packages/SystemUI \
...
```
## Other useful links
- [go/o-o-summit-20](go/o-o-summit-20) (Video presentations from the WM team)
- [go/o-o-summit-21](go/o-o-summit-21)

View File

@@ -0,0 +1,55 @@
# Shell & SystemUI
---
## Setup
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**&nbsp; Initialize everything as needed in the WM component scope and export only well
defined interfaces to SysUI.
## Initialization
There are more details in the Dagger docs, but the general overview of the SysUI/Shell
initialization flow is such:
1) SysUI Global scope is initialize (see `GlobalModule` and its included modules)
2) WM Shell scope is initialized, for example
1) On phones: `WMComponent` includes `WMShellModule` which includes `WMShellBaseModule`
(common to all SysUI)
2) On TVs: `TvWMComponent` includes `TvWMShellModule` which includes `WMShellBaseModule`
3) etc.
3) SysUI explicitly passes interfaces provided from the `WMComponent` to `SysUIComponent` via
the `SysUIComponent#Builder`, then builds the SysUI scoped components
4) `WMShell` 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.
5) SysUI can inject the interfaces into its own components
More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).
## Interfaces to Shell components
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:
1) (Shell) ShellFeature interface to be used from SysUI
2) (Shell) ShellFeatureController handles logic, implements ShellFeature interface and posts to
main Shell thread
3) SysUI application init injects Optional<ShellFeature> as an interface to SysUI to call
4) (SysUI) SysUIFeature depends on ShellFeature interface
5) (SysUI) SysUIFeature injects Optional<ShellFeature>, and sets up a callback for the Shell to
call, and the callback posts to the main SysUI thread
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.

View File

@@ -0,0 +1,49 @@
# Testing
---
## Unit tests
New WM Shell unit tests can be added to the
[Shell/tests/unittest](frameworks/base/libs/WindowManager/Shell/tests/unittest) directory, and can
be run via command line using `atest`:
```shell
atest WMShellUnitTests
```
If you use the SysUI Studio project, you can run and debug tests directly in the source files
(click on the little arrows next to the test class or test method).
These unit tests are run as a part of WindowManager presubmit, and the dashboards for these unit
tests tests can be found at [go/wm-tests](http://go/wm-tests).
This [GCL file](http://go/wm-unit-tests-gcl) configures the tests being run on the server.
## Flicker tests
Flicker tests are tests that perform actions and make assertions on the state in Window Manager
and SurfaceFlinger traces captured during the run.
New WM Shell Flicker tests can be added to the
[Shell/tests/flicker](frameworks/base/libs/WindowManager/Shell/tests/flicker) directory, and can
be run via command line using `atest`:
```shell
atest WMShellFlickerTests
```
**Note**: Currently Flicker tests can only be run from the commandline and not via SysUI Studio
A subset of the flicker tests tests are run as a part of WindowManager presubmit, and the
dashboards for these tests tests can be found at [go/wm-tests-flicker](http://go/wm-tests-flicker).
## CTS tests
Some windowing features also have CTS tests to ensure consistent behavior across OEMs. For example:
- Picture-in-Picture:
[PinnedStackTests](cts/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java)
- etc.
These can also be run via commandline only using `atest`, for example:
```shell
atest PinnedStackTests
```

View File

@@ -0,0 +1,83 @@
# Threading
---
## Boundaries
```text
Thread boundary
|
WM Shell | SystemUI
|
|
FeatureController <-> FeatureInterface <--|--> WMShell <-> SysUI
| (^post to shell thread) | (^post to main thread)
... |
| |
OtherControllers |
```
## Threads
We currently have multiple threads in use in the Shell library depending on the configuration by
the product.
- SysUI main thread (standard main thread)
- `ShellMainThread` (only used if the resource `config_enableShellMainThread` is set true
(ie. phones))
- This falls back to the SysUI main thread otherwise
- **Note**:
- This thread runs with `THREAD_PRIORITY_DISPLAY` priority since so many windowing-critical
components depend on it
- This is also the UI thread for almost all UI created by the Shell
- The Shell main thread Handler (and the Executor that wraps it) is async, so
messages/runnables used via this Handler are handled immediately if there is no sync
messages prior to it in the queue.
- `ShellBackgroundThread` (for longer running tasks where we don't want to block the shell main
thread)
- This is always another thread even if config_enableShellMainThread is not set true
- **Note**:
- This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
animations could be offloaded here)
- `ShellSplashScreenThread` (only for use with splashscreens)
## Dagger setup
The threading-related components are provided by the [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java),
for example, the Executors and Handlers for the various threads that are used. You can request
an executor of the necessary type by using the appropriate annotation for each of the threads (ie.
`@ShellMainThread Executor`) when injecting into your Shell component.
To get the SysUI main thread, you can use the `@Main` annotation.
## Best practices
### Components
- Don't do initialization in the Shell component constructors
- If the host SysUI is not careful, it may construct the WMComponent dependencies on the main
thread, and this reduces the likelihood that components will intiailize on the wrong thread
in such cases
- Be careful of using CountDownLatch and other blocking synchronization mechanisms in Shell code
- If the Shell main thread is not a separate thread, this will cause a deadlock
- Callbacks, Observers, Listeners to any non-shell component should post onto main Shell thread
- This includes Binder calls, SysUI calls, BroadcastReceivers, etc. Basically any API that
takes a runnable should either be registered with the right Executor/Handler or posted to
the main Shell thread manually
- Since everything in the Shell runs on the main Shell thread, you do **not** need to explicitly
`synchronize` your code (unless you are trying to prevent reentrantcy, but that can also be
done in other ways)
### Handlers/Executors
- You generally **never** need to create Handlers explicitly, instead inject `@ShellMainThread
ShellExecutor` instead
- This is a common pattern to defer logic in UI code, but the Handler created wraps the Looper
that is currently running, which can be wrong (see above for initialization vs construction)
- That said, sometimes Handlers are necessary because Framework API only takes Handlers or you
want to dedupe multiple messages
- In such cases inject `@ShellMainThread Handler` or use view.getHandler() which should be OK
assuming that the view root was initialized on the main Shell thread
- **Never use Looper.getMainLooper()**
- It's likely going to be wrong, you can inject `@Main ShellExecutor` to get the SysUI main thread
### Testing
- You can use a `TestShellExecutor` to control the processing of messages