1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.dagger;
18 
19 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
20 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
21 import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
22 
23 import android.content.Context;
24 import android.os.Build;
25 import android.os.Handler;
26 import android.os.HandlerThread;
27 import android.os.Looper;
28 import android.os.Trace;
29 import android.view.Choreographer;
30 
31 import androidx.annotation.Nullable;
32 
33 import com.android.wm.shell.R;
34 import com.android.wm.shell.common.HandlerExecutor;
35 import com.android.wm.shell.common.ShellExecutor;
36 import com.android.wm.shell.shared.annotations.ExternalMainThread;
37 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
38 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
39 import com.android.wm.shell.shared.annotations.ShellMainThread;
40 import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
41 
42 import dagger.Module;
43 import dagger.Provides;
44 
45 /**
46  * Provides basic concurrency-related dependencies from {@link com.android.wm.shell}, these
47  * dependencies are only accessible from components within the WM subcomponent.
48  */
49 @Module
50 public abstract class WMShellConcurrencyModule {
51 
52     private static final int MSGQ_SLOW_DELIVERY_THRESHOLD_MS = 30;
53     private static final int MSGQ_SLOW_DISPATCH_THRESHOLD_MS = 30;
54 
55     /**
56      * Returns whether to enable a separate shell thread for the shell features.
57      */
enableShellMainThread(Context context)58     public static boolean enableShellMainThread(Context context) {
59         return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
60     }
61 
62     //
63     // Shell Concurrency - Components used for managing threading in the Shell and SysUI
64     //
65 
66 
67     /**
68      * Provide a SysUI main-thread Handler.
69      *
70      * Prefer the Main Executor when possible.
71      */
72     @Provides
73     @ExternalMainThread
provideMainHandler()74     public static Handler provideMainHandler() {
75         return new Handler(Looper.getMainLooper());
76     }
77 
78     /**
79      * Provide a SysUI main-thread Executor.
80      */
81     @WMSingleton
82     @Provides
83     @ExternalMainThread
provideSysUIMainExecutor( @xternalMainThread Handler sysuiMainHandler)84     public static ShellExecutor provideSysUIMainExecutor(
85             @ExternalMainThread Handler sysuiMainHandler) {
86         return new HandlerExecutor(sysuiMainHandler);
87     }
88 
89     /**
90      * Creates a shell main thread to be injected into the shell components.  This does not provide
91      * the {@param HandleThread}, but is used to create the thread prior to initializing the
92      * WM component, and is explicitly bound.
93      *
94      * See {@link com.android.systemui.SystemUIFactory#init(Context, boolean)}.
95      */
createShellMainThread()96     public static HandlerThread createShellMainThread() {
97         HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
98         return mainThread;
99     }
100 
101     /**
102      * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe
103      * multiple types of messages, etc.)
104      *
105      * @param mainThread If non-null, this thread is expected to be started already
106      */
107     @WMSingleton
108     @Provides
109     @ShellMainThread
provideShellMainHandler(Context context, @Nullable @ShellMainThread HandlerThread mainThread, @ExternalMainThread Handler sysuiMainHandler)110     public static Handler provideShellMainHandler(Context context,
111             @Nullable @ShellMainThread HandlerThread mainThread,
112             @ExternalMainThread Handler sysuiMainHandler) {
113         if (enableShellMainThread(context)) {
114             if (mainThread == null) {
115                 // If this thread wasn't pre-emptively started, then create and start it
116                 mainThread = createShellMainThread();
117                 mainThread.start();
118             }
119             if (Build.IS_DEBUGGABLE) {
120                 mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
121                 mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
122                         MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
123             }
124             return Handler.createAsync(mainThread.getLooper());
125         }
126         return sysuiMainHandler;
127     }
128 
129     /**
130      * Provide a Shell main-thread Executor.
131      */
132     @WMSingleton
133     @Provides
134     @ShellMainThread
provideShellMainExecutor(Context context, @ShellMainThread Handler mainHandler, @ExternalMainThread ShellExecutor sysuiMainExecutor)135     public static ShellExecutor provideShellMainExecutor(Context context,
136             @ShellMainThread Handler mainHandler,
137             @ExternalMainThread ShellExecutor sysuiMainExecutor) {
138         if (enableShellMainThread(context)) {
139             return new HandlerExecutor(mainHandler);
140         }
141         return sysuiMainExecutor;
142     }
143 
144     /**
145      * Provide a Shell main-thread {@link Choreographer} with the app vsync.
146      *
147      * @param executor the executor of the shell main thread
148      */
149     @WMSingleton
150     @Provides
151     @ShellMainThread
provideShellMainChoreographer( @hellMainThread ShellExecutor executor)152     public static Choreographer provideShellMainChoreographer(
153             @ShellMainThread ShellExecutor executor) {
154         try {
155             final Choreographer[] choreographer = new Choreographer[1];
156             executor.executeBlocking(() -> choreographer[0] = Choreographer.getInstance());
157             return choreographer[0];
158         } catch (InterruptedException e) {
159             throw new RuntimeException("Failed to obtain main Choreographer.", e);
160         }
161     }
162 
163     /**
164      * Provide a Shell animation-thread Executor.
165      */
166     @WMSingleton
167     @Provides
168     @ShellAnimationThread
provideShellAnimationExecutor()169     public static ShellExecutor provideShellAnimationExecutor() {
170          HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
171                  THREAD_PRIORITY_DISPLAY);
172          shellAnimationThread.start();
173         if (Build.IS_DEBUGGABLE) {
174             shellAnimationThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
175             shellAnimationThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
176                     MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
177         }
178          return new HandlerExecutor(Handler.createAsync(shellAnimationThread.getLooper()));
179     }
180 
181     /**
182      * Provides a Shell splashscreen-thread Executor
183      */
184     @WMSingleton
185     @Provides
186     @ShellSplashscreenThread
provideSplashScreenExecutor()187     public static ShellExecutor provideSplashScreenExecutor() {
188         HandlerThread shellSplashscreenThread = new HandlerThread("wmshell.splashscreen",
189                 THREAD_PRIORITY_TOP_APP_BOOST);
190         shellSplashscreenThread.start();
191         return new HandlerExecutor(shellSplashscreenThread.getThreadHandler());
192     }
193 
194     /**
195      * Provides a Shell background thread Handler for low priority background tasks.
196      */
197     @WMSingleton
198     @Provides
199     @ShellBackgroundThread
provideSharedBackgroundHandler()200     public static Handler provideSharedBackgroundHandler() {
201         HandlerThread shellBackgroundThread = new HandlerThread("wmshell.background",
202                 THREAD_PRIORITY_BACKGROUND);
203         shellBackgroundThread.start();
204         return shellBackgroundThread.getThreadHandler();
205     }
206 
207     /**
208      * Provides a Shell background thread Executor for low priority background tasks.
209      */
210     @WMSingleton
211     @Provides
212     @ShellBackgroundThread
provideSharedBackgroundExecutor( @hellBackgroundThread Handler handler)213     public static ShellExecutor provideSharedBackgroundExecutor(
214             @ShellBackgroundThread Handler handler) {
215         return new HandlerExecutor(handler);
216     }
217 }
218