1 /* 2 * Copyright (C) 2018 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 android.view; 18 19 import android.annotation.SuppressLint; 20 import android.graphics.Matrix; 21 import android.graphics.Rect; 22 import android.view.SurfaceControl.Transaction; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 26 import java.util.function.Consumer; 27 28 /** 29 * Helper class to apply surface transactions in sync with RenderThread. 30 * @hide 31 */ 32 public class SyncRtSurfaceTransactionApplier { 33 34 public static final int FLAG_ALL = 0xffffffff; 35 public static final int FLAG_ALPHA = 1; 36 public static final int FLAG_MATRIX = 1 << 1; 37 public static final int FLAG_WINDOW_CROP = 1 << 2; 38 public static final int FLAG_LAYER = 1 << 3; 39 public static final int FLAG_CORNER_RADIUS = 1 << 4; 40 public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5; 41 public static final int FLAG_VISIBILITY = 1 << 6; 42 public static final int FLAG_TRANSACTION = 1 << 7; 43 public static final int FLAG_EARLY_WAKEUP_START = 1 << 8; 44 public static final int FLAG_EARLY_WAKEUP_END = 1 << 9; 45 public static final int FLAG_OPAQUE = 1 << 10; 46 47 private SurfaceControl mTargetSc; 48 private final ViewRootImpl mTargetViewRootImpl; 49 private final float[] mTmpFloat9 = new float[9]; 50 51 /** 52 * @param targetView The view in the surface that acts as synchronization anchor. 53 */ SyncRtSurfaceTransactionApplier(View targetView)54 public SyncRtSurfaceTransactionApplier(View targetView) { 55 mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; 56 } 57 58 /** 59 * Schedules applying surface parameters on the next frame. 60 * 61 * @param params The surface parameters to apply. 62 */ scheduleApply(final SurfaceParams... params)63 public void scheduleApply(final SurfaceParams... params) { 64 if (mTargetViewRootImpl == null) { 65 return; 66 } 67 mTargetSc = mTargetViewRootImpl.getSurfaceControl(); 68 final Transaction t = new Transaction(); 69 applyParams(t, params); 70 71 mTargetViewRootImpl.registerRtFrameCallback(frame -> { 72 if (mTargetSc != null && mTargetSc.isValid()) { 73 applyTransaction(t, frame); 74 } 75 // The transaction was either dropped, successfully applied, or merged with a future 76 // transaction, so we can safely release its resources. 77 t.close(); 78 }); 79 80 // Make sure a frame gets scheduled. 81 mTargetViewRootImpl.getView().invalidate(); 82 } 83 84 /** 85 * Applies surface parameters on the next frame. 86 * @param t transaction to apply all parameters in. 87 * @param frame frame to synchronize to. Set -1 when sync is not required. 88 * @param params The surface parameters to apply. 89 */ applyParams(Transaction t, final SurfaceParams... params)90 void applyParams(Transaction t, final SurfaceParams... params) { 91 for (int i = params.length - 1; i >= 0; i--) { 92 SurfaceParams surfaceParams = params[i]; 93 SurfaceControl surface = surfaceParams.surface; 94 applyParams(t, surfaceParams, mTmpFloat9); 95 } 96 } 97 applyTransaction(Transaction t, long frame)98 void applyTransaction(Transaction t, long frame) { 99 if (mTargetViewRootImpl != null) { 100 mTargetViewRootImpl.mergeWithNextTransaction(t, frame); 101 } else { 102 t.apply(); 103 } 104 } 105 106 @SuppressLint("MissingPermission") applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9)107 public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { 108 if ((params.flags & FLAG_TRANSACTION) != 0) { 109 t.merge(params.mergeTransaction); 110 } 111 112 if ((params.flags & FLAG_MATRIX) != 0) { 113 t.setMatrix(params.surface, params.matrix, tmpFloat9); 114 } 115 if ((params.flags & FLAG_WINDOW_CROP) != 0) { 116 t.setWindowCrop(params.surface, params.windowCrop); 117 } 118 if ((params.flags & FLAG_ALPHA) != 0) { 119 t.setAlpha(params.surface, params.alpha); 120 } 121 if ((params.flags & FLAG_LAYER) != 0) { 122 t.setLayer(params.surface, params.layer); 123 } 124 if ((params.flags & FLAG_CORNER_RADIUS) != 0) { 125 t.setCornerRadius(params.surface, params.cornerRadius); 126 } 127 if ((params.flags & FLAG_BACKGROUND_BLUR_RADIUS) != 0) { 128 t.setBackgroundBlurRadius(params.surface, params.backgroundBlurRadius); 129 } 130 if ((params.flags & FLAG_VISIBILITY) != 0) { 131 if (params.visible) { 132 t.show(params.surface); 133 } else { 134 t.hide(params.surface); 135 } 136 } 137 if ((params.flags & FLAG_EARLY_WAKEUP_START) != 0) { 138 t.setEarlyWakeupStart(); 139 } 140 if ((params.flags & FLAG_EARLY_WAKEUP_END) != 0) { 141 t.setEarlyWakeupEnd(); 142 } 143 if ((params.flags & FLAG_OPAQUE) != 0) { 144 t.setOpaque(params.surface, params.opaque); 145 } 146 } 147 148 /** 149 * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is 150 * attached if necessary. 151 */ create(final View targetView, final Consumer<SyncRtSurfaceTransactionApplier> callback)152 public static void create(final View targetView, 153 final Consumer<SyncRtSurfaceTransactionApplier> callback) { 154 if (targetView == null) { 155 // No target view, no applier 156 callback.accept(null); 157 } else if (targetView.getViewRootImpl() != null) { 158 // Already attached, we're good to go 159 callback.accept(new SyncRtSurfaceTransactionApplier(targetView)); 160 } else { 161 // Haven't been attached before we can get the view root 162 targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 163 @Override 164 public void onViewAttachedToWindow(View v) { 165 targetView.removeOnAttachStateChangeListener(this); 166 callback.accept(new SyncRtSurfaceTransactionApplier(targetView)); 167 } 168 169 @Override 170 public void onViewDetachedFromWindow(View v) { 171 // Do nothing 172 } 173 }); 174 } 175 } 176 177 public static class SurfaceParams { 178 179 public static class Builder { 180 final SurfaceControl surface; 181 int flags; 182 float alpha; 183 float cornerRadius; 184 int backgroundBlurRadius; 185 Matrix matrix; 186 Rect windowCrop; 187 int layer; 188 boolean visible; 189 boolean opaque; 190 Transaction mergeTransaction; 191 192 /** 193 * @param surface The surface to modify. 194 */ Builder(SurfaceControl surface)195 public Builder(SurfaceControl surface) { 196 this.surface = surface; 197 } 198 199 /** 200 * @param alpha The alpha value to apply to the surface. 201 * @return this Builder 202 */ withAlpha(float alpha)203 public Builder withAlpha(float alpha) { 204 this.alpha = alpha; 205 flags |= FLAG_ALPHA; 206 return this; 207 } 208 209 /** 210 * @param matrix The matrix to apply to the surface. 211 * @return this Builder 212 */ withMatrix(Matrix matrix)213 public Builder withMatrix(Matrix matrix) { 214 this.matrix = new Matrix(matrix); 215 flags |= FLAG_MATRIX; 216 return this; 217 } 218 219 /** 220 * @param windowCrop The window crop to apply to the surface. 221 * @return this Builder 222 */ withWindowCrop(Rect windowCrop)223 public Builder withWindowCrop(Rect windowCrop) { 224 this.windowCrop = new Rect(windowCrop); 225 flags |= FLAG_WINDOW_CROP; 226 return this; 227 } 228 229 /** 230 * @param layer The layer to assign the surface. 231 * @return this Builder 232 */ withLayer(int layer)233 public Builder withLayer(int layer) { 234 this.layer = layer; 235 flags |= FLAG_LAYER; 236 return this; 237 } 238 239 /** 240 * @param radius the Radius for rounded corners to apply to the surface. 241 * @return this Builder 242 */ withCornerRadius(float radius)243 public Builder withCornerRadius(float radius) { 244 this.cornerRadius = radius; 245 flags |= FLAG_CORNER_RADIUS; 246 return this; 247 } 248 249 /** 250 * @param radius the Radius for blur to apply to the background surfaces. 251 * @return this Builder 252 */ withBackgroundBlur(int radius)253 public Builder withBackgroundBlur(int radius) { 254 this.backgroundBlurRadius = radius; 255 flags |= FLAG_BACKGROUND_BLUR_RADIUS; 256 return this; 257 } 258 259 /** 260 * @param visible The visibility to apply to the surface. 261 * @return this Builder 262 */ withVisibility(boolean visible)263 public Builder withVisibility(boolean visible) { 264 this.visible = visible; 265 flags |= FLAG_VISIBILITY; 266 return this; 267 } 268 269 /** 270 * @param mergeTransaction The transaction to apply to the surface. Note this is applied 271 * first before all the other properties. 272 * @return this Builder 273 */ withMergeTransaction(Transaction mergeTransaction)274 public Builder withMergeTransaction(Transaction mergeTransaction) { 275 this.mergeTransaction = mergeTransaction; 276 flags |= FLAG_TRANSACTION; 277 return this; 278 } 279 280 /** 281 * Provides a hint to SurfaceFlinger to change its offset so that SurfaceFlinger 282 * wakes up earlier to compose surfaces. 283 * @return this Builder 284 */ withEarlyWakeupStart()285 public Builder withEarlyWakeupStart() { 286 flags |= FLAG_EARLY_WAKEUP_START; 287 return this; 288 } 289 290 /** 291 * Removes the early wake up hint set by earlyWakeupStart. 292 * @return this Builder 293 */ withEarlyWakeupEnd()294 public Builder withEarlyWakeupEnd() { 295 flags |= FLAG_EARLY_WAKEUP_END; 296 return this; 297 } 298 299 /** 300 * @param opaque Indicates weather the surface must be considered opaque. 301 * @return this Builder 302 */ withOpaque(boolean opaque)303 public Builder withOpaque(boolean opaque) { 304 this.opaque = opaque; 305 flags |= FLAG_OPAQUE; 306 return this; 307 } 308 309 /** 310 * @return a new SurfaceParams instance 311 */ build()312 public SurfaceParams build() { 313 return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer, 314 cornerRadius, backgroundBlurRadius, visible, mergeTransaction, 315 opaque); 316 } 317 } 318 SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, Rect windowCrop, int layer, float cornerRadius, int backgroundBlurRadius, boolean visible, Transaction mergeTransaction, boolean opaque)319 private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix, 320 Rect windowCrop, int layer, float cornerRadius, 321 int backgroundBlurRadius, boolean visible, 322 Transaction mergeTransaction, boolean opaque) { 323 this.flags = params; 324 this.surface = surface; 325 this.alpha = alpha; 326 this.matrix = matrix; 327 this.windowCrop = windowCrop; 328 this.layer = layer; 329 this.cornerRadius = cornerRadius; 330 this.backgroundBlurRadius = backgroundBlurRadius; 331 this.visible = visible; 332 this.mergeTransaction = mergeTransaction; 333 this.opaque = opaque; 334 } 335 336 private final int flags; 337 338 @VisibleForTesting 339 public final SurfaceControl surface; 340 341 @VisibleForTesting 342 public final float alpha; 343 344 @VisibleForTesting 345 public final float cornerRadius; 346 347 @VisibleForTesting 348 public final int backgroundBlurRadius; 349 350 @VisibleForTesting 351 public final Matrix matrix; 352 353 @VisibleForTesting 354 public final Rect windowCrop; 355 356 @VisibleForTesting 357 public final int layer; 358 359 public final boolean visible; 360 361 public final Transaction mergeTransaction; 362 public final boolean opaque; 363 } 364 } 365