Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29e2e151 authored by Valentin Iftime's avatar Valentin Iftime
Browse files

Add support for external lazy camera HALs

 The lazy camera provider HAL will be started when
 a device is attached (USB hotplug) or opened and
 then shut down when there are no active clients using it.
 When a USB device is unplugged, the provider removes the camera
 devices associated with it.

Test: atest CtsCameraTestCases
Test: atest cameraservice_test
Test: -Boot device with/without camera attached.
      -(plug-in camera)
      -Open app that uses camera
      -adb shell ps | grep camera
        -> shows "android.hardware.camera.provider@2.7-external-service-lazy"
      - Close app and wait 5-10 seconds
      - adb shell ps | grep camera
       -> does not show "android.hardware.camera.provider@2.7-external-service-lazy"

Bug: 191248460

Change-Id: I5eba8392f17919ed26c1d888cedb4c5428e881c4
parent 63697e06
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -187,6 +187,8 @@ interface ICameraService
     */
    const int EVENT_NONE = 0;
    const int EVENT_USER_SWITCHED = 1; // The argument is the set of new foreground user IDs.
    const int EVENT_USB_DEVICE_ATTACHED = 2; // The argument is the deviceId and vendorId
    const int EVENT_USB_DEVICE_DETACHED = 3; // The argument is the deviceId and vendorId
    oneway void notifySystemEvent(int eventId, in int[] args);

    /**
+7 −0
Original line number Diff line number Diff line
@@ -2333,6 +2333,13 @@ Status CameraService::notifySystemEvent(int32_t eventId,
            doUserSwitch(/*newUserIds*/ args);
            break;
        }
        case ICameraService::EVENT_USB_DEVICE_ATTACHED:
        case ICameraService::EVENT_USB_DEVICE_DETACHED: {
            // Notify CameraProviderManager for lazy HALs
            mCameraProviderManager->notifyUsbDeviceEvent(eventId,
                                                        std::to_string(args[0]));
            break;
        }
        case ICameraService::EVENT_NONE:
        default: {
            ALOGW("%s: Received invalid system event from system_server: %d", __FUNCTION__,
+174 −25
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ using hardware::camera::provider::V2_7::CameraIdAndStreamCombination;

namespace {
const bool kEnableLazyHal(property_get_bool("ro.camera.enableLazyHal", false));
const std::string kExternalProviderName = "external/0";
} // anonymous namespace

const float CameraProviderManager::kDepthARTolerance = .1f;
@@ -395,6 +396,73 @@ status_t CameraProviderManager::setUpVendorTags() {
    return OK;
}

sp<CameraProviderManager::ProviderInfo> CameraProviderManager::startExternalLazyProvider() const {
    std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
    std::lock_guard<std::mutex> lock(mInterfaceMutex);

    for (const auto& providerInfo : mProviders) {
        if (providerInfo->isExternalLazyHAL()) {
            const sp<provider::V2_4::ICameraProvider>
                  interface = providerInfo->startProviderInterface();
            if (interface == nullptr) {
                return nullptr;
            } else {
                return providerInfo;
            }
        }
    }
    return nullptr;
}

status_t CameraProviderManager::notifyUsbDeviceEvent(int32_t eventId,
                                                     const std::string& usbDeviceId) {
    if (!kEnableLazyHal) {
        return OK;
    }

    ALOGV("notifySystemEvent: %d usbDeviceId : %s", eventId, usbDeviceId.c_str());

    if (eventId == android::hardware::ICameraService::EVENT_USB_DEVICE_ATTACHED) {
        sp<ProviderInfo> externalProvider = startExternalLazyProvider();
        if (externalProvider != nullptr) {
            auto usbDevices = mExternalUsbDevicesForProvider.first;
            usbDevices.push_back(usbDeviceId);
            mExternalUsbDevicesForProvider = {usbDevices, externalProvider};
        }
    } else if (eventId
          == android::hardware::ICameraService::EVENT_USB_DEVICE_DETACHED) {
        usbDeviceDetached(usbDeviceId);
    }

    return OK;
}

status_t CameraProviderManager::usbDeviceDetached(const std::string &usbDeviceId) {
    std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
    std::lock_guard<std::mutex> interfaceLock(mInterfaceMutex);

    auto usbDevices = mExternalUsbDevicesForProvider.first;
    auto foundId = std::find(usbDevices.begin(), usbDevices.end(), usbDeviceId);
    if (foundId != usbDevices.end()) {
        sp<ProviderInfo> providerInfo = mExternalUsbDevicesForProvider.second;
        if (providerInfo == nullptr) {
              ALOGE("%s No valid external provider for USB device: %s",
                    __FUNCTION__,
                    usbDeviceId.c_str());
              mExternalUsbDevicesForProvider = {std::vector<std::string>(), nullptr};
              return DEAD_OBJECT;
        } else {
            mInterfaceMutex.unlock();
            providerInfo->removeAllDevices();
            mInterfaceMutex.lock();
            mExternalUsbDevicesForProvider = {std::vector<std::string>(), nullptr};
        }
    } else {
        return DEAD_OBJECT;
    }
    return OK;
}

status_t CameraProviderManager::notifyDeviceStateChange(
        hardware::hidl_bitfield<provider::V2_5::DeviceState> newState) {
    std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -1314,7 +1382,8 @@ status_t CameraProviderManager::addProviderLocked(const std::string& newProvider
        if (providerInfo->mProviderName == newProvider) {
            ALOGW("%s: Camera provider HAL with name '%s' already registered",
                    __FUNCTION__, newProvider.c_str());
            if (preexisting) {
            // Do not add new instances for lazy HAL external provider
            if (preexisting || providerInfo->isExternalLazyHAL()) {
                return ALREADY_EXISTS;
            } else {
                ALOGW("%s: The new provider instance will get initialized immediately after the"
@@ -1606,13 +1675,30 @@ CameraProviderManager::ProviderInfo::startProviderInterface() {

    auto interface = mActiveInterface.promote();
    if (interface == nullptr) {
        ALOGI("Camera HAL provider needs restart, calling getService(%s)", mProviderName.c_str());
        // Try to get service without starting
        interface = mManager->mServiceProxy->tryGetService(mProviderName);
        if (interface == nullptr) {
            ALOGV("Camera provider actually needs restart, calling getService(%s)",
                  mProviderName.c_str());
            interface = mManager->mServiceProxy->getService(mProviderName);

            // Set all devices as ENUMERATING, provider should update status
            // to PRESENT after initializing.
            // This avoids failing getCameraDeviceInterface_V3_x before devices
            // are ready.
            for (auto& device : mDevices) {
              device->mIsDeviceAvailable = false;
            }

            interface->setCallback(this);
        hardware::Return<bool> linked = interface->linkToDeath(this, /*cookie*/ mId);
            hardware::Return<bool>
                linked = interface->linkToDeath(this, /*cookie*/ mId);
            if (!linked.isOk()) {
            ALOGE("%s: Transaction error in linking to camera provider '%s' death: %s",
                    __FUNCTION__, mProviderName.c_str(), linked.description().c_str());
              ALOGE(
                  "%s: Transaction error in linking to camera provider '%s' death: %s",
                  __FUNCTION__,
                  mProviderName.c_str(),
                  linked.description().c_str());
              mManager->removeProvider(mProviderName);
              return nullptr;
            } else if (!linked) {
@@ -1621,7 +1707,8 @@ CameraProviderManager::ProviderInfo::startProviderInterface() {
            }
            // Send current device state
            if (mMinorVersion >= 5) {
            auto castResult = provider::V2_5::ICameraProvider::castFrom(interface);
              auto castResult =
                  provider::V2_5::ICameraProvider::castFrom(interface);
              if (castResult.isOk()) {
                sp<provider::V2_5::ICameraProvider> interface_2_5 = castResult;
                if (interface_2_5 != nullptr) {
@@ -1631,11 +1718,13 @@ CameraProviderManager::ProviderInfo::startProviderInterface() {
                }
              }
            }

        }
        mActiveInterface = interface;
    } else {
        ALOGV("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str());
        ALOGV("Camera provider (%s) already in use. Re-using instance.",
              mProviderName.c_str());
    }

    return interface;
}

@@ -1713,12 +1802,47 @@ void CameraProviderManager::ProviderInfo::removeDevice(std::string id) {
                    mUniqueAPI1CompatibleCameraIds.begin(),
                    mUniqueAPI1CompatibleCameraIds.end(), id));
            }

            // Remove reference to camera provider to avoid pointer leak when
            // unplugging external camera while in use with lazy HALs
            mManager->removeRef(DeviceMode::CAMERA, id);
            mManager->removeRef(DeviceMode::TORCH, id);

            mDevices.erase(it);
            break;
        }
    }
}

void CameraProviderManager::ProviderInfo::removeAllDevices() {
    std::lock_guard<std::mutex> lock(mLock);

    auto itDevices = mDevices.begin();
    while (itDevices != mDevices.end()) {
        std::string id = (*itDevices)->mId;
        std::string deviceName = (*itDevices)->mName;
        removeDevice(id);
        // device was removed, reset iterator
        itDevices = mDevices.begin();

        //notify CameraService of status change
        sp<StatusListener> listener = mManager->getStatusListener();
        if (listener != nullptr) {
            mLock.unlock();
            ALOGV("%s: notify device not_present: %s",
                  __FUNCTION__,
                  deviceName.c_str());
            listener->onDeviceStatusChanged(String8(id.c_str()),
                                            CameraDeviceStatus::NOT_PRESENT);
            mLock.lock();
        }
    }
}

bool CameraProviderManager::ProviderInfo::isExternalLazyHAL() const {
    return kEnableLazyHal && (mProviderName == kExternalProviderName);
}

status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector<String16>&) const {
    dprintf(fd, "== Camera Provider HAL %s (v2.%d, %s) static info: %zu devices: ==\n",
            mProviderInstance.c_str(),
@@ -1898,12 +2022,16 @@ status_t CameraProviderManager::ProviderInfo::cameraDeviceStatusChangeLocked(
    std::string cameraId;
    for (auto& deviceInfo : mDevices) {
        if (deviceInfo->mName == cameraDeviceName) {
            Mutex::Autolock l(deviceInfo->mDeviceAvailableLock);
            ALOGI("Camera device %s status is now %s, was %s", cameraDeviceName.c_str(),
                    deviceStatusToString(newStatus), deviceStatusToString(deviceInfo->mStatus));
            deviceInfo->mStatus = newStatus;
            // TODO: Handle device removal (NOT_PRESENT)
            cameraId = deviceInfo->mId;
            known = true;
            deviceInfo->mIsDeviceAvailable =
                (newStatus == CameraDeviceStatus::PRESENT);
            deviceInfo->mDeviceAvailableSignal.signal();
            break;
        }
    }
@@ -1917,6 +2045,11 @@ status_t CameraProviderManager::ProviderInfo::cameraDeviceStatusChangeLocked(
        addDevice(cameraDeviceName, newStatus, &cameraId);
    } else if (newStatus == CameraDeviceStatus::NOT_PRESENT) {
        removeDevice(cameraId);
    } else if (isExternalLazyHAL()) {
        // Do not notify CameraService for PRESENT->PRESENT (lazy HAL restart)
        // because NOT_AVAILABLE is set on CameraService::connect and a PRESENT
        // notif. would overwrite it
        return BAD_VALUE;
    }
    if (reCacheConcurrentStreamingCameraIdsLocked() != OK) {
        ALOGE("%s: CameraProvider %s could not re-cache concurrent streaming camera id list ",
@@ -2297,11 +2430,27 @@ CameraProviderManager::ProviderInfo::DeviceInfo::~DeviceInfo() {}

template<class InterfaceT>
sp<InterfaceT> CameraProviderManager::ProviderInfo::DeviceInfo::startDeviceInterface() {
    Mutex::Autolock l(mDeviceAvailableLock);
    sp<InterfaceT> device;
    ATRACE_CALL();
    if (mSavedInterface == nullptr) {
        sp<ProviderInfo> parentProvider = mParentProvider.promote();
        if (parentProvider != nullptr) {
            // Wait for lazy HALs to confirm device availability
            if (parentProvider->isExternalLazyHAL() && !mIsDeviceAvailable) {
                ALOGV("%s: Wait for external device to become available %s",
                      __FUNCTION__,
                      mId.c_str());

                auto res = mDeviceAvailableSignal.waitRelative(mDeviceAvailableLock,
                                                         kDeviceAvailableTimeout);
                if (res != OK) {
                    ALOGE("%s: Failed waiting for device to become available",
                          __FUNCTION__);
                    return nullptr;
                }
            }

            device = parentProvider->startDeviceInterface<InterfaceT>(mName);
        }
    } else {
+29 −0
Original line number Diff line number Diff line
@@ -29,7 +29,9 @@
#include <camera/CameraParameters2.h>
#include <camera/CameraMetadata.h>
#include <camera/CameraBase.h>
#include <utils/Condition.h>
#include <utils/Errors.h>
#include <android/hardware/ICameraService.h>
#include <android/hardware/camera/common/1.0/types.h>
#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
#include <android/hardware/camera/provider/2.6/ICameraProviderCallback.h>
@@ -357,6 +359,8 @@ public:

    status_t filterSmallJpegSizes(const std::string& cameraId);

    status_t notifyUsbDeviceEvent(int32_t eventId, const std::string &usbDeviceId);

    static const float kDepthARTolerance;
private:
    // All private members, unless otherwise noted, expect mInterfaceMutex to be locked before use
@@ -487,6 +491,17 @@ private:
                                &halCameraIdsAndStreamCombinations,
                bool *isSupported);

        /**
         * Remove all devices associated with this provider and notify listeners
         * with NOT_PRESENT state.
         */
        void removeAllDevices();

        /**
         * Provider is an external lazy HAL
         */
        bool isExternalLazyHAL() const;

        // Basic device information, common to all camera devices
        struct DeviceInfo {
            const std::string mName;  // Full instance name
@@ -510,6 +525,12 @@ private:
            int32_t mTorchMaximumStrengthLevel;
            int32_t mTorchDefaultStrengthLevel;

            // Wait for lazy HALs to confirm device availability
            static const nsecs_t kDeviceAvailableTimeout = 2000e6; // 2000 ms
            Mutex     mDeviceAvailableLock;
            Condition mDeviceAvailableSignal;
            bool mIsDeviceAvailable = true;

            bool hasFlashUnit() const { return mHasFlashUnit; }
            bool supportNativeZoomRatio() const { return mSupportNativeZoomRatio; }
            virtual status_t setTorchMode(bool enabled) = 0;
@@ -734,6 +755,12 @@ private:
            hardware::hidl_version minVersion = hardware::hidl_version{0,0},
            hardware::hidl_version maxVersion = hardware::hidl_version{1000,0}) const;

    // Map external providers to USB devices in order to handle USB hotplug
    // events for lazy HALs
    std::pair<std::vector<std::string>, sp<ProviderInfo>>
        mExternalUsbDevicesForProvider;
    sp<ProviderInfo> startExternalLazyProvider() const;

    status_t addProviderLocked(const std::string& newProvider, bool preexisting = false);

    status_t tryToInitializeProviderLocked(const std::string& providerName,
@@ -781,6 +808,8 @@ private:
              hardware::hidl_vec<hardware::camera::provider::V2_7::CameraIdAndStreamCombination>
                      *halCameraIdsAndStreamCombinations,
              bool *earlyExit);

    status_t usbDeviceDetached(const std::string &usbDeviceId);
};

} // namespace android