[安卓相机系列] 一、相机设备的注册发现和状态维护

前言

安卓相机系列连载中……
前文:

疑问

后续文章的组织结构,都会把「疑问」放在前面,希望大家阅读本文时,先对这些问题有个思考,再带着尚未解决的问题继续读下去。

  1. 调用 CameraManager.getCameraIdList() 时,发生了什么?有哪些值得注意的重要过程?
  2. 如果摄像头软/硬件发生故障,此时会发生什么?

约定

为了便于行文,作出如下约定。

关键字约定

若未特定说明,以下简写分别对应的名次如下:

  • CS:CameraServer 或者 CameraService
  • CP:CameraProvider
  • CPM:CameraProviderManager
  • CD:CameraDevice
  • SM:ServiceManager
  • hwSM:Hardware ServiceManager

相机设备的发现与注册

CameraService 与 CameraProvider 的互联建立

前文提到 CameraService 与 CameraProvider 的启动,不免有些疑问:

  1. 它两启动的先后顺序?
  2. 如果 Camera Provider 后启动,Camera Service 如何发现并管理设备?

问题一很好解释,Android,或者说 Kernel 中有个至关重要的进程:init 进程,它是运行在用户空间的第一个进程。安卓中各种 Native Service、大名鼎鼎的 Zygote 都是它创建出来的。它会扫描系统特定目录下所有的 .rc 文件,并挨个解析然后孵化对应进程。

补充:这里指的特定目录,通常是这两个目录:

  • /system/etc/init/
  • /vendor/etc/init

更多资料:https://android.googlesource.com/platform/system/core/+/refs/heads/master/init/README.md

以 CameraServer 为例,如何查询它所对应的 .rc文件?—— 通过它的编译文件 Android.bp。

// $AOSP/frameworks/av/camera/cameraserver/Android.bp
cc_binary {
    name: "cameraserver",
    // ...
    init_rc: ["cameraserver.rc"],
    // ...
}

在 Android.bp 中,如果目标是一个 cc_binrary 类型,就可以通过 init_rc 指定它的 *.rc 文件。最终它会打包到系统镜像内的制定目录下。

Android.bp 文件是安卓 Soong 构建系统支持的一种规定格式文件,有它特定的语法。可以类比理解为 Makefile。

  • 关于 Soong 构建系统:https://ci.android.com/builds/submitted/8655980/linux/latest/view/soong_build.html
  • 关于 Android.bp 的关键字及其语法:https://ci.android.com/builds/latest/branches/aosp-build-tools/targets/linux/view/soong_build.html

一般,我们可以假设这个解析顺序是未定义的,即 CameraServer 和 CameraProvider 之间的启动顺序不存在耦合关系

理想的情况,CameraProvider 先于 CameraServer 启动,后者启动时,由于能获取到对方的实例,自然能发现相机设备。但考虑到它两的启动顺序是没有保障的,我们重点看下 CameraProvider 晚于 CameraServer 启动的情况。

特殊 Case 1:CameraProvider 晚于 CameraServer 启动

前文提到,CS 启动时,有个枚举 CP 的动作 CameraService::enumerateProviders(),从里面看到,实际上 CS 通过类 CameraProviderManager 来发现和管理 CP。

CameraProviderManager::initialize()
status_t CameraProviderManager::initialize(wp<CameraProviderManager::StatusListener> listener,
        ServiceInteractionProxy* proxy) {
    std::lock_guard<std::mutex> lock(mInterfaceMutex);
    if (proxy == nullptr) {
        ALOGE("%s: No valid service interaction proxy provided", __FUNCTION__);
        return BAD_VALUE;
    }
    // 保存listner用于通知CS有关相机设备的事件
    mListener = listener;
    mServiceProxy = proxy;
    mDeviceState = static_cast<hardware::hidl_bitfield<provider::V2_5::DeviceState>>(
        provider::V2_5::DeviceState::NORMAL);
    // Registering will trigger notifications for all already-known providers
    // 注册监听,以便CP启动并注册到hwSM后,CameraProviderManager能及时知道
    bool success = mServiceProxy->registerForNotifications(
        /* instance name, empty means no filter */ "",
        this);
    if (!success) {
        ALOGE("%s: Unable to register with hardware service manager for notifications "
                "about camera providers", __FUNCTION__);
        return INVALID_OPERATION;
    }
    // 获取当前已经注册的CP服务,封装以便CameraProviderManager使用
    // 如果CP启动晚于CS,此处为空!!!!符合预期
    for (const auto& instance : mServiceProxy->listServices()) {
        this->addProviderLocked(instance);
    }
    IPCThreadState::self()->flushCommands();
    return OK;
}

initialize() 参数未显式指定的情况下,默认使用了 sHardwareServiceInteractionProxy,它的类型是 HardwareServiceInteractionProxy

// Standard use case - call into the normal generated static methods which invoke
// the real hardware service manager
struct HardwareServiceInteractionProxy : public ServiceInteractionProxy {
    virtual bool registerForNotifications(
            const std::string &serviceName,
            const sp<hidl::manager::V1_0::IServiceNotification>
            &notification) override {
        // 实现来自于 HIDL 实现的接口
        return hardware::camera::provider::V2_4::ICameraProvider::registerForNotifications(
                serviceName, notification);
    }
    virtual sp<hardware::camera::provider::V2_4::ICameraProvider> tryGetService(
            const std::string &serviceName) override {
        return hardware::camera::provider::V2_4::ICameraProvider::tryGetService(serviceName);
    }
    virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
            const std::string &serviceName) override {
        return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);
    }
    virtual hardware::hidl_vec<hardware::hidl_string> listServices() override;
};

CameraProviderManager 继承自 hidl::manager::V1_0::IServiceNotification,实现了 CameraProviderManager::onRegistration() 接口。

// 当 CP 注册到 hwSM 后,hwSM 将会调用这个方法,通知 CS 有关于 CP 的注册
hardware::Return<void> CameraProviderManager::onRegistration(
        const hardware::hidl_string& /*fqName*/,
        const hardware::hidl_string& name,
        bool preexisting) {
    status_t res = OK;
    std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
    {
        std::lock_guard<std::mutex> lock(mInterfaceMutex);
        // 添加到 CMP 管理的 provider list
        res = addProviderLocked(name, preexisting);
    }
    sp<StatusListener> listener = getStatusListener();
    if (nullptr != listener.get() && res == OK) {
    	// CMP 添加 provider 成功,然后再通知 CS
        listener->onNewProviderRegistered();
    }
    IPCThreadState::self()->flushCommands();
    return hardware::Return<void>();
}

这样通过 ICameraProvider::registerForNotifications(),即可使得 CP 在完成注册时,CS 及时收到回调。

CS 收到 onNewProviderRegistered() 回调后,又会再走一遍 enumerateProviders()

void CameraService::onNewProviderRegistered() {
    enumerateProviders();
}
特殊 Case 2:CameraProvider 在与 CameraServer 建立连接后,发生异常挂掉

得益于 HIDL 机制,这个实现很简单,只需要继承类 hidl_death_recipient 并实现 serviceDied()

// 当 CP 挂掉,hwSM 会通过这个方法通知 CS。
void CameraProviderManager::ProviderInfo::serviceDied(uint64_t cookie,
        const wp<hidl::base::V1_0::IBase>& who) {
    (void) who;
    ALOGI("Camera provider '%s' has died; removing it", mProviderInstance.c_str());
    if (cookie != mId) {
        ALOGW("%s: Unexpected serviceDied cookie %" PRIu64 ", expected %" PRIu32,
                __FUNCTION__, cookie, mId);
    }
    mManager->removeProvider(mProviderInstance);
}
status_t CameraProviderManager::removeProvider(const std::string& provider) {
    std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
    std::unique_lock<std::mutex> lock(mInterfaceMutex);
    std::vector<String8> removedDeviceIds;
    status_t res = NAME_NOT_FOUND;
    std::string removedProviderName;
    // 找到需要移除的 CP,并移除
    for (auto it = mProviders.begin(); it != mProviders.end(); it++) {
        if ((*it)->mProviderInstance == provider) {
            removedDeviceIds.reserve((*it)->mDevices.size());
            for (auto& deviceInfo : (*it)->mDevices) {
                removedDeviceIds.push_back(String8(deviceInfo->mId.c_str()));
            }
            removedProviderName = (*it)->mProviderName;
            mProviders.erase(it);
            res = OK;
            break;
        }
    }
    if (res != OK) {
        ALOGW("%s: Camera provider HAL with name '%s' is not registered", __FUNCTION__,
                provider.c_str());
    } else {
        // Check if there are any newer camera instances from the same provider and try to
        // initialize.
        for (const auto& providerInfo : mProviders) {
            if (providerInfo->mProviderName == removedProviderName) {
                return tryToInitializeProviderLocked(removedProviderName, providerInfo);
            }
        }
        // Inform camera service of loss of presence for all the devices from this provider,
        // without lock held for reentrancy
        sp<StatusListener> listener = getStatusListener();
        if (listener != nullptr) {
        	// 移除锁,为了不阻塞后面 CP 挂掉的事件。例如多个 CP 同时挂掉。
            lock.unlock();
            for (auto& id : removedDeviceIds) {
            	// 通知 CS 有关 CP 挂掉了的事件
                listener->onDeviceStatusChanged(id, CameraDeviceStatus::NOT_PRESENT);
            }
            lock.lock();
        }
    }
    return res;
}

Camera Device 的注册与状态维护

CameraDevice 的注册

在 CP 注册到 hwSM,CS 收到 onRegistration() -> onNewProviderRegistered() 的回调后,将会在 CameraService::enumerateProviders() 中完成 CD 的注册。


status_t CameraService::enumerateProviders() {
    status_t res;
    std::vector<std::string> deviceIds;
    {
        Mutex::Autolock l(mServiceLock);
        if (nullptr == mCameraProviderManager.get()) {
            mCameraProviderManager = new CameraProviderManager();
            res = mCameraProviderManager->initialize(this);
            if (res != OK) {
                ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
                        __FUNCTION__, strerror(-res), res);
                logServiceError(String8::format("Unable to initialize camera provider manager"),
                ERROR_DISCONNECTED);
                return res;
            }
        }
        // 一旦收到 CP 注册的消息,立即初始化 VendorTag,即把 VendorTag 缓存到 CS
        // Setup vendor tags before we call get_camera_info the first time
        // because HAL might need to setup static vendor keys in get_camera_info
        // TODO: maybe put this into CameraProviderManager::initialize()?
        mCameraProviderManager->setUpVendorTags();
        if (nullptr == mFlashlight.get()) {
            mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
        }
        res = mFlashlight->findFlashUnits();
        if (res != OK) {
            ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
        }
        // 获取所有 CameraId,会把所有 CP 的 cameraId 都加进来
        // TODO:这里存在一个问题,如果多个 CP 存在相同的 cameraId,会出现异常。
        deviceIds = mCameraProviderManager->getCameraDeviceIds();
    }
    for (auto& cameraId : deviceIds) {
        String8 id8 = String8(cameraId.c_str());
        if (getCameraState(id8) == nullptr) {
            onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT);
        }
    }
    // Derive primary rear/front cameras, and filter their charactierstics.
    // This needs to be done after all cameras are enumerated and camera ids are sorted.
    if (SessionConfigurationUtils::IS_PERF_CLASS) {
        // Assume internal cameras are advertised from the same
        // provider. If multiple providers are registered at different time,
        // and each provider contains multiple internal color cameras, the current
        // logic may filter the characteristics of more than one front/rear color
        // cameras.
        Mutex::Autolock l(mServiceLock);
        filterSPerfClassCharacteristicsLocked();
    }
    return OK;
}
CameraService VendorTag 的初始化
status_t CameraProviderManager::setUpVendorTags() {
	// 通过 VendorTagDescriptorCache 来管理全部的 VendorTag
    sp<VendorTagDescriptorCache> tagCache = new VendorTagDescriptorCache();
    for (auto& provider : mProviders) {
        tagCache->addVendorDescriptor(provider->mProviderTagid, provider->mVendorTagDescriptor);
    }
    VendorTagDescriptorCache::setAsGlobalVendorTagCache(tagCache);
    return OK;
}

其中,mVendorTagDescriptor 的初始化是在 CP 的注册阶段。调用栈:CameraProviderManager::onRegistration()(CS 先于 CP 启动) / CameraProviderManager::initialize()(CP 先于 CS 启动并注册完成) -> CameraProviderManager::addProviderLocked() -> CameraProviderManager::tryToInitializeProviderLocked() -> CameraProviderManager::ProviderInfo::initialize() -> CameraProviderManager::ProviderInfo::setUpVendorTags()

onDeviceStatusChanged() && updateStatus()

CD 的状态通过 CameraService::onDeviceStatusChanged() 完成。

void CameraService::onDeviceStatusChanged(const String8& id,
        CameraDeviceStatus newHalStatus) {
    ALOGI("%s: Status changed for cameraId=%s, newStatus=%d", __FUNCTION__,
            id.string(), newHalStatus);
    // CameraDeviceStatus 是 HAL 定义的相机设备状态,需要转换成 CS 内部使用的值
    StatusInternal newStatus = mapToInternal(newHalStatus);
    // 获取当前 cameraId 对应的相机状态
    std::shared_ptr<CameraState> state = getCameraState(id);
    if (state == nullptr) {
    	// 如果当前 cameraId 未添加过,并且本次状态为 PRESENT,那么添加到 CS,并通知
        if (newStatus == StatusInternal::PRESENT) {
            ALOGI("%s: Unknown camera ID %s, a new camera is added",
                    __FUNCTION__, id.string());
            // First add as absent to make sure clients are notified below
            addStates(id);
            updateStatus(newStatus, id);
        } else {
            ALOGE("%s: Bad camera ID %s", __FUNCTION__, id.string());
        }
        return;
    }
    StatusInternal oldStatus = state->getStatus();
    // 避免状态被重复通知
    if (oldStatus == newStatus) {
        ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);
        return;
    }
    // 如果 cameraId 对应的 CameraDevice 掉线了,移除缓存的 Parameters
    if (newStatus == StatusInternal::NOT_PRESENT) {
        logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
                newStatus));
        // Set the device status to NOT_PRESENT, clients will no longer be able to connect
        // to this device until the status changes
        updateStatus(StatusInternal::NOT_PRESENT, id);
        sp<BasicClient> clientToDisconnectOnline, clientToDisconnectOffline;
        {
            // Don't do this in updateStatus to avoid deadlock over mServiceLock
            Mutex::Autolock lock(mServiceLock);
            // Remove cached shim parameters
            state->setShimParams(CameraParameters());
            // Remove online as well as offline client from the list of active clients,
            // if they are present
            clientToDisconnectOnline = removeClientLocked(id);
            clientToDisconnectOffline = removeClientLocked(kOfflineDevice + id);
        }
        disconnectClient(id, clientToDisconnectOnline);
        disconnectClient(kOfflineDevice + id, clientToDisconnectOffline);
        removeStates(id);
    } else {
        if (oldStatus == StatusInternal::NOT_PRESENT) {
            logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
                    newStatus));
        }
        updateStatus(newStatus, id);
    }
}
真正像上层通知状态,是通过 `updateStatus()` 。
```cpp
void CameraService::updateStatus(StatusInternal status, const String8& cameraId) {
    updateStatus(status, cameraId, {});
}
void CameraService::updateStatus(StatusInternal status, const String8& cameraId,
        std::initializer_list<StatusInternal> rejectSourceStates) {
    // 状态已更新,则必定能获取到 CameraState,否则是异常,直接返回。
    // Do not lock mServiceLock here or can get into a deadlock from
    // connect() -> disconnect -> updateStatus
    auto state = getCameraState(cameraId);
    if (state == nullptr) {
        ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__,
                cameraId.string());
        return;
    }
    // Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
    SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
    if (getSystemCameraKind(cameraId, &deviceKind) != OK) {
        ALOGE("%s: Invalid camera id %s, skipping", __FUNCTION__, cameraId.string());
        return;
    }
    // Collect the logical cameras without holding mStatusLock in updateStatus
    // as that can lead to a deadlock(b/162192331).
    auto logicalCameraIds = getLogicalCameras(cameraId);
    // Update the status for this camera state, then send the onStatusChangedCallbacks to each
    // of the listeners with both the mStatusLock and mStatusListenerLock held
    state->updateStatus(status, cameraId, rejectSourceStates, [this, &deviceKind,
                        &logicalCameraIds]
            (const String8& cameraId, StatusInternal status) {
            if (status != StatusInternal::ENUMERATING) {
                // Update torch status if it has a flash unit.
                Mutex::Autolock al(mTorchStatusMutex);
                TorchModeStatus torchStatus;
                if (getTorchStatusLocked(cameraId, &torchStatus) !=
                        NAME_NOT_FOUND) {
                    TorchModeStatus newTorchStatus =
                            status == StatusInternal::PRESENT ?
                            TorchModeStatus::AVAILABLE_OFF :
                            TorchModeStatus::NOT_AVAILABLE;
                    if (torchStatus != newTorchStatus) {
                    	// 通知闪光灯状态变化
                        onTorchStatusChangedLocked(cameraId, newTorchStatus, deviceKind);
                    }
                }
            }
            Mutex::Autolock lock(mStatusListenerLock);
            notifyPhysicalCameraStatusLocked(mapToInterface(status), String16(cameraId),
                    logicalCameraIds, deviceKind);
            for (auto& listener : mListenerList) {
                bool isVendorListener = listener->isVendorListener();
                if (shouldSkipStatusUpdates(deviceKind, isVendorListener,
                        listener->getListenerPid(), listener->getListenerUid()) ||
                        isVendorListener) {
                    ALOGV("Skipping discovery callback for system-only camera device %s",
                            cameraId.c_str());
                    continue;
                }
                // 通知上层观察者相机的状态。
                listener->getListener()->onStatusChanged(mapToInterface(status),
                        String16(cameraId));
            }
        });
}

listener 通过 CameraService::addListener 注册,这期间有一次 AIDL 调用。
调用栈:(Java Begin)CameraManager.connectCameraServiceLocked() (Java End)-> (Native Begin)CameraService::addListener() -> CameraService::addListenerHelper() (Native End)

关于 AIDL 通信,亦或者是 HIDL 通信的细节,内容较为复杂。此处不作细说。

所以每当有一个应用,通过 Android SDK 接口使用相机时,就会创建一个 listener 并注册到 CS。相机设备的状态,通过该 listener callback 回应用。

QA

  1. 为什么 CS 中,CP 注册时,VendorTag 的初始化要先于 CD 的状态回调?
    答:为了确保上层调用 CameraManager.getCameraCharacteristic() / Camera.getCameraInfo() 时,能获取到全部的 Metadata / VendorTag。

技巧

  1. 如何判断 AIDL 调用、 HIDL 调用或者是普通函数?
    除了通过 AIDL、HIDL 等接口文件外,还可以通过实现文件中的返回值。以 C++ 源文件为例。
  • 返回值为 hardware::Return<T> 类型的函数,一般为 HIDL 接口。
  • 返回值为 binder:Status 类型的函数,一般为 AIDL 接口。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值