Merge "Update README.md and dagger.md." into tm-qpr-dev am: 212f86c915

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

Change-Id: I1eb007d6c3746e7ac7d969245c34d99b092ad15f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Dave Mankoff
2023-01-18 20:43:32 +00:00
committed by Automerger Merge Worker
2 changed files with 131 additions and 143 deletions

View File

@@ -5,46 +5,72 @@
SystemUI is a persistent process that provides UI for the system but outside SystemUI is a persistent process that provides UI for the system but outside
of the system_server process. of the system_server process.
The starting point for most of sysui code is a list of services that extend
SystemUI that are started up by SystemUIApplication. These services then depend
on some custom dependency injection provided by Dependency.
Inputs directed at sysui (as opposed to general listeners) generally come in Inputs directed at sysui (as opposed to general listeners) generally come in
through IStatusBar. Outputs from sysui are through a variety of private APIs to through IStatusBar. Outputs from sysui are through a variety of private APIs to
the android platform all over. the android platform all over.
## SystemUIApplication ## SystemUIApplication
When SystemUIApplication starts up, it will start up the services listed in When SystemUIApplication starts up, it instantiates a Dagger graph from which
config_systemUIServiceComponents or config_systemUIServiceComponentsPerUser. various pieces of the application are built.
Each of these services extend SystemUI. SystemUI provides them with a Context To support customization, SystemUIApplication relies on the AndroidManifest.xml
and gives them callbacks for onConfigurationChanged (this historically was having an `android.app.AppComponentFactory` specified. Specifically, it relies
the main path for onConfigurationChanged, now also happens through on an `AppComponentFactory` that subclases `SystemUIAppComponentFactoryBase`.
ConfigurationController). They also receive a callback for onBootCompleted Implementations of this abstract base class must override
`#createSystemUIInitializer(Context)` which returns a `SystemUIInitializer`.
`SystemUIInitializer` primary job in turn is to intialize and return the Dagger
root component back to the `SystemUIApplication`.
Writing a custom `SystemUIAppComponentFactoryBase` and `SystemUIInitializer`,
should be enough for most implementations to stand up a customized Dagger
graph, and launch a custom version of SystemUI.
## Dagger / Dependency Injection
See [dagger.md](docs/dagger.md) and https://dagger.dev/.
## CoreStartable
The starting point for most of SystemUI code is a list of classes that
implement `CoreStartable` that are started up by SystemUIApplication.
CoreStartables are like miniature services. They have their `#start` method
called after being instantiated, and a reference to them is stored inside
SystemUIApplication. They are in charge of their own behavior beyond this,
registering and unregistering with the rest of the system as needed.
`CoreStartable` also receives a callback for `#onBootCompleted`
since these objects may be started before the device has finished booting. since these objects may be started before the device has finished booting.
Each SystemUI service is expected to be a major part of system ui and the `CoreStartable` is an ideal place to add new features and functionality
goal is to minimize communication between them. So in general they should be that does not belong directly under the umbrella of an existing feature.
relatively silo'd. It is better to define a new `CoreStartable` than to stick unrelated
initialization code together in catch-all methods.
## Dependencies CoreStartables are tied to application startup via Dagger:
The first SystemUI service that is started should always be Dependency. ```kotlin
Dependency provides a static method for getting a hold of dependencies that class FeatureStartable
have a lifecycle that spans sysui. Dependency has code for how to create all @Inject
dependencies manually added. SystemUIFactory is also capable of constructor(
adding/replacing these dependencies. /* ... */
) : CoreStartable {
override fun start() {
// ...
}
}
Dependencies are lazily initialized, so if a Dependency is never referenced at @Module
runtime, it will never be created. abstract class FeatureModule {
@Binds
@IntoMap
@ClassKey(FeatureStartable::class)
abstract fun bind(impl: FeatureStartable): CoreStartable
}
```
If an instantiated dependency implements Dumpable it will be included in dumps Including `FeatureModule` in the Dagger graph such as this will ensure that
of sysui (and bug reports), allowing it to include current state information. `FeatureStartable` gets constructed and that its `#start` method is called.
This is how \*Controllers dump state to bug reports.
If an instantiated dependency implements ConfigurationChangeReceiver it will
receive onConfigurationChange callbacks when the configuration changes.
## IStatusBar ## IStatusBar
@@ -64,12 +90,6 @@ across sysui. Such as when StatusBar calls CommandQueue#recomputeDisableFlags.
This is generally used a shortcut to directly trigger CommandQueue rather than This is generally used a shortcut to directly trigger CommandQueue rather than
calling StatusManager and waiting for the call to come back to IStatusBar. calling StatusManager and waiting for the call to come back to IStatusBar.
## Default SystemUI services list
### [com.android.systemui.Dependency](/packages/SystemUI/src/com/android/systemui/Dependency.java)
Provides custom dependency injection.
### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java) ### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java)
Creates/initializes the channels sysui uses when posting notifications. Creates/initializes the channels sysui uses when posting notifications.
@@ -88,11 +108,11 @@ activity. It provides this cached data to RecentsActivity when it is started.
Registers all the callbacks/listeners required to show the Volume dialog when Registers all the callbacks/listeners required to show the Volume dialog when
it should be shown. it should be shown.
### [com.android.systemui.status.phone.StatusBar](/packages/SystemUI/src/com/android/systemui/status/phone/StatusBar.java) ### [com.android.systemui.status.phone.CentralSurfaces](/packages/SystemUI/src/com/android/systemui/status/phone/CentralSurfaces.java)
This shows the UI for the status bar and the notification shade it contains. This shows the UI for the status bar and the notification shade it contains.
It also contains a significant amount of other UI that interacts with these It also contains a significant amount of other UI that interacts with these
surfaces (keyguard, AOD, etc.). StatusBar also contains a notification listener surfaces (keyguard, AOD, etc.). CentralSurfaces also contains a notification listener
to receive notification callbacks. to receive notification callbacks.
### [com.android.systemui.usb.StorageNotification](/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java) ### [com.android.systemui.usb.StorageNotification](/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java)

View File

@@ -8,105 +8,110 @@ Go read about Dagger 2.
- [User's guide](https://google.github.io/dagger/users-guide) - [User's guide](https://google.github.io/dagger/users-guide)
TODO: Add some links.
## State of the world ## State of the world
Dagger 2 has been turned on for SystemUI and a early first pass has been taken Dagger 2 has been turned on for SystemUI and much of
for converting everything in [Dependency.java](packages/systemui/src/com/android/systemui/Dependency.java) [Dependency.java](../src/com/android/systemui/Dependency.java)
to use Dagger. Since a lot of SystemUI depends on Dependency, stubs have been added to Dependency has been converted to use Dagger. Since a lot of SystemUI depends on Dependency,
to proxy any gets through to the instances provided by dagger, this will allow migration of SystemUI stubs have been added to Dependency to proxy any gets through to the instances
through a number of CLs. provided by dagger, this will allow migration of SystemUI through a number of CLs.
### How it works in SystemUI ### How it works in SystemUI
There are three high level "scopes" of concern in SystemUI. They all represent
singleton scopes, but serve different purposes.
* `@Singleton` - Instances that are shared everywhere. There isn't a lot of
code in this scope. Things like the main thread, and Android Framework
provided instances mostly.
* `@WMShell` - WindowManager related code in the SystemUI process. We don't
want this code relying on the rest of SystemUI, and we don't want the rest
of SystemUI peeking into its internals, so it runs in its own Subcomponent.
* `@SysUISingleton` - Most of what would be considered "SystemUI". Most feature
work by SystemUI developers goes into this scope. Useful interfaces from
WindowManager are made available inside this Subcomponent.
The root dagger graph is created by an instance of `SystemUIInitializer`.
See [README.md](../README.md) for more details.
For the classes that we're using in Dependency and are switching to dagger, the For the classes that we're using in Dependency and are switching to dagger, the
equivalent dagger version is using `@Singleton` and therefore only has one instance. equivalent dagger version is using `@Singleton` and therefore only has one instance.
To have the single instance span all of SystemUI and be easily accessible for To have the single instance span all of SystemUI and be easily accessible for
other components, there is a single root `@Component` that exists that generates other components, there is a single root `@Component` that exists that generates
these. The component lives in [SystemUIFactory](packages/systemui/src/com/android/systemui/SystemUIFactory.java) these. The component lives in
and is called `SystemUIRootComponent`. [ReferenceGlobalRootComponent.java](../src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java).
```java
@Singleton
@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
ContextHolder.class})
public interface SystemUIRootComponent {
@Singleton
Dependency.DependencyInjector createDependency();
}
```
The root component is composed of root modules, which in turn provide the global singleton
dependencies across all of SystemUI.
- `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
variants (like other form factors e.g. Car).
- `DependencyBinder` creates the mapping from interfaces to implementation classes.
- `DependencyProvider` provides or binds any remaining depedencies required.
### Adding injection to a new SystemUI object
SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
information in that file to locate and construct an instance of the requested SystemUI class.
### Adding a new injectable object ### Adding a new injectable object
First tag the constructor with `@Inject`. Also tag it with `@Singleton` if only one First annotate the constructor with `@Inject`. Also annotate it with
instance should be created. `@SysUISingleton` if only one instance should be created.
```java ```kotlin
@Singleton @SysUISingleton
public class SomethingController { class FeatureStartable
@Inject @Inject
public SomethingController(Context context, constructor(
@Named(MAIN_HANDLER_NAME) Handler mainHandler) { /* ... */
// context and mainHandler will be automatically populated. ) {
} // ...
} }
``` ```
If you have an interface class and an implementation class, dagger needs to know If you have an interface class and an implementation class, Dagger needs to
how to map it. The simplest way to do this is to add an `@Provides` method to know how to map it. The simplest way to do this is to add an `@Binds` method
DependencyProvider. The type of the return value tells dagger which dependency it's providing. in a module. The type of the return value tells dagger which dependency it's
providing:
```java ```kotlin
public class DependencyProvider { @Module
//... abstract class FeatureModule {
@Singleton @Binds
@Provides abstract fun bindsFeature(impl: FeatureImpl): Feature
public SomethingController provideSomethingController(Context context,
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
return new SomethingControllerImpl(context, mainHandler);
}
} }
``` ```
If you need to access this from Dependency#get, then add an adapter to Dependency If you have a class that you want to make injectable that has can not
that maps to the instance provided by Dagger. The changes should be similar be easily constructed by Dagger, write a `@Provides` method for it:
to the following diff.
```java ```kotlin
public class Dependency { @Module
//... abstract class FeatureModule {
@Inject Lazy<SomethingController> mSomethingController; @Module
//... companion object {
public void start() { @Provides
//... fun providesFeature(ctx: Context): Feature {
mProviders.put(SomethingController.class, mSomethingController::get); return FeatureImpl.constructFromContext(ctx)
} }
}
} }
``` ```
### Module Organization
Please define your modules on _at least_ per-package level. If the scope of a
package grows to encompass a great number of features, create per-feature
modules.
**Do not create catch-all modules.** Those quickly grow unwieldy and
unmaintainable. Any that exist today should be refactored into obsolescence.
You can then include your module in one of three places:
1) Within another module that depends on it. Ideally, this creates a clean
dependency graph between features and utilities.
2) For features that should exist in all versions of SystemUI (AOSP and
any variants), include the module in
[SystemUIModule.java](../src/com/android/systemui/dagger/SystemUIModule.java).
3) For features that should exist only in AOSP, include the module in
[ReferenceSystemUIModule.java](../src/com/android/systemui/dagger/ReferenceSystemUIModule.java).
Similarly, if you are working on a custom version of SystemUI and have code
specific to your version, include it in a module specific to your version.
### Using injection with Fragments ### Using injection with Fragments
Fragments are created as part of the FragmentManager, so they need to be Fragments are created as part of the FragmentManager, so they need to be
setup so the manager knows how to create them. To do that, add a method setup so the manager knows how to create them. To do that, add a method
to com.android.systemui.fragments.FragmentService$FragmentCreator that to com.android.systemui.fragments.FragmentService$FragmentCreator that
returns your fragment class. Thats all thats required, once the method returns your fragment class. That is all that is required, once the method
exists, FragmentService will automatically pick it up and use injection exists, FragmentService will automatically pick it up and use injection
whenever your fragment needs to be created. whenever your fragment needs to be created.
@@ -123,48 +128,11 @@ then the FragmentHostManager can do this for you.
FragmentHostManager.get(view).create(NavigationBarFragment.class); FragmentHostManager.get(view).create(NavigationBarFragment.class);
``` ```
### Using injection with Views
DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
Needing to inject objects into your View's constructor generally implies you
are doing more work in your presentation layer than is advisable.
Instead, create an injected controller for you view, inject into the
controller, and then attach the view to the controller after inflation.
View injection generally causes headaches while testing, as inflating a view
(which may in turn inflate other views) implicitly causes a Dagger graph to
be stood up, which may or may not contain the appropriately
faked/mocked/stubbed objects. It is a hard to control process.
## Updating Dagger2 ## Updating Dagger2
We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates We depend on the Dagger source found in external/dagger2. We should automatically pick up on updates
when that repository is updated. when that repository is updated.
*Deprecated:*
Binaries can be downloaded from https://repo1.maven.org/maven2/com/google/dagger/ and then loaded
into
[/prebuilts/tools/common/m2/repository/com/google/dagger/](http://cs/android/prebuilts/tools/common/m2/repository/com/google/dagger/)
The following commands should work, substituting in the version that you are looking for:
````
cd prebuilts/tools/common/m2/repository/com/google/dagger/
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger/2.28.1/
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-compiler/2.28.1/
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-spi/2.28.1/
wget -r -np -nH --cut-dirs=4 -erobots=off -R "index.html*" -U "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" https://repo1.maven.org/maven2/com/google/dagger/dagger-producers/2.28.1/
````
Then update `prebuilts/tools/common/m2/Android.bp` to point at your new jars.
## TODO List ## TODO List
- Eliminate usages of Dependency#get - Eliminate usages of Dependency#get: http://b/hotlists/3940788
- Add links in above TODO