summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
blob: 0fc9ef96f6e9a522ed9015bd3616735bd53330cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * 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 androidx.annotation.Nullable;

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.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;
    }

    @Nullable
    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 {
        GlobalRootComponent.Builder globalBuilder = getGlobalRootComponentBuilder();
        if (globalBuilder == null) {
            return;
        }

        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())
                    .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() {})
                    .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);
    }
}