diff options
10 files changed, 468 insertions, 13 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt deleted file mode 100644 index 1cd69edf7cd2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt +++ /dev/null @@ -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)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md new file mode 100644 index 000000000000..73a7348d5aca --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md @@ -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
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md new file mode 100644 index 000000000000..f4e2f20f4637 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md @@ -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.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md new file mode 100644 index 000000000000..6c01d962adc9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md @@ -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.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md new file mode 100644 index 000000000000..52f0c4222b64 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md @@ -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** 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. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md new file mode 100644 index 000000000000..061ae00e2b25 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md @@ -0,0 +1,13 @@ +# Extending the Shell for Products/OEMs + +--- + +## General Do's & Dont's + +Do: +- + +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
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md new file mode 100644 index 000000000000..a88ef6aea2ec --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md @@ -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™ 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)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md new file mode 100644 index 000000000000..68f970ff48df --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md @@ -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** 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.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md new file mode 100644 index 000000000000..8a80333facc4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md @@ -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 +```
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md new file mode 100644 index 000000000000..eac748894432 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md @@ -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
\ No newline at end of file |