blob: 67c08fef43dac37010dca5884986ce35bea7081f [file] [log] [blame]
Timothy Knightd8b3e522013-07-25 15:54:46 -07001/*
2 * Copyright 2013 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
17package android.hardware.camera2.cts;
18
Zhijun Hec66004f2014-03-04 17:58:56 -080019import static org.mockito.Mockito.*;
20import static org.mockito.AdditionalMatchers.not;
21import static org.mockito.AdditionalMatchers.and;
22
Timothy Knightd8b3e522013-07-25 15:54:46 -070023import android.content.Context;
24import android.content.pm.PackageManager;
25import android.hardware.camera2.CameraAccessException;
Igor Murashkinafce1a12013-09-10 12:29:54 -070026import android.hardware.camera2.CameraCharacteristics;
Timothy Knightd8b3e522013-07-25 15:54:46 -070027import android.hardware.camera2.CameraDevice;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -070028import android.hardware.camera2.CameraDevice.StateCallback;
Timothy Knightd8b3e522013-07-25 15:54:46 -070029import android.hardware.camera2.CameraManager;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -070030import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback;
Zhijun He884dc002014-03-20 00:03:10 -070031import android.hardware.camera2.cts.helpers.CameraErrorCollector;
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -070032import android.os.Handler;
Zhijun Heb5b458b2014-01-07 11:49:03 -080033import android.os.HandlerThread;
Timothy Knightd8b3e522013-07-25 15:54:46 -070034import android.test.AndroidTestCase;
35import android.util.Log;
36
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -070037import com.android.ex.camera2.blocking.BlockingStateCallback;
Zhijun Hec66004f2014-03-04 17:58:56 -080038
39import org.mockito.ArgumentCaptor;
40import org.mockito.InOrder;
41
42import java.util.ArrayList;
Timothy Knightd8b3e522013-07-25 15:54:46 -070043import java.util.Arrays;
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -070044import java.util.HashSet;
Zhijun Hec66004f2014-03-04 17:58:56 -080045import java.util.List;
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -070046import java.util.concurrent.LinkedBlockingQueue;
Timothy Knightd8b3e522013-07-25 15:54:46 -070047
48/**
49 * <p>Basic test for CameraManager class.</p>
50 */
51public class CameraManagerTest extends AndroidTestCase {
52 private static final String TAG = "CameraManagerTest";
53 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
Timothy Knightd8b3e522013-07-25 15:54:46 -070054 private static final int NUM_CAMERA_REOPENS = 10;
55
56 private PackageManager mPackageManager;
57 private CameraManager mCameraManager;
58 private NoopCameraListener mListener;
Zhijun Heb5b458b2014-01-07 11:49:03 -080059 private HandlerThread mHandlerThread;
Igor Murashkin14b7a5e2013-09-17 14:31:16 -070060 private Handler mHandler;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -070061 private BlockingStateCallback mCameraListener;
Zhijun He884dc002014-03-20 00:03:10 -070062 private CameraErrorCollector mCollector;
Timothy Knightd8b3e522013-07-25 15:54:46 -070063
64 @Override
65 public void setContext(Context context) {
66 super.setContext(context);
67 mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
68 assertNotNull("Can't connect to camera manager", mCameraManager);
69 mPackageManager = context.getPackageManager();
70 assertNotNull("Can't get package manager", mPackageManager);
71 mListener = new NoopCameraListener();
72 }
73
74 @Override
75 protected void setUp() throws Exception {
76 super.setUp();
Brett Chabot4d98e312014-03-17 16:54:41 -070077
Igor Murashkin99f0a852014-06-19 13:28:59 -070078 /**
79 * Workaround for mockito and JB-MR2 incompatibility
80 *
81 * Avoid java.lang.IllegalArgumentException: dexcache == null
82 * https://code.google.com/p/dexmaker/issues/detail?id=2
83 */
84 System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
85
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -070086 mCameraListener = spy(new BlockingStateCallback());
Igor Murashkin14b7a5e2013-09-17 14:31:16 -070087
Zhijun Heb5b458b2014-01-07 11:49:03 -080088 mHandlerThread = new HandlerThread(TAG);
89 mHandlerThread.start();
90 mHandler = new Handler(mHandlerThread.getLooper());
Zhijun He884dc002014-03-20 00:03:10 -070091 mCollector = new CameraErrorCollector();
Timothy Knightd8b3e522013-07-25 15:54:46 -070092 }
93
94 @Override
95 protected void tearDown() throws Exception {
Zhijun Heb5b458b2014-01-07 11:49:03 -080096 mHandlerThread.quitSafely();
Igor Murashkin14b7a5e2013-09-17 14:31:16 -070097 mHandler = null;
98
Zhijun He884dc002014-03-20 00:03:10 -070099 try {
100 mCollector.verify();
101 } catch (Throwable e) {
102 // When new Exception(e) is used, exception info will be printed twice.
103 throw new Exception(e.getMessage());
104 } finally {
105 super.tearDown();
106 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700107 }
108
Zhijun Hec66004f2014-03-04 17:58:56 -0800109 /**
110 * Verifies that the reason is in the range of public-only codes.
111 */
112 private static int checkCameraAccessExceptionReason(CameraAccessException e) {
113 int reason = e.getReason();
114
115 switch (reason) {
116 case CameraAccessException.CAMERA_DISABLED:
117 case CameraAccessException.CAMERA_DISCONNECTED:
118 case CameraAccessException.CAMERA_ERROR:
119 return reason;
120 }
121
122 fail("Invalid CameraAccessException code: " + reason);
123
124 return -1; // unreachable
125 }
126
Timothy Knightd8b3e522013-07-25 15:54:46 -0700127 public void testCameraManagerGetDeviceIdList() throws Exception {
128
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700129 // Test: that the getCameraIdList method runs without exceptions.
130 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700131 if (VERBOSE) Log.v(TAG, "CameraManager ids: " + Arrays.toString(ids));
132
Zhijun Hef639d7c2013-10-01 12:28:39 -0700133 /**
134 * Test: that if there is at least one reported id, then the system must have
135 * the FEATURE_CAMERA_ANY feature.
136 */
137 assertTrue("System camera feature and camera id list don't match",
138 ids.length == 0 ||
139 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
Timothy Knightd8b3e522013-07-25 15:54:46 -0700140
141 /**
Zhijun Hef639d7c2013-10-01 12:28:39 -0700142 * Test: that if the device has front or rear facing cameras, then there
143 * must be matched system features.
Timothy Knightd8b3e522013-07-25 15:54:46 -0700144 */
Zhijun Hef639d7c2013-10-01 12:28:39 -0700145 for (int i = 0; i < ids.length; i++) {
146 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
147 assertNotNull("Can't get camera characteristics for camera " + ids[i], props);
148 Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING);
149 assertNotNull("Can't get lens facing info", lensFacing);
150 if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
151 assertTrue("System doesn't have front camera feature",
Takayuki Hoshi473c2262015-10-01 12:52:15 +0900152 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
153 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
Zhijun Hef639d7c2013-10-01 12:28:39 -0700154 } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
155 assertTrue("System doesn't have back camera feature",
156 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
157 } else {
158 fail("Unknown camera lens facing " + lensFacing.toString());
159 }
160 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700161
162 /**
163 * Test: that if there is one camera device, then the system must have some
164 * specific features.
165 */
166 assertTrue("Missing system feature: FEATURE_CAMERA_ANY",
167 ids.length == 0
168 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
169 assertTrue("Missing system feature: FEATURE_CAMERA or FEATURE_CAMERA_FRONT",
170 ids.length == 0
171 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
172 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
173 }
174
175 // Test: that properties can be queried from each device, without exceptions.
Igor Murashkinafce1a12013-09-10 12:29:54 -0700176 public void testCameraManagerGetCameraCharacteristics() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700177 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700178 for (int i = 0; i < ids.length; i++) {
Zhijun He9dbe4172013-09-27 13:34:12 -0700179 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700180 assertNotNull(
Zhijun He9dbe4172013-09-27 13:34:12 -0700181 String.format("Can't get camera characteristics from: ID %s", ids[i]), props);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700182 }
183 }
184
185 // Test: that an exception is thrown if an invalid device id is passed down.
186 public void testCameraManagerInvalidDevice() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700187 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700188 // Create an invalid id by concatenating all the valid ids together.
189 StringBuilder invalidId = new StringBuilder();
190 invalidId.append("INVALID");
191 for (int i = 0; i < ids.length; i++) {
192 invalidId.append(ids[i]);
193 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800194
Timothy Knightd8b3e522013-07-25 15:54:46 -0700195 try {
Zhijun Hec66004f2014-03-04 17:58:56 -0800196 mCameraManager.getCameraCharacteristics(
Timothy Knightd8b3e522013-07-25 15:54:46 -0700197 invalidId.toString());
198 fail(String.format("Accepted invalid camera ID: %s", invalidId.toString()));
Zhijun Hec66004f2014-03-04 17:58:56 -0800199 } catch (IllegalArgumentException e) {
Timothy Knightd8b3e522013-07-25 15:54:46 -0700200 // This is the exception that should be thrown in this case.
201 }
202 }
203
204 // Test: that each camera device can be opened one at a time, several times.
205 public void testCameraManagerOpenCamerasSerially() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700206 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700207 for (int i = 0; i < ids.length; i++) {
208 for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800209 CameraDevice camera = null;
210 try {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700211 MockStateCallback mockListener = MockStateCallback.mock();
212 mCameraListener = new BlockingStateCallback(mockListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800213
214 mCameraManager.openCamera(ids[i], mCameraListener, mHandler);
215
216 // Block until unConfigured
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700217 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun Hec66004f2014-03-04 17:58:56 -0800218 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
219
220 // Ensure state transitions are in right order:
221 // -- 1) Opened
Zhijun Hec66004f2014-03-04 17:58:56 -0800222 // Ensure no other state transitions have occurred:
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700223 camera = verifyCameraStateOpened(ids[i], mockListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800224 } finally {
225 if (camera != null) {
226 camera.close();
227 }
228 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700229 }
230 }
231 }
232
233 /**
Zhijun Hec66004f2014-03-04 17:58:56 -0800234 * Test: one or more camera devices can be open at the same time, or the right error state
235 * is set if this can't be done.
Timothy Knightd8b3e522013-07-25 15:54:46 -0700236 */
237 public void testCameraManagerOpenAllCameras() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700238 String[] ids = mCameraManager.getCameraIdList();
Zhijun Hec66004f2014-03-04 17:58:56 -0800239 assertNotNull("Camera ids shouldn't be null", ids);
240
241 // Skip test if the device doesn't have multiple cameras.
242 if (ids.length <= 1) {
243 return;
244 }
245
246 List<CameraDevice> cameraList = new ArrayList<CameraDevice>();
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700247 List<MockStateCallback> listenerList = new ArrayList<MockStateCallback>();
248 List<BlockingStateCallback> blockingListenerList = new ArrayList<BlockingStateCallback>();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700249 try {
250 for (int i = 0; i < ids.length; i++) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800251 // Ignore state changes from other cameras
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700252 MockStateCallback mockListener = MockStateCallback.mock();
253 mCameraListener = new BlockingStateCallback(mockListener);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700254
Zhijun Hec66004f2014-03-04 17:58:56 -0800255 /**
256 * Track whether or not we got a synchronous error from openCamera.
257 *
258 * A synchronous error must also be accompanied by an asynchronous
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700259 * StateCallback#onError callback.
Zhijun Hec66004f2014-03-04 17:58:56 -0800260 */
261 boolean expectingError = false;
262
263 String cameraId = ids[i];
264 try {
265 mCameraManager.openCamera(cameraId, mCameraListener,
266 mHandler);
267 } catch (CameraAccessException e) {
268 if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) {
269 expectingError = true;
270 } else {
271 // TODO: We should handle a Disabled camera by passing here and elsewhere
272 fail("Camera must not be disconnected or disabled for this test" + ids[i]);
273 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700274 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800275
276 List<Integer> expectedStates = new ArrayList<Integer>();
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700277 expectedStates.add(BlockingStateCallback.STATE_OPENED);
278 expectedStates.add(BlockingStateCallback.STATE_ERROR);
Zhijun Hec66004f2014-03-04 17:58:56 -0800279 int state = mCameraListener.waitForAnyOfStates(
280 expectedStates, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
281
282 // It's possible that we got an asynchronous error transition only. This is ok.
283 if (expectingError) {
284 assertEquals("Throwing a CAMERA_ERROR exception must be accompanied with a " +
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700285 "StateCallback#onError callback",
286 BlockingStateCallback.STATE_ERROR, state);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700287 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800288
289 /**
290 * Two situations are considered passing:
291 * 1) The camera opened successfully.
292 * => No error must be set.
293 * 2) The camera did not open because there were too many other cameras opened.
294 * => Only MAX_CAMERAS_IN_USE error must be set.
295 *
296 * Any other situation is considered a failure.
297 *
298 * For simplicity we treat disconnecting asynchronously as a failure, so
299 * camera devices should not be physically unplugged during this test.
300 */
301
302 CameraDevice camera;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700303 if (state == BlockingStateCallback.STATE_ERROR) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800304 // Camera did not open because too many other cameras were opened
305 // => onError called exactly once with a non-null camera
306 assertTrue("At least one camera must be opened successfully",
307 cameraList.size() > 0);
308
309 ArgumentCaptor<CameraDevice> argument =
310 ArgumentCaptor.forClass(CameraDevice.class);
311
312 verify(mockListener)
313 .onError(
314 argument.capture(),
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700315 eq(CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE));
Zhijun Hec66004f2014-03-04 17:58:56 -0800316 verifyNoMoreInteractions(mockListener);
317
318 camera = argument.getValue();
319 assertNotNull("Expected a non-null camera for the error transition for ID: "
320 + ids[i], camera);
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700321 } else if (state == BlockingStateCallback.STATE_OPENED) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800322 // Camera opened successfully.
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700323 // => onOpened called exactly once
324 camera = verifyCameraStateOpened(cameraId,
Zhijun Hec66004f2014-03-04 17:58:56 -0800325 mockListener);
326 } else {
327 fail("Unexpected state " + state);
328 camera = null; // unreachable. but need this for java compiler
329 }
330
331 // Keep track of cameras so we can close it later
332 cameraList.add(camera);
333 listenerList.add(mockListener);
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700334 blockingListenerList.add(mCameraListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800335 }
336 } finally {
337 for (CameraDevice camera : cameraList) {
338 camera.close();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700339 }
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700340 for (BlockingStateCallback blockingListener : blockingListenerList) {
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700341 blockingListener.waitForState(
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700342 BlockingStateCallback.STATE_CLOSED,
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700343 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
344 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700345 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800346
347 /*
348 * Ensure that no state transitions have bled through from one camera to another
349 * after closing the cameras.
350 */
351 int i = 0;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700352 for (MockStateCallback listener : listenerList) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800353 CameraDevice camera = cameraList.get(i);
354
355 verify(listener).onClosed(eq(camera));
356 verifyNoMoreInteractions(listener);
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700357 i++;
Zhijun Hec66004f2014-03-04 17:58:56 -0800358 // Only a #close can happen on the camera since we were done with it.
359 // Also nothing else should've happened between the close and the open.
Timothy Knightd8b3e522013-07-25 15:54:46 -0700360 }
361 }
362
Zhijun Hec66004f2014-03-04 17:58:56 -0800363 /**
364 * Verifies the camera in this listener was opened and then unconfigured exactly once.
365 *
366 * <p>This assumes that no other action to the camera has been done (e.g.
367 * it hasn't been configured, or closed, or disconnected). Verification is
368 * performed immediately without any timeouts.</p>
369 *
370 * <p>This checks that the state has previously changed first for opened and then unconfigured.
371 * Any other state transitions will fail. A test failure is thrown if verification fails.</p>
372 *
373 * @param cameraId Camera identifier
374 * @param listener Listener which was passed to {@link CameraManager#openCamera}
375 *
376 * @return The camera device (non-{@code null}).
377 */
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700378 private static CameraDevice verifyCameraStateOpened(String cameraId,
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700379 MockStateCallback listener) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800380 ArgumentCaptor<CameraDevice> argument =
381 ArgumentCaptor.forClass(CameraDevice.class);
382 InOrder inOrder = inOrder(listener);
383
384 /**
385 * State transitions (in that order):
386 * 1) onOpened
Zhijun Hec66004f2014-03-04 17:58:56 -0800387 *
388 * No other transitions must occur for successful #openCamera
389 */
390 inOrder.verify(listener)
391 .onOpened(argument.capture());
392
393 CameraDevice camera = argument.getValue();
394 assertNotNull(
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700395 String.format("Failed to open camera device ID: %s", cameraId),
Zhijun Hec66004f2014-03-04 17:58:56 -0800396 camera);
397
Zhijun Hec66004f2014-03-04 17:58:56 -0800398 // Do not use inOrder here since that would skip anything called before onOpened
399 verifyNoMoreInteractions(listener);
400
401 return camera;
402 }
403
404 /**
405 * Test: that opening the same device multiple times and make sure the right
406 * error state is set.
407 */
Timothy Knightd8b3e522013-07-25 15:54:46 -0700408 public void testCameraManagerOpenCameraTwice() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700409 String[] ids = mCameraManager.getCameraIdList();
Zhijun Hec66004f2014-03-04 17:58:56 -0800410
Zhijun Hec66004f2014-03-04 17:58:56 -0800411 // Test across every camera device.
412 for (int i = 0; i < ids.length; ++i) {
413 CameraDevice successCamera = null;
Zhijun He884dc002014-03-20 00:03:10 -0700414 mCollector.setCameraId(ids[i]);
Zhijun Hec66004f2014-03-04 17:58:56 -0800415
Timothy Knightd8b3e522013-07-25 15:54:46 -0700416 try {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700417 MockStateCallback mockSuccessListener = MockStateCallback.mock();
418 MockStateCallback mockFailListener = MockStateCallback.mock();
Zhijun Hec66004f2014-03-04 17:58:56 -0800419
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700420 BlockingStateCallback successListener =
421 new BlockingStateCallback(mockSuccessListener);
422 BlockingStateCallback failListener =
423 new BlockingStateCallback(mockFailListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800424
425 mCameraManager.openCamera(ids[i], successListener, mHandler);
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700426 mCameraManager.openCamera(ids[i], failListener,
427 mHandler);
Zhijun Hec66004f2014-03-04 17:58:56 -0800428
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700429 successListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun He884dc002014-03-20 00:03:10 -0700430 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
Zhijun He884dc002014-03-20 00:03:10 -0700431 ArgumentCaptor<CameraDevice> argument =
432 ArgumentCaptor.forClass(CameraDevice.class);
433 verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture());
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700434 verify(mockSuccessListener, atLeastOnce()).onDisconnected(argument.capture());
Zhijun He884dc002014-03-20 00:03:10 -0700435
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700436 failListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun Hec66004f2014-03-04 17:58:56 -0800437 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700438 verify(mockFailListener, atLeastOnce()).onOpened(argument.capture());
Zhijun Hec66004f2014-03-04 17:58:56 -0800439
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700440 successCamera = verifyCameraStateOpened(
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700441 ids[i], mockFailListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800442
Zhijun Hec66004f2014-03-04 17:58:56 -0800443 verifyNoMoreInteractions(mockFailListener);
444 } finally {
445 if (successCamera != null) {
446 successCamera.close();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700447 }
448 }
449 }
450 }
451
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700452 private class NoopCameraListener extends CameraManager.AvailabilityCallback {
Timothy Knightd8b3e522013-07-25 15:54:46 -0700453 @Override
454 public void onCameraAvailable(String cameraId) {
455 // No-op
456 }
457
458 @Override
459 public void onCameraUnavailable(String cameraId) {
460 // No-op
461 }
462 }
463
464 /**
465 * Test: that the APIs to register and unregister a listener run successfully;
466 * doesn't test that the listener actually gets invoked at the right time.
467 * Registering a listener multiple times should have no effect, and unregistering
468 * a listener that isn't registered should have no effect.
469 */
470 public void testCameraManagerListener() throws Exception {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700471 mCameraManager.unregisterAvailabilityCallback(mListener);
472 mCameraManager.registerAvailabilityCallback(mListener, mHandler);
473 mCameraManager.registerAvailabilityCallback(mListener, mHandler);
474 mCameraManager.unregisterAvailabilityCallback(mListener);
475 mCameraManager.unregisterAvailabilityCallback(mListener);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700476 }
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700477
478 /**
479 * Test that the availability callbacks fire when expected
480 */
481 public void testCameraManagerListenerCallbacks() throws Exception {
482 final int AVAILABILITY_TIMEOUT_MS = 10;
483
484 final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
485 final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
486
487 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
488 @Override
489 public void onCameraAvailable(String cameraId) {
490 availableEventQueue.offer(cameraId);
491 }
492
493 @Override
494 public void onCameraUnavailable(String cameraId) {
495 unavailableEventQueue.offer(cameraId);
496 }
497 };
498
499 mCameraManager.registerAvailabilityCallback(ac, mHandler);
500 String[] cameras = mCameraManager.getCameraIdList();
501
Eino-Ville Talvalab7e50032014-12-10 15:35:00 -0800502 if (cameras.length == 0) {
503 Log.i(TAG, "No cameras present, skipping test");
504 return;
505 }
506
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700507 // Verify we received available for all cameras' initial state in a reasonable amount of time
508 HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
509 while (expectedAvailableCameras.size() > 0) {
510 String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
511 java.util.concurrent.TimeUnit.MILLISECONDS);
512 assertTrue("Did not receive initial availability notices for some cameras",
513 id != null);
514 expectedAvailableCameras.remove(id);
515 }
516 // Verify no unavailable cameras were reported
517 assertTrue("Some camera devices are initially unavailable",
518 unavailableEventQueue.size() == 0);
519
520 // Verify transitions for individual cameras
521 for (String id : cameras) {
522 MockStateCallback mockListener = MockStateCallback.mock();
523 mCameraListener = new BlockingStateCallback(mockListener);
524
525 mCameraManager.openCamera(id, mCameraListener, mHandler);
526
527 // Block until opened
528 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
529 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
530 // Then verify only open happened, and get the camera handle
531 CameraDevice camera = verifyCameraStateOpened(id, mockListener);
532
533 // Verify that we see the expected 'unavailable' event.
534 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
535 java.util.concurrent.TimeUnit.MILLISECONDS);
536 assertTrue(String.format("Received unavailability notice for wrong ID " +
537 "(expected %s, got %s)", id, candidateId),
Przemyslaw Szczepaniak73d9ee12015-10-21 14:34:25 +0100538 id.equals(candidateId));
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700539 assertTrue("Availability events received unexpectedly",
540 availableEventQueue.size() == 0);
541
542 // Verify that we see the expected 'available' event after closing the camera
543
544 camera.close();
545
546 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
547 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
548
549 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
550 java.util.concurrent.TimeUnit.MILLISECONDS);
551 assertTrue(String.format("Received availability notice for wrong ID " +
552 "(expected %s, got %s)", id, candidateId),
Przemyslaw Szczepaniak73d9ee12015-10-21 14:34:25 +0100553 id.equals(candidateId));
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700554 assertTrue("Unavailability events received unexpectedly",
555 unavailableEventQueue.size() == 0);
556
557 }
558
559 // Verify that we can unregister the listener and see no more events
560 assertTrue("Availability events received unexpectedly",
561 availableEventQueue.size() == 0);
562 assertTrue("Unavailability events received unexpectedly",
563 unavailableEventQueue.size() == 0);
564
565 mCameraManager.unregisterAvailabilityCallback(ac);
566
567 {
568 // Open an arbitrary camera and make sure we don't hear about it
569
570 MockStateCallback mockListener = MockStateCallback.mock();
571 mCameraListener = new BlockingStateCallback(mockListener);
572
573 mCameraManager.openCamera(cameras[0], mCameraListener, mHandler);
574
575 // Block until opened
576 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
577 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
578 // Then verify only open happened, and close the camera
579 CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener);
580
581 camera.close();
582
583 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
584 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
585
586 // No unavailability or availability callback should have occured
587 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
588 java.util.concurrent.TimeUnit.MILLISECONDS);
589 assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ",
590 candidateId),
591 candidateId == null);
592
593 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
594 java.util.concurrent.TimeUnit.MILLISECONDS);
595 assertTrue(String.format("Received availability notice for ID %s unexpectedly ",
596 candidateId),
597 candidateId == null);
598
599
600 }
601
602 } // testCameraManagerListenerCallbacks
603
Timothy Knightd8b3e522013-07-25 15:54:46 -0700604}