Android基本框架笔记

最近学习下Android 的Camera开发,做一下相关笔记,首先对Android的基本框架有一定了解,

Android 基本框架

可以参考《Android 系统源代码情景分析》、《Android驱动开发权威指南》相关书籍。
这个是很经典的两个Android基本架构图:
Android基本框架笔记_第1张图片
Android基本框架笔记_第2张图片
从图中可以很清楚得看出,整个架构可以分为五大层次:

1、System Apps:即系统应用层,这一层中都是我们使用手机时都会直接接触到的各种应用。
2、Java API Framework:即 Java 接口框架层,这一层是为了上层应用提供各种接口。
3、Native C/C++ Libraries && Android Runtime:分别是原生 C/C++ 库,安卓运行时环境。这一层中,C/C++ 库集成了许多诸如 OpenGL ES 这样的开源库,提供了很多封装好的方法。而运行时环境则是与一些核心库、Dalvik Virtual Machine 相关的东西。
4、HAL(Hardware Abstract Layer):这是一个硬件抽象层,它主要是在 Framework 层和 Linux Kernel 层之间起到一个链接作用。
5、Linux Kernel:即 Linux 内核层,整个 Android 系统实际上是基于 Linux 的内核搭建起来的。


系统应用层
1)它包含了一系列使用 Java 编写的核心程序包(Home,Phone,Browser…)。
2)应用程序通过调用框架层的接口,或者 JNI (Java 原生接口)来完成自己的业务逻辑。
3)值得注意的是,要使用 JNI 开发原生应用程序,需要与 Android NDK 配合,NDK 使得 Java 可以与 C/C++ 进行交互。


Java 接口框架层
1)也有人称为应用程序框架层,实际上这一层主要是提供给上层应用一些访问核心功能的 API 框架。
2)框架是应用程序的核心,也是开发者需要共同遵守的一个约定,大家可以在这个约定的基础之上进行一些必要的扩展,但是主体结构需要保持一致。
3)框架层提供了大量的接口,当需要使用到某些接口的时候,可以去看看它对应的官方文档。


C/C++ 库 && Android 运行时环境
1)官方 API 不是万能的,开发者常常需要自己一些 API 以实现自己的业务逻辑,这时候我们就可以通过调用 C/C++ 本地库的接口来进行个性化设计。
2)需要注意的是,这些 C/C++ 库(第三方库)是独立于 Android 系统架构实现的,但是它与系统架构处于相同的地位:
他们都使用 Linux 内核层提供服务,实现、封装模块,以供应用层调用。
3)运行时环境提供了一些核心链接库。
以及 Dalvik 虚拟机:
DVM 代替了 JVM,“.java”文件编译为“.class”文件后,再编译得到“.dex”程序,最后又打包成为 Android 可执行文件“.apk”。
每个应用都运行在自己的进程上(一个应用程序对应一个虚拟机,一一对应关系),而 DVM 为它分配自有实例。
Dalvik 的好处是,使得一台设备可以运行多个虚拟机程序,并且消耗比较少的资源。


硬件抽象层
1)向下屏蔽了硬件驱动模块的实现细节,并向上提供硬件访问服务。
2)这一层的存在,实际上主要是为了保护移动设备厂商的商业利益:
Linux 内核源码遵循 GPL 协议,基于它所修改的源码需要完全公开,如果设备厂商通过修改内核源码来提供服务,就需要公开对应的代码,这就暴露了很多硬件相关的参数和实现的细节。
3)为了保证商业利益,在 Android 架构中提供一个硬件抽象层给设备厂商对硬件做出具体实现,而 Linux 内核仅提供简单的硬件访问通道。由于 Android 源码遵循商业友好的Apache License 协议,这样设备厂商的利益就得到了保障。

HAL屏蔽了不同硬件设备的差异,为Android提供了统一的访问硬件设备的接口。不同的硬件厂商遵循HAL标准来实现自己的硬件控制逻辑,但开发者不必关心不同硬件设备的差异,只需要按照HAL提供的标准接口访问硬件就可以了。
有了HAL层之后,他们可以把一些核心的算法之类的东西的实现放在HAL层,而HAL层位于用户空间,不属于linux内核,和android源码一样遵循的是Apache license协议,这个是可以开源或者不开的。


Linux 内核层
1)Android 是基于 Linux 内核开发的(官方给出的例子是,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和底层内存管理)。
2)一个很重要的部分是 Linux 内核驱动部分,在 Camera 流程中,我们的控制指令最终要通过内核驱动发送给 Camera 设备,同时也要通过它对设备返回的数据向上传输。
3)在 Android 中,增加了 YAFFS2(Yet Another Flash File System, 2nd edition)文件系统。(这个系统没有去深入了解)
增加了一个进程间通信的机制 IPC Binder,通过 Binder ,可以使不同进程可以访问同一块共享内存。Binder 机制在整个 Camera 流程中起到了十分重要的作用,它主要使用在 CameraClient 与 CameraServer 的交互中(Camera 架构中,采用了一个 C/S 交互的流程,在之后的学习中会有比较深入的了解)。

Android 硬件抽象层

1.1 HAL 层模块编写规范

Android 系统的硬件抽象层以模块的形式来管理各个硬件访问接口。每一个硬件模块都对应有一个动态链接库文件。

在系统内部,每一个硬件抽象层模块都使用结构体 hw_moudle_t 描述,而硬件设备则使用结构体 hw_device_t 来描述。下面分别描述硬件抽象层模块文件的命令规范以及结构体 hw_moudle_t 和 hw_device_t 的含义。

1.1.1 硬件抽象层模块编写规范

硬件抽象层的模块文件的命令规范定义在 hardware/libhardware/hardware.c 文件中

/**
 * There are a set of variant filename for modules. The form of the filename
 * is ".variant.so" so for the led module the Dream variants 
 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
 *
 * led.trout.so
 * led.msm7k.so
 * led.ARMV6.so
 * led.default.so
 */

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"
};
  • 硬件抽象层模块文件的命令规范:.variant.so
  • MOUDLE_ID 表示模块的ID variant 表示variant_keys四个系统属性值的其中一个,顺序依次
  • variant 如果四个属性值都不存在,则将 variant 的值设为 default

variant变量取值过程: ro.hardware: /prop/cpuinfo 的 Hardware
字段其他三个在:/system/build.prop 中查找

1.1.2 硬件抽象层模块结构体定义规范

结构体 hw_device_t 和 hw_moudle_t 及其它相关结构体定义在文件 hardware/hardware.h 中

/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;

    uint16_t module_api_version;
    
    uint16_t hal_api_version;
#define version_minor hal_api_version

    /** Identifier of module */
    const char *id;

    /** Name of this module */
    const char *name;

    /** Author/owner/implementor of the module */
    const char *author;

    /** Modules methods */
    struct hw_module_methods_t* methods;

    /** module's dso */
    void* dso;

#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif
}
  1. 硬件抽象层中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须是 hw_moudle_t。
  2. 硬件抽象层的每一个模块都必须存在一个导出符号 HAL_MODULE_INFO_SYM,它指向一个自定义的硬件抽象层模块结构体。
  3. 结构体 hw_moudle_t 的成员变量 tag 值必须设置为
    HARDWARE_MODULE_TAG,用来标识这是一个硬件抽象层模块结构体。
  4. 结构体 hw_moudle_t 的成员变量 dso
    用来保存加载硬件设备层模块后得到的句柄值。我们知道每一个硬件抽象层模块都对应有一个动态链接库文件。加载硬件抽象层模块的过程实际上就是调用dlopen 函数来加载与其对应的动态链接库文件的过程。在调用 dlclose 函数来卸载这个硬件抽象层模块时,需要用到这个句柄值。
  5. hw_module_methods_t 变量定义了硬件抽象层模块的操作方法列表。
typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;
  1. 结构体 hw_device_t 用来描述一个已经打开的硬件设备。
    typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

    /** Close this device */
    int (*close)(struct hw_device_t* device);

} hw_device_t;


###################################
(1) 硬件抽象层模块中的每一个硬件设备必须自定义一个硬件设备结构体
    而且它的第一个成员变量的类型必须为 hw_device_t
(2) 结构体 hw_device_t 的成员变量 tag 的值必须设置为 HARDWARE_DEVICE_TAG
    用来标识这是一个硬件抽象层中的硬件设备结构体
(3) close 是一个函数指针,用来关闭一个硬件设备

1.2 编写硬件抽象层模块接口

每一个硬件抽象层模块在内核中都对应一个驱动程序,硬件抽象层模块就是通过这些驱动程序来访问硬件设备的,他们通过读写设备文件来进行通信。

硬件抽象层的模块接口源文件一般保存在 harware/libhardware 中,以 freg模块 寄存器操作为例,进行说明,它的目录结构如下:

~ Android/hardware/libhardware
---- include
    ----- hardware
        ---- freg.h
        
---- moudles
    ---- power
        ---- freg.c
        ---- Android.mk

其中 freg.h 和 freg.c 是源文件,Android.mk 是模块的编译脚本文件

—> frag.h

#ifndef ANDROID_INCLUDE_HARDWARE_FRAG_H
#define ANDROID_INCLUDE_HARDWARE_FRAG_H

#include 

__BEGIN_DECLS

// 硬件模块ID
#define FRAG_HARDWARE_MODULE_ID "freg"

// 硬件设备ID
#define FRAG_HARDWARE_DEVICE_ID "freg"

// 自定义硬件模块结构体
typedef struct freg_moudle_t {
    struct hw_module_t common;
}

// 自定义设备结构体
typedef struct freg_device_t {
    struct hw_device_t common;
    int fd;
    int (*set_val)(struct freg_device_t* dev, int val);
    int (*get_val)(struct freg_device_t* dev, int* val);
}

__END_DECLS

#endif  // ANDROID_INCLUDE_HARDWARE_FRAG_H

这个文件中的常量结构体都是按照硬件抽象层模块编写规范来定义的。

宏 FRAG_HARDWARE_MODULE_ID 描述模块ID
宏 FRAG_HARDWARE_DEVICE_ID 描述设备ID
结构体 freg_moudle_t 描述自定义的模块结构体
结构体 freg_device_t 描述虚拟硬件设备,其中 fd 用来描述打开的文件设备 /dev/frag,成员变量 set_val 和 get_val 是函数指针,用来写和读虚拟硬件设备freg的寄存器地址。
—> frag.c

#define DEVICE_NAME "/dev/frag"
#define DMOUDLE_NAME "frag"
#define MOUDLE_AUTHOR "kevin"

/* 设备打开与关闭接口 */
static int freg_device_open(const struct hw_moudle_t* moudle, const char* id, struct hw_device_t** device);
static int freg_device_open( struct hw_device_t** device);

/* 设备寄存器读写接口 */
static int freg_get_val(struct freg_device_t* dev, int* val);
static int freg_set_val(struct freg_device_t* dev, int val);

/* 定义模块操作方法结构体变量  */
static struct hw_moudle_methonds_t freg_moudle_methods = {
    open: freg_device_open
}

/* 定义模块结构体变量 */
struct freg_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = 1,
        .hal_api_version = 1,
        .id = POWER_HARDWARE_MODULE_ID,
        .name = MOUDLE_NAME,
        .author = MOUDLE_AUTHOR,
        .methods = &freg_moudle_methods,
    },

    .init = power_init,
    .setInteractive = power_set_interactive,
    .powerHint = power_hint,
};

在这段代码中,最值得关注的是模块变量 HAL_MODULE_INFO_SYM 的定义,按照硬件抽象层模块编写规范,每一个硬件抽象层模块必须导出一个名称为 HAL_MODULE_INFO_SYM 的符号,它指向一个自定义的硬件抽象层模块结构体。

—> Android.mk

 1. LOCAL_PATH := $(call my-dir)
 2. include $(CLEAR_VARS)
 3. LOCAL_MODULE_TAGS := optional
 4. LOCAL_PROPRIETARY_MODULE := true
 5. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 6. LOCAL_SHARED_LIBRARIES := liblog
 7. LOCAL_SRC_FILES := freg.cpp
 8. LOCAL_MODULE := freg.default
 9. include $(BUILD_SHARED_LIBRARY)
  1. 这是硬件抽象层的编译脚本文件,第九行指定将模块编译成一个动态链接库文件。
  2. LOCAL_MODULE_PATH 指定了库保存的路径(8.1: /vendor/lib/hw)。
  3. 将硬件抽象层模块 freg 对应的文件命令为 freg.default,编译成功后在指定的位置成功
    freg.default.so,当我们要加载 freg 抽象层模块 freg 时,只需要指定它的ID值,就能找到对应的 so 文件。

1.3 硬件抽象层模块的加载过程

在 Android 硬件抽象层,负责加载硬件抽象模块的函数时 hw_get_moudle,它的原型如下:

#Android/hardware/libhardware/include/hardware/hardware.h

/**
 * Get the module info associated with a module by id.
 *
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module(const char *id, const struct hw_module_t **module);

===================================================
id:输入参数,表示要加载的硬件抽象层模块ID
moudle:输出参数,如果加载成功,它指向一个自定义的硬件抽象层模块结构体

函数返回值等于0代表加载成功,小于0则表示加载失败

简单阐述一下 hw_get_module 的实现过程,代码在 hardware.c 中

  1. 从 variant_keys 中查找对应注册的模块,获取动态链接库路径。
  2. 调用 dlopen 函数将动态连接库加载到内存中,成功则返回
    handler 句柄。
  3. 使用 dlsym 函数获取名称为 HAL_MODULE_INFO_SYM_AS_STR 的符号,这个
    HAL_MODULE_INFO_SYM_AS_STR 符号指向一个自定义的抽象层模块结构体,它包含了模块的所有信息。
  4. HAL_MODULE_INFO_SYM_AS_STR 自定义抽象层模块结构体的第一个成员变量为 hw_moudle_t,将
    hw_moudle_t.id 与所要加载的模块ID做比较,返回结果。

你可能感兴趣的:(Android)