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 }