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_DISPLAY;
20 import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
21 
22 import android.animation.AnimationHandler;
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 
30 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
31 import com.android.wm.shell.common.HandlerExecutor;
32 import com.android.wm.shell.common.ShellExecutor;
33 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
34 import com.android.wm.shell.common.annotations.ExternalMainThread;
35 import com.android.wm.shell.common.annotations.ShellAnimationThread;
36 import com.android.wm.shell.common.annotations.ShellMainThread;
37 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
38 import com.android.wm.shell.R;
39 
40 import dagger.Module;
41 import dagger.Provides;
42 
43 /**
44  * Provides basic concurrency-related dependencies from {@link com.android.wm.shell}, these
45  * dependencies are only accessible from components within the WM subcomponent.
46  */
47 @Module
48 public abstract class WMShellConcurrencyModule {
49 
50     private static final int MSGQ_SLOW_DELIVERY_THRESHOLD_MS = 30;
51     private static final int MSGQ_SLOW_DISPATCH_THRESHOLD_MS = 30;
52 
53     /**
54      * Returns whether to enable a separate shell thread for the shell features.
55      */
enableShellMainThread(Context context)56     private static boolean enableShellMainThread(Context context) {
57         return context.getResources().getBoolean(R.bool.config_enableShellMainThread);
58     }
59 
60     //
61     // Shell Concurrency - Components used for managing threading in the Shell and SysUI
62     //
63 
64 
65     /**
66      * Provide a SysUI main-thread Handler.
67      *
68      * Prefer the Main Executor when possible.
69      */
70     @Provides
71     @ExternalMainThread
provideMainHandler()72     public static Handler provideMainHandler() {
73         return new Handler(Looper.getMainLooper());
74     }
75 
76     /**
77      * Provide a SysUI main-thread Executor.
78      */
79     @WMSingleton
80     @Provides
81     @ExternalMainThread
provideSysUIMainExecutor( @xternalMainThread Handler sysuiMainHandler)82     public static ShellExecutor provideSysUIMainExecutor(
83             @ExternalMainThread Handler sysuiMainHandler) {
84         return new HandlerExecutor(sysuiMainHandler);
85     }
86 
87     /**
88      * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe
89      * multiple types of messages, etc.)
90      */
91     @WMSingleton
92     @Provides
93     @ShellMainThread
provideShellMainHandler(Context context, @ExternalMainThread Handler sysuiMainHandler)94     public static Handler provideShellMainHandler(Context context,
95             @ExternalMainThread Handler sysuiMainHandler) {
96         if (enableShellMainThread(context)) {
97              HandlerThread mainThread = new HandlerThread("wmshell.main", THREAD_PRIORITY_DISPLAY);
98              mainThread.start();
99              if (Build.IS_DEBUGGABLE) {
100                  mainThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
101                  mainThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
102                          MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
103              }
104              return Handler.createAsync(mainThread.getLooper());
105         }
106         return sysuiMainHandler;
107     }
108 
109     /**
110      * Provide a Shell main-thread Executor.
111      */
112     @WMSingleton
113     @Provides
114     @ShellMainThread
provideShellMainExecutor(Context context, @ShellMainThread Handler mainHandler, @ExternalMainThread ShellExecutor sysuiMainExecutor)115     public static ShellExecutor provideShellMainExecutor(Context context,
116             @ShellMainThread Handler mainHandler,
117             @ExternalMainThread ShellExecutor sysuiMainExecutor) {
118         if (enableShellMainThread(context)) {
119             return new HandlerExecutor(mainHandler);
120         }
121         return sysuiMainExecutor;
122     }
123 
124     /**
125      * Provide a Shell animation-thread Executor.
126      */
127     @WMSingleton
128     @Provides
129     @ShellAnimationThread
provideShellAnimationExecutor()130     public static ShellExecutor provideShellAnimationExecutor() {
131          HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
132                  THREAD_PRIORITY_DISPLAY);
133          shellAnimationThread.start();
134         if (Build.IS_DEBUGGABLE) {
135             shellAnimationThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
136             shellAnimationThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
137                     MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
138         }
139          return new HandlerExecutor(Handler.createAsync(shellAnimationThread.getLooper()));
140     }
141 
142     /**
143      * Provides a Shell splashscreen-thread Executor
144      */
145     @WMSingleton
146     @Provides
147     @ShellSplashscreenThread
provideSplashScreenExecutor()148     public static ShellExecutor provideSplashScreenExecutor() {
149         HandlerThread shellSplashscreenThread = new HandlerThread("wmshell.splashscreen",
150                 THREAD_PRIORITY_TOP_APP_BOOST);
151         shellSplashscreenThread.start();
152         return new HandlerExecutor(shellSplashscreenThread.getThreadHandler());
153     }
154 
155     /**
156      * Provide a Shell main-thread AnimationHandler.  The AnimationHandler can be set on
157      * {@link android.animation.ValueAnimator}s and will ensure that the animation will run on
158      * the Shell main-thread with the SF vsync.
159      */
160     @WMSingleton
161     @Provides
162     @ChoreographerSfVsync
provideShellMainExecutorSfVsyncAnimationHandler( @hellMainThread ShellExecutor mainExecutor)163     public static AnimationHandler provideShellMainExecutorSfVsyncAnimationHandler(
164             @ShellMainThread ShellExecutor mainExecutor) {
165         try {
166             AnimationHandler handler = new AnimationHandler();
167             mainExecutor.executeBlocking(() -> {
168                 // This is called on the animation thread since it calls
169                 // Choreographer.getSfInstance() which returns a thread-local Choreographer instance
170                 // that uses the SF vsync
171                 handler.setProvider(new SfVsyncFrameCallbackProvider());
172             });
173             return handler;
174         } catch (InterruptedException e) {
175             throw new RuntimeException("Failed to initialize SfVsync animation handler in 1s", e);
176         }
177     }
178 }
179