android分層學習筆記(一)

來源:互聯網
上載者:User

對於Android系統移植,主要是資訊中framework的移植,而且都會涉及到硬體。關於硬體相關,資料目前不算小,最先比較詳細介紹的是Jollen,其他資料也大部分基於他的分析而寫出了一些自己的理解,他的部落格地址是http://www.jollen.org/blog/2009/。

 

 

以下是自己的學習筆記及理解,以為備忘。

 

本文的主要內容如下:

寫在前面:關於分層

一、  Stub編寫

二、  JNI編寫

三、  framework的java編寫

四、  應用程式編寫

五、  linux硬體驅動編寫

 

盡量把每一部分內家寫得短些,太長看了會比較累。

------------------------------------------------------------------------------

寫在前面:關於分層

    一個系統的分層,可以有比較清晰的層次感,基本上我們的世界都是在一個分層的理念中,生物圈,管理、行政體系、人類需求……都可以看到分層的影子,還有一些邏輯上的概念,如tcp/ip分層。

    有些時候,我們稱之為等級。

    分層不能太多層,否則鞭長莫及。也不能不太小,否則層面太寬,失去分層之要義。所謂適當的、有效、健壯的有分層,就是我們所常說的“中庸”,當然此中庸,非彼中庸了。

    anroid系統的分層,有一張圖很清楚地顯示了,網上比較多這張圖,此處略去。apps->framework(apps service, manager)->jni->stub->linux kernel/driver。

    對於整個系統來說,這個分層的確有點太多層了,不花點時間,會顧此失彼,而且涉及到的知識面比較廣。

    對於效率來說,分層太多,效率肯定會打折扣,何況還應用程式還是用java寫的,java比較進階的語言了。目前來看,android系統對硬體要求也比較高了,似乎這些折扣,可以通過硬體的方式來補償了。硬體,硬體,發展也是會有瓶頸的吧??

 

一、Stub編寫

1、Stub的編寫

     Stub是與linux驅動打交道,也就是直接調用了open,close,ioctl,read,write等函數。實際上等價於我們直接在linux上寫一些應用程式時所調用硬體驅動功能。只是在android裡叫做stub而已。

     首先熟悉一下,兩個結構體:hw_module_t 和hw_device_t。

     此兩結構體定義在 hardware/libhardware/include/hardware/hardware.h

    typedef struct hw_module_t {<br /> /** tag must be initialized to HARDWARE_MODULE_TAG */<br /> uint32_t tag;<br /> /** major version number for the module */<br /> uint16_t version_major;<br /> /** minor version number of the module */<br /> uint16_t version_minor;<br /> /** Identifier of module */<br /> const char *id;<br /> /** Name of this module */<br /> const char *name;<br /> /** Author/owner/implementor of the module */<br /> const char *author;<br /> /** Modules methods */<br /> struct hw_module_methods_t* methods;<br /> /** module's dso */<br /> void* dso;<br /> /** padding to 128 bytes, reserved for future use */<br /> uint32_t reserved[32-7];<br />} hw_module_t;<br />typedef struct hw_module_methods_t {<br /> /** Open a specific device */<br /> int (*open)(const struct hw_module_t* module, const char* id,<br /> struct hw_device_t** device);<br />} hw_module_methods_t;<br />/**<br /> * Every device data structure must begin with hw_device_t<br /> * followed by module specific public methods and attributes.<br /> */<br />typedef struct hw_device_t {<br /> /** tag must be initialized to HARDWARE_DEVICE_TAG */<br /> uint32_t tag;<br /> /** version number for hw_device_t */<br /> uint32_t version;<br /> /** reference to the module this device belongs to */<br /> struct hw_module_t* module;<br /> /** padding reserved for future use */<br /> uint32_t reserved[12];<br /> /** Close this device */<br /> int (*close)(struct hw_device_t* device);<br />} hw_device_t;

 

   寫Stub代碼,就是填充這兩個結體體。

   在訪問硬體裝置時,是通過檔案標識來操作,因此實際上在hw_device_t還應該添一個檔案標識變fd,有兩種方式可以實現:1)把hw_device_t中reseverd變數來放檔案標識,這是被系統保留,應該說不太安全。2)自訂一個結構體,把hw_device_t 當作此結構體的成員變數。如下操作:

    struct gpio_device_t {
              struct hw_device_t hwdev;

               /* attributes */
                int fd;  //the file description of the device in /dev/xxx

     }

   這樣在open裝置的時候,就可以把返回的檔案標識值,傳給fd了。

 

   下面就是簡單寫Stub的流程:

   1、 在vendor目錄下建一個hal目錄,再分別建立framework和modules目錄。對應於framework代碼和c/c++模組代碼,在moueles建立gpio目錄。gpio比較通用,led也是屬於gpio範疇。

   2、建立gpio.h 如下

      #include <hardware/hardware.h><br />#include <fcntl.h><br />#include <errno.h><br />#include <cutils/log.h><br />#include <cutils/atomic.h><br />struct gpio_module_t {<br /> struct hw_module_t hwmod;<br />};<br />struct gpio_device_t {<br /> struct hw_device_t hwdev;<br /> /* attributes */<br /> int fd; //the file description of the device in /dev/xxx<br /> /* supporting control APIs go here */<br /> int (*SetGPIO)(struct gpio_device_t *dev, int32_t gpio);<br /> int (*ClrGPIO)(struct gpio_device_t *dev, int32_t gpio);<br />};<br />#define GPIO_HARDWARE_MODULE_ID "gpio"

    標頭檔:

#include <hardware/hardware.h>   //上兩個基本結構體定義處

#include <fcntl.h>
#include <errno.h>

#include <cutils/log.h>
#include <cutils/atomic.h>

 

自己定義的兩個結構體

struct gpio_module_t {
   struct hw_module_t hwmod;
};

struct gpio_device_t {
   struct hw_device_t hwdev;

   int fd;  //the file description of the device in /dev/xxx
 
   int (*SetGPIO)(struct gpio_device_t *dev, int32_t gpio);
   int (*ClrGPIO)(struct gpio_device_t *dev, int32_t gpio);
};

定義一個模組id,#define GPIO_HARDWARE_MODULE_ID "gpio"

儲存到gpio目錄下。

 

3 編寫gpio.c

#include "gpio.h"<br />#define LOG_TAG "GPIO"<br />int gpio_device_close(struct hw_device_t* device)<br />{<br />struct gpio_device_t* gdev = (struct gpio_device_t*)device;<br />if (gdev)<br />{<br />free(gdev);<br />}<br />return 0;<br />}<br />int gpio_set(struct gpio_device_t *dev, int32_t gpio)<br />{<br />LOGI("gpio : %d set .", gpio);<br />if (dev->fd >= 0)<br />{</p><p>}<br />return 0;<br />}<br />int gpio_clr(struct gpio_device_t *dev, int32_t gpio)<br />{<br />LOGI("gpio : %d clr.", gpio);<br />if (dev->fd >= 0)<br />{</p><p>}<br />return 0;<br />}<br />static int gpio_device_open(const struct hw_module_t* module, const char* name,<br /> struct hw_device_t** device)<br />{<br />struct gpio_device_t *dev;<br />dev = (struct gpio_device_t *)malloc(sizeof(struct gpio_device_t));<br />memset(dev, 0, sizeof(struct gpio_device_t));<br />dev->hwdev.tag = HARDWARE_DEVICE_TAG;<br />dev->hwdev.version = 0;<br />dev->hwdev.module = module;<br />dev->hwdev.close = gpio_device_close;<br />dev->SetGPIO = gpio_set;<br />dev->ClrGPIO = gpio_clr;<br />dev->fd = open("/dev/gpio0", O_RDWR);<br />*device = &dev->hwdev;<br />if (dev->fd < 0)<br />{<br />free(dev);<br />dev = NULL;<br />return -1;<br />}<br />return 0;<br />}<br />static struct hw_module_methods_t gpio_module_methods =<br />{<br /> open: gpio_device_open<br />};<br />const struct gpio_module_t HAL_MODULE_INFO_SYM =<br />{<br /> hwmod:<br /> {<br /> tag: HARDWARE_MODULE_TAG,<br /> version_major: 1,<br /> version_minor: 0,<br /> id: GPIO_HARDWARE_MODULE_ID,<br /> name: "gpio Stub",<br /> author: "guim",<br /> methods: &gpio_module_methods,<br /> }</p><p>};

int gpio_device_close(struct hw_device_t* device) //關閉gpio裝置

 

int gpio_set(struct gpio_device_t *dev, int32_t gpio)//gpio置1

int gpio_clr(struct gpio_device_t *dev, int32_t gpio)//gpio置0

 

static int gpio_device_open(const struct hw_module_t* module, const char* name,
        struct hw_device_t** device)  //開啟gpio裝置,這個函數是一定要實現,因為之後要傳給hw_module_methods_t中的open函數指標:

static struct hw_module_methods_t gpio_module_methods =
{
    open: gpio_device_open
};

    在此函數中,要填充gpio_device_t一些介面,供上層調用:

    dev->hwdev.tag =  HARDWARE_DEVICE_TAG;  //預設都為此值
    dev->hwdev.version = 0;                     //應該可以隨便寫,根據自己定義
    dev->hwdev.module = module;
    dev->hwdev.close = gpio_device_close;

    dev->SetGPIO = gpio_set;
    dev->ClrGPIO = gpio_clr;
    dev->fd = open("/dev/gpio0", O_RDWR); //開啟gpio裝置

  //註冊介面,HAL_MODULE_INFO_SYM

 const struct gpio_module_t HAL_MODULE_INFO_SYM =  //固定為HAL_MODULE_INFO_SYM,不可修改,不知為什麼,照做。
{
    hwmod:
    {
        tag: HARDWARE_MODULE_TAG,
        version_major: 1,
        version_minor: 0,
        id: GPIO_HARDWARE_MODULE_ID,
        name: "gpio Stub",
        author: "guim",
        methods: &gpio_module_methods,
    }
   
};

 

4 編寫Android.mk檔案

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := gpio.c

#模組名為gpio.default
LOCAL_MODULE := gpio.default
include $(BUILD_SHARED_LIBRARY)

 

其中LOCAL_PRELINK_MODULE要置為false,否則編譯時間會出錯。

 

5 關於模組名的命名

  在jni調用stub方法時,是用到hw_get_module函數來尋找這個庫。hw_get_module函數實現如下(位置hardware/libhardware/Hardware.c ):

  在尋找模組的時候,預設是尋找system/lib/hwh目錄下的,也就是上面編譯好的gpio.default.so是放在此目錄下

  #define HAL_LIBRARY_PATH "/system/lib/hw" 

  //模組的索引值,在尋找庫的時候,要根據模組的id,即GPIO_HARDWARE_MODULE_ID來尋找模組檔案
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.board.platform",
     "ro.arch"
};

static const int HAL_VARIANT_KEYS_COUNT =
     (sizeof(variant_keys)/sizeof(variant_keys[0]));
int hw_get_module(const char *id, 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];

     /*
      * 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, prop, NULL) == 0) {
                 continue;
             }
             snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH, id, prop);
          } else {
             snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH, id);
         }
         if (access(path, R_OK)) {
             continue;
         }
         /* we found a library matching this id/variant */
          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(id, path, module);
     }

     return status;
}

 

property_get(variant_keys, prop, NULL)  會從variant_keys數組中去擷取相應變數所對應的值,然後返回給 prop :
數組中的變數對應的值,如下:
"ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
"ro.board.platform=$TARGET_BOARD_PLATFORM"

TARGET_BOOTLOADER_BOARD_NAME會根據具體的設定而確定,當然要還要看在編譯android系統的時所選的target了,此處預設選擇genic,TARGET_BOOTLOADER_BOARD_NAME和TARGET_BOARD_PLATFORM都為空白,因此,此處就找不到對應的模組檔案,如果找到會傳給prop值,然後根據snprintf(path, sizeof(path), "%s/%s.%s.so",   HAL_LIBRARY_PATH, id, prop);得到模組的絕對檔案名稱。否則則用snprintf(path,
sizeof(path), "%s/%s.default.so",  HAL_LIBRARY_PATH, id);設定模組的全路徑名,即/system/lib/hw/xxx.default.so。因此,上面產生模組名是定義為gpio.default.so, 此處xxx即為gpio。如果都沒有找到模組檔案,那麼上層調用時就會提示出錯,可以通過LOG資訊來跟蹤。

  此部分詳情,將由JNI的編寫具體討論。

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.