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