Android框架之Camera(2)HAL及so庫的加載

HAL層作爲紐帶,把Framework層的APIs和底層驅動連接起來。簡言之,上層需要什麼操作接口,HAL層就負責實現之。

HAL在整個Camera框架中的位置如下圖紅框


Android Camera框架及Camera服務HAL(紅框)

HAL層的代碼編譯爲so庫,通常位於/system/lib/hw/或/vender/lib/hw/目錄下。接下來看下so庫的加載實現。

廣而知之,C/C++的入口爲main()函數,Android應用的入口是主Activity的onCreate(),那so庫的入口呢?就是:

int hw_get_module(const char *id, const struct hw_module_t **module)
參數id:模塊的名稱,HAL的so庫命名規則:xxx.yyy.so,所以這裏的id就是"xxx"。之所以這麼定是和so的查找規則對應的,稍後詳述。

參數module:通過她獲取指向實際模塊對象的指針。

對於Camera,啓動加載流程的函數:

/**
 * The id of this module
 */
#define CAMERA_HARDWARE_MODULE_ID "camera"
CameraService.cpp (frameworks\av\services\camera\libcameraservice)
camera_module_t *mModule;

void CameraService::onFirstRef()
{
    if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
                (const hw_module_t **)&mModule) < 0) {
        ALOGE("Could not load camera HAL module");
        mNumberOfCameras = 0;
    } else {
		// so成功加載
        ALOGI("Loaded \"%s\" camera module", mModule->common.name);
        mNumberOfCameras = mModule->get_number_of_cameras();
        ....
    }
}
(const hw_module_t **)&mModule就展示瞭如何通過參數module獲取指向Camera模塊camera_module_t指針的方法。這是如何做到的呢?

在定義自己模塊的時候,把hw_module_t成員放在起始位置,如這裏的camera_module_t:

typedef struct camera_module {
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    int (*set_callbacks)(const camera_module_callbacks_t *callbacks);
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
} camera_module_t;
結構體成員的內存地址是連續的(記得kernel中的container_of吧),此刻指向common成員的指針和指向整個模塊的指針在數值上就是相等的(簡單理解可以認爲是同一個指針),這樣通過common就獲取了自己模塊的指針。

整個模塊在實現HAL的時候已經分配好了空間且已經初始化:

camera_module_t HAL_MODULE_INFO_SYM = {
    common: {
         // init code
    },
    get_number_of_cameras: camera_get_number_of_cameras,
    get_camera_info: camera_get_camera_info,
    set_callbacks:NULL,
    get_vendor_tag_ops:NULL
};
所以就可以通過獲取到的指針操作自定義的模塊的所有成員。

這是對上,對下就是so庫的加載,看函數hw_get_module:

int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}


int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int status;
    int i;
    const struct hw_module_t *hmi = NULL;
    char prop[PATH_MAX];
    char path[PATH_MAX];
    char name[PATH_MAX];

    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);

    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */

    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
        if (i < HAL_VARIANT_KEYS_COUNT) {
            if (property_get(variant_keys[i], prop, NULL) == 0) {
                continue;
            }
            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH2, name, prop);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH1, name, prop);
            if (access(path, R_OK) == 0) break;
        } else {
            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH2, name);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH1, name);
            if (access(path, R_OK) == 0) break;
        }
    }

    status = -ENOENT;
    if (i < HAL_VARIANT_KEYS_COUNT+1) {
        /* load the module, if this fails, we're doomed, and we should not try
         * to load a different variant. */
        status = load(class_id, path, module);
    }

    return status;
}
這個函數很簡單,主要做兩件事:
1、拼湊so庫的路徑:路徑+name+屬性+".so"
路徑之前提到過,/system/lib/hw、/vendor/lib/hw。
name就是穿進來的id,比如camera。
屬性,這裏指定的屬性有:

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.product.device",
    "ro.board.platform",
    "ro.arch"
};

so庫的完整路徑比如/system/lib/hw/camera.default.so等等。

2、查找庫是否存在

我們看到,這些so是有優先級的,找到滿意的即刻break,正應了句老話:弱水三千,只取一瓢飲。
有了so庫,調用load加載之。


擴展:onFirstRef()的來源及調用

C++裏面有智能指針的概念,用於對象內存的自動釋放;類似地,Android實現類似功能的是sp(Strong Pointer)、wp(Weak Pointer),要使用它們需要在定義自己類的時候繼承RefBase類。RefBase概覽:

class RefBase
{
public:
	void	incStrong(const void* id) const;
	void	decStrong(const void* id) const;

protected:
    RefBase();
    virtual	~RefBase

    virtual void  onFirstRef();
};
onFirstRef()的來源有了。來看其調用過程:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}
創建一個對象,它的引用計數就自動加1;如果是第一次引用,12行分支不執行,則onFirstRef()函數會被調用,即CameraService#onFirstRef()。

我們寫一個小程序體驗下:

#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/String16.h>
#include <utils/Vector.h>

namespace android {

class ITest: public virtual RefBase
{
public:
	ITest() {
		printf("ITest create.\n");
	}

private:
	virtual void onFirstRef();
};

void ITest::onFirstRef()
{
	printf("onFirstRef\n");
}


};


using namespace android;
int main(int argc, char **argv)
{
	sp<ITest> spTest(new ITest());
	return 0;
}
Log:

ITest create.
onFirstRef

發佈了168 篇原創文章 · 獲贊 150 · 訪問量 56萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章