1 /* 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 17 package android.hardware.camera2.cts; 18 19 import static org.mockito.Mockito.*; 20 import static org.mockito.AdditionalMatchers.not; 21 import static org.mockito.AdditionalMatchers.and; 22 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.hardware.camera2.CameraAccessException; 26 import android.hardware.camera2.CameraCharacteristics; 27 import android.hardware.camera2.CameraDevice; 28 import android.hardware.camera2.CameraDevice.StateCallback; 29 import android.hardware.camera2.CameraManager; 30 import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor; 31 import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback; 32 import android.hardware.camera2.cts.helpers.CameraErrorCollector; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.platform.test.annotations.AppModeFull; 36 import android.test.AndroidTestCase; 37 import android.util.Log; 38 39 import com.android.ex.camera2.blocking.BlockingStateCallback; 40 41 import org.mockito.ArgumentCaptor; 42 import org.mockito.InOrder; 43 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.concurrent.Executor; 49 import java.util.concurrent.LinkedBlockingQueue; 50 51 /** 52 * <p>Basic test for CameraManager class.</p> 53 */ 54 @AppModeFull 55 public class CameraManagerTest extends AndroidTestCase { 56 private static final String TAG = "CameraManagerTest"; 57 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 58 private static final int NUM_CAMERA_REOPENS = 10; 59 60 private PackageManager mPackageManager; 61 private CameraManager mCameraManager; 62 private NoopCameraListener mListener; 63 private HandlerThread mHandlerThread; 64 private Handler mHandler; 65 private BlockingStateCallback mCameraListener; 66 private CameraErrorCollector mCollector; 67 68 @Override 69 public void setContext(Context context) { 70 super.setContext(context); 71 mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE); 72 assertNotNull("Can't connect to camera manager", mCameraManager); 73 mPackageManager = context.getPackageManager(); 74 assertNotNull("Can't get package manager", mPackageManager); 75 mListener = new NoopCameraListener(); 76 } 77 78 @Override 79 protected void setUp() throws Exception { 80 super.setUp(); 81 82 /** 83 * Workaround for mockito and JB-MR2 incompatibility 84 * 85 * Avoid java.lang.IllegalArgumentException: dexcache == null 86 * https://code.google.com/p/dexmaker/issues/detail?id=2 87 */ 88 System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); 89 90 mCameraListener = spy(new BlockingStateCallback()); 91 92 mHandlerThread = new HandlerThread(TAG); 93 mHandlerThread.start(); 94 mHandler = new Handler(mHandlerThread.getLooper()); 95 mCollector = new CameraErrorCollector(); 96 } 97 98 @Override 99 protected void tearDown() throws Exception { 100 mHandlerThread.quitSafely(); 101 mHandler = null; 102 103 try { 104 mCollector.verify(); 105 } catch (Throwable e) { 106 // When new Exception(e) is used, exception info will be printed twice. 107 throw new Exception(e.getMessage()); 108 } finally { 109 super.tearDown(); 110 } 111 } 112 113 /** 114 * Verifies that the reason is in the range of public-only codes. 115 */ 116 private static int checkCameraAccessExceptionReason(CameraAccessException e) { 117 int reason = e.getReason(); 118 119 switch (reason) { 120 case CameraAccessException.CAMERA_DISABLED: 121 case CameraAccessException.CAMERA_DISCONNECTED: 122 case CameraAccessException.CAMERA_ERROR: 123 return reason; 124 } 125 126 fail("Invalid CameraAccessException code: " + reason); 127 128 return -1; // unreachable 129 } 130 131 public void testCameraManagerGetDeviceIdList() throws Exception { 132 133 // Test: that the getCameraIdList method runs without exceptions. 134 String[] ids = mCameraManager.getCameraIdList(); 135 if (VERBOSE) Log.v(TAG, "CameraManager ids: " + Arrays.toString(ids)); 136 137 /** 138 * Test: that if there is at least one reported id, then the system must have 139 * the FEATURE_CAMERA_ANY feature. 140 */ 141 assertTrue("System camera feature and camera id list don't match", 142 ids.length == 0 || 143 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)); 144 145 /** 146 * Test: that if the device has front or rear facing cameras, then there 147 * must be matched system features. 148 */ 149 boolean externalCameraConnected = false; 150 for (int i = 0; i < ids.length; i++) { 151 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]); 152 assertNotNull("Can't get camera characteristics for camera " + ids[i], props); 153 Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING); 154 assertNotNull("Can't get lens facing info", lensFacing); 155 if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) { 156 assertTrue("System doesn't have front camera feature", 157 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)); 158 } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) { 159 assertTrue("System doesn't have back camera feature", 160 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)); 161 } else if (lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL) { 162 externalCameraConnected = true; 163 assertTrue("System doesn't have external camera feature", 164 mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)); 165 } else { 166 fail("Unknown camera lens facing " + lensFacing.toString()); 167 } 168 } 169 170 // Test an external camera is connected if FEATURE_CAMERA_EXTERNAL is advertised 171 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)) { 172 assertTrue("External camera is not connected on device with FEATURE_CAMERA_EXTERNAL", 173 externalCameraConnected); 174 } 175 176 /** 177 * Test: that if there is one camera device, then the system must have some 178 * specific features. 179 */ 180 assertTrue("Missing system feature: FEATURE_CAMERA_ANY", 181 ids.length == 0 182 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)); 183 assertTrue("Missing system feature: FEATURE_CAMERA, FEATURE_CAMERA_FRONT or FEATURE_CAMERA_EXTERNAL", 184 ids.length == 0 185 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) 186 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) 187 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)); 188 } 189 190 // Test: that properties can be queried from each device, without exceptions. 191 public void testCameraManagerGetCameraCharacteristics() throws Exception { 192 String[] ids = mCameraManager.getCameraIdList(); 193 for (int i = 0; i < ids.length; i++) { 194 CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]); 195 assertNotNull( 196 String.format("Can't get camera characteristics from: ID %s", ids[i]), props); 197 } 198 } 199 200 // Test: that an exception is thrown if an invalid device id is passed down. 201 public void testCameraManagerInvalidDevice() throws Exception { 202 String[] ids = mCameraManager.getCameraIdList(); 203 // Create an invalid id by concatenating all the valid ids together. 204 StringBuilder invalidId = new StringBuilder(); 205 invalidId.append("INVALID"); 206 for (int i = 0; i < ids.length; i++) { 207 invalidId.append(ids[i]); 208 } 209 210 try { 211 mCameraManager.getCameraCharacteristics( 212 invalidId.toString()); 213 fail(String.format("Accepted invalid camera ID: %s", invalidId.toString())); 214 } catch (IllegalArgumentException e) { 215 // This is the exception that should be thrown in this case. 216 } 217 } 218 219 // Test: that each camera device can be opened one at a time, several times. 220 public void testCameraManagerOpenCamerasSerially() throws Exception { 221 testCameraManagerOpenCamerasSerially(/*useExecutor*/ false); 222 testCameraManagerOpenCamerasSerially(/*useExecutor*/ true); 223 } 224 225 private void testCameraManagerOpenCamerasSerially(boolean useExecutor) throws Exception { 226 final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null; 227 String[] ids = mCameraManager.getCameraIdList(); 228 for (int i = 0; i < ids.length; i++) { 229 for (int j = 0; j < NUM_CAMERA_REOPENS; j++) { 230 CameraDevice camera = null; 231 try { 232 MockStateCallback mockListener = MockStateCallback.mock(); 233 mCameraListener = new BlockingStateCallback(mockListener); 234 235 if (useExecutor) { 236 mCameraManager.openCamera(ids[i], executor, mCameraListener); 237 } else { 238 mCameraManager.openCamera(ids[i], mCameraListener, mHandler); 239 } 240 241 // Block until unConfigured 242 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED, 243 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 244 245 // Ensure state transitions are in right order: 246 // -- 1) Opened 247 // Ensure no other state transitions have occurred: 248 camera = verifyCameraStateOpened(ids[i], mockListener); 249 } finally { 250 if (camera != null) { 251 camera.close(); 252 } 253 } 254 } 255 } 256 } 257 258 /** 259 * Test: one or more camera devices can be open at the same time, or the right error state 260 * is set if this can't be done. 261 */ 262 public void testCameraManagerOpenAllCameras() throws Exception { 263 testCameraManagerOpenAllCameras(/*useExecutor*/ false); 264 testCameraManagerOpenAllCameras(/*useExecutor*/ true); 265 } 266 267 private void testCameraManagerOpenAllCameras(boolean useExecutor) throws Exception { 268 String[] ids = mCameraManager.getCameraIdList(); 269 assertNotNull("Camera ids shouldn't be null", ids); 270 271 // Skip test if the device doesn't have multiple cameras. 272 if (ids.length <= 1) { 273 return; 274 } 275 276 final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null; 277 List<CameraDevice> cameraList = new ArrayList<CameraDevice>(); 278 List<MockStateCallback> listenerList = new ArrayList<MockStateCallback>(); 279 List<BlockingStateCallback> blockingListenerList = new ArrayList<BlockingStateCallback>(); 280 try { 281 for (int i = 0; i < ids.length; i++) { 282 // Ignore state changes from other cameras 283 MockStateCallback mockListener = MockStateCallback.mock(); 284 mCameraListener = new BlockingStateCallback(mockListener); 285 286 /** 287 * Track whether or not we got a synchronous error from openCamera. 288 * 289 * A synchronous error must also be accompanied by an asynchronous 290 * StateCallback#onError callback. 291 */ 292 boolean expectingError = false; 293 294 String cameraId = ids[i]; 295 try { 296 if (useExecutor) { 297 mCameraManager.openCamera(cameraId, executor, mCameraListener); 298 } else { 299 mCameraManager.openCamera(cameraId, mCameraListener, mHandler); 300 } 301 } catch (CameraAccessException e) { 302 if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) { 303 expectingError = true; 304 } else { 305 // TODO: We should handle a Disabled camera by passing here and elsewhere 306 fail("Camera must not be disconnected or disabled for this test" + ids[i]); 307 } 308 } 309 310 List<Integer> expectedStates = new ArrayList<Integer>(); 311 expectedStates.add(BlockingStateCallback.STATE_OPENED); 312 expectedStates.add(BlockingStateCallback.STATE_ERROR); 313 int state = mCameraListener.waitForAnyOfStates( 314 expectedStates, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 315 316 // It's possible that we got an asynchronous error transition only. This is ok. 317 if (expectingError) { 318 assertEquals("Throwing a CAMERA_ERROR exception must be accompanied with a " + 319 "StateCallback#onError callback", 320 BlockingStateCallback.STATE_ERROR, state); 321 } 322 323 /** 324 * Two situations are considered passing: 325 * 1) The camera opened successfully. 326 * => No error must be set. 327 * 2) The camera did not open because there were too many other cameras opened. 328 * => Only MAX_CAMERAS_IN_USE error must be set. 329 * 330 * Any other situation is considered a failure. 331 * 332 * For simplicity we treat disconnecting asynchronously as a failure, so 333 * camera devices should not be physically unplugged during this test. 334 */ 335 336 CameraDevice camera; 337 if (state == BlockingStateCallback.STATE_ERROR) { 338 // Camera did not open because too many other cameras were opened 339 // => onError called exactly once with a non-null camera 340 assertTrue("At least one camera must be opened successfully", 341 cameraList.size() > 0); 342 343 ArgumentCaptor<CameraDevice> argument = 344 ArgumentCaptor.forClass(CameraDevice.class); 345 346 verify(mockListener) 347 .onError( 348 argument.capture(), 349 eq(CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE)); 350 verifyNoMoreInteractions(mockListener); 351 352 camera = argument.getValue(); 353 assertNotNull("Expected a non-null camera for the error transition for ID: " 354 + ids[i], camera); 355 } else if (state == BlockingStateCallback.STATE_OPENED) { 356 // Camera opened successfully. 357 // => onOpened called exactly once 358 camera = verifyCameraStateOpened(cameraId, 359 mockListener); 360 } else { 361 fail("Unexpected state " + state); 362 camera = null; // unreachable. but need this for java compiler 363 } 364 365 // Keep track of cameras so we can close it later 366 cameraList.add(camera); 367 listenerList.add(mockListener); 368 blockingListenerList.add(mCameraListener); 369 } 370 } finally { 371 for (int i = 0; i < cameraList.size(); i++) { 372 // With conflicting devices, opening of one camera could result in the other camera 373 // being disconnected. To handle such case, reset the mock before close. 374 reset(listenerList.get(i)); 375 cameraList.get(i).close(); 376 } 377 for (BlockingStateCallback blockingListener : blockingListenerList) { 378 blockingListener.waitForState( 379 BlockingStateCallback.STATE_CLOSED, 380 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 381 } 382 } 383 384 /* 385 * Ensure that no state transitions have bled through from one camera to another 386 * after closing the cameras. 387 */ 388 int i = 0; 389 for (MockStateCallback listener : listenerList) { 390 CameraDevice camera = cameraList.get(i); 391 392 verify(listener).onClosed(eq(camera)); 393 verifyNoMoreInteractions(listener); 394 i++; 395 // Only a #close can happen on the camera since we were done with it. 396 // Also nothing else should've happened between the close and the open. 397 } 398 } 399 400 /** 401 * Verifies the camera in this listener was opened and then unconfigured exactly once. 402 * 403 * <p>This assumes that no other action to the camera has been done (e.g. 404 * it hasn't been configured, or closed, or disconnected). Verification is 405 * performed immediately without any timeouts.</p> 406 * 407 * <p>This checks that the state has previously changed first for opened and then unconfigured. 408 * Any other state transitions will fail. A test failure is thrown if verification fails.</p> 409 * 410 * @param cameraId Camera identifier 411 * @param listener Listener which was passed to {@link CameraManager#openCamera} 412 * 413 * @return The camera device (non-{@code null}). 414 */ 415 private static CameraDevice verifyCameraStateOpened(String cameraId, 416 MockStateCallback listener) { 417 ArgumentCaptor<CameraDevice> argument = 418 ArgumentCaptor.forClass(CameraDevice.class); 419 InOrder inOrder = inOrder(listener); 420 421 /** 422 * State transitions (in that order): 423 * 1) onOpened 424 * 425 * No other transitions must occur for successful #openCamera 426 */ 427 inOrder.verify(listener) 428 .onOpened(argument.capture()); 429 430 CameraDevice camera = argument.getValue(); 431 assertNotNull( 432 String.format("Failed to open camera device ID: %s", cameraId), 433 camera); 434 435 // Do not use inOrder here since that would skip anything called before onOpened 436 verifyNoMoreInteractions(listener); 437 438 return camera; 439 } 440 441 /** 442 * Test: that opening the same device multiple times and make sure the right 443 * error state is set. 444 */ 445 public void testCameraManagerOpenCameraTwice() throws Exception { 446 testCameraManagerOpenCameraTwice(/*useExecutor*/ false); 447 testCameraManagerOpenCameraTwice(/*useExecutor*/ true); 448 } 449 450 private void testCameraManagerOpenCameraTwice(boolean useExecutor) throws Exception { 451 String[] ids = mCameraManager.getCameraIdList(); 452 final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null; 453 454 // Test across every camera device. 455 for (int i = 0; i < ids.length; ++i) { 456 CameraDevice successCamera = null; 457 mCollector.setCameraId(ids[i]); 458 459 try { 460 MockStateCallback mockSuccessListener = MockStateCallback.mock(); 461 MockStateCallback mockFailListener = MockStateCallback.mock(); 462 463 BlockingStateCallback successListener = 464 new BlockingStateCallback(mockSuccessListener); 465 BlockingStateCallback failListener = 466 new BlockingStateCallback(mockFailListener); 467 468 if (useExecutor) { 469 mCameraManager.openCamera(ids[i], executor, successListener); 470 mCameraManager.openCamera(ids[i], executor, failListener); 471 } else { 472 mCameraManager.openCamera(ids[i], successListener, mHandler); 473 mCameraManager.openCamera(ids[i], failListener, mHandler); 474 } 475 476 successListener.waitForState(BlockingStateCallback.STATE_OPENED, 477 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 478 ArgumentCaptor<CameraDevice> argument = 479 ArgumentCaptor.forClass(CameraDevice.class); 480 verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture()); 481 verify(mockSuccessListener, atLeastOnce()).onDisconnected(argument.capture()); 482 483 failListener.waitForState(BlockingStateCallback.STATE_OPENED, 484 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 485 verify(mockFailListener, atLeastOnce()).onOpened(argument.capture()); 486 487 successCamera = verifyCameraStateOpened( 488 ids[i], mockFailListener); 489 490 verifyNoMoreInteractions(mockFailListener); 491 } finally { 492 if (successCamera != null) { 493 successCamera.close(); 494 } 495 } 496 } 497 } 498 499 private class NoopCameraListener extends CameraManager.AvailabilityCallback { 500 @Override 501 public void onCameraAvailable(String cameraId) { 502 // No-op 503 } 504 505 @Override 506 public void onCameraUnavailable(String cameraId) { 507 // No-op 508 } 509 } 510 511 /** 512 * Test: that the APIs to register and unregister a listener run successfully; 513 * doesn't test that the listener actually gets invoked at the right time. 514 * Registering a listener multiple times should have no effect, and unregistering 515 * a listener that isn't registered should have no effect. 516 */ 517 public void testCameraManagerListener() throws Exception { 518 mCameraManager.unregisterAvailabilityCallback(mListener); 519 // Test Handler API 520 mCameraManager.registerAvailabilityCallback(mListener, mHandler); 521 mCameraManager.registerAvailabilityCallback(mListener, mHandler); 522 mCameraManager.unregisterAvailabilityCallback(mListener); 523 mCameraManager.unregisterAvailabilityCallback(mListener); 524 // Test Executor API 525 Executor executor = new HandlerExecutor(mHandler); 526 mCameraManager.registerAvailabilityCallback(executor, mListener); 527 mCameraManager.registerAvailabilityCallback(executor, mListener); 528 mCameraManager.unregisterAvailabilityCallback(mListener); 529 mCameraManager.unregisterAvailabilityCallback(mListener); 530 } 531 532 /** 533 * Test that the availability callbacks fire when expected 534 */ 535 public void testCameraManagerListenerCallbacks() throws Exception { 536 testCameraManagerListenerCallbacks(/*useExecutor*/ false); 537 testCameraManagerListenerCallbacks(/*useExecutor*/ true); 538 } 539 540 private void testCameraManagerListenerCallbacks(boolean useExecutor) throws Exception { 541 final int AVAILABILITY_TIMEOUT_MS = 10; 542 543 final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>(); 544 final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>(); 545 final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null; 546 547 CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() { 548 @Override 549 public void onCameraAvailable(String cameraId) { 550 availableEventQueue.offer(cameraId); 551 } 552 553 @Override 554 public void onCameraUnavailable(String cameraId) { 555 unavailableEventQueue.offer(cameraId); 556 } 557 }; 558 559 if (useExecutor) { 560 mCameraManager.registerAvailabilityCallback(executor, ac); 561 } else { 562 mCameraManager.registerAvailabilityCallback(ac, mHandler); 563 } 564 String[] cameras = mCameraManager.getCameraIdList(); 565 566 if (cameras.length == 0) { 567 Log.i(TAG, "No cameras present, skipping test"); 568 return; 569 } 570 571 // Verify we received available for all cameras' initial state in a reasonable amount of time 572 HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras)); 573 while (expectedAvailableCameras.size() > 0) { 574 String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 575 java.util.concurrent.TimeUnit.MILLISECONDS); 576 assertTrue("Did not receive initial availability notices for some cameras", 577 id != null); 578 expectedAvailableCameras.remove(id); 579 } 580 // Verify no unavailable cameras were reported 581 assertTrue("Some camera devices are initially unavailable", 582 unavailableEventQueue.size() == 0); 583 584 // Verify transitions for individual cameras 585 for (String id : cameras) { 586 MockStateCallback mockListener = MockStateCallback.mock(); 587 mCameraListener = new BlockingStateCallback(mockListener); 588 589 if (useExecutor) { 590 mCameraManager.openCamera(id, executor, mCameraListener); 591 } else { 592 mCameraManager.openCamera(id, mCameraListener, mHandler); 593 } 594 595 // Block until opened 596 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED, 597 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 598 // Then verify only open happened, and get the camera handle 599 CameraDevice camera = verifyCameraStateOpened(id, mockListener); 600 601 // Verify that we see the expected 'unavailable' event. 602 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 603 java.util.concurrent.TimeUnit.MILLISECONDS); 604 assertTrue(String.format("Received unavailability notice for wrong ID " + 605 "(expected %s, got %s)", id, candidateId), 606 id.equals(candidateId)); 607 assertTrue("Availability events received unexpectedly", 608 availableEventQueue.size() == 0); 609 610 // Verify that we see the expected 'available' event after closing the camera 611 612 camera.close(); 613 614 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED, 615 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS); 616 617 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 618 java.util.concurrent.TimeUnit.MILLISECONDS); 619 assertTrue(String.format("Received availability notice for wrong ID " + 620 "(expected %s, got %s)", id, candidateId), 621 id.equals(candidateId)); 622 assertTrue("Unavailability events received unexpectedly", 623 unavailableEventQueue.size() == 0); 624 625 } 626 627 // Verify that we can unregister the listener and see no more events 628 assertTrue("Availability events received unexpectedly", 629 availableEventQueue.size() == 0); 630 assertTrue("Unavailability events received unexpectedly", 631 unavailableEventQueue.size() == 0); 632 633 mCameraManager.unregisterAvailabilityCallback(ac); 634 635 { 636 // Open an arbitrary camera and make sure we don't hear about it 637 638 MockStateCallback mockListener = MockStateCallback.mock(); 639 mCameraListener = new BlockingStateCallback(mockListener); 640 641 if (useExecutor) { 642 mCameraManager.openCamera(cameras[0], executor, mCameraListener); 643 } else { 644 mCameraManager.openCamera(cameras[0], mCameraListener, mHandler); 645 } 646 647 // Block until opened 648 mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED, 649 CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS); 650 // Then verify only open happened, and close the camera 651 CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener); 652 653 camera.close(); 654 655 mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED, 656 CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS); 657 658 // No unavailability or availability callback should have occured 659 String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 660 java.util.concurrent.TimeUnit.MILLISECONDS); 661 assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ", 662 candidateId), 663 candidateId == null); 664 665 candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS, 666 java.util.concurrent.TimeUnit.MILLISECONDS); 667 assertTrue(String.format("Received availability notice for ID %s unexpectedly ", 668 candidateId), 669 candidateId == null); 670 671 672 } 673 674 } // testCameraManagerListenerCallbacks 675 676 } 677