1 /* 2 * Copyright (C) 2020 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.statusbar 18 19 import android.annotation.SuppressLint 20 import android.app.ActivityManager 21 import android.content.res.Resources 22 import android.os.Build 23 import android.os.SystemProperties 24 import android.os.Trace 25 import android.os.Trace.TRACE_TAG_APP 26 import android.util.IndentingPrintWriter 27 import android.util.Log 28 import android.util.MathUtils 29 import android.view.CrossWindowBlurListeners 30 import android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED 31 import android.view.SyncRtSurfaceTransactionApplier 32 import android.view.ViewRootImpl 33 import androidx.annotation.VisibleForTesting 34 import com.android.systemui.Dumpable 35 import com.android.systemui.Flags 36 import com.android.systemui.dagger.SysUISingleton 37 import com.android.systemui.dagger.qualifiers.Main 38 import com.android.systemui.dump.DumpManager 39 import com.android.systemui.keyguard.ui.transitions.BlurConfig 40 import com.android.systemui.res.R 41 import java.io.PrintWriter 42 import javax.inject.Inject 43 44 @SysUISingleton 45 open class BlurUtils 46 @Inject 47 constructor( 48 @Main resources: Resources, 49 blurConfig: BlurConfig, 50 private val crossWindowBlurListeners: CrossWindowBlurListeners, 51 dumpManager: DumpManager, 52 ) : Dumpable { 53 val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat() 54 val maxBlurRadius = 55 if (Flags.notificationShadeBlur()) { 56 blurConfig.maxBlurRadiusPx 57 } else { 58 resources.getDimensionPixelSize(R.dimen.max_window_blur_radius).toFloat() 59 } 60 61 private var lastAppliedBlur = 0 62 private var lastTargetViewRootImpl: ViewRootImpl? = null 63 private var _transactionApplier = SyncRtSurfaceTransactionApplier(null) 64 @VisibleForTesting 65 open val transactionApplier: SyncRtSurfaceTransactionApplier 66 get() = _transactionApplier 67 68 private var earlyWakeupEnabled = false 69 70 /** When this is true, early wakeup flag is not reset on surface flinger when blur drops to 0 */ 71 private var persistentEarlyWakeupRequired = false 72 73 init { 74 dumpManager.registerDumpable(this) 75 } 76 77 /** Translates a ratio from 0 to 1 to a blur radius in pixels. */ blurRadiusOfRationull78 fun blurRadiusOfRatio(ratio: Float): Float { 79 if (ratio == 0f) { 80 return 0f 81 } 82 return MathUtils.lerp(minBlurRadius, maxBlurRadius, ratio) 83 } 84 85 /** Translates a blur radius in pixels to a ratio between 0 to 1. */ ratioOfBlurRadiusnull86 fun ratioOfBlurRadius(blur: Float): Float { 87 if (blur == 0f) { 88 return 0f 89 } 90 return MathUtils.map( 91 minBlurRadius, 92 maxBlurRadius, 93 0f /* maxStart */, 94 1f /* maxStop */, 95 blur, 96 ) 97 } 98 99 /** 100 * This method should be called before [applyBlur] so that, if needed, we can set the 101 * early-wakeup flag in SurfaceFlinger. 102 */ prepareBlurnull103 fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) { 104 if ( 105 viewRootImpl == null || 106 !viewRootImpl.surfaceControl.isValid || 107 !shouldBlur(radius) || 108 earlyWakeupEnabled 109 ) { 110 return 111 } 112 updateTransactionApplier(viewRootImpl) 113 val builder = 114 SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(viewRootImpl.surfaceControl) 115 if (lastAppliedBlur == 0 && radius != 0) { 116 earlyWakeupStart(builder, "eEarlyWakeup (prepareBlur)") 117 transactionApplier.scheduleApply(builder.build()) 118 } 119 } 120 121 /** 122 * Applies background blurs to a {@link ViewRootImpl}. 123 * 124 * @param viewRootImpl The window root. 125 * @param radius blur radius in pixels. 126 * @param opaque if surface is opaque, regardless or having blurs or no. 127 */ applyBlurnull128 fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaque: Boolean) { 129 if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid) { 130 return 131 } 132 updateTransactionApplier(viewRootImpl) 133 val builder = 134 SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(viewRootImpl.surfaceControl) 135 if (shouldBlur(radius)) { 136 builder.withBackgroundBlur(radius) 137 if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) { 138 earlyWakeupStart(builder, "eEarlyWakeup (applyBlur)") 139 } 140 if ( 141 earlyWakeupEnabled && 142 lastAppliedBlur != 0 && 143 radius == 0 && 144 !persistentEarlyWakeupRequired 145 ) { 146 earlyWakeupEnd(builder, "applyBlur") 147 } 148 lastAppliedBlur = radius 149 } 150 builder.withOpaque(opaque) 151 transactionApplier.scheduleApply(builder.build()) 152 } 153 updateTransactionAppliernull154 private fun updateTransactionApplier(viewRootImpl: ViewRootImpl) { 155 if (lastTargetViewRootImpl == viewRootImpl) return 156 _transactionApplier = SyncRtSurfaceTransactionApplier(viewRootImpl.view) 157 lastTargetViewRootImpl = viewRootImpl 158 } 159 vnull160 private fun v(verboseLog: String) { 161 if (isLoggable) Log.v(TAG, verboseLog) 162 } 163 164 @SuppressLint("MissingPermission") earlyWakeupStartnull165 private fun earlyWakeupStart( 166 builder: SyncRtSurfaceTransactionApplier.SurfaceParams.Builder, 167 traceMethodName: String, 168 ) { 169 v("earlyWakeupStart from $traceMethodName") 170 Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, traceMethodName, 0) 171 builder.withEarlyWakeupStart() 172 earlyWakeupEnabled = true 173 } 174 175 @SuppressLint("MissingPermission") earlyWakeupEndnull176 private fun earlyWakeupEnd( 177 builder: SyncRtSurfaceTransactionApplier.SurfaceParams.Builder, 178 loggingContext: String, 179 ) { 180 v("earlyWakeupEnd from $loggingContext") 181 builder.withEarlyWakeupEnd() 182 Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0) 183 earlyWakeupEnabled = false 184 } 185 shouldBlurnull186 private fun shouldBlur(radius: Int): Boolean { 187 return supportsBlursOnWindows() || 188 ((Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()) && 189 supportsBlursOnWindowsBase() && 190 lastAppliedBlur > 0 && 191 radius == 0) 192 } 193 194 /** 195 * If this device can render blurs. 196 * 197 * @return {@code true} when supported. 198 * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int) 199 */ supportsBlursOnWindowsnull200 open fun supportsBlursOnWindows(): Boolean { 201 return supportsBlursOnWindowsBase() && crossWindowBlurListeners.isCrossWindowBlurEnabled 202 } 203 supportsBlursOnWindowsBasenull204 private fun supportsBlursOnWindowsBase(): Boolean { 205 return CROSS_WINDOW_BLUR_SUPPORTED && 206 ActivityManager.isHighEndGfx() && 207 !SystemProperties.getBoolean("persist.sysui.disableBlur", false) 208 } 209 dumpnull210 override fun dump(pw: PrintWriter, args: Array<out String>) { 211 IndentingPrintWriter(pw, " ").let { 212 it.println("BlurUtils:") 213 it.increaseIndent() 214 it.println("minBlurRadius: $minBlurRadius") 215 it.println("maxBlurRadius: $maxBlurRadius") 216 it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}") 217 it.println("CROSS_WINDOW_BLUR_SUPPORTED: $CROSS_WINDOW_BLUR_SUPPORTED") 218 it.println("isHighEndGfx: ${ActivityManager.isHighEndGfx()}") 219 } 220 } 221 222 /** 223 * Enables/disables the early wakeup flag on surface flinger. Keeps the early wakeup flag on 224 * until it reset by passing false to this method. 225 */ setPersistentEarlyWakeupnull226 fun setPersistentEarlyWakeup(persistentWakeup: Boolean, viewRootImpl: ViewRootImpl?) { 227 persistentEarlyWakeupRequired = persistentWakeup 228 if (viewRootImpl == null || !supportsBlursOnWindows()) return 229 230 updateTransactionApplier(viewRootImpl) 231 val builder = 232 SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(viewRootImpl.surfaceControl) 233 if (persistentEarlyWakeupRequired) { 234 if (earlyWakeupEnabled) return 235 earlyWakeupStart(builder, "setEarlyWakeup") 236 transactionApplier.scheduleApply(builder.build()) 237 } else { 238 if (!earlyWakeupEnabled) return 239 if (lastAppliedBlur > 0) { 240 Log.w( 241 TAG, 242 "resetEarlyWakeup invoked when lastAppliedBlur $lastAppliedBlur is " + 243 "non-zero, this means that the early wakeup signal was reset while blur" + 244 " was still active", 245 ) 246 } 247 earlyWakeupEnd(builder, "resetEarlyWakeup") 248 transactionApplier.scheduleApply(builder.build()) 249 } 250 } 251 252 companion object { 253 const val TRACK_NAME = "BlurUtils" 254 private const val TAG = "BlurUtils" 255 private val isLoggable = Log.isLoggable(TAG, Log.VERBOSE) || Build.isDebuggable() 256 } 257 } 258