• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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