1 /* 2 * Copyright (C) 2016 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 package android.view.cts.surfacevalidator; 17 18 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; 19 20 import static org.junit.Assert.assertTrue; 21 22 import android.graphics.Bitmap; 23 import android.graphics.Point; 24 import android.graphics.Rect; 25 import android.hardware.HardwareBuffer; 26 import android.media.Image; 27 import android.media.ImageReader; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.os.Trace; 31 import android.util.Log; 32 import android.util.SparseArray; 33 import android.view.Surface; 34 35 36 import androidx.annotation.Nullable; 37 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.TimeUnit; 40 41 public class SurfacePixelValidator2 { 42 private static final String TAG = "SurfacePixelValidator"; 43 44 private static final boolean DEBUG = false; 45 46 private static final int MAX_CAPTURED_FAILURES = 5; 47 private static final int PIXEL_STRIDE = 4; 48 49 private final int mWidth; 50 private final int mHeight; 51 52 private final HandlerThread mWorkerThread; 53 54 private final PixelChecker mPixelChecker; 55 private final Rect mBoundsToCheck; 56 private ImageReader mImageReader; 57 58 private int mResultSuccessFrames; 59 private int mResultFailureFrames; 60 private final SparseArray<Bitmap> mFirstFailures = new SparseArray<>(MAX_CAPTURED_FAILURES); 61 private long mFrameNumber = 0; 62 63 private final int mRequiredNumFrames; 64 65 private final CountDownLatch mRequiredNumFramesDrawnLatch = new CountDownLatch(1); 66 67 private final Handler mHandler; 68 69 private final ImageReader.OnImageAvailableListener mOnImageAvailable = 70 new ImageReader.OnImageAvailableListener() { 71 @Override 72 public void onImageAvailable(ImageReader reader) { 73 if (mImageReader == null) { 74 return; 75 } 76 77 Trace.beginSection("Read buffer"); 78 Image image = reader.acquireNextImage(); 79 80 Image.Plane plane = image.getPlanes()[0]; 81 if (plane.getPixelStride() != PIXEL_STRIDE) { 82 throw new IllegalStateException("pixel stride != " + PIXEL_STRIDE + "? " 83 + plane.getPixelStride()); 84 } 85 Trace.endSection(); 86 87 int totalFramesSeen; 88 boolean success = mPixelChecker.validatePlane(plane, mFrameNumber++, 89 mBoundsToCheck, mWidth, mHeight); 90 if (success) { 91 mResultSuccessFrames++; 92 } else { 93 mResultFailureFrames++; 94 } 95 96 totalFramesSeen = mResultSuccessFrames + mResultFailureFrames; 97 if (DEBUG) { 98 Log.d(TAG, "Received image " + success + " numSuccess=" 99 + mResultSuccessFrames + " numFail=" + mResultFailureFrames 100 + " total=" + totalFramesSeen); 101 } 102 103 if (!success) { 104 Log.d(TAG, "Failure (" + mPixelChecker.getLastError() 105 + ") occurred on frame " + totalFramesSeen); 106 107 if (mFirstFailures.size() < MAX_CAPTURED_FAILURES) { 108 Log.d(TAG, "Capturing bitmap #" + mFirstFailures.size()); 109 // error, worth looking at... 110 Bitmap capture = Bitmap.wrapHardwareBuffer( 111 image.getHardwareBuffer(), null) 112 .copy(Bitmap.Config.ARGB_8888, false); 113 mFirstFailures.put(totalFramesSeen, capture); 114 } 115 } 116 117 image.close(); 118 if (totalFramesSeen >= mRequiredNumFrames) { 119 mRequiredNumFramesDrawnLatch.countDown(); 120 } 121 } 122 }; 123 SurfacePixelValidator2(Point size, @Nullable Rect boundsToCheck, PixelChecker pixelChecker, int requiredNumFrames)124 public SurfacePixelValidator2(Point size, @Nullable Rect boundsToCheck, 125 PixelChecker pixelChecker, int requiredNumFrames) { 126 mWidth = size.x; 127 mHeight = size.y; 128 129 mWorkerThread = new HandlerThread("SurfacePixelValidator"); 130 mWorkerThread.start(); 131 132 mPixelChecker = pixelChecker; 133 if (boundsToCheck == null) { 134 mBoundsToCheck = new Rect(0, 0, mWidth, mHeight); 135 } else { 136 mBoundsToCheck = new Rect(boundsToCheck); 137 } 138 139 Log.d(TAG, "boundsToCheck=" + mBoundsToCheck + " size=" + mWidth + "x" + mHeight); 140 141 mRequiredNumFrames = requiredNumFrames; 142 mHandler = new Handler(mWorkerThread.getLooper()); 143 144 mImageReader = ImageReader.newInstance(mWidth, mHeight, HardwareBuffer.RGBA_8888, 1, 145 HardwareBuffer.USAGE_GPU_COLOR_OUTPUT | HardwareBuffer.USAGE_CPU_READ_OFTEN 146 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 147 mImageReader.setOnImageAvailableListener(mOnImageAvailable, mHandler); 148 } 149 getSurface()150 public Surface getSurface() { 151 return mImageReader.getSurface(); 152 } 153 154 /** 155 * Shuts down processing pipeline, and returns current pass/fail counts. 156 * 157 * Wait for pipeline to flush before calling this method. If not, frames that are still in 158 * flight may be lost. 159 */ finish(CapturedActivity.TestResult testResult)160 public void finish(CapturedActivity.TestResult testResult) { 161 CountDownLatch countDownLatch = new CountDownLatch(1); 162 // Post the imageReader close on the same thread it's processing data to avoid shutting down 163 // while still in the middle of processing an image. 164 mHandler.post(() -> { 165 testResult.failFrames = mResultFailureFrames; 166 testResult.passFrames = mResultSuccessFrames; 167 168 for (int i = 0; i < mFirstFailures.size(); i++) { 169 testResult.failures.put(mFirstFailures.keyAt(i), mFirstFailures.valueAt(i)); 170 } 171 mImageReader.close(); 172 mImageReader = null; 173 mWorkerThread.quitSafely(); 174 countDownLatch.countDown(); 175 }); 176 177 try { 178 assertTrue("Failed to wait for results", 179 countDownLatch.await(5L * HW_TIMEOUT_MULTIPLIER, TimeUnit.SECONDS)); 180 } catch (InterruptedException e) { 181 } 182 } 183 waitForAllFrames(long timeoutMs)184 public boolean waitForAllFrames(long timeoutMs) { 185 try { 186 return mRequiredNumFramesDrawnLatch.await(timeoutMs, TimeUnit.MILLISECONDS); 187 } catch (InterruptedException e) { 188 return false; 189 } 190 } 191 } 192