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