Home | History | Annotate | Download | only in cts
      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