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