1 /* 2 * Copyright (C) 2023 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.server.wm; 18 19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 21 import android.content.Context; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.util.Log; 28 import android.view.SurfaceHolder; 29 import android.view.SurfaceView; 30 import android.view.ViewGroup; 31 import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase; 32 import android.view.cts.surfacevalidator.PixelChecker; 33 import android.widget.FrameLayout; 34 import android.window.SurfaceSyncGroup; 35 36 import androidx.annotation.NonNull; 37 38 /** 39 * A validator class that will create a SurfaceView and then update its size over and over. The code 40 * will request to sync the SurfaceView content with the main window and validate that there was 41 * never an empty area (black color). The test uses {@link SurfaceSyncGroup} class to gather the 42 * content it wants to synchronize. 43 */ 44 public class SurfaceViewSyncValidatorTestCase implements ISurfaceValidatorTestCase { 45 private static final String TAG = "SurfaceSyncGroupValidatorTestCase"; 46 47 private final Runnable mRunnable = new Runnable() { 48 @Override 49 public void run() { 50 updateSurfaceViewSize(); 51 mHandler.postDelayed(this, 100); 52 } 53 }; 54 55 private Handler mHandler; 56 private SurfaceView mSurfaceView; 57 private boolean mLastExpanded = true; 58 59 private RenderingThread mRenderingThread; 60 private FrameLayout mParent; 61 62 private SurfaceSyncGroup mSyncGroup; 63 64 final SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() { 65 @Override 66 public void surfaceCreated(@NonNull SurfaceHolder holder) { 67 final Canvas canvas = holder.lockCanvas(); 68 canvas.drawARGB(255, 100, 100, 100); 69 holder.unlockCanvasAndPost(canvas); 70 Log.d(TAG, "surfaceCreated"); 71 mRenderingThread = new RenderingThread(holder); 72 mRenderingThread.start(); 73 } 74 75 @Override 76 public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, 77 int height) { 78 if (mSyncGroup != null) { 79 mSyncGroup.add(mSurfaceView, frameCallback -> 80 mRenderingThread.setFrameCallback(frameCallback)); 81 mSyncGroup.markSyncReady(); 82 mSyncGroup = null; 83 } 84 } 85 86 @Override 87 public void surfaceDestroyed(@NonNull SurfaceHolder holder) { 88 mRenderingThread.stopRendering(); 89 } 90 }; 91 92 @Override getChecker()93 public PixelChecker getChecker() { 94 return new PixelChecker(Color.BLACK) { 95 @Override 96 public boolean checkPixels(int matchingPixelCount, int width, int height) { 97 return matchingPixelCount == 0; 98 } 99 }; 100 } 101 102 @Override 103 public void start(Context context, FrameLayout parent) { 104 mSurfaceView = new SurfaceView(context); 105 mSurfaceView.getHolder().addCallback(mCallback); 106 mParent = parent; 107 108 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(MATCH_PARENT, 600); 109 parent.addView(mSurfaceView, layoutParams); 110 mHandler = new Handler(Looper.getMainLooper()); 111 mHandler.post(mRunnable); 112 } 113 114 @Override 115 public void end() { 116 mHandler.removeCallbacks(mRunnable); 117 } 118 119 public void updateSurfaceViewSize() { 120 if (mRenderingThread == null || mSyncGroup != null || !mRenderingThread.isReadyToSync()) { 121 return; 122 } 123 124 Log.d(TAG, "updateSurfaceViewSize"); 125 126 final int height; 127 if (mLastExpanded) { 128 height = 300; 129 } else { 130 height = 600; 131 } 132 mLastExpanded = !mLastExpanded; 133 134 mRenderingThread.pauseRendering(); 135 mSyncGroup = new SurfaceSyncGroup(TAG); 136 mSyncGroup.add(mParent.getRootSurfaceControl(), null /* runanble */); 137 138 ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams(); 139 svParams.height = height; 140 mSurfaceView.setLayoutParams(svParams); 141 } 142 143 private static class RenderingThread extends HandlerThread { 144 private final SurfaceHolder mSurfaceHolder; 145 private SurfaceSyncGroup.SurfaceViewFrameCallback mFrameCallback; 146 private boolean mPauseRendering; 147 private boolean mComplete; 148 149 int mColorValue = 0; 150 int mColorDelta = 10; 151 152 @Override 153 public void run() { 154 try { 155 while (true) { 156 sleep(10); 157 synchronized (this) { 158 if (mComplete) { 159 break; 160 } 161 if (mPauseRendering) { 162 continue; 163 } 164 165 if (mFrameCallback != null) { 166 Log.d(TAG, "onFrameStarted"); 167 mFrameCallback.onFrameStarted(); 168 } 169 170 mColorValue += mColorDelta; 171 if (mColorValue > 245 || mColorValue < 10) { 172 mColorDelta *= -1; 173 } 174 175 Canvas c = mSurfaceHolder.lockCanvas(); 176 if (c != null) { 177 c.drawRGB(255, mColorValue, 255 - mColorValue); 178 mSurfaceHolder.unlockCanvasAndPost(c); 179 } 180 181 mFrameCallback = null; 182 } 183 } 184 } catch (InterruptedException e) { 185 } 186 } 187 188 RenderingThread(SurfaceHolder holder) { 189 super("RenderingThread"); 190 mSurfaceHolder = holder; 191 } 192 193 public void pauseRendering() { 194 synchronized (this) { 195 mPauseRendering = true; 196 } 197 } 198 199 private boolean isReadyToSync() { 200 synchronized (this) { 201 return mFrameCallback == null; 202 } 203 } 204 public void setFrameCallback(SurfaceSyncGroup.SurfaceViewFrameCallback frameCallback) { 205 synchronized (this) { 206 mFrameCallback = frameCallback; 207 mPauseRendering = false; 208 } 209 } 210 211 public void stopRendering() { 212 synchronized (this) { 213 mComplete = true; 214 } 215 } 216 } 217 } 218