Camera: various external camera fixes

1. Add EXIF MAKE/MODEL tag
2. Add retry loop for V4L2 open failure
3. Do not add external camera if ExternalCameraDevice cannot be
   initialized

Bug: 72261912
Bug: 72569850
Change-Id: I06df1fbbb4afabea1a9a74aca9e288b24966cb0b
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index 6b05d4a..ee7ffaa 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -42,6 +42,9 @@
     V4L2_PIX_FMT_MJPEG
 }}; // double braces required in C++11
 
+constexpr int MAX_RETRY = 5; // Allow retry v4l2 open failures a few times.
+constexpr int OPEN_RETRY_SLEEP_US = 100000; // 100ms * MAX_RETRY = 0.5 seconds
+
 } // anonymous namespace
 
 ExternalCameraDevice::ExternalCameraDevice(
@@ -122,11 +125,22 @@
 
     unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
     if (fd.get() < 0) {
-        ALOGE("%s: v4l2 device open %s failed: %s",
-                __FUNCTION__, mCameraId.c_str(), strerror(errno));
-        mLock.unlock();
-        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
-        return Void();
+        int numAttempt = 0;
+        do {
+            ALOGW("%s: v4l2 device %s open failed, wait 33ms and try again",
+                    __FUNCTION__, mCameraId.c_str());
+            usleep(OPEN_RETRY_SLEEP_US); // sleep and try again
+            fd.reset(::open(mCameraId.c_str(), O_RDWR));
+            numAttempt++;
+        } while (fd.get() < 0 && numAttempt <= MAX_RETRY);
+
+        if (fd.get() < 0) {
+            ALOGE("%s: v4l2 device open %s failed: %s",
+                    __FUNCTION__, mCameraId.c_str(), strerror(errno));
+            mLock.unlock();
+            _hidl_cb(Status::INTERNAL_ERROR, nullptr);
+            return Void();
+        }
     }
 
     session = new ExternalCameraDeviceSession(
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 0ede889..08c4947 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -49,7 +49,7 @@
                                        // method
 constexpr int MAX_RETRY = 15; // Allow retry some ioctl failures a few times to account for some
                              // webcam showing temporarily ioctl failures.
-constexpr int IOCTL_RETRY_SLEEP_US = 33000; // 33ms * MAX_RETRY = 5 seconds
+constexpr int IOCTL_RETRY_SLEEP_US = 33000; // 33ms * MAX_RETRY = 0.5 seconds
 
 // Constants for tryLock during dumpstate
 static constexpr int kDumpLockRetries = 50;
@@ -115,6 +115,35 @@
         return true;
     }
 
+    struct v4l2_capability capability;
+    int ret = ioctl(mV4l2Fd.get(), VIDIOC_QUERYCAP, &capability);
+    std::string make, model;
+    if (ret < 0) {
+        ALOGW("%s v4l2 QUERYCAP failed", __FUNCTION__);
+        make = "Generic UVC webcam";
+        model = "Generic UVC webcam";
+    } else {
+        // capability.card is UTF-8 encoded
+        char card[32];
+        int j = 0;
+        for (int i = 0; i < 32; i++) {
+            if (capability.card[i] < 128) {
+                card[j++] = capability.card[i];
+            }
+            if (capability.card[i] == '\0') {
+                break;
+            }
+        }
+        if (j == 0 || card[j - 1] != '\0') {
+            make = "Generic UVC webcam";
+            model = "Generic UVC webcam";
+        } else {
+            make = card;
+            model = card;
+        }
+    }
+    mOutputThread->setExifMakeModel(make, model);
+
     status_t status = initDefaultRequests();
     if (status != OK) {
         ALOGE("%s: init default requests failed!", __FUNCTION__);
@@ -826,6 +855,12 @@
 
 ExternalCameraDeviceSession::OutputThread::~OutputThread() {}
 
+void ExternalCameraDeviceSession::OutputThread::setExifMakeModel(
+        const std::string& make, const std::string& model) {
+    mExifMake = make;
+    mExifModel = model;
+}
+
 uint32_t ExternalCameraDeviceSession::OutputThread::getFourCcFromLayout(
         const YCbCrLayout& layout) {
     intptr_t cb = reinterpret_cast<intptr_t>(layout.cb);
@@ -1611,6 +1646,8 @@
     utils->initialize();
 
     utils->setFromMetadata(meta, jpegSize.width, jpegSize.height);
+    utils->setMake(mExifMake);
+    utils->setModel(mExifModel);
 
     ret = utils->generateApp1(outputThumbnail ? &thumbCode[0] : 0, thumbCodeSize);
 
@@ -2150,7 +2187,7 @@
         int numAttempt = 0;
         while (ret < 0) {
             ALOGW("%s: VIDIOC_S_FMT failed, wait 33ms and try again", __FUNCTION__);
-            usleep(IOCTL_RETRY_SLEEP_US); // sleep 100 ms and try again
+            usleep(IOCTL_RETRY_SLEEP_US); // sleep and try again
             ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_FMT, &fmt));
             if (numAttempt == MAX_RETRY) {
                 break;
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 9843a50..a867a48 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -230,6 +230,7 @@
         void dump(int fd);
         virtual bool threadLoop() override;
 
+        void setExifMakeModel(const std::string& make, const std::string& model);
     private:
         static const uint32_t FLEX_YUV_GENERIC = static_cast<uint32_t>('F') |
                 static_cast<uint32_t>('L') << 8 | static_cast<uint32_t>('E') << 16 |
@@ -287,6 +288,9 @@
         std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
         YCbCrLayout mYu12FrameLayout;
         YCbCrLayout mYu12ThumbFrameLayout;
+
+        std::string mExifMake;
+        std::string mExifModel;
     };
 
     // Protect (most of) HIDL interface methods from synchronized-entering
diff --git a/camera/provider/2.4/default/ExternalCameraProvider.cpp b/camera/provider/2.4/default/ExternalCameraProvider.cpp
index faa4e3a..c98a432 100644
--- a/camera/provider/2.4/default/ExternalCameraProvider.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProvider.cpp
@@ -161,29 +161,35 @@
 }
 
 void ExternalCameraProvider::deviceAdded(const char* devName) {
-    int fd = -1;
-    if ((fd = ::open(devName, O_RDWR)) < 0) {
-        ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
-        return;
-    }
+    {
+        base::unique_fd fd(::open(devName, O_RDWR));
+        if (fd.get() < 0) {
+            ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
+            return;
+        }
 
-    do {
         struct v4l2_capability capability;
-        int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
+        int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability);
         if (ret < 0) {
             ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
-            break;
+            return;
         }
 
         if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
             ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName);
-            break;
+            return;
         }
+    }
+    // See if we can initialize ExternalCameraDevice correctly
+    sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl =
+            new device::V3_4::implementation::ExternalCameraDevice(devName, mCfg);
+    if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
+        ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName);
+        return;
+    }
+    deviceImpl.clear();
 
-        addExternalCamera(devName);
-    } while (0);
-
-    close(fd);
+    addExternalCamera(devName);
     return;
 }