blob: 4c351886a8d7f8ef0436bc7e9a52e67f19f3b8ae [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",
152 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
153 } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
154 assertTrue("System doesn't have back camera feature",
155 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
156 } else {
157 fail("Unknown camera lens facing " + lensFacing.toString());
158 }
159 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700160
161 /**
162 * Test: that if there is one camera device, then the system must have some
163 * specific features.
164 */
165 assertTrue("Missing system feature: FEATURE_CAMERA_ANY",
166 ids.length == 0
167 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
168 assertTrue("Missing system feature: FEATURE_CAMERA or FEATURE_CAMERA_FRONT",
169 ids.length == 0
170 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
171 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
172 }
173
174 // Test: that properties can be queried from each device, without exceptions.
Igor Murashkinafce1a12013-09-10 12:29:54 -0700175 public void testCameraManagerGetCameraCharacteristics() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700176 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700177 for (int i = 0; i < ids.length; i++) {
Zhijun He9dbe4172013-09-27 13:34:12 -0700178 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700179 assertNotNull(
Zhijun He9dbe4172013-09-27 13:34:12 -0700180 String.format("Can't get camera characteristics from: ID %s", ids[i]), props);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700181 }
182 }
183
184 // Test: that an exception is thrown if an invalid device id is passed down.
185 public void testCameraManagerInvalidDevice() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700186 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700187 // Create an invalid id by concatenating all the valid ids together.
188 StringBuilder invalidId = new StringBuilder();
189 invalidId.append("INVALID");
190 for (int i = 0; i < ids.length; i++) {
191 invalidId.append(ids[i]);
192 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800193
Timothy Knightd8b3e522013-07-25 15:54:46 -0700194 try {
Zhijun Hec66004f2014-03-04 17:58:56 -0800195 mCameraManager.getCameraCharacteristics(
Timothy Knightd8b3e522013-07-25 15:54:46 -0700196 invalidId.toString());
197 fail(String.format("Accepted invalid camera ID: %s", invalidId.toString()));
Zhijun Hec66004f2014-03-04 17:58:56 -0800198 } catch (IllegalArgumentException e) {
Timothy Knightd8b3e522013-07-25 15:54:46 -0700199 // This is the exception that should be thrown in this case.
200 }
201 }
202
203 // Test: that each camera device can be opened one at a time, several times.
204 public void testCameraManagerOpenCamerasSerially() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700205 String[] ids = mCameraManager.getCameraIdList();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700206 for (int i = 0; i < ids.length; i++) {
207 for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800208 CameraDevice camera = null;
209 try {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700210 MockStateCallback mockListener = MockStateCallback.mock();
211 mCameraListener = new BlockingStateCallback(mockListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800212
213 mCameraManager.openCamera(ids[i], mCameraListener, mHandler);
214
215 // Block until unConfigured
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700216 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun Hec66004f2014-03-04 17:58:56 -0800217 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
218
219 // Ensure state transitions are in right order:
220 // -- 1) Opened
Zhijun Hec66004f2014-03-04 17:58:56 -0800221 // Ensure no other state transitions have occurred:
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700222 camera = verifyCameraStateOpened(ids[i], mockListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800223 } finally {
224 if (camera != null) {
225 camera.close();
226 }
227 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700228 }
229 }
230 }
231
232 /**
Zhijun Hec66004f2014-03-04 17:58:56 -0800233 * Test: one or more camera devices can be open at the same time, or the right error state
234 * is set if this can't be done.
Timothy Knightd8b3e522013-07-25 15:54:46 -0700235 */
236 public void testCameraManagerOpenAllCameras() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700237 String[] ids = mCameraManager.getCameraIdList();
Zhijun Hec66004f2014-03-04 17:58:56 -0800238 assertNotNull("Camera ids shouldn't be null", ids);
239
240 // Skip test if the device doesn't have multiple cameras.
241 if (ids.length <= 1) {
242 return;
243 }
244
245 List<CameraDevice> cameraList = new ArrayList<CameraDevice>();
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700246 List<MockStateCallback> listenerList = new ArrayList<MockStateCallback>();
247 List<BlockingStateCallback> blockingListenerList = new ArrayList<BlockingStateCallback>();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700248 try {
249 for (int i = 0; i < ids.length; i++) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800250 // Ignore state changes from other cameras
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700251 MockStateCallback mockListener = MockStateCallback.mock();
252 mCameraListener = new BlockingStateCallback(mockListener);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700253
Zhijun Hec66004f2014-03-04 17:58:56 -0800254 /**
255 * Track whether or not we got a synchronous error from openCamera.
256 *
257 * A synchronous error must also be accompanied by an asynchronous
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700258 * StateCallback#onError callback.
Zhijun Hec66004f2014-03-04 17:58:56 -0800259 */
260 boolean expectingError = false;
261
262 String cameraId = ids[i];
263 try {
264 mCameraManager.openCamera(cameraId, mCameraListener,
265 mHandler);
266 } catch (CameraAccessException e) {
267 if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) {
268 expectingError = true;
269 } else {
270 // TODO: We should handle a Disabled camera by passing here and elsewhere
271 fail("Camera must not be disconnected or disabled for this test" + ids[i]);
272 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700273 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800274
275 List<Integer> expectedStates = new ArrayList<Integer>();
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700276 expectedStates.add(BlockingStateCallback.STATE_OPENED);
277 expectedStates.add(BlockingStateCallback.STATE_ERROR);
Zhijun Hec66004f2014-03-04 17:58:56 -0800278 int state = mCameraListener.waitForAnyOfStates(
279 expectedStates, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
280
281 // It's possible that we got an asynchronous error transition only. This is ok.
282 if (expectingError) {
283 assertEquals("Throwing a CAMERA_ERROR exception must be accompanied with a " +
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700284 "StateCallback#onError callback",
285 BlockingStateCallback.STATE_ERROR, state);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700286 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800287
288 /**
289 * Two situations are considered passing:
290 * 1) The camera opened successfully.
291 * => No error must be set.
292 * 2) The camera did not open because there were too many other cameras opened.
293 * => Only MAX_CAMERAS_IN_USE error must be set.
294 *
295 * Any other situation is considered a failure.
296 *
297 * For simplicity we treat disconnecting asynchronously as a failure, so
298 * camera devices should not be physically unplugged during this test.
299 */
300
301 CameraDevice camera;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700302 if (state == BlockingStateCallback.STATE_ERROR) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800303 // Camera did not open because too many other cameras were opened
304 // => onError called exactly once with a non-null camera
305 assertTrue("At least one camera must be opened successfully",
306 cameraList.size() > 0);
307
308 ArgumentCaptor<CameraDevice> argument =
309 ArgumentCaptor.forClass(CameraDevice.class);
310
311 verify(mockListener)
312 .onError(
313 argument.capture(),
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700314 eq(CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE));
Zhijun Hec66004f2014-03-04 17:58:56 -0800315 verifyNoMoreInteractions(mockListener);
316
317 camera = argument.getValue();
318 assertNotNull("Expected a non-null camera for the error transition for ID: "
319 + ids[i], camera);
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700320 } else if (state == BlockingStateCallback.STATE_OPENED) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800321 // Camera opened successfully.
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700322 // => onOpened called exactly once
323 camera = verifyCameraStateOpened(cameraId,
Zhijun Hec66004f2014-03-04 17:58:56 -0800324 mockListener);
325 } else {
326 fail("Unexpected state " + state);
327 camera = null; // unreachable. but need this for java compiler
328 }
329
330 // Keep track of cameras so we can close it later
331 cameraList.add(camera);
332 listenerList.add(mockListener);
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700333 blockingListenerList.add(mCameraListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800334 }
335 } finally {
336 for (CameraDevice camera : cameraList) {
337 camera.close();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700338 }
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700339 for (BlockingStateCallback blockingListener : blockingListenerList) {
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700340 blockingListener.waitForState(
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700341 BlockingStateCallback.STATE_CLOSED,
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700342 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
343 }
Timothy Knightd8b3e522013-07-25 15:54:46 -0700344 }
Zhijun Hec66004f2014-03-04 17:58:56 -0800345
346 /*
347 * Ensure that no state transitions have bled through from one camera to another
348 * after closing the cameras.
349 */
350 int i = 0;
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700351 for (MockStateCallback listener : listenerList) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800352 CameraDevice camera = cameraList.get(i);
353
354 verify(listener).onClosed(eq(camera));
355 verifyNoMoreInteractions(listener);
Eino-Ville Talvalaf8892612014-06-03 18:38:56 -0700356 i++;
Zhijun Hec66004f2014-03-04 17:58:56 -0800357 // Only a #close can happen on the camera since we were done with it.
358 // Also nothing else should've happened between the close and the open.
Timothy Knightd8b3e522013-07-25 15:54:46 -0700359 }
360 }
361
Zhijun Hec66004f2014-03-04 17:58:56 -0800362 /**
363 * Verifies the camera in this listener was opened and then unconfigured exactly once.
364 *
365 * <p>This assumes that no other action to the camera has been done (e.g.
366 * it hasn't been configured, or closed, or disconnected). Verification is
367 * performed immediately without any timeouts.</p>
368 *
369 * <p>This checks that the state has previously changed first for opened and then unconfigured.
370 * Any other state transitions will fail. A test failure is thrown if verification fails.</p>
371 *
372 * @param cameraId Camera identifier
373 * @param listener Listener which was passed to {@link CameraManager#openCamera}
374 *
375 * @return The camera device (non-{@code null}).
376 */
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700377 private static CameraDevice verifyCameraStateOpened(String cameraId,
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700378 MockStateCallback listener) {
Zhijun Hec66004f2014-03-04 17:58:56 -0800379 ArgumentCaptor<CameraDevice> argument =
380 ArgumentCaptor.forClass(CameraDevice.class);
381 InOrder inOrder = inOrder(listener);
382
383 /**
384 * State transitions (in that order):
385 * 1) onOpened
Zhijun Hec66004f2014-03-04 17:58:56 -0800386 *
387 * No other transitions must occur for successful #openCamera
388 */
389 inOrder.verify(listener)
390 .onOpened(argument.capture());
391
392 CameraDevice camera = argument.getValue();
393 assertNotNull(
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700394 String.format("Failed to open camera device ID: %s", cameraId),
Zhijun Hec66004f2014-03-04 17:58:56 -0800395 camera);
396
Zhijun Hec66004f2014-03-04 17:58:56 -0800397 // Do not use inOrder here since that would skip anything called before onOpened
398 verifyNoMoreInteractions(listener);
399
400 return camera;
401 }
402
403 /**
404 * Test: that opening the same device multiple times and make sure the right
405 * error state is set.
406 */
Timothy Knightd8b3e522013-07-25 15:54:46 -0700407 public void testCameraManagerOpenCameraTwice() throws Exception {
Eino-Ville Talvala92f595c2013-09-09 13:48:27 -0700408 String[] ids = mCameraManager.getCameraIdList();
Zhijun Hec66004f2014-03-04 17:58:56 -0800409
Zhijun Hec66004f2014-03-04 17:58:56 -0800410 // Test across every camera device.
411 for (int i = 0; i < ids.length; ++i) {
412 CameraDevice successCamera = null;
Zhijun He884dc002014-03-20 00:03:10 -0700413 mCollector.setCameraId(ids[i]);
Zhijun Hec66004f2014-03-04 17:58:56 -0800414
Timothy Knightd8b3e522013-07-25 15:54:46 -0700415 try {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700416 MockStateCallback mockSuccessListener = MockStateCallback.mock();
417 MockStateCallback mockFailListener = MockStateCallback.mock();
Zhijun Hec66004f2014-03-04 17:58:56 -0800418
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700419 BlockingStateCallback successListener =
420 new BlockingStateCallback(mockSuccessListener);
421 BlockingStateCallback failListener =
422 new BlockingStateCallback(mockFailListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800423
424 mCameraManager.openCamera(ids[i], successListener, mHandler);
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700425 mCameraManager.openCamera(ids[i], failListener,
426 mHandler);
Zhijun Hec66004f2014-03-04 17:58:56 -0800427
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700428 successListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun He884dc002014-03-20 00:03:10 -0700429 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
Zhijun He884dc002014-03-20 00:03:10 -0700430 ArgumentCaptor<CameraDevice> argument =
431 ArgumentCaptor.forClass(CameraDevice.class);
432 verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture());
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700433 verify(mockSuccessListener, atLeastOnce()).onDisconnected(argument.capture());
Zhijun He884dc002014-03-20 00:03:10 -0700434
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700435 failListener.waitForState(BlockingStateCallback.STATE_OPENED,
Zhijun Hec66004f2014-03-04 17:58:56 -0800436 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700437 verify(mockFailListener, atLeastOnce()).onOpened(argument.capture());
Zhijun Hec66004f2014-03-04 17:58:56 -0800438
Eino-Ville Talvalab80c6612014-07-06 17:58:46 -0700439 successCamera = verifyCameraStateOpened(
Ruben Brunkbb1e64f2015-05-12 14:56:29 -0700440 ids[i], mockFailListener);
Zhijun Hec66004f2014-03-04 17:58:56 -0800441
Zhijun Hec66004f2014-03-04 17:58:56 -0800442 verifyNoMoreInteractions(mockFailListener);
443 } finally {
444 if (successCamera != null) {
445 successCamera.close();
Timothy Knightd8b3e522013-07-25 15:54:46 -0700446 }
447 }
448 }
449 }
450
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700451 private class NoopCameraListener extends CameraManager.AvailabilityCallback {
Timothy Knightd8b3e522013-07-25 15:54:46 -0700452 @Override
453 public void onCameraAvailable(String cameraId) {
454 // No-op
455 }
456
457 @Override
458 public void onCameraUnavailable(String cameraId) {
459 // No-op
460 }
461 }
462
463 /**
464 * Test: that the APIs to register and unregister a listener run successfully;
465 * doesn't test that the listener actually gets invoked at the right time.
466 * Registering a listener multiple times should have no effect, and unregistering
467 * a listener that isn't registered should have no effect.
468 */
469 public void testCameraManagerListener() throws Exception {
Eino-Ville Talvalac0dd0222014-09-04 15:07:58 -0700470 mCameraManager.unregisterAvailabilityCallback(mListener);
471 mCameraManager.registerAvailabilityCallback(mListener, mHandler);
472 mCameraManager.registerAvailabilityCallback(mListener, mHandler);
473 mCameraManager.unregisterAvailabilityCallback(mListener);
474 mCameraManager.unregisterAvailabilityCallback(mListener);
Timothy Knightd8b3e522013-07-25 15:54:46 -0700475 }
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700476
477 /**
478 * Test that the availability callbacks fire when expected
479 */
480 public void testCameraManagerListenerCallbacks() throws Exception {
481 final int AVAILABILITY_TIMEOUT_MS = 10;
482
483 final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
484 final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
485
486 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
487 @Override
488 public void onCameraAvailable(String cameraId) {
489 availableEventQueue.offer(cameraId);
490 }
491
492 @Override
493 public void onCameraUnavailable(String cameraId) {
494 unavailableEventQueue.offer(cameraId);
495 }
496 };
497
498 mCameraManager.registerAvailabilityCallback(ac, mHandler);
499 String[] cameras = mCameraManager.getCameraIdList();
500
Eino-Ville Talvalab7e50032014-12-10 15:35:00 -0800501 if (cameras.length == 0) {
502 Log.i(TAG, "No cameras present, skipping test");
503 return;
504 }
505
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700506 // Verify we received available for all cameras' initial state in a reasonable amount of time
507 HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
508 while (expectedAvailableCameras.size() > 0) {
509 String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
510 java.util.concurrent.TimeUnit.MILLISECONDS);
511 assertTrue("Did not receive initial availability notices for some cameras",
512 id != null);
513 expectedAvailableCameras.remove(id);
514 }
515 // Verify no unavailable cameras were reported
516 assertTrue("Some camera devices are initially unavailable",
517 unavailableEventQueue.size() == 0);
518
519 // Verify transitions for individual cameras
520 for (String id : cameras) {
521 MockStateCallback mockListener = MockStateCallback.mock();
522 mCameraListener = new BlockingStateCallback(mockListener);
523
524 mCameraManager.openCamera(id, mCameraListener, mHandler);
525
526 // Block until opened
527 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
528 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
529 // Then verify only open happened, and get the camera handle
530 CameraDevice camera = verifyCameraStateOpened(id, mockListener);
531
532 // Verify that we see the expected 'unavailable' event.
533 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
534 java.util.concurrent.TimeUnit.MILLISECONDS);
535 assertTrue(String.format("Received unavailability notice for wrong ID " +
536 "(expected %s, got %s)", id, candidateId),
Przemyslaw Szczepaniak73d9ee12015-10-21 14:34:25 +0100537 id.equals(candidateId));
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700538 assertTrue("Availability events received unexpectedly",
539 availableEventQueue.size() == 0);
540
541 // Verify that we see the expected 'available' event after closing the camera
542
543 camera.close();
544
545 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
546 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
547
548 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
549 java.util.concurrent.TimeUnit.MILLISECONDS);
550 assertTrue(String.format("Received availability notice for wrong ID " +
551 "(expected %s, got %s)", id, candidateId),
Przemyslaw Szczepaniak73d9ee12015-10-21 14:34:25 +0100552 id.equals(candidateId));
Eino-Ville Talvala851d48e2014-10-29 16:16:40 -0700553 assertTrue("Unavailability events received unexpectedly",
554 unavailableEventQueue.size() == 0);
555
556 }
557
558 // Verify that we can unregister the listener and see no more events
559 assertTrue("Availability events received unexpectedly",
560 availableEventQueue.size() == 0);
561 assertTrue("Unavailability events received unexpectedly",
562 unavailableEventQueue.size() == 0);
563
564 mCameraManager.unregisterAvailabilityCallback(ac);
565
566 {
567 // Open an arbitrary camera and make sure we don't hear about it
568
569 MockStateCallback mockListener = MockStateCallback.mock();
570 mCameraListener = new BlockingStateCallback(mockListener);
571
572 mCameraManager.openCamera(cameras[0], mCameraListener, mHandler);
573
574 // Block until opened
575 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
576 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
577 // Then verify only open happened, and close the camera
578 CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener);
579
580 camera.close();
581
582 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
583 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
584
585 // No unavailability or availability callback should have occured
586 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
587 java.util.concurrent.TimeUnit.MILLISECONDS);
588 assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ",
589 candidateId),
590 candidateId == null);
591
592 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
593 java.util.concurrent.TimeUnit.MILLISECONDS);
594 assertTrue(String.format("Received availability notice for ID %s unexpectedly ",
595 candidateId),
596 candidateId == null);
597
598
599 }
600
601 } // testCameraManagerListenerCallbacks
602
Timothy Knightd8b3e522013-07-25 15:54:46 -0700603}