Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2015 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 #define LOG_NDEBUG 0
     18 #define LOG_TAG "CameraBinderTests"
     19 
     20 #include <binder/IInterface.h>
     21 #include <binder/IServiceManager.h>
     22 #include <binder/Parcel.h>
     23 #include <binder/ProcessState.h>
     24 #include <utils/Errors.h>
     25 #include <utils/Log.h>
     26 #include <utils/List.h>
     27 #include <utils/String8.h>
     28 #include <utils/String16.h>
     29 #include <utils/Condition.h>
     30 #include <utils/Mutex.h>
     31 #include <system/graphics.h>
     32 #include <hardware/gralloc.h>
     33 
     34 #include <camera/CameraMetadata.h>
     35 #include <android/hardware/ICameraService.h>
     36 #include <android/hardware/ICameraServiceListener.h>
     37 #include <android/hardware/BnCameraServiceListener.h>
     38 #include <android/hardware/camera2/ICameraDeviceUser.h>
     39 #include <android/hardware/camera2/ICameraDeviceCallbacks.h>
     40 #include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
     41 #include <camera/camera2/CaptureRequest.h>
     42 #include <camera/camera2/OutputConfiguration.h>
     43 #include <camera/camera2/SubmitInfo.h>
     44 
     45 #include <gui/BufferItemConsumer.h>
     46 #include <gui/IGraphicBufferProducer.h>
     47 #include <gui/Surface.h>
     48 
     49 #include <gtest/gtest.h>
     50 #include <unistd.h>
     51 #include <stdint.h>
     52 #include <utility>
     53 #include <vector>
     54 #include <map>
     55 #include <algorithm>
     56 
     57 using namespace android;
     58 
     59 #define ASSERT_NOT_NULL(x) \
     60     ASSERT_TRUE((x) != nullptr)
     61 
     62 #define SETUP_TIMEOUT 2000000000 // ns
     63 #define IDLE_TIMEOUT 2000000000 // ns
     64 
     65 // Stub listener implementation
     66 class TestCameraServiceListener : public hardware::BnCameraServiceListener {
     67     std::map<String16, int32_t> mCameraTorchStatuses;
     68     std::map<String16, int32_t> mCameraStatuses;
     69     mutable Mutex mLock;
     70     mutable Condition mCondition;
     71     mutable Condition mTorchCondition;
     72 public:
     73     virtual ~TestCameraServiceListener() {};
     74 
     75     virtual binder::Status onStatusChanged(int32_t status, const String16& cameraId) {
     76         Mutex::Autolock l(mLock);
     77         mCameraStatuses[cameraId] = status;
     78         mCondition.broadcast();
     79         return binder::Status::ok();
     80     };
     81 
     82     virtual binder::Status onTorchStatusChanged(int32_t status, const String16& cameraId) {
     83         Mutex::Autolock l(mLock);
     84         mCameraTorchStatuses[cameraId] = status;
     85         mTorchCondition.broadcast();
     86         return binder::Status::ok();
     87     };
     88 
     89     bool waitForNumCameras(size_t num) const {
     90         Mutex::Autolock l(mLock);
     91 
     92         if (mCameraStatuses.size() == num) {
     93             return true;
     94         }
     95 
     96         while (mCameraStatuses.size() < num) {
     97             if (mCondition.waitRelative(mLock, SETUP_TIMEOUT) != OK) {
     98                 return false;
     99             }
    100         }
    101         return true;
    102     };
    103 
    104     bool waitForTorchState(int32_t status, int32_t cameraId) const {
    105         Mutex::Autolock l(mLock);
    106 
    107         const auto& iter = mCameraTorchStatuses.find(String16(String8::format("%d", cameraId)));
    108         if (iter != mCameraTorchStatuses.end() && iter->second == status) {
    109             return true;
    110         }
    111 
    112         bool foundStatus = false;
    113         while (!foundStatus) {
    114             if (mTorchCondition.waitRelative(mLock, SETUP_TIMEOUT) != OK) {
    115                 return false;
    116             }
    117             const auto& iter =
    118                     mCameraTorchStatuses.find(String16(String8::format("%d", cameraId)));
    119             foundStatus = (iter != mCameraTorchStatuses.end() && iter->second == status);
    120         }
    121         return true;
    122     };
    123 
    124     int32_t getTorchStatus(int32_t cameraId) const {
    125         Mutex::Autolock l(mLock);
    126         const auto& iter = mCameraTorchStatuses.find(String16(String8::format("%d", cameraId)));
    127         if (iter == mCameraTorchStatuses.end()) {
    128             return hardware::ICameraServiceListener::TORCH_STATUS_UNKNOWN;
    129         }
    130         return iter->second;
    131     };
    132 
    133     int32_t getStatus(const String16& cameraId) const {
    134         Mutex::Autolock l(mLock);
    135         const auto& iter = mCameraStatuses.find(cameraId);
    136         if (iter == mCameraStatuses.end()) {
    137             return hardware::ICameraServiceListener::STATUS_UNKNOWN;
    138         }
    139         return iter->second;
    140     };
    141 };
    142 
    143 // Callback implementation
    144 class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
    145 public:
    146     enum Status {
    147         IDLE,
    148         ERROR,
    149         PREPARED,
    150         RUNNING,
    151         SENT_RESULT,
    152         UNINITIALIZED,
    153         REPEATING_REQUEST_ERROR,
    154         REQUEST_QUEUE_EMPTY,
    155     };
    156 
    157 protected:
    158     bool mError;
    159     int32_t mLastStatus;
    160     mutable std::vector<int32_t> mStatusesHit;
    161     mutable Mutex mLock;
    162     mutable Condition mStatusCondition;
    163 public:
    164     TestCameraDeviceCallbacks() : mError(false), mLastStatus(UNINITIALIZED) {}
    165 
    166     virtual ~TestCameraDeviceCallbacks() {}
    167 
    168     virtual binder::Status onDeviceError(int errorCode,
    169             const CaptureResultExtras& resultExtras) {
    170         (void) resultExtras;
    171         ALOGE("%s: onDeviceError occurred with: %d", __FUNCTION__, static_cast<int>(errorCode));
    172         Mutex::Autolock l(mLock);
    173         mError = true;
    174         mLastStatus = ERROR;
    175         mStatusesHit.push_back(mLastStatus);
    176         mStatusCondition.broadcast();
    177         return binder::Status::ok();
    178     }
    179 
    180     virtual binder::Status onDeviceIdle() {
    181         Mutex::Autolock l(mLock);
    182         mLastStatus = IDLE;
    183         mStatusesHit.push_back(mLastStatus);
    184         mStatusCondition.broadcast();
    185         return binder::Status::ok();
    186     }
    187 
    188     virtual binder::Status onCaptureStarted(const CaptureResultExtras& resultExtras,
    189             int64_t timestamp) {
    190         (void) resultExtras;
    191         (void) timestamp;
    192         Mutex::Autolock l(mLock);
    193         mLastStatus = RUNNING;
    194         mStatusesHit.push_back(mLastStatus);
    195         mStatusCondition.broadcast();
    196         return binder::Status::ok();
    197     }
    198 
    199 
    200     virtual binder::Status onResultReceived(const CameraMetadata& metadata,
    201             const CaptureResultExtras& resultExtras) {
    202         (void) metadata;
    203         (void) resultExtras;
    204         Mutex::Autolock l(mLock);
    205         mLastStatus = SENT_RESULT;
    206         mStatusesHit.push_back(mLastStatus);
    207         mStatusCondition.broadcast();
    208         return binder::Status::ok();
    209     }
    210 
    211     virtual binder::Status onPrepared(int streamId) {
    212         (void) streamId;
    213         Mutex::Autolock l(mLock);
    214         mLastStatus = PREPARED;
    215         mStatusesHit.push_back(mLastStatus);
    216         mStatusCondition.broadcast();
    217         return binder::Status::ok();
    218     }
    219 
    220     virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber) {
    221         (void) lastFrameNumber;
    222         Mutex::Autolock l(mLock);
    223         mLastStatus = REPEATING_REQUEST_ERROR;
    224         mStatusesHit.push_back(mLastStatus);
    225         mStatusCondition.broadcast();
    226         return binder::Status::ok();
    227     }
    228 
    229     virtual binder::Status onRequestQueueEmpty() {
    230         Mutex::Autolock l(mLock);
    231         mLastStatus = REQUEST_QUEUE_EMPTY;
    232         mStatusesHit.push_back(mLastStatus);
    233         mStatusCondition.broadcast();
    234         return binder::Status::ok();
    235     }
    236 
    237     // Test helper functions:
    238 
    239     bool hadError() const {
    240         Mutex::Autolock l(mLock);
    241         return mError;
    242     }
    243 
    244     bool waitForStatus(Status status) const {
    245         Mutex::Autolock l(mLock);
    246         if (mLastStatus == status) {
    247             return true;
    248         }
    249 
    250         while (std::find(mStatusesHit.begin(), mStatusesHit.end(), status)
    251                 == mStatusesHit.end()) {
    252 
    253             if (mStatusCondition.waitRelative(mLock, IDLE_TIMEOUT) != OK) {
    254                 mStatusesHit.clear();
    255                 return false;
    256             }
    257         }
    258         mStatusesHit.clear();
    259 
    260         return true;
    261 
    262     }
    263 
    264     void clearStatus() const {
    265         Mutex::Autolock l(mLock);
    266         mStatusesHit.clear();
    267     }
    268 
    269     bool waitForIdle() const {
    270         return waitForStatus(IDLE);
    271     }
    272 
    273 };
    274 
    275 namespace {
    276     Mutex                     gLock;
    277     class DeathNotifier : public IBinder::DeathRecipient
    278     {
    279     public:
    280         DeathNotifier() {}
    281 
    282         virtual void binderDied(const wp<IBinder>& /*who*/) {
    283             ALOGV("binderDied");
    284             Mutex::Autolock _l(gLock);
    285             ALOGW("Camera service died!");
    286         }
    287     };
    288     sp<DeathNotifier>         gDeathNotifier;
    289 }; // anonymous namespace
    290 
    291 // Exercise basic binder calls for the camera service
    292 TEST(CameraServiceBinderTest, CheckBinderCameraService) {
    293     ProcessState::self()->startThreadPool();
    294     sp<IServiceManager> sm = defaultServiceManager();
    295     sp<IBinder> binder = sm->getService(String16("media.camera"));
    296     ASSERT_NOT_NULL(binder);
    297     if (gDeathNotifier == NULL) {
    298         gDeathNotifier = new DeathNotifier();
    299     }
    300     binder->linkToDeath(gDeathNotifier);
    301     sp<hardware::ICameraService> service =
    302             interface_cast<hardware::ICameraService>(binder);
    303 
    304     binder::Status res;
    305 
    306     int32_t numCameras = 0;
    307     res = service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_ALL, &numCameras);
    308     EXPECT_TRUE(res.isOk()) << res;
    309     EXPECT_LE(0, numCameras);
    310 
    311     // Check listener binder calls
    312     sp<TestCameraServiceListener> listener(new TestCameraServiceListener());
    313     std::vector<hardware::CameraStatus> statuses;
    314     res = service->addListener(listener, &statuses);
    315     EXPECT_TRUE(res.isOk()) << res;
    316 
    317     EXPECT_EQ(numCameras, static_cast<const int>(statuses.size()));
    318 
    319     for (int32_t i = 0; i < numCameras; i++) {
    320         String16 cameraId = String16(String8::format("%d", i));
    321         bool isSupported = false;
    322         res = service->supportsCameraApi(cameraId,
    323                 hardware::ICameraService::API_VERSION_2, &isSupported);
    324         EXPECT_TRUE(res.isOk()) << res;
    325 
    326         // We only care about binder calls for the Camera2 API.  Camera1 is deprecated.
    327         if (!isSupported) {
    328             continue;
    329         }
    330 
    331         // Check metadata binder call
    332         CameraMetadata metadata;
    333         res = service->getCameraCharacteristics(cameraId, &metadata);
    334         EXPECT_TRUE(res.isOk()) << res;
    335         EXPECT_FALSE(metadata.isEmpty());
    336 
    337         // Make sure we're available, or skip device tests otherwise
    338         int32_t s = listener->getStatus(cameraId);
    339         EXPECT_EQ(::android::hardware::ICameraServiceListener::STATUS_PRESENT, s);
    340         if (s != ::android::hardware::ICameraServiceListener::STATUS_PRESENT) {
    341             continue;
    342         }
    343 
    344         // Check connect binder calls
    345         sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
    346         sp<hardware::camera2::ICameraDeviceUser> device;
    347         res = service->connectDevice(callbacks, cameraId, String16("meeeeeeeee!"),
    348                 hardware::ICameraService::USE_CALLING_UID, /*out*/&device);
    349         EXPECT_TRUE(res.isOk()) << res;
    350         ASSERT_NE(nullptr, device.get());
    351         device->disconnect();
    352         EXPECT_FALSE(callbacks->hadError());
    353 
    354         int32_t torchStatus = listener->getTorchStatus(i);
    355         if (torchStatus == hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF) {
    356             // Check torch calls
    357             res = service->setTorchMode(cameraId,
    358                     /*enabled*/true, callbacks);
    359             EXPECT_TRUE(res.isOk()) << res;
    360             EXPECT_TRUE(listener->waitForTorchState(
    361                     hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON, i));
    362             res = service->setTorchMode(cameraId,
    363                     /*enabled*/false, callbacks);
    364             EXPECT_TRUE(res.isOk()) << res;
    365             EXPECT_TRUE(listener->waitForTorchState(
    366                     hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF, i));
    367         }
    368     }
    369 
    370     res = service->removeListener(listener);
    371     EXPECT_TRUE(res.isOk()) << res;
    372 }
    373 
    374 // Test fixture for client focused binder tests
    375 class CameraClientBinderTest : public testing::Test {
    376 protected:
    377     sp<hardware::ICameraService> service;
    378     int32_t numCameras;
    379     std::vector<std::pair<sp<TestCameraDeviceCallbacks>, sp<hardware::camera2::ICameraDeviceUser>>>
    380             openDeviceList;
    381     sp<TestCameraServiceListener> serviceListener;
    382 
    383     std::pair<sp<TestCameraDeviceCallbacks>, sp<hardware::camera2::ICameraDeviceUser>>
    384             openNewDevice(const String16& deviceId) {
    385         sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
    386         sp<hardware::camera2::ICameraDeviceUser> device;
    387         {
    388             SCOPED_TRACE("openNewDevice");
    389             binder::Status res = service->connectDevice(callbacks, deviceId, String16("meeeeeeeee!"),
    390                     hardware::ICameraService::USE_CALLING_UID, /*out*/&device);
    391             EXPECT_TRUE(res.isOk()) << res;
    392         }
    393         auto p = std::make_pair(callbacks, device);
    394         openDeviceList.push_back(p);
    395         return p;
    396     }
    397 
    398     void closeDevice(std::pair<sp<TestCameraDeviceCallbacks>,
    399             sp<hardware::camera2::ICameraDeviceUser>>& p) {
    400         if (p.second.get() != nullptr) {
    401             binder::Status res = p.second->disconnect();
    402             EXPECT_TRUE(res.isOk()) << res;
    403             {
    404                 SCOPED_TRACE("closeDevice");
    405                 EXPECT_FALSE(p.first->hadError());
    406             }
    407         }
    408         auto iter = std::find(openDeviceList.begin(), openDeviceList.end(), p);
    409         if (iter != openDeviceList.end()) {
    410             openDeviceList.erase(iter);
    411         }
    412     }
    413 
    414     virtual void SetUp() {
    415         ProcessState::self()->startThreadPool();
    416         sp<IServiceManager> sm = defaultServiceManager();
    417         sp<IBinder> binder = sm->getService(String16("media.camera"));
    418         service = interface_cast<hardware::ICameraService>(binder);
    419         serviceListener = new TestCameraServiceListener();
    420         std::vector<hardware::CameraStatus> statuses;
    421         service->addListener(serviceListener, &statuses);
    422         service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE,
    423                 &numCameras);
    424     }
    425 
    426     virtual void TearDown() {
    427         service = nullptr;
    428         numCameras = 0;
    429         for (auto& p : openDeviceList) {
    430             closeDevice(p);
    431         }
    432     }
    433 
    434 };
    435 
    436 TEST_F(CameraClientBinderTest, CheckBinderCameraDeviceUser) {
    437     ASSERT_NOT_NULL(service);
    438     EXPECT_TRUE(serviceListener->waitForNumCameras(numCameras));
    439     for (int32_t i = 0; i < numCameras; i++) {
    440         // Make sure we're available, or skip device tests otherwise
    441         String16 cameraId(String8::format("%d",i));
    442         int32_t s = serviceListener->getStatus(cameraId);
    443         EXPECT_EQ(hardware::ICameraServiceListener::STATUS_PRESENT, s);
    444         if (s != hardware::ICameraServiceListener::STATUS_PRESENT) {
    445             continue;
    446         }
    447         binder::Status res;
    448         auto p = openNewDevice(cameraId);
    449         sp<TestCameraDeviceCallbacks> callbacks = p.first;
    450         sp<hardware::camera2::ICameraDeviceUser> device = p.second;
    451 
    452         // Setup a buffer queue; I'm just using the vendor opaque format here as that is
    453         // guaranteed to be present
    454         sp<IGraphicBufferProducer> gbProducer;
    455         sp<IGraphicBufferConsumer> gbConsumer;
    456         BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
    457         sp<BufferItemConsumer> opaqueConsumer = new BufferItemConsumer(gbConsumer,
    458                 GRALLOC_USAGE_SW_READ_NEVER, /*maxImages*/2, /*controlledByApp*/true);
    459         EXPECT_TRUE(opaqueConsumer.get() != nullptr);
    460         opaqueConsumer->setName(String8("nom nom nom"));
    461 
    462         // Set to VGA dimens for default, as that is guaranteed to be present
    463         EXPECT_EQ(OK, gbConsumer->setDefaultBufferSize(640, 480));
    464         EXPECT_EQ(OK, gbConsumer->setDefaultBufferFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED));
    465 
    466         sp<Surface> surface(new Surface(gbProducer, /*controlledByApp*/false));
    467 
    468         OutputConfiguration output(gbProducer, /*rotation*/0);
    469 
    470         // Can we configure?
    471         res = device->beginConfigure();
    472         EXPECT_TRUE(res.isOk()) << res;
    473         status_t streamId;
    474         res = device->createStream(output, &streamId);
    475         EXPECT_TRUE(res.isOk()) << res;
    476         EXPECT_LE(0, streamId);
    477         res = device->endConfigure(/*isConstrainedHighSpeed*/ false);
    478         EXPECT_TRUE(res.isOk()) << res;
    479         EXPECT_FALSE(callbacks->hadError());
    480 
    481         // Can we make requests?
    482         CameraMetadata requestTemplate;
    483         res = device->createDefaultRequest(/*preview template*/1,
    484                 /*out*/&requestTemplate);
    485         EXPECT_TRUE(res.isOk()) << res;
    486 
    487         hardware::camera2::CaptureRequest request;
    488         request.mMetadata = requestTemplate;
    489         request.mSurfaceList.add(surface);
    490         request.mIsReprocess = false;
    491         int64_t lastFrameNumber = 0;
    492         int64_t lastFrameNumberPrev = 0;
    493         callbacks->clearStatus();
    494 
    495         hardware::camera2::utils::SubmitInfo info;
    496         res = device->submitRequest(request, /*streaming*/true, /*out*/&info);
    497         EXPECT_TRUE(res.isOk()) << res;
    498         EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT));
    499         EXPECT_LE(0, info.mRequestId);
    500 
    501         // Can we stop requests?
    502         res = device->cancelRequest(info.mRequestId, /*out*/&lastFrameNumber);
    503         EXPECT_TRUE(res.isOk()) << res;
    504         EXPECT_TRUE(callbacks->waitForIdle());
    505         EXPECT_FALSE(callbacks->hadError());
    506 
    507         // Can we do it again?
    508         lastFrameNumberPrev = info.mLastFrameNumber;
    509         lastFrameNumber = 0;
    510         requestTemplate.clear();
    511         res = device->createDefaultRequest(hardware::camera2::ICameraDeviceUser::TEMPLATE_PREVIEW,
    512                 /*out*/&requestTemplate);
    513         EXPECT_TRUE(res.isOk()) << res;
    514         hardware::camera2::CaptureRequest request2;
    515         request2.mMetadata = requestTemplate;
    516         request2.mSurfaceList.add(surface);
    517         request2.mIsReprocess = false;
    518         callbacks->clearStatus();
    519         hardware::camera2::utils::SubmitInfo info2;
    520         res = device->submitRequest(request2, /*streaming*/true,
    521                 /*out*/&info2);
    522         EXPECT_TRUE(res.isOk()) << res;
    523         EXPECT_EQ(hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES,
    524                 info2.mLastFrameNumber);
    525         lastFrameNumber = 0;
    526         EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT));
    527         EXPECT_LE(0, info2.mRequestId);
    528         res = device->cancelRequest(info2.mRequestId, /*out*/&lastFrameNumber);
    529         EXPECT_TRUE(res.isOk()) << res;
    530         EXPECT_TRUE(callbacks->waitForIdle());
    531         EXPECT_LE(lastFrameNumberPrev, lastFrameNumber);
    532         sleep(/*second*/1); // allow some time for errors to show up, if any
    533         EXPECT_FALSE(callbacks->hadError());
    534 
    535         // Can we do it with a request list?
    536         lastFrameNumberPrev = lastFrameNumber;
    537         lastFrameNumber = 0;
    538         requestTemplate.clear();
    539         CameraMetadata requestTemplate2;
    540         res = device->createDefaultRequest(hardware::camera2::ICameraDeviceUser::TEMPLATE_PREVIEW,
    541                 /*out*/&requestTemplate);
    542         EXPECT_TRUE(res.isOk()) << res;
    543         res = device->createDefaultRequest(hardware::camera2::ICameraDeviceUser::TEMPLATE_PREVIEW,
    544                 /*out*/&requestTemplate2);
    545         EXPECT_TRUE(res.isOk()) << res;
    546         android::hardware::camera2::CaptureRequest request3;
    547         android::hardware::camera2::CaptureRequest request4;
    548         request3.mMetadata = requestTemplate;
    549         request3.mSurfaceList.add(surface);
    550         request3.mIsReprocess = false;
    551         request4.mMetadata = requestTemplate2;
    552         request4.mSurfaceList.add(surface);
    553         request4.mIsReprocess = false;
    554         std::vector<hardware::camera2::CaptureRequest> requestList;
    555         requestList.push_back(request3);
    556         requestList.push_back(request4);
    557 
    558         callbacks->clearStatus();
    559         hardware::camera2::utils::SubmitInfo info3;
    560         res = device->submitRequestList(requestList, /*streaming*/false,
    561                 /*out*/&info3);
    562         EXPECT_TRUE(res.isOk()) << res;
    563         EXPECT_LE(0, info3.mRequestId);
    564         EXPECT_TRUE(callbacks->waitForStatus(TestCameraDeviceCallbacks::SENT_RESULT));
    565         EXPECT_TRUE(callbacks->waitForIdle());
    566         EXPECT_LE(lastFrameNumberPrev, info3.mLastFrameNumber);
    567         sleep(/*second*/1); // allow some time for errors to show up, if any
    568         EXPECT_FALSE(callbacks->hadError());
    569 
    570         // Can we unconfigure?
    571         res = device->beginConfigure();
    572         EXPECT_TRUE(res.isOk()) << res;
    573         res = device->deleteStream(streamId);
    574         EXPECT_TRUE(res.isOk()) << res;
    575         res = device->endConfigure(/*isConstrainedHighSpeed*/ false);
    576         EXPECT_TRUE(res.isOk()) << res;
    577 
    578         sleep(/*second*/1); // allow some time for errors to show up, if any
    579         EXPECT_FALSE(callbacks->hadError());
    580 
    581         closeDevice(p);
    582     }
    583 
    584 };
    585