1. 程式人生 > >Android 8.0系統原始碼分析--openCamera(HAL)啟動過程原始碼分析

Android 8.0系統原始碼分析--openCamera(HAL)啟動過程原始碼分析

     前面我們詳細分析了從應用層呼叫CameraManager的openCamera的方法來開啟相機的邏輯,上次的分析我們來到了CameraServer程序當中,但是還沒有真正看到open操作裝置節點來實現真正開啟的邏輯,遺留的問題也就是從frameworks\av\services\camera\libcameraservice\device3\Camera3Device.cpp檔案中的status_t Camera3Device::initialize(sp<CameraProviderManager> manager)方法的status_t res = manager->openSession(mId.string(), this, /*out*/ &session);這句程式碼進去後進行的處理。

     我們知道,Android的核心是Linux核心,Linux上所有的裝置都被抽象成檔案節點,對裝置的操作都變成了對檔案節點的操作,非常方便,我們openCamera的邏輯最終肯定也是通過open系統函式來開啟camera的檔案節點,而open系統呼叫的處理就是對應的camera驅動了。

     好,我們回到正文,status_t res = manager->openSession(mId.string(), this, /*out*/ &session);這句邏輯中的manager是一個sp<CameraProviderManager>物件,那我們就來看一下frameworks\av\services\camera\libcameraservice\common\CameraProviderManager.cpp檔案中的openSession方法的實現,原始碼如下:

status_t CameraProviderManager::openSession(const std::string &id,
        const sp<hardware::camera::device::V3_2::ICameraDeviceCallback>& callback,
        /*out*/
        sp<hardware::camera::device::V3_2::ICameraDeviceSession> *session) {

    std::lock_guard<std::mutex> lock(mInterfaceMutex);

    auto deviceInfo = findDeviceInfoLocked(id,
            /*minVersion*/ {3,0}, /*maxVersion*/ {4,0});
    if (deviceInfo == nullptr) return NAME_NOT_FOUND;

    auto *deviceInfo3 = static_cast<ProviderInfo::DeviceInfo3*>(deviceInfo);

    Status status;
    hardware::Return<void> ret;
    ret = deviceInfo3->mInterface->open(callback, [&status, &session]
            (Status s, const sp<device::V3_2::ICameraDeviceSession>& cameraSession) {
                status = s;
                if (status == Status::OK) {
                    *session = cameraSession;
                }
            });
    if (!ret.isOk()) {
        ALOGE("%s: Transaction error opening a session for camera device %s: %s",
                __FUNCTION__, id.c_str(), ret.description().c_str());
        return DEAD_OBJECT;
    }
    return mapToStatusT(status);
}

     該方法中根據傳入的最小和最大版本號呼叫findDeviceInfoLocked方法獲取到一個DeviceInfo物件,其中的邏輯比較簡單,原始碼如下:

CameraProviderManager::ProviderInfo::DeviceInfo* CameraProviderManager::findDeviceInfoLocked(
        const std::string& id,
        hardware::hidl_version minVersion, hardware::hidl_version maxVersion) const {
    for (auto& provider : mProviders) {
        for (auto& deviceInfo : provider->mDevices) {
            if (deviceInfo->mId == id &&
                    minVersion <= deviceInfo->mVersion && maxVersion >= deviceInfo->mVersion) {
                return deviceInfo.get();
            }
        }
    }
    return nullptr;
}

     可以看到,該方法的邏輯就是對成員變數mProviders進行遍歷,判斷每個DeviceInfo的id值、最小版本、最大版本號是否符合傳入的最小和最大版本,符合的話,就返回該物件,那我們就要問一下了,mProviders中的值是什麼時候新增的呢?我們大概追究一下,它是在CameraService進行啟動時,初始化CameraProviderManager物件的邏輯中,通過addProviderLocked方法生成具體的DeviceInfo物件,新增到mProviders成員變數中的。addProviderLocked方法的原始碼如下:

status_t CameraProviderManager::addProviderLocked(const std::string& newProvider, bool expected) {
    for (const auto& providerInfo : mProviders) {
        if (providerInfo->mProviderName == newProvider) {
            ALOGW("%s: Camera provider HAL with name '%s' already registered", __FUNCTION__,
                    newProvider.c_str());
            return ALREADY_EXISTS;
        }
    }

    sp<provider::V2_4::ICameraProvider> interface;
    interface = mServiceProxy->getService(newProvider);

    if (interface == nullptr) {
        if (expected) {
            ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
                    newProvider.c_str());
            return BAD_VALUE;
        } else {
            return OK;
        }
    }

    sp<ProviderInfo> providerInfo =
            new ProviderInfo(newProvider, interface, this);
    status_t res = providerInfo->initialize();
    if (res != OK) {
        return res;
    }

    mProviders.push_back(providerInfo);

    return OK;
}

     這個方法中有一個非常重要的物件,就是interface = mServiceProxy->getService(newProvider)邏輯返回的interface,它又是一個binder物件,獲取camera屬性、數量等實質性的方法都是通過它來完成的,獲取成功後,以它為引數構造一個ProviderInfo物件,最後新增到mProviders成員變數當中。

     好了,我們回到openSession方法當中,找到deviceInfo物件之後,然後呼叫deviceInfo3->mInterface->open,而它的成員變數mInterface就是在前面我們說構造ProviderInfo時獲取到的binder物件了,它實際上是hardware\interfaces\camera\device\3.2\default\CameraDevice.cpp物件了,來到這裡,我們就進入了CameraDaemon程序當中,兩個程序的通訊是通過HIDL,其實還是binder程序間通訊機制,只是它是用來提供給HAL層服務的,所以和AIDL類似,取了個HIDL的名字。這裡需要說一下,為是什麼是3.2下面的這個類呢?因為我們在呼叫openSession方法時,傳入的第二個引數const sp<hardware::camera::device::V3_2::ICameraDeviceCallback>& callback和第三個引數sp<hardware::camera::device::V3_2::ICameraDeviceSession> *session就可以非常明顯的看出來,當前呼叫的版本是V3_2。接下來,我們就來看一下CameraDevice.cpp類的open方法的實現,原始碼如下:

Return<void> CameraDevice::open(const sp<ICameraDeviceCallback>& callback, open_cb _hidl_cb)  {
    Status status = initStatus();
    sp<CameraDeviceSession> session = nullptr;

    if (callback == nullptr) {
        ALOGE("%s: cannot open camera %s. callback is null!",
                __FUNCTION__, mCameraId.c_str());
        _hidl_cb(Status::ILLEGAL_ARGUMENT, session);
        return Void();
    }

    if (status != Status::OK) {
        // Provider will never pass initFailed device to client, so
        // this must be a disconnected camera
        ALOGE("%s: cannot open camera %s. camera is disconnected!",
                __FUNCTION__, mCameraId.c_str());
        _hidl_cb(Status::CAMERA_DISCONNECTED, session);
        return Void();
    } else {
        mLock.lock();

        ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mCameraIdInt);
        session = mSession.promote();
        if (session != nullptr && !session->isClosed()) {
            ALOGE("%s: cannot open an already opened camera!", __FUNCTION__);
            mLock.unlock();
            _hidl_cb(Status::CAMERA_IN_USE, nullptr);
            return Void();
        }

        /** Open HAL device */
        status_t res;
        camera3_device_t *device;

        ATRACE_BEGIN("camera3->open");
        res = mModule->open(mCameraId.c_str(),
                reinterpret_cast<hw_device_t**>(&device));
        ATRACE_END();

        if (res != OK) {
            ALOGE("%s: cannot open camera %s!", __FUNCTION__, mCameraId.c_str());
            mLock.unlock();
            _hidl_cb(getHidlStatus(res), nullptr);
            return Void();
        }

        /** Cross-check device version */
        if (device->common.version < CAMERA_DEVICE_API_VERSION_3_2) {
            ALOGE("%s: Could not open camera: "
                    "Camera device should be at least %x, reports %x instead",
                    __FUNCTION__,
                    CAMERA_DEVICE_API_VERSION_3_2,
                    device->common.version);
            device->common.close(&device->common);
            mLock.unlock();
            _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
            return Void();
        }

        struct camera_info info;
        res = mModule->getCameraInfo(mCameraIdInt, &info);
        if (res != OK) {
            ALOGE("%s: Could not open camera: getCameraInfo failed", __FUNCTION__);
            device->common.close(&device->common);
            mLock.unlock();
            _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
            return Void();
        }

        session = new CameraDeviceSession(
                device, info.static_camera_characteristics, callback);
        if (session == nullptr) {
            ALOGE("%s: camera device session allocation failed", __FUNCTION__);
            mLock.unlock();
            _hidl_cb(Status::INTERNAL_ERROR, nullptr);
            return Void();
        }
        if (session->isInitFailed()) {
            ALOGE("%s: camera device session init failed", __FUNCTION__);
            session = nullptr;
            mLock.unlock();
            _hidl_cb(Status::INTERNAL_ERROR, nullptr);
            return Void();
        }
        mSession = session;
        mLock.unlock();
    }
    _hidl_cb(status, session);
    return Void();
}

     我們先來看一下該方法的引數,第一個是callback物件,它的使用方法和我們之前講的應用層呼叫openCamera時在CameraManager中傳入的binder型別的callback是一樣的,Server端拿到這個callback之後,就可以針對需要的節點事件回撥應用層,而這裡是在CameraDaemon回撥CameraServer,道理是一樣的。這個callback引數最終賦值給HAL層中的CameraDeviceSession類的mResultBatcher成員變量了;第二個引數是open_cb型別,從它的命名中可以看出來,它也是一個回撥函式,非常方便,就像一個函式指標一樣,它在CameraProviderManager一側中像一個結構體一樣傳了過來,當CameraDevice類中的open執行完成後,就會將session物件作為引數回傳到CameraProviderManager這一側,我們就拿到了session,後續對camera的操作都是通過這個sesson物件來進行中轉完成的。CameraProviderManager這一側傳入的結構體如下:


     我們繼續閱讀程式碼,open方法中先判斷status,正常的話,接著呼叫res = mModule->open(mCameraId.c_str(), reinterpret_cast<hw_device_t**>(&device))來執行相機的開啟操作,mModule物件是CameraDevice類的成員變數,它是在CameraDevice的建構函式中傳入的,而CameraDevice類的物件是在hardware\interfaces\camera\provider\2.4\default\CameraProvider.cpp檔案中的getCameraDeviceInterface_V3_x方法中構造的,該方法也是CameraDaemon程序為CameraServer程序提供的,當新增相機裝置時,CameraServer就需要查詢和獲取camera裝置,也就會使用到這個介面,getCameraDeviceInterface_V3_x方法原始碼如下:

Return<void> CameraProvider::getCameraDeviceInterface_V3_x(
        const hidl_string& cameraDeviceName, getCameraDeviceInterface_V3_x_cb _hidl_cb)  {
    std::string cameraId, deviceVersion;
    bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
    if (!match) {
        _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
        return Void();
    }

    std::string deviceName(cameraDeviceName.c_str());
    ssize_t index = mCameraDeviceNames.indexOf(std::make_pair(cameraId, deviceName));
    if (index == NAME_NOT_FOUND) { // Either an illegal name or a device version mismatch
        Status status = Status::OK;
        ssize_t idx = mCameraIds.indexOf(cameraId);
        if (idx == NAME_NOT_FOUND) {
            ALOGE("%s: cannot find camera %s!", __FUNCTION__, cameraId.c_str());
            status = Status::ILLEGAL_ARGUMENT;
        } else { // invalid version
            ALOGE("%s: camera device %s does not support version %s!",
                    __FUNCTION__, cameraId.c_str(), deviceVersion.c_str());
            status = Status::OPERATION_NOT_SUPPORTED;
        }
        _hidl_cb(status, nullptr);
        return Void();
    }

    if (mCameraStatusMap.count(cameraId) == 0 ||
            mCameraStatusMap[cameraId] != CAMERA_DEVICE_STATUS_PRESENT) {
        _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
        return Void();
    }

    sp<android::hardware::camera::device::V3_2::implementation::CameraDevice> device =
            new android::hardware::camera::device::V3_2::implementation::CameraDevice(
                    mModule, cameraId, mCameraDeviceNames);

    if (device == nullptr) {
        ALOGE("%s: cannot allocate camera device for id %s", __FUNCTION__, cameraId.c_str());
        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
        return Void();
    }

    if (device->isInitFailed()) {
        ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str());
        device = nullptr;
        _hidl_cb(Status::INTERNAL_ERROR, nullptr);
        return Void();
    }

    _hidl_cb (Status::OK, device);
    return Void();
}

     可以看到,在構造CameraDevice物件時,傳入的第一個引數mModule,它也是CameraProvider類的成員變數,它的定義在hardware\interfaces\camera\provider\2.4\default\CameraProvider.h標頭檔案中,原始碼如下:


     它是一個CameraModule物件,那麼我們接下來就看看hardware\interfaces\camera\common\1.0\default\CameraModule.cpp類的open方法,來看看它是如何處理的。它的open方法原始碼如下:

int CameraModule::open(const char* id, struct hw_device_t** device) {
    int res;
    ATRACE_BEGIN("camera_module->open");
    res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
    ATRACE_END();
    return res;
}

     該方法非常簡潔,就是呼叫mModule類的common.methods的open方法處理,它的mModule也是在CameraModule類的建構函式中傳入的,而CameraModule的構造方法是在CameraProvider類的initialize()方法中呼叫的,原始碼如下:

bool CameraProvider::initialize() {
    camera_module_t *rawModule;
    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
            (const hw_module_t **)&rawModule);
    if (err < 0) {
        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
        return true;
    }

    mModule = new CameraModule(rawModule);
    err = mModule->init();
    if (err != OK) {
        ALOGE("Could not initialize camera HAL module: %d (%s)", err, strerror(-err));
        mModule.clear();
        return true;
    }
    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());

    // Setup vendor tags here so HAL can setup vendor keys in camera characteristics
    VendorTagDescriptor::clearGlobalVendorTagDescriptor();
    if (!setUpVendorTags()) {
        ALOGE("%s: Vendor tag setup failed, will not be available.", __FUNCTION__);
    }

    // Setup callback now because we are going to try openLegacy next
    err = mModule->setCallbacks(this);
    if (err != OK) {
        ALOGE("Could not set camera module callback: %d (%s)", err, strerror(-err));
        mModule.clear();
        return true;
    }

    mNumberOfLegacyCameras = mModule->getNumberOfCameras();
    for (int i = 0; i < mNumberOfLegacyCameras; i++) {
        struct camera_info info;
        auto rc = mModule->getCameraInfo(i, &info);
        if (rc != NO_ERROR) {
            ALOGE("%s: Camera info query failed!", __func__);
            mModule.clear();
            return true;
        }

        if (checkCameraVersion(i, info) != OK) {
            ALOGE("%s: Camera version check failed!", __func__);
            mModule.clear();
            return true;
        }

        char cameraId[kMaxCameraIdLen];
        snprintf(cameraId, sizeof(cameraId), "%d", i);
        std::string cameraIdStr(cameraId);
        mCameraStatusMap[cameraIdStr] = CAMERA_DEVICE_STATUS_PRESENT;
        mCameraIds.add(cameraIdStr);

        // initialize mCameraDeviceNames and mOpenLegacySupported
        mOpenLegacySupported[cameraIdStr] = false;
        int deviceVersion = mModule->getDeviceVersion(i);
        mCameraDeviceNames.add(
                std::make_pair(cameraIdStr,
                               getHidlDeviceName(cameraIdStr, deviceVersion)));
        if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
                mModule->isOpenLegacyDefined()) {
            // try open_legacy to see if it actually works
            struct hw_device_t* halDev = nullptr;
            int ret = mModule->openLegacy(cameraId, CAMERA_DEVICE_API_VERSION_1_0, &halDev);
            if (ret == 0) {
                mOpenLegacySupported[cameraIdStr] = true;
                halDev->close(halDev);
                mCameraDeviceNames.add(
                        std::make_pair(cameraIdStr,
                                getHidlDeviceName(cameraIdStr, CAMERA_DEVICE_API_VERSION_1_0)));
            } else if (ret == -EBUSY || ret == -EUSERS) {
                // Looks like this provider instance is not initialized during
                // system startup and there are other camera users already.
                // Not a good sign but not fatal.
                ALOGW("%s: open_legacy try failed!", __FUNCTION__);
            }
        }
    }

    return false; // mInitFailed
}

     這裡構造CameraModule時傳入的引數rawModule就是在該方法一開始時,通過呼叫int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t **)&rawModule)獲取到的,看到這裡大家是不是覺得有些熟悉,CAMERA_HARDWARE_MODULE_ID就是HAL層定義的module,從這裡往下就和對應的裝置廠商有密切關係了,當然具體的實現也就差別很大了,而不像我們前面到這裡,不分廠商,所有裝置,只要是android都是通用的。好了,我這裡下載的8.0原始碼中提供了三個廠商的實現,分別命名為google、huawei、lge,截圖如下:


     google目錄下具體的實現使用的是高通的裝置,我們就來看看這個是怎麼實現的。高通相機中的hw_module_t、camera_module_t兩個結構體定義在device\google\marlin\camera\QCamera2\QCamera2Hal.cpp檔案中,在CameraProvider類的initialize()方法中呼叫hw_get_module獲取到的就是這裡定義的camera_module_t,它也就是構造CameraModule時傳入的引數,好了,回到CameraModule類的open方法中,繼續呼叫它的common(這裡就是camera_module_t結構體的第一個成員變數common了,它指向camera_common結構體)的methods(就是static hw_module_t camera_common結構體的倒數第三個屬性methods了,它指向&qcamera::QCamera2Factory::mModuleMethods)的open方法,我們還是以goole目錄下高通的實現為例,繼續看一下QCamera2Factory類的mModuleMethods的定義,device\google\marlin\camera\QCamera2\QCamera2Factory.cpp檔案中的mModuleMethods定義原始碼如下:

struct hw_module_methods_t QCamera2Factory::mModuleMethods = {
    .open = QCamera2Factory::camera_device_open,
};

     這裡的open又指向了QCamera2Factory類的camera_device_open方法,該方法的原始碼如下:

int QCamera2Factory::camera_device_open(
    const struct hw_module_t *module, const char *id,
    struct hw_device_t **hw_device)
{
    int rc = NO_ERROR;
    if (module != &HAL_MODULE_INFO_SYM.common) {
        LOGE("Invalid module. Trying to open %p, expect %p",
            module, &HAL_MODULE_INFO_SYM.common);
        return INVALID_OPERATION;
    }
    if (!id) {
        LOGE("Invalid camera id");
        return BAD_VALUE;
    }

    if(gQCameraMuxer)
        rc =  gQCameraMuxer->camera_device_open(module, id, hw_device);
    else
        rc = gQCamera2Factory->cameraDeviceOpen(atoi(id), hw_device);

    return rc;
}

     這裡先判斷成員變數gQCameraMuxer是否為空,它是在QCamera2Factory類的建構函式中,判斷如果當前為雙攝(bDualCamera為true,它是通過配置項persist.camera.dual.camera獲取的)的情況下獲取的,這裡我們假設它不為空,那麼就呼叫rc =  gQCameraMuxer->camera_device_open(module, id, hw_device)來繼續開啟camera,gQCameraMuxer的型別為QCameraMuxer,接下來看一下device\google\marlin\camera\QCamera2\HAL\QCameraMuxer.cpp類中的camera_device_open方法的實現,原始碼如下:

int QCameraMuxer::camera_device_open(
        __unused const struct hw_module_t *module, const char *id,
        struct hw_device_t **hw_device)
{
    int rc = NO_ERROR;
    LOGH("id= %d",atoi(id));
    if (!id) {
        LOGE("Invalid camera id");
        return BAD_VALUE;
    }

    rc =  gMuxer->cameraDeviceOpen(atoi(id), hw_device);
    LOGH("id= %d, rc: %d", atoi(id), rc);
    return rc;
}

     該方法的邏輯也非常簡潔,引數正確,就接著呼叫gMuxer變數的cameraDeviceOpen方法來處理,gMuxer就是當前類的例項,它的cameraDeviceOpen方法的原始碼如下:

int QCameraMuxer::cameraDeviceOpen(int camera_id,
        struct hw_device_t **hw_device)
{
    int rc = NO_ERROR;
    uint32_t phyId = 0;
    qcamera_logical_descriptor_t *cam = NULL;

    if (camera_id < 0 || camera_id >= m_nLogicalCameras) {
        LOGE("Camera id %d not found!", camera_id);
        return -ENODEV;
    }

    if ( NULL == m_pLogicalCamera) {
        LOGE("Hal descriptor table is not initialized!");
        return NO_INIT;
    }

    char prop[PROPERTY_VALUE_MAX];
    property_get("persist.camera.dc.frame.sync", prop, "1");
    m_bFrameSyncEnabled = atoi(prop);

    // Get logical camera
    cam = &m_pLogicalCamera[camera_id];

    if (m_pLogicalCamera[camera_id].device_version ==
            CAMERA_DEVICE_API_VERSION_1_0) {
        // HW Dev Holders
        hw_device_t *hw_dev[cam->numCameras];

        if (m_pPhyCamera[cam->pId[0]].type != CAM_TYPE_MAIN) {
            LOGE("Physical camera at index 0 is not main!");
            return UNKNOWN_ERROR;
        }

        // Open all physical cameras
        for (uint32_t i = 0; i < cam->numCameras; i++) {
            phyId = cam->pId[i];
            QCamera2HardwareInterface *hw =
                    new QCamera2HardwareInterface((uint32_t)phyId);
            if (!hw) {
                LOGE("Allocation of hardware interface failed");
                return NO_MEMORY;
            }
            hw_dev[i] = NULL;

            // Make Camera HWI aware of its mode
            cam_sync_related_sensors_event_info_t info;
            info.sync_control = CAM_SYNC_RELATED_SENSORS_ON;
            info.mode = m_pPhyCamera[phyId].mode;
            info.type = m_pPhyCamera[phyId].type;
            rc = hw->setRelatedCamSyncInfo(&info);
            hw->setFrameSyncEnabled(m_bFrameSyncEnabled);
            if (rc != NO_ERROR) {
                LOGE("setRelatedCamSyncInfo failed %d", rc);
                delete hw;
                return rc;
            }

            rc = hw->openCamera(&hw_dev[i]);
            if (rc != NO_ERROR) {
                delete hw;
                return rc;
            }
            hw->getCameraSessionId(&m_pPhyCamera[phyId].camera_server_id);
            m_pPhyCamera[phyId].dev = reinterpret_cast<camera_device_t*>(hw_dev[i]);
            m_pPhyCamera[phyId].hwi = hw;
            cam->sId[i] = m_pPhyCamera[phyId].camera_server_id;
            LOGH("camera id %d server id : %d hw device %x, hw %x",
                     phyId, cam->sId[i], hw_dev[i], hw);
        }
    } else {
        LOGE("Device version for camera id %d invalid %d",
                 camera_id, m_pLogicalCamera[camera_id].device_version);
        return BAD_VALUE;
    }

    cam->dev.common.tag = HARDWARE_DEVICE_TAG;
    cam->dev.common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
    cam->dev.common.close = close_camera_device;
    cam->dev.ops = &mCameraMuxerOps;
    cam->dev.priv = (void*)cam;
    *hw_device = &cam->dev.common;
    return rc;
}

     該方法中最重要的就是中間的for迴圈了,先構造QCamera2HardwareInterface物件,然後呼叫它的openCamera方法開啟camera,接下來就看一下device\google\marlin\camera\QCamera2\HAL3\QCamera3HWI.cpp類的openCamera方法的實現,原始碼如下:

int QCamera3HardwareInterface::openCamera(struct hw_device_t **hw_device)
{
    int rc = 0;
    if (mState != CLOSED) {
        *hw_device = NULL;
        return PERMISSION_DENIED;
    }

    m_perfLock.lock_acq();
    LOGI("[KPI Perf]: E PROFILE_OPEN_CAMERA camera id %d",
             mCameraId);

    rc = openCamera();
    if (rc == 0) {
        *hw_device = &mCameraDevice.common;
    } else
        *hw_device = NULL;

    m_perfLock.lock_rel();
    LOGI("[KPI Perf]: X PROFILE_OPEN_CAMERA camera id %d, rc: %d",
             mCameraId, rc);

    if (rc == NO_ERROR) {
        mState = OPENED;
    }
    return rc;
}

     它又是轉調另一個openCamera方法來處理的,原始碼如下:

int QCamera3HardwareInterface::openCamera()
{
    int rc = 0;
    char value[PROPERTY_VALUE_MAX];

    KPI_ATRACE_CALL();
    if (mCameraHandle) {
        LOGE("Failure: Camera already opened");
        return ALREADY_EXISTS;
    }

    rc = QCameraFlash::getInstance().reserveFlashForCamera(mCameraId);
    if (rc < 0) {
        LOGE("Failed to reserve flash for camera id: %d",
                mCameraId);
        return UNKNOWN_ERROR;
    }

    rc = camera_open((uint8_t)mCameraId, &mCameraHandle);
    if (rc) {
        LOGE("camera_open failed. rc = %d, mCameraHandle = %p", rc, mCameraHandle);
        return rc;
    }

    if (!mCameraHandle) {
        LOGE("camera_open failed. mCameraHandle = %p", mCameraHandle);
        return -ENODEV;
    }

    rc = mCameraHandle->ops->register_event_notify(mCameraHandle->camera_handle,
            camEvtHandle, (void *)this);

    if (rc < 0) {
        LOGE("Error, failed to register event callback");
        /* Not closing camera here since it is already handled in destructor */
        return FAILED_TRANSACTION;
    }

    mExifParams.debug_params =
            (mm_jpeg_debug_exif_params_t *) malloc (sizeof(mm_jpeg_debug_exif_params_t));
    if (mExifParams.debug_params) {
        memset(mExifParams.debug_params, 0, sizeof(mm_jpeg_debug_exif_params_t));
    } else {
        LOGE("Out of Memory. Allocation failed for 3A debug exif params");
        return NO_MEMORY;
    }
    mFirstConfiguration = true;

    //Notify display HAL that a camera session is active.
    //But avoid calling the same during bootup because camera service might open/close
    //cameras at boot time during its initialization and display service will also internally
    //wait for camera service to initialize first while calling this display API, resulting in a
    //deadlock situation. Since boot time camera open/close calls are made only to fetch
    //capabilities, no need of this display bw optimization.
    //Use "service.bootanim.exit" property to know boot status.
    property_get("service.bootanim.exit", value, "0");
    if (atoi(value) == 1) {
        pthread_mutex_lock(&gCamLock);
        if (gNumCameraSessions++ == 0) {
            setCameraLaunchStatus(true);
        }
        pthread_mutex_unlock(&gCamLock);
    }

    //fill the session id needed while linking dual cam
    pthread_mutex_lock(&gCamLock);
    rc = mCameraHandle->ops->get_session_id(mCameraHandle->camera_handle,
        &sessionId[mCameraId]);
    pthread_mutex_unlock(&gCamLock);

    if (rc < 0) {
        LOGE("Error, failed to get sessiion id");
        return UNKNOWN_ERROR;
    } else {
        //Allocate related cam sync buffer
        //this is needed for the payload that goes along with bundling cmd for related
        //camera use cases
        m_pRelCamSyncHeap = new QCamera3HeapMemory(1);
        rc = m_pRelCamSyncHeap->allocate(sizeof(cam_sync_related_sensors_event_info_t));
        if(rc != OK) {
            rc = NO_MEMORY;
            LOGE("Dualcam: Failed to allocate Related cam sync Heap memory");
            return NO_MEMORY;
        }

        //Map memory for related cam sync buffer
        rc = mCameraHandle->ops->map_buf(mCameraHandle->camera_handle,
                CAM_MAPPING_BUF_TYPE_SYNC_RELATED_SENSORS_BUF,
                m_pRelCamSyncHeap->getFd(0),
                sizeof(cam_sync_related_sensors_event_info_t),
                m_pRelCamSyncHeap->getPtr(0));
        if(rc < 0) {
            LOGE("Dualcam: failed to map Related cam sync buffer");
            rc = FAILED_TRANSACTION;
            return NO_MEMORY;
        }
        m_pRelCamSyncBuf =
                (cam_sync_related_sensors_event_info_t*) DATA_PTR(m_pRelCamSyncHeap,0);
    }

    LOGH("mCameraId=%d",mCameraId);

    return NO_ERROR;
}

     這裡又轉調camera_open方法來處理,camera_open方法的實現是在device\google\marlin\camera\QCamera2\stack\mm-camera-interface\src\mm_camera_interface.c檔案中,原始碼如下:

int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl)
{
    int32_t rc = 0;
    mm_camera_obj_t *cam_obj = NULL;

#ifdef QCAMERA_REDEFINE_LOG
    mm_camera_set_dbg_log_properties();
#endif

    LOGD("E camera_idx = %d\n", camera_idx);
    if (camera_idx >= g_cam_ctrl.num_cam) {
        LOGE("Invalid camera_idx (%d)", camera_idx);
        return -EINVAL;
    }

    pthread_mutex_lock(&g_intf_lock);
    /* opened already */
    if(NULL != g_cam_ctrl.cam_obj[camera_idx]) {
        /* Add reference */
        g_cam_ctrl.cam_obj[camera_idx]->ref_count++;
        pthread_mutex_unlock(&g_intf_lock);
        LOGD("opened alreadyn");
        *camera_vtbl = &g_cam_ctrl.cam_obj[camera_idx]->vtbl;
        return rc;
    }

    cam_obj = (mm_camera_obj_t *)malloc(sizeof(mm_camera_obj_t));
    if(NULL == cam_obj) {
        pthread_mutex_unlock(&g_intf_lock);
        LOGE("no mem");
        return -EINVAL;
    }

    /* initialize camera obj */
    memset(cam_obj, 0, sizeof(mm_camera_obj_t));
    cam_obj->ctrl_fd = -1;
    cam_obj->ds_fd = -1;
    cam_obj->ref_count++;
    cam_obj->my_hdl = mm_camera_util_generate_handler(camera_idx);
    cam_obj->vtbl.camera_handle = cam_obj->my_hdl; /* set handler */
    cam_obj->vtbl.ops = &mm_camera_ops;
    pthread_mutex_init(&cam_obj->cam_lock, NULL);
    /* unlock global interface lock, if not, in dual camera use case,
      * current open will block operation of another opened camera obj*/
    pthread_mutex_lock(&cam_obj->cam_lock);
    pthread_mutex_unlock(&g_intf_lock);

    rc = mm_camera_open(cam_obj);

    pthread_mutex_lock(&g_intf_lock);
    if (rc != 0) {
        LOGE("mm_camera_open err = %d", rc);
        pthread_mutex_destroy(&cam_obj->cam_lock);
        g_cam_ctrl.cam_obj[camera_idx] = NULL;
        free(cam_obj);
        cam_obj = NULL;
        pthread_mutex_unlock(&g_intf_lock);
        *camera_vtbl = NULL;
        return rc;
    } else {
        LOGD("Open succeded\n");
        g_cam_ctrl.cam_obj[camera_idx] = cam_obj;
        pthread_mutex_unlock(&g_intf_lock);
        *camera_vtbl = &cam_obj->vtbl;
        return 0;
    }
}

     這裡呼叫malloc給方法變數分配記憶體,memset初始化,然後接著呼叫mm_camera_open繼續處理,mm_camera_open方法的實現在device\google\marlin\camera\QCamera2\stack\mm-camera-interface\src\mm_camera_interface.c檔案中,原始碼如下:

int32_t mm_camera_open(mm_camera_obj_t *my_obj)
{
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
    int32_t rc = 0;
    int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES;
    uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP;
    int cam_idx = 0;
    const char *dev_name_value = NULL;
    int l_errno = 0;

    LOGD("begin\n");

    if (NULL == my_obj) {
        goto on_error;
    }
    dev_name_value = mm_camera_util_get_dev_name(my_obj->my_hdl);
    if (NULL == dev_name_value) {
        goto on_error;
    }
    snprintf(dev_name, sizeof(dev_name), "/dev/%s",
             dev_name_value);
    sscanf(dev_name, "/dev/video%d", &cam_idx);
    LOGD("dev name = %s, cam_idx = %d", dev_name, cam_idx);

    do{
        n_try--;
        errno = 0;
        my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK);
        l_errno = errno;
        LOGD("ctrl_fd = %d, errno == %d", my_obj->ctrl_fd, l_errno);
        if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 )) {
            break;
        }
        LOGE("Failed with %s error, retrying after %d milli-seconds",
              strerror(errno), sleep_msec);
        usleep(sleep_msec * 1000U);
    }while (n_try > 0);

    if (my_obj->ctrl_fd < 0) {
        LOGE("cannot open control fd of '%s' (%s)\n",
                  dev_name, strerror(l_errno));
        if (l_errno == EBUSY)
            rc = -EUSERS;
        else
            rc = -1;
        goto on_error;
    } else {
        mm_camera_get_session_id(my_obj, &my_obj->sessionid);
        LOGH("Camera Opened id = %d sessionid = %d", cam_idx, my_obj->sessionid);
    }

#ifdef DAEMON_PRESENT
    /* open domain socket*/
    n_try = MM_CAMERA_DEV_OPEN_TRIES;
    do {
        n_try--;
        my_obj->ds_fd = mm_camera_socket_create(cam_idx, MM_CAMERA_SOCK_TYPE_UDP);
        l_errno = errno;
        LOGD("ds_fd = %d, errno = %d", my_obj->ds_fd, l_errno);
        if((my_obj->ds_fd >= 0) || (n_try <= 0 )) {
            LOGD("opened, break out while loop");
            break;
        }
        LOGD("failed with I/O error retrying after %d milli-seconds",
              sleep_msec);
        usleep(sleep_msec * 1000U);
    } while (n_try > 0);

    if (my_obj->ds_fd < 0) {
        LOGE("cannot open domain socket fd of '%s'(%s)\n",
                  dev_name, strerror(l_errno));
        rc = -1;
        goto on_error;
    }
#else /* DAEMON_PRESENT */
    cam_status_t cam_status;
    cam_status = mm_camera_module_open_session(my_obj->sessionid,
            mm_camera_module_event_handler);
    if (cam_status < 0) {
        LOGE("Failed to open session");
        if (cam_status == CAM_STATUS_BUSY) {
            rc = -EUSERS;
        } else {
            rc = -1;
        }
        goto on_error;
    }
#endif /* DAEMON_PRESENT */

    pthread_mutex_init(&my_obj->msg_lock, NULL);
    pthread_mutex_init(&my_obj->cb_lock, NULL);
    pthread_mutex_init(&my_obj->evt_lock, NULL);
    PTHREAD_COND_INIT(&my_obj->evt_cond);

    LOGD("Launch evt Thread in Cam Open");
    snprintf(my_obj->evt_thread.threadName, THREAD_NAME_SIZE, "CAM_Dispatch");
    mm_camera_cmd_thread_launch(&my_obj->evt_thread,
                                mm_camera_dispatch_app_event,
                                (void *)my_obj);

    /* launch event poll thread
     * we will add evt fd into event poll thread upon user first register for evt */
    LOGD("Launch evt Poll Thread in Cam Open");
    snprintf(my_obj->evt_poll_thread.threadName, THREAD_NAME_SIZE, "CAM_evntPoll");
    mm_camera_poll_thread_launch(&my_obj->evt_poll_thread,
                                 MM_CAMERA_POLL_TYPE_EVT);
    mm_camera_evt_sub(my_obj, TRUE);

    /* unlock cam_lock, we need release global intf_lock in camera_open(),
     * in order not block operation of other Camera in dual camera use case.*/
    pthread_mutex_unlock(&my_obj->cam_lock);
    LOGD("end (rc = %d)\n", rc);
    return rc;

on_error:

    if (NULL == dev_name_value) {
        LOGE("Invalid device name\n");
        rc = -1;
    }

    if (NULL == my_obj) {
        LOGE("Invalid camera object\n");
        rc = -1;
    } else {
        if (my_obj->ctrl_fd >= 0) {
            close(my_obj->ctrl_fd);
            my_obj->ctrl_fd = -1;
        }
#ifdef DAEMON_PRESENT
        if (my_obj->ds_fd >= 0) {
            mm_camera_socket_close(my_obj->ds_fd);
            my_obj->ds_fd = -1;
        }
#endif
    }

    /* unlock cam_lock, we need release global intf_lock in camera_open(),
     * in order not block operation of other Camera in dual camera use case.*/
    pthread_mutex_unlock(&my_obj->cam_lock);
    return rc;
}

     到這裡,終於看到了open的系統呼叫,HAL端也就是從這裡進入核心,呼叫驅動來處理的,這裡大家可以看到,是通過do/while迴圈來處理的,有一個重試機制,重試次數n_try不斷的減小,當它等於0時,相機裝置還未正常開啟,就退出do/while迴圈了,它的初值為MM_CAMERA_DEV_OPEN_TRIES,該巨集定義的值為20,在do判斷中,只要if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 ))條件中有一個成立,就跳出迴圈,(my_obj->ctrl_fd >= 0)的意思是就camera開啟成功,返回的FD有效;(errno != EIO && errno != ETIMEDOUT)的意思是未出現IO或者超時錯誤;(n_try <= 0 )意思是重試次數已用完,開啟成功後,還要進行一些其他初始化的操作。

     到這裡,openCamera才算完成了,大家可以看到,中間的過程真是複雜,當然我們所分析的部分還沒有包含驅動部分,想要搞清楚整個邏輯,還是要花費很大的氣力的,也希望我們搞技術的同事,能夠認認真真的靜下心來專心搞技術,以此來提升我們的能力。

     好了,今天就到這裡,休息一下!

相關推薦

Android 8.0系統原始碼分析--openCameraHAL啟動過程原始碼分析

     前面我們詳細分析了從應用層呼叫CameraManager的openCamera的方法來開啟相機的邏輯,上次的分析我們來到了CameraServer程序當中,但是還沒有真正看到open操作裝置節點來實現真正開啟的邏輯,遺留的問題也就是從frameworks\av\se

Android 8.0系統原始碼分析--Binder程序間通訊

 開始我們的沉澱之路,老羅的書中第二章講的是Android HAL層的知識,而且直接自己實現了一個虛擬的freg驅動程式,後面的幾節是分別從native、java層如何訪問這個虛擬的驅動程式介面,我這裡沒有這樣的環境,所以就不分析這節了,第三章的智慧指標我對比8.0系統原

Android 8.0系統原始碼分析--openCamera啟動過程原始碼分析

     說起Android相機的東西,從應用層的角度來看,基本就是四個重要的節點了:openCamera、createCaptureSession、preview、capture,最複雜的就是preview了,要理解preview,那麼就要求大家對And

Android 8.0系統原始碼分析--Activity的視窗Window物件新增過程原始碼分析

     這節我們來看一下Activity的視窗Window物件的建立過程,Activity作為Android提供的四大元件之首,我們之所以能非常簡單的使用它,就是因為它的建立過程中,framework為我們作了大量的初始化工作,包括它的視窗Window、視訊記憶體Surf

Android 8.0系統原始碼分析--開篇

     好久沒寫部落格了,在這裡上班基本都加班,而且公司上不了外網,手機都不能帶進辦公室,所以就間斷了。昨天中午沒事翻老羅的CSDN部落格,忽然發現老羅的《Android系統原始碼情景分析 [羅昇陽著

Android 8.0系統原始碼分析--startService啟動過程原始碼分析

   作過android應用開發的同事都非常清楚,android提供了四個元件Activity、Service、BroastcastReceiver、ContentProvider,分別都有不同的作用,這也給我們的應用開發提供了非常大的幫助,因為這四大元件本身就已經處理了很

Android 8.0系統原始碼分析--相機createCaptureSession建立過程原始碼分析

     上一次我們詳細分析了openCamera啟動過程的原始碼,從CameraServer程序建立了很多物件,比如CameraDeviceClient、Camera3Device、FrameProcessorBase,而真正開啟相機還是在驅動層中上電後才完成的,有時候真想

Android 8.0 系統啟動流程之Linux核心啟動--kernel_init程序

    在上一篇文章中詳細的分析了kthreadd程序的啟動,init程序也是有idle程序去觸發啟動的,init程序分為前後兩部分,前一部分是在核心啟動的,主要是完成建立和核心初始化工作,內容都是跟Linux核心相關的;後一部分是在使用者空間啟動的,主要完成A

Android 8.0 系統學習6---Linux核心介面要求

您可以將以下配置設定用作 Android 核心配置的基礎。設定會整理到 android-base、android-base-<arch> 和 android-recommended.cfg 檔案中:android-base。這些選項可實現核心 Android 功能,並且應配置為所有裝置指定的選項。

Android 8.0 系統啟動流程之init程序--第二階段(五)

1、概述     上一篇中講了init程序的第一階段,我們接著講第二階段,主要有以下內容 建立程序會話金鑰並初始化屬性系統 進行SELinux第二階段並恢復一些檔案安全上下文 新建epoll並初始化子程序終止訊號處理函式 設定其他系統屬性並開啟系統屬性服務

Android 8.0 系統啟動流程之init.rc解析與service流程(七)

1、概述     上一篇文章中我們介紹了一下init.rc檔案中的語法規則,而本文將分析如何解析rc檔案,並對rc檔案中的某一service啟動過程進行分析。 2、解析.rc檔案 之前我們在文件中看到.rc檔案主要有根目錄下的 /init.rc ,以及

Android 8.0 系統啟動流程之init程序--第一階段(四)

1、概述     上一篇中講到,Linux系統執行完初始化操作最後會執行根目錄下的init檔案,init是一個可執行程式,它的原始碼在platform/system/core/init/init.cpp。init程序是使用者空間的第一個程序,我們熟悉的app應

Android 8.0 系統啟動流程之init.rc語法規則(六)

1、概述     init經過前兩個階段後,已經建立了屬性系統和SELinux系統,但是init程序還需要執行很多其他的操作,還要啟動許多關鍵的系統服務,但是如果都是像屬性系統和SELinux系統那樣一行行程式碼去做,顯得有點雜亂繁瑣,而且不容易擴充套件,所以

Android 8.0系統,版本升級不跳轉應用安裝頁面的完美解決

   最近開發遇到了個問題,app升級的時候,其他手機都能正常升級,下載完安裝包,跳到安裝頁面進行新版本的安裝。但卻有使用者反映,華為P10和華為Mate 9升級時,怎麼也無法跳轉到安裝頁面。起初我以為是華為手機自身系統的問題(因為手上的華為測試機都是正常的),還特地對比了幾

Android 8.0 系統啟動流程之zygote程序(八)

前言 在上一篇中我們講到,init程序會解析.rc檔案,並介紹了.rc檔案中的語法規則。其中不乏有許多service啟動,這些service通常不是普通的服務,文件裡面的稱呼是daemon(守護程序). 所謂守護程序就是這些服務程序會在系統初始化時啟動,並一

Android 8.0系統的應用圖示不顯示問題

Android 8.0之後,APP的應用圖片會顯示成AndroidStudio的預設圖示(機器人的那個),那麼如果才能顯示我們自己設計的圖示呢?結論是:要做Android 8.0的系統圖片適配。1、在下圖中的mipmap-anydpi-v26資料夾上右鍵,New->Im

一起來學習Android 8.0系統的通知欄適配吧

大家早上好,今天我們繼續來學習Android 8.0系統的適配。之前我們已經講到了,Androi

解決Android 8.0 WebView回退失效判斷canGoBack()和呼叫goBack()

前言 最新開發專案使用WebView的時候發現華為手機判斷是否能回退(canGoBack)的時候失效了,無論開啟多少層網頁,點選返回按鈕都會關閉Activity,一開始以為是華為手機的問題,然後用其他版本高一點的手機測試也是一樣(8.0版本),查閱了官方文件才

Android 6.0 指紋識別功能學習----阿冬專欄!!!

轉載自:http://www.cnblogs.com/changyuet/p/5535082.html     由於畢設需要設計增強的身份認證(生物特徵認證方式),所以需要學習指紋識別相關的android6.0功能,進而設計自己的畢設:支援媒體可信通訊的andro

Android 9.0 功能和 API概覽中文版

Android 9 功能和 API 官方搬運: Android 9(API 級別 28)為使用者和開發者引入了眾多新特性和新功能。 本文重點介紹面向開發者的新功能。 利用 Wi-Fi RTT 進行室內定位 Android 9 添加了對 IEEE 802.1