Allow Services to be created through Dagger.

This is a significant change that allows Services to have their
constructed injected into.

The DozeService is now labeled as injectable (thought it takes no arguments).
At least one Service has to be marked as injectable for Dagger to be happy
compiling this code.

Bug: 138787339
Test: atest SystemUITests
Change-Id: I0ec92e01dd236bcee3083c8f8744c53b7ccc92d3
Merged-In: Ib58f8763c996fbc2aea07ead56493d2d9e936f5b
This commit is contained in:
Dave Mankoff 2019-07-03 13:26:55 -04:00
parent fa6f50e13d
commit 1a0e382745
13 changed files with 291 additions and 38 deletions

View File

@ -26,8 +26,8 @@ android_app {
],
static_libs: [
"CarNotificationLib",
"SystemUI-core",
"CarNotificationLib",
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",

View File

@ -20,6 +20,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.systemui"
android:sharedUserId="android.uid.systemui"
xmlns:tools="http://schemas.android.com/tools"
coreApp="true">
<!-- Using OpenGL ES 2.0 -->
@ -259,7 +260,8 @@
android:theme="@style/Theme.SystemUI"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
tools:replace="android:appComponentFactory"
android:appComponentFactory=".SystemUIAppComponentFactory">
<!-- Keep theme in sync with SystemUIApplication.onCreate().
Setting the theme on the application does not affect views inflated by services.
The application theme is set again from onCreate to take effect for those views. -->

View File

@ -53,7 +53,7 @@ variants (like other form factors e.g. Car).
### Adding injection to a new SystemUI object
Anything that depends on any `@Singleton` provider from SystemUIRootComponent
should be declared as an `@Subcomponent` of the root component, this requires
should be declared as a `@Subcomponent` of the root component. This requires
declaring your own interface for generating your own modules or just the
object you need injected. The subcomponent also needs to be added to
SystemUIRootComponent in SystemUIFactory so it can be acquired.
@ -204,6 +204,13 @@ public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet a
}
```
## Updating Dagger2
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/)
## TODO List
- Eliminate usages of Dependency#get

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui;
/**
* Interface necessary to make Dagger happy. See {@link ContextComponentResolver}.
*/
public interface ContextComponentHelper {
/** Turns a classname into an instance of the class or returns null. */
<T> T resolve(String className);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
/**
* Used during Service and Activity instantiation to make them injectable.
*/
public class ContextComponentResolver implements ContextComponentHelper {
private final Map<Class<?>, Provider<Object>> mCreators;
@Inject
ContextComponentResolver(Map<Class<?>, Provider<Object>> creators) {
mCreators = creators;
}
/**
* Looks up the class name to see if Dagger has an instance of it.
*/
@Override
public <T> T resolve(String className) {
for (Map.Entry<Class<?>, Provider<Object>> p : mCreators.entrySet()) {
if (p.getKey().getName().equals(className)) {
return (T) p.getValue().get();
}
}
return null;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui;
import com.android.systemui.doze.DozeService;
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
* Services and Activities that are injectable should go here.
*/
@Module
public abstract class ServiceBinder {
@Binds
public abstract ContextComponentHelper bindComponentHelper(
ContextComponentResolver componentHelper);
@Binds
@IntoMap
@ClassKey(DozeService.class)
public abstract Object bindDozeService(DozeService service);
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui;
import android.app.Application;
import android.app.Service;
import android.content.Intent;
import androidx.core.app.CoreComponentFactory;
import javax.inject.Inject;
/**
* Implementation of AppComponentFactory that injects into constructors.
*/
public class SystemUIAppComponentFactory extends CoreComponentFactory {
@Inject
public ContextComponentHelper mComponentHelper;
public SystemUIAppComponentFactory() {
super();
}
@Override
public Application instantiateApplication(ClassLoader cl, String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Application app = super.instantiateApplication(cl, className);
if (app instanceof SystemUIApplication) {
((SystemUIApplication) app).setContextAvailableCallback(
context -> {
SystemUIFactory.createFromConfig(context);
SystemUIFactory.getInstance().getRootComponent().inject(
SystemUIAppComponentFactory.this);
}
);
}
return app;
}
@Override
public Service instantiateService(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Service service = mComponentHelper.resolve(className);
if (service != null) {
return checkCompatWrapper(service);
}
return super.instantiateService(cl, className, intent);
}
static <T> T checkCompatWrapper(T obj) {
if (obj instanceof CompatWrapped) {
T wrapper = (T) ((CompatWrapped) obj).getWrapper();
if (wrapper != null) {
return wrapper;
}
}
return obj;
}
}

View File

@ -60,16 +60,20 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
private boolean mServicesStarted;
private boolean mBootCompleted;
private final Map<Class<?>, Object> mComponents = new HashMap<>();
private ContextAvailableCallback mContextAvailableCallback;
@Override
public void onCreate() {
super.onCreate();
// This line is used to setup Dagger's dependency injection and should be kept at the
// top of this method.
mContextAvailableCallback.onContextAvailable(this);
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
// the theme set there.
setTheme(R.style.Theme_SystemUI);
SystemUIFactory.createFromConfig(this);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
@ -286,4 +290,12 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
public SystemUI[] getServices() {
return mServices;
}
void setContextAvailableCallback(ContextAvailableCallback callback) {
mContextAvailableCallback = callback;
}
interface ContextAvailableCallback {
void onContextAvailable(Context context);
}
}

View File

@ -36,7 +36,6 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManagerFactory;
import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
@ -68,8 +67,6 @@ import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeDialogComponent;
import java.util.function.Consumer;
@ -77,7 +74,6 @@ import java.util.function.Consumer;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
@ -115,7 +111,7 @@ public class SystemUIFactory {
public SystemUIFactory() {}
protected void init(Context context) {
mRootComponent = DaggerSystemUIFactory_SystemUIRootComponent.builder()
mRootComponent = DaggerSystemUIRootComponent.builder()
.systemUIFactory(this)
.dependencyProvider(new com.android.systemui.DependencyProvider())
.contextHolder(new ContextHolder(context))
@ -267,29 +263,4 @@ public class SystemUIFactory {
return mContext;
}
}
@Singleton
@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
ContextHolder.class})
public interface SystemUIRootComponent {
@Singleton
Dependency.DependencyInjector createDependency();
@Singleton
StatusBar.StatusBarInjector getStatusBarInjector();
/**
* FragmentCreator generates all Fragments that need injection.
*/
@Singleton
FragmentService.FragmentCreator createFragmentCreator();
/**
* ViewCreator generates all Views that need injection.
*/
InjectionInflationController.ViewCreator createViewCreator();
@Singleton
GarbageMonitor createGarbageMonitor();
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.leak.GarbageMonitor;
import javax.inject.Singleton;
import dagger.Component;
/**
* Root component for Dagger injection.
*/
@Singleton
@Component(modules = {SystemUIFactory.class, DependencyProvider.class, DependencyBinder.class,
ServiceBinder.class, SystemUIFactory.ContextHolder.class})
public interface SystemUIRootComponent {
/**
* Main dependency providing module.
*/
@Singleton
Dependency.DependencyInjector createDependency();
/**
* Injects the StatusBar.
*/
@Singleton
StatusBar.StatusBarInjector getStatusBarInjector();
/**
* FragmentCreator generates all Fragments that need injection.
*/
@Singleton
FragmentService.FragmentCreator createFragmentCreator();
/**
* ViewCreator generates all Views that need injection.
*/
InjectionInflationController.ViewCreator createViewCreator();
/**
* Creatse a GarbageMonitor.
*/
@Singleton
GarbageMonitor createGarbageMonitor();
/**
* Injects into the supplied argument.
*/
void inject(SystemUIAppComponentFactory factory);
}

View File

@ -31,6 +31,8 @@ import com.android.systemui.shared.plugins.PluginManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import javax.inject.Inject;
public class DozeService extends DreamService
implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> {
private static final String TAG = "DozeService";
@ -40,6 +42,7 @@ public class DozeService extends DreamService
private DozeServicePlugin mDozePlugin;
private PluginManager mPluginManager;
@Inject
public DozeService() {
setDebug(DEBUG);
}

View File

@ -22,7 +22,7 @@ import android.view.View;
import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SystemUIRootComponent;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
@ -51,7 +51,7 @@ public class FragmentService implements ConfigurationChangedReceiver, Dumpable {
private final FragmentCreator mFragmentCreator;
@Inject
public FragmentService(SystemUIFactory.SystemUIRootComponent rootComponent) {
public FragmentService(SystemUIRootComponent rootComponent) {
mFragmentCreator = rootComponent.createFragmentCreator();
initInjectionMap();
}

View File

@ -26,7 +26,7 @@ import android.view.View;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SystemUIRootComponent;
import com.android.systemui.qs.QSCarrierGroup;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@ -62,7 +62,7 @@ public class InjectionInflationController {
private final LayoutInflater.Factory2 mFactory = new InjectionFactory();
@Inject
public InjectionInflationController(SystemUIFactory.SystemUIRootComponent rootComponent) {
public InjectionInflationController(SystemUIRootComponent rootComponent) {
mViewCreator = rootComponent.createViewCreator();
initInjectionMap();
}