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.hardware.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 23 import android.hardware.Camera; 24 import android.hardware.Camera.AutoFocusCallback; 25 import android.hardware.Camera.ErrorCallback; 26 import android.hardware.Camera.PictureCallback; 27 import android.hardware.Camera.PreviewCallback; 28 import android.os.Looper; 29 import android.util.Log; 30 31 import java.util.concurrent.locks.Condition; 32 import java.util.concurrent.locks.Lock; 33 import java.util.concurrent.locks.ReentrantLock; 34 35 public class CameraPerformanceTestHelper { 36 private static final String TAG = "CameraTestCase"; 37 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 38 39 public static final int NO_ERROR = -1; 40 public static final long WAIT_FOR_COMMAND_TO_COMPLETE_NS = 5000000000L; 41 public static final long WAIT_FOR_FOCUS_TO_COMPLETE_NS = 5000000000L; 42 public static final long WAIT_FOR_SNAPSHOT_TO_COMPLETE_NS = 5000000000L; 43 44 private Looper mLooper = null; 45 private int mCameraErrorCode; 46 private Camera mCamera; 47 48 /** 49 * Initializes the message looper so that the Camera object can 50 * receive the callback messages. 51 */ initializeMessageLooper(final int cameraId)52 public void initializeMessageLooper(final int cameraId) throws InterruptedException { 53 Lock startLock = new ReentrantLock(); 54 Condition startDone = startLock.newCondition(); 55 mCameraErrorCode = NO_ERROR; 56 new Thread() { 57 @Override 58 public void run() { 59 // Set up a looper to be used by camera. 60 Looper.prepare(); 61 // Save the looper so that we can terminate this thread 62 // after we are done with it. 63 mLooper = Looper.myLooper(); 64 try { 65 mCamera = Camera.open(cameraId); 66 mCamera.setErrorCallback(new ErrorCallback() { 67 @Override 68 public void onError(int error, Camera camera) { 69 mCameraErrorCode = error; 70 } 71 }); 72 } catch (RuntimeException e) { 73 Log.e(TAG, "Fail to open camera." + e); 74 } 75 startLock.lock(); 76 startDone.signal(); 77 startLock.unlock(); 78 Looper.loop(); // Blocks forever until Looper.quit() is called. 79 if (VERBOSE) Log.v(TAG, "initializeMessageLooper: quit."); 80 } 81 }.start(); 82 83 startLock.lock(); 84 try { 85 assertTrue( 86 "initializeMessageLooper: start timeout", 87 startDone.awaitNanos(WAIT_FOR_COMMAND_TO_COMPLETE_NS) > 0L); 88 } finally { 89 startLock.unlock(); 90 } 91 92 assertNotNull("Fail to open camera.", mCamera); 93 } 94 95 /** 96 * Terminates the message looper thread, optionally allowing evict error 97 */ terminateMessageLooper()98 public void terminateMessageLooper() throws Exception { 99 mLooper.quit(); 100 // Looper.quit() is asynchronous. The looper may still has some 101 // preview callbacks in the queue after quit is called. The preview 102 // callback still uses the camera object (setHasPreviewCallback). 103 // After camera is released, RuntimeException will be thrown from 104 // the method. So we need to join the looper thread here. 105 mLooper.getThread().join(); 106 mCamera.release(); 107 mCamera = null; 108 assertEquals("Got camera error callback.", NO_ERROR, mCameraErrorCode); 109 } 110 111 /** 112 * Start preview and wait for the first preview callback, which indicates the 113 * preview becomes active. 114 */ startPreview()115 public void startPreview() throws InterruptedException { 116 Lock previewLock = new ReentrantLock(); 117 Condition previewDone = previewLock.newCondition(); 118 119 mCamera.setPreviewCallback(new PreviewCallback() { 120 @Override 121 public void onPreviewFrame(byte[] data, android.hardware.Camera camera) { 122 previewLock.lock(); 123 previewDone.signal(); 124 previewLock.unlock(); 125 } 126 }); 127 mCamera.startPreview(); 128 129 previewLock.lock(); 130 try { 131 assertTrue( 132 "Preview done timeout", 133 previewDone.awaitNanos(WAIT_FOR_COMMAND_TO_COMPLETE_NS) > 0L); 134 } finally { 135 previewLock.unlock(); 136 } 137 138 mCamera.setPreviewCallback(null); 139 } 140 141 /** 142 * Trigger and wait for autofocus to complete. 143 */ autoFocus()144 public void autoFocus() throws InterruptedException { 145 Lock focusLock = new ReentrantLock(); 146 Condition focusDone = focusLock.newCondition(); 147 148 mCamera.autoFocus(new AutoFocusCallback() { 149 @Override 150 public void onAutoFocus(boolean success, Camera camera) { 151 focusLock.lock(); 152 focusDone.signal(); 153 focusLock.unlock(); 154 } 155 }); 156 157 focusLock.lock(); 158 try { 159 assertTrue( 160 "Autofocus timeout", 161 focusDone.awaitNanos(WAIT_FOR_FOCUS_TO_COMPLETE_NS) > 0L); 162 } finally { 163 focusLock.unlock(); 164 } 165 } 166 167 /** 168 * Trigger and wait for snapshot to finish. 169 */ takePicture()170 public void takePicture() throws InterruptedException { 171 Lock snapshotLock = new ReentrantLock(); 172 Condition snapshotDone = snapshotLock.newCondition(); 173 174 mCamera.takePicture(/*shutterCallback*/ null, /*rawPictureCallback*/ null, 175 new PictureCallback() { 176 @Override 177 public void onPictureTaken(byte[] rawData, Camera camera) { 178 snapshotLock.lock(); 179 try { 180 assertNotNull("Empty jpeg data", rawData); 181 snapshotDone.signal(); 182 } finally { 183 snapshotLock.unlock(); 184 } 185 } 186 }); 187 188 snapshotLock.lock(); 189 try { 190 assertTrue( 191 "TakePicture timeout", 192 snapshotDone.awaitNanos(WAIT_FOR_SNAPSHOT_TO_COMPLETE_NS) > 0L); 193 } finally { 194 snapshotLock.unlock(); 195 } 196 } 197 getCamera()198 public Camera getCamera() { 199 return mCamera; 200 } 201 }