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 android.view.cts.util; 18 19 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; 20 21 import static org.junit.Assert.assertTrue; 22 23 import android.content.Context; 24 import android.graphics.Color; 25 import android.graphics.PixelFormat; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.util.Size; 31 import android.view.AttachedSurfaceControl; 32 import android.view.Gravity; 33 import android.view.SurfaceControl; 34 import android.view.SurfaceControlViewHost.SurfacePackage; 35 import android.view.SurfaceView; 36 import android.view.ViewGroup; 37 import android.view.WindowManager; 38 import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase; 39 import android.view.cts.surfacevalidator.PixelChecker; 40 import android.view.cts.util.aidl.IAttachEmbeddedWindow; 41 import android.widget.FrameLayout; 42 import android.widget.TextView; 43 import android.window.SurfaceSyncGroup; 44 45 import java.util.concurrent.CountDownLatch; 46 import java.util.concurrent.TimeUnit; 47 48 public class SyncValidatorSCVHTestCase implements ISurfaceValidatorTestCase { 49 private static final String TAG = "SCVHSyncValidatorTestCase"; 50 51 private final Size[] mSizes = new Size[]{new Size(500, 500), new Size(700, 400), 52 new Size(300, 800), new Size(200, 200)}; 53 private int mLastSizeIndex = 0; 54 55 private final long mDelayMs; 56 57 private SurfaceControlViewHostHelper mSurfaceControlViewHostHelper; 58 59 private IAttachEmbeddedWindow mIAttachEmbeddedWindow; 60 61 private Handler mHandler; 62 private TextView mTextView; 63 private SurfaceView mSurfaceView; 64 private SurfacePackage mSurfacePackage; 65 66 private final CountDownLatch mReadyToStart = new CountDownLatch(1); 67 68 private final boolean mInProcess; 69 SyncValidatorSCVHTestCase(long delayMs, boolean inProcess)70 public SyncValidatorSCVHTestCase(long delayMs, boolean inProcess) { 71 mDelayMs = delayMs; 72 mInProcess = inProcess; 73 } 74 75 private final Runnable mResizeWithSurfaceSyncGroup = new Runnable() { 76 @Override 77 public void run() { 78 Size size = mSizes[mLastSizeIndex % mSizes.length]; 79 80 Runnable svResizeRunnable = () -> { 81 ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams(); 82 svParams.width = size.getWidth(); 83 svParams.height = size.getHeight(); 84 mSurfaceView.setLayoutParams(svParams); 85 86 mTextView.setText(size.getWidth() + "x" + size.getHeight()); 87 }; 88 89 Runnable embeddedResizeRunnable = () -> { 90 try { 91 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 92 size.getWidth(), size.getHeight(), 93 WindowManager.LayoutParams.TYPE_APPLICATION, 0, 94 PixelFormat.TRANSPARENT); 95 mIAttachEmbeddedWindow.relayout(lp); 96 } catch (RemoteException e) { 97 Log.e(TAG, "Failed to call relayout for embedded window"); 98 } 99 }; 100 101 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 102 syncGroup.add(mSurfaceView.getRootSurfaceControl(), svResizeRunnable); 103 syncGroup.add(mSurfacePackage, embeddedResizeRunnable); 104 syncGroup.markSyncReady(); 105 106 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 107 t.addTransactionCommittedListener(Runnable::run, () -> { 108 if (mSurfaceView.isAttachedToWindow()) { 109 mHandler.postDelayed(mResizeWithSurfaceSyncGroup, mDelayMs); 110 } 111 }); 112 AttachedSurfaceControl attachedSurfaceControl = mSurfaceView.getRootSurfaceControl(); 113 if (attachedSurfaceControl != null) { 114 mSurfaceView.getRootSurfaceControl().applyTransactionOnDraw(t); 115 } 116 117 mLastSizeIndex++; 118 } 119 }; 120 121 @Override getChecker()122 public PixelChecker getChecker() { 123 return new PixelChecker(Color.BLACK) { 124 @Override 125 public boolean checkPixels(int matchingPixelCount, int width, int height) { 126 // Content has been set up yet. 127 if (mSurfacePackage == null) { 128 return true; 129 } 130 return matchingPixelCount == 0; 131 } 132 }; 133 } 134 135 @Override 136 public void start(Context context, FrameLayout parent) { 137 mHandler = new Handler(Looper.getMainLooper()); 138 139 mSurfaceControlViewHostHelper = new SurfaceControlViewHostHelper(TAG, mReadyToStart, 140 context, mDelayMs, mSizes[0]); 141 142 mSurfaceControlViewHostHelper.bindEmbeddedService(mInProcess); 143 144 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(mSizes[0].getWidth(), 145 mSizes[0].getHeight()); 146 layoutParams.gravity = Gravity.CENTER; 147 148 mSurfaceView = mSurfaceControlViewHostHelper.attachSurfaceView(parent, layoutParams); 149 150 mTextView = new TextView(context); 151 mTextView.setTextColor(Color.GREEN); 152 mTextView.setText(mSizes[0].getWidth() + "x" + mSizes[0].getHeight()); 153 FrameLayout.LayoutParams txtParams = new FrameLayout.LayoutParams( 154 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 155 txtParams.gravity = Gravity.TOP | Gravity.LEFT; 156 parent.addView(mTextView, txtParams); 157 } 158 159 @Override 160 public boolean waitForReady() { 161 boolean ready; 162 try { 163 ready = mReadyToStart.await(3L * HW_TIMEOUT_MULTIPLIER, TimeUnit.SECONDS); 164 } catch (InterruptedException e) { 165 Log.e(TAG, "Failed to wait for SCVH to attach"); 166 return false; 167 } 168 169 assertTrue("Failed to attach SCVH", ready); 170 171 mSurfacePackage = mSurfaceControlViewHostHelper.getSurfacePackage(); 172 mIAttachEmbeddedWindow = mSurfaceControlViewHostHelper.getAttachedEmbeddedWindow(); 173 174 mHandler.post(mResizeWithSurfaceSyncGroup); 175 return true; 176 } 177 178 @Override 179 public void end() { 180 mHandler.removeCallbacks(mResizeWithSurfaceSyncGroup); 181 } 182 } 183