1 /* 2 * Copyright (C) 2024 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.systemui.util.kotlin 18 19 import android.os.Handler 20 import com.android.systemui.coroutines.newTracingContext 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.dagger.qualifiers.NotifInflation 25 import com.android.systemui.dagger.qualifiers.UiBackground 26 import com.android.systemui.util.settings.SettingsSingleThreadBackground 27 import dagger.Module 28 import dagger.Provides 29 import java.util.concurrent.Executor 30 import kotlin.coroutines.CoroutineContext 31 import kotlinx.coroutines.CoroutineDispatcher 32 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.DelicateCoroutinesApi 34 import kotlinx.coroutines.Dispatchers 35 import kotlinx.coroutines.android.asCoroutineDispatcher 36 import kotlinx.coroutines.asCoroutineDispatcher 37 import kotlinx.coroutines.newFixedThreadPoolContext 38 import kotlinx.coroutines.plus 39 40 private const val LIMIT_BACKGROUND_DISPATCHER_THREADS = true 41 42 /** Providers for various SystemUI-specific coroutines-related constructs. */ 43 @Module 44 class SysUICoroutinesModule { 45 @Provides 46 @SysUISingleton 47 @Background bgApplicationScopenull48 fun bgApplicationScope( 49 @Application applicationScope: CoroutineScope, 50 @Background coroutineContext: CoroutineContext, 51 ): CoroutineScope = applicationScope.plus(coroutineContext) 52 53 /** 54 * Default Coroutine dispatcher for background operations. 55 * 56 * Note that this is explicitly limiting the number of threads. In the past, we used 57 * [Dispatchers.IO]. This caused >40 threads to be spawned, and a lot of thread list lock 58 * contention between then, eventually causing jank. 59 */ 60 @OptIn(DelicateCoroutinesApi::class) 61 @Provides 62 @SysUISingleton 63 @Background 64 @Deprecated( 65 "Use @Background CoroutineContext instead", 66 ReplaceWith("bgCoroutineContext()", "kotlin.coroutines.CoroutineContext"), 67 ) 68 fun bgDispatcher(): CoroutineDispatcher { 69 return if (LIMIT_BACKGROUND_DISPATCHER_THREADS) { 70 // Why a new ThreadPool instead of just using Dispatchers.IO with 71 // CoroutineDispatcher.limitedParallelism? Because, if we were to use Dispatchers.IO, we 72 // would share those threads with other dependencies using Dispatchers.IO. 73 // Using a dedicated thread pool we have guarantees only SystemUI is able to schedule 74 // code on those. 75 newFixedThreadPoolContext( 76 nThreads = Runtime.getRuntime().availableProcessors(), 77 name = "SystemUIBg", 78 ) 79 } else { 80 Dispatchers.IO 81 } 82 } 83 84 @Provides 85 @SysUISingleton 86 @SettingsSingleThreadBackground settingsBgDispatchernull87 fun settingsBgDispatcher(@Background bgHandler: Handler): CoroutineDispatcher { 88 // Handlers are guaranteed to be sequential so we use that one for now. 89 return bgHandler.asCoroutineDispatcher("SettingsBg") 90 } 91 92 @Provides 93 @SysUISingleton 94 @SettingsSingleThreadBackground settingsScopenull95 fun settingsScope(@Background bgDispatcher: CoroutineDispatcher): CoroutineScope { 96 return CoroutineScope(bgDispatcher + newTracingContext("SettingsProxy")) 97 } 98 99 @Provides 100 @Background 101 @SysUISingleton bgCoroutineContextnull102 fun bgCoroutineContext( 103 @Background bgCoroutineDispatcher: CoroutineDispatcher 104 ): CoroutineContext { 105 return bgCoroutineDispatcher 106 } 107 108 /** Coroutine dispatcher for background operations on for UI. */ 109 @Provides 110 @SysUISingleton 111 @UiBackground 112 @Deprecated( 113 "Use @UiBackground CoroutineContext instead", 114 ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext"), 115 ) uiBgDispatchernull116 fun uiBgDispatcher(@UiBackground uiBgExecutor: Executor): CoroutineDispatcher = 117 uiBgExecutor.asCoroutineDispatcher() 118 119 @Provides 120 @UiBackground 121 @SysUISingleton 122 fun uiBgCoroutineContext( 123 @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher 124 ): CoroutineContext { 125 return uiBgCoroutineDispatcher 126 } 127 128 /** Coroutine dispatcher for background notification inflation. */ 129 @Provides 130 @NotifInflation 131 @SysUISingleton notifInflationCoroutineDispatchernull132 fun notifInflationCoroutineDispatcher( 133 @NotifInflation notifInflationExecutor: Executor, 134 @Background bgCoroutineDispatcher: CoroutineDispatcher, 135 ): CoroutineDispatcher { 136 if (com.android.systemui.Flags.useNotifInflationThreadForFooter()) { 137 return notifInflationExecutor.asCoroutineDispatcher() 138 } else { 139 return bgCoroutineDispatcher 140 } 141 } 142 } 143