blob: 2b468cf7c16b06e6b245a9fd252283400c19714b [file] [log] [blame]
/*
* Copyright (C) 2022 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.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.util.InitializationChecker;
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
import com.android.wm.shell.keyguard.KeyguardTransitions;
import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
/**
* Initializer that stands up SystemUI.
*
* Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own
* Dagger root component.
*/
public abstract class SystemUIInitializer {
private static final String TAG = "SystemUIFactory";
private final Context mContext;
private GlobalRootComponent mRootComponent;
private WMComponent mWMComponent;
private SysUIComponent mSysUIComponent;
private InitializationChecker mInitializationChecker;
public SystemUIInitializer(Context context) {
mContext = context;
}
protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
/**
* Prepares the SysUIComponent builder before it is built.
* @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
* @param wm the built WMComponent from the root component's getWMComponent() method
*/
protected SysUIComponent.Builder prepareSysUIComponentBuilder(
SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
return sysUIBuilder;
}
/**
* Starts the initialization process. This stands up the Dagger graph.
*/
public void init(boolean fromTest) throws ExecutionException, InterruptedException {
mRootComponent = getGlobalRootComponentBuilder()
.context(mContext)
.instrumentationTest(fromTest)
.build();
mInitializationChecker = mRootComponent.getInitializationChecker();
boolean initializeComponents = mInitializationChecker.initializeComponents();
// Stand up WMComponent
setupWmComponent(mContext);
// And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
if (initializeComponents) {
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
builder = prepareSysUIComponentBuilder(builder, mWMComponent)
.setShell(mWMComponent.getShell())
.setPip(mWMComponent.getPip())
.setSplitScreen(mWMComponent.getSplitScreen())
.setOneHanded(mWMComponent.getOneHanded())
.setBubbles(mWMComponent.getBubbles())
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
.setTransitions(mWMComponent.getTransitions())
.setKeyguardTransitions(mWMComponent.getKeyguardTransitions())
.setStartingSurface(mWMComponent.getStartingSurface())
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
.setBackAnimation(mWMComponent.getBackAnimation())
.setDesktopMode(mWMComponent.getDesktopMode());
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
mWMComponent.init();
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
builder = prepareSysUIComponentBuilder(builder, mWMComponent)
.setShell(new ShellInterface() {})
.setPip(Optional.ofNullable(null))
.setSplitScreen(Optional.ofNullable(null))
.setOneHanded(Optional.ofNullable(null))
.setBubbles(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(new ShellTransitions() {})
.setKeyguardTransitions(new KeyguardTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
.setBackAnimation(Optional.ofNullable(null))
.setDesktopMode(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (initializeComponents) {
mSysUIComponent.init();
}
// Every other part of our codebase currently relies on Dependency, so we
// really need to ensure the Dependency gets initialized early on.
Dependency dependency = mSysUIComponent.createDependency();
dependency.start();
}
/**
* Sets up {@link #mWMComponent}. On devices where the Shell runs on its own main thread,
* this will pre-create the thread to ensure that the components are constructed on the
* same thread, to reduce the likelihood of side effects from running the constructors on
* a different thread than the rest of the class logic.
*/
private void setupWmComponent(Context context) {
WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder();
if (!mInitializationChecker.initializeComponents()
|| !WMShellConcurrencyModule.enableShellMainThread(context)) {
// If running under tests or shell thread is not enabled, we don't need anything special
mWMComponent = wmBuilder.build();
return;
}
// If the shell main thread is enabled, initialize the component on that thread
HandlerThread shellThread = WMShellConcurrencyModule.createShellMainThread();
shellThread.start();
// Use an async handler since we don't care about synchronization
Handler shellHandler = Handler.createAsync(shellThread.getLooper());
boolean built = shellHandler.runWithScissors(() -> {
wmBuilder.setShellMainThread(shellThread);
mWMComponent = wmBuilder.build();
}, 5000);
if (!built) {
Log.w(TAG, "Failed to initialize WMComponent");
throw new RuntimeException();
}
}
public GlobalRootComponent getRootComponent() {
return mRootComponent;
}
public WMComponent getWMComponent() {
return mWMComponent;
}
public SysUIComponent getSysUIComponent() {
return mSysUIComponent;
}
/**
* Returns the list of additional system UI components that should be started.
*/
public String getVendorComponent(Resources resources) {
return resources.getString(R.string.config_systemUIVendorServiceComponent);
}
}