当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 学习笔记 > 嵌入式之Android移植学习笔记

嵌入式之Android移植学习笔记 时间:2018-08-23      来源:未知

jni:java native interface java调用c语言的接口

HAL(hardware abtract layer) 硬件抽象层

binder 虚拟设备,不是硬件,用于进程间通讯(独立的进程通讯机制)

更为安全高效、拿一段内存进行设备通讯、相比共享内存安全、可以创建多个

一般学习过程首先研究binder

总结:binder是更为安全高效的进程间通讯机制

libraries 库 native 本地

C/C++开发的库,一般成为本地库,上层开发一般是java,对于内核而言是应用层

surface Manager 界面开发、图形开发

Media Framework 音频框架

SQLite 数据库、本地的库

OpenGL|ES 图形渲染

FreeType 字体素材

WebKit 浏览器的内核

SGL 2d图形渲染

SSL 加密套接层

Libc 标准C库

Android Runtime 安卓运行时环境 runtime 一般指运行环境

Core Libreries

java 虚拟机将代码解析到对应的平台

Application Framework

Activity Manager 活动窗口(界面)

Window Manager 窗口管理

Content Provider 数据共享,可使2个app共享数据

View system 界面渲染

Notification Manager 通知

Package Manager apk包的管理

Telephone Manager 打电话

Resource Manager 资源管理

Location Manager 与GPS相关,定位

XMPP Service 短信接收和发送

Application

应用程序

home等(laucher)

boot.img 是一个uboot + kernal + rootfs

①source bulid/envsetup.sh

②lunch //envsetup里面的一个函数

③extract-bsp

④make -j2

⑤pack //打包

boot.img + system.img + recovery.img = 最终产物

如何进行数据的传输?

①adb devices

②adb push .\文件名 /system/lib

出现系统只读则需要重新挂载 执行 adb remount

③使用adb shell 进入到对应的安卓命令行 //安卓和电脑使用数据线连接,usb可调式确认后才可使用

④使用 adb pull /system/lib/文件名 将对应的文件获取到当前目录

使用 exit 退出安卓平台

⑤使用apk 安装

adb install *.apk //有个push过程,再进行安装

关于linux网络这块问题解决

使用局域网时用静态ip连接开发板 桥接

使用wifi时用动态ip连接网络 自定义桥接 使用wifi网卡

使用网线动态ip连接网络 自定义桥接 使用以太网卡

5层模型

app 应用

app framework 应用框架

libs 核心库

HAL 硬件抽象层

kernel 内核

①source build/envsetup.sh

455-460 添加默认的编译菜单

1506-1512 查找device和vendor目录下名称为vendorsetup.sh脚本并执行

查看device/softwinner/fspad-733/vendorsetup.sh

添加产品编译

8个产品编译选项保存在 LUNCH_MENU_CHOICES数组中

②lunch

执行build/envsetup.sh 481

488-490,打印选项菜单,读取用户输入值

495-507,根据用户输入选项,从LUNCH_MENU_CHOICES数组菜单项内容,保存到selection变量

518-536,将产品名和编译类型提取,产品名保存到product,编译类型保存到variant

544-546,赋值到对应的全局变量,导出

550,设置剩余的环境变量

551,打印变量列表

③extrap-bsp

将lichee编译出来,boot.img和modules.ko拷贝到androidL下面,为下一步打包做准备

④make

执行androidL/Makefile

执行build/core/main.mk

执行对应93行,包含build/core/config.mk

跳转到build/core/config.mk

63-99,赋值一些列编译系统内部的子makefile路径,为后面直接使用,可以编译成不同类型产物

151,包含build/core/envsetup.mk

跳到该mk下

138行,包含了build/core/product_config.mk

跳转到该文件

189,读取源码树下面所有的名称为AndroidProducts.mk产品makefile

获取device/softwinner/fspad-733/AndroidProducts.mk

该文件获取device/softwinner/fspad-733/fspad_733.mk

定义软件系统配置 app属性 copy产品信息

150-155行,查找device和vendor目录下面产品目录名为BoardConfig.mk获取文件

跳转到mk下

获取文件为 device/softwinner/fspad-733/BoardConfig.mk

该文件进行硬件配置

配置板子上硬件,主要是wifi和bt配置

⑤pack //由于一开始source后即可使用pack命令来进行解析

打包 boot.img+system.img+recovery.img

打包为 lichee/tools/pack/sun8iw3p1_andorid_fspad_733_card0.img

添加一个产品的时候需要在哪些文件上添加?

①device/osoftwinner/fspad-733/vendorsetup.sh 下添加产品菜单

②device/softwinner/fspad-733/AndroidProducts.mk 下添加对应产品文件名

③device/softwinner/fspad-733/fspad_733.mk 软件添加文件

④device/softwinner/fspad-733/BoardConfig.mk 硬件添加文件

A := a A赋值为a

新建一个文件夹hello

在里面

编写 hello.c

编写自己的Android.mk

#current module path

①LOCAL_PATH := $(call my-dir)

#clear vars old value

②include $(CLEAR_VARS)

#module name don't need to specify a path.

③LOCAL_MODULE := hello

#specify source file

④LOCAL_SRC_FILES := hello.c \

world.c\

#build this module to executable binary file

⑤include $(BUILD_EXECUTABLE)

最后

编译

source build/envsetup.sh

lunch 9

mmm device/softwinner/fspad-733/hello/hello.c //绝对路径

如果在当前目录下则为 mm 使用当前路径来进行编译模块

为什么会提示对应的依赖错误?

No private recovery resources for TARGET_DEVICE fspad-733

make: Entering directory `/home/linux/fs733/androidL'

make: *** No rule to make target `/hello.c', needed by `out/target/product/fspad-733/obj/EXECUTABLES/hello_intermediates/hello.o'. Stop.

make: Leaving directory `/home/linux/fs733/androidL'

路径出错

可能是自己对应的代码敲写错误导致

adb的使用说明:

adb的使用

使用过程:

1.通过usb线将主机和平板从机连接,打开PhoenixSuit工具,必须显示和设备已经连接上。

2.使用以下命令与平板android系统交互:

a.adb shell 进入到android的linux的命令行模式

exit 进入到命令行模式之后需要的退出

b.adb push 主机需要被推送的文件目录 平板存放推送文件的位置

将文件推送到从机指定目录

c.adb pull 平板中需要被拉去出来的文件的位置

从从机拉取文件到主机

d.adb install xxxx.apk 将xxxx.apk android应用程序安装到从机上

e.adb devices 列出所有连接到主机的从机信息

tips:在敲adb命令的时候,如果提示read-only file system的时候,你就使用命令adb remount 重新挂载一下

android的文件系统就可以了。如果说还是不行,任然提示该信息,那么就要修改从机目录属性。

编译完成后根据

Install: out/target/product/fspad-733/system/bin/hello

来找到对应目录将文件拷贝到windows平台下

打开phoenixSuit程序,连接安卓后

在含有adb.exe的文件下面的空白处点击 shift + 鼠标右键 训责phoenixSuit运行

使用adb push .\hello /system/bin (由于是执行phoenixSuit后,/system/bin直接执行)

使用adb shell进入安卓shell

hello 即可打印对应的函数

init启动流程

①解析/init.rc

action_list 、service_list

②添加action到action_queue中

③执行action相关命令(与action的序列有关),查看是否有需要重新启动的服务,如果有则启动这些服务

④poll监听3路socket

属性设置→修改属性

组合键→对应服务

服务进程退出→将标志位置为重启与restart进行结合重启进程

init启动zygote

①修改进程名

②android Runtime的start方法 启动虚拟机 vm

③调用执行zygote init的main方法

C++代码截止

init启动后执行system/core/init/init.c

1059 判断ueventd 还是init

1060 如果启动设备,解析设置uevent事件,创建设备文件

1062 若是启动watchdogd 设置看门狗以及喂狗

1072-1081 创建一些目录并挂在文件系统

1120 从文件中加载系统属性默认值

1123 解析init.rc 建立服务链表和动作链表

1128-1134 创建内部action

1139 创建init动作init到动作链表

1152 添加early-init动作到动作链表

action_for_each_trigger("early-init", action_add_queue_tail);

1145-1154 创建内部action并触发

①early_init

②wait_for_coldboot_done(内部动作)

等待冷启动设备扫描(为每个设备创建设备文件)

③keychord_init

打开组合键的监听文件

④console_init

检测console终端是否存在,显示logo

⑤property_service_init

打开属性修改服务的socket

⑥signal_init

设置sigchild信号处理,回收孤儿进程资源

⑦late-init

⑧queue_property_trigger

促发rc脚本中property:属性名= 属性值 形式的动作

1166 主循环

①execute_one_command();

执行一个命令

②restart_processes();

扫描是否有服务需要重启

③ 监控并处理属性修改请求、组合按键启动服务请求和检测服务进程退出

init.rc本地服务被启动

①ueventd 接收、解析和处理uevent,新建/删除设备文件,固件加载

②console 终端,启动shell程序

③adbc adb程序服务端

④service manager 服务管理程序

⑤vold(volume daemon) 完成系统cdrom,usb大量存储,mmc卡等扩展存储的挂载任务

⑥netd 网络服务

⑦surfaceflinger 绘制android程序的ui,完成2d和3d的无缝融合

⑧zygote 启动虚拟机服务

⑨media 多媒体服务

⑩bootanim 负责android播放开机启动动画以及背景音乐

其中关键

①zygote

定义 在init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

class main

socket zygote stream 660 root system

onrestart write /sys/android_power/request_state wake

onrestart write /sys/power/state on

onrestart restart media

onrestart restart netd

可以看到主要启动的程序 app_process

实现在frameworks/base/cmds/app_process/app_main.cpp‘

186 main函数

解析参数后

201行之后得到

227-236 将参数 runtime

246-264 将后面参数解析出来,得到结果

301-304 进程名更改nicename = zygote

307 运行类com.android.internal.os.ZygoteInit

runtime.start();

rumtime是AndroidRuntime类

通过找到androidruntime类找到调用的start成员函数

frameworks/base/core/jni/AndroidRuntime.cpp 930行

966 启动java虚拟机

974 注册andorid本地函数

988 找到string类

1006 将传进来的classname com.android.internal.os.ZygoteInit

转换为路径com/android/internal/os/ZygoteInit

1007 找到com/android/internal/os/ZygoteInit类

1018 com/android/internal/os/ZygoteInit类的main函数进入到java

ZygoteInit类定义在目录

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

转到里面 main函数 644

通过644里面函数 同时AndroidRuntime那边将参数"start-system-server"也传进来

668行通过注册 zygote socket服务端

671 预加载了一些资源和库

686 调用startSystemServer函数启动了systemserver

查看startSystemServer 在573行定义

607行 创建一个子进程来运行forkSystemServer

624调用handleSystemServerProcess实现在494行

537行 RuntimeInit.zygoteInit();

该函数定义在rameworks/base/core/java/com/android/internal/os/RuntimeInit.java 267行

查看applicationInit函数在297行定义

关键321行,invokeStaticMain();

其中第一个参数就是前面传递过来的 com.android.server.SystemServer

该句子调用了com.android.server.SystemServer类的main函数

定义在frameworks/base/services/java/com/android/server/SystemServer.java 170

运行了run函数,定义在179

255-257 启动了好多services

其中316行,ActivityManagerService的systemReady函数,表示ActivityManagerService的ready

ActivityManagerService的定义。frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

在11373,在systemReady调用startHomeActivityLocked启动桌面应用

bootanimation

init.rc 中

service bootanim /system/bin/bootanimation

class main

user graphic

group graphics

disabled

oneshot

找到bootanimation源码 frameworks/base/cmds/bootanimation/bootanimation_main.cpp 39行

55行,创建开机动画播放线程

找到BootAnimation类,打开可以看到是利用opengl渲染的。

230行,看readyToRun函数

291行~301行,判断在/sytem/media目录下否有bootanimation的素材压缩包。

313行,在线程循环函数中判断了上面判断结果,是否有bootanimation.zip

如果有就播放,如果没有就调用android函数。

328行,android函数定义

330,331行,可以看到定义图片的位置。

下面opengl播放图片。

找到图片放的位置, find . -depth -name android-logo-mask.png

在frameworks/base/core/res/assets/images/android-logo-mask.png

所以如何定制开机启动动画?

1.在/data/local或/system/media目录下添加自己的动画bootanimation.zip文件(同时也可以添加相应的开机音乐boot.mp3),这样在系统启动时就会播放自己的开机动画和播放音乐。

2.可以替换一下frameworks/base/core/res/assets/images/ 目录下两张图片。

粗略启动流程

uboot

内核

挂载根文件系统

挂载system系统 里面含有很多文件

执行第一个进程init

解析init.rc文件

执行了一些列命令,启动了一些服务,后退化成为一个守护进程

该守护进程 ①系统属性修改 ②重启子进程 ③组合键按下启动对应服务

其中有一个很重要的服务zygote

创建虚拟机

运行systemserver 启动很多java服务

其中有个很重要的服务activityManager执行一个systemReady函数运行了一个界面

VFS 统一文件系统差异,为上层提供接口

HAL

统一下层硬件差异,为上层提供接口

为了保护硬件供应商的知识产权

不是所用硬件设备都有标准的linux kernel接口

为什么open在modules中,而close在device

每个device是对应不同的关闭方式,所以在device中关闭,而打开只需统一打开

hal_*.h

#include

//stub具体某一个device id号

#define LED1 1

//led模块stub的id

#define LED_HARDWARE_MODULE_ID "led"

①定义一个led_module_t 模块结构体

里面第一个结构体必须为struct hw_module_t 的对象

struct led_module_t {

struct hw_module_t common;

};

②定义led_device_t 设备结构体

里面第一个为struct hw_device_t 结构体

struct led_device_t {

struct hw_device_t common;

int fd; //存放调用open返回描述符

int (*led_on)(struct hw_device_t *dev); //

int (*led_off)(struct hw_device_t *dev);

};

hal_*.c

①定义一个hw_module_t 对象HAL_MODULE_INFO_SYM必须为这个对象

在对象里面对其成员进行初始化

struct hw_module_t HAL_MODULE_INFO_SYM = {

tag: HARDWARE_MODULE_TAG,

version_major: 1,

version_minor: 0,

id: LED_HARDWARE_MODULE_ID,//头文件中含有

name: "led module",

author: "farsight",

methods: &led_module_methods,//主要是实现一些方法

};

②定义一个 hw_module_methods_t 对象led_module_methods 里面装着一个led_open函数地址

static struct hw_module_methods_t led_module_methods = {

open: led_open,

};

③定义 led_open函数

static int led_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)

{

char devpath[128] = "/dev/";

struct led_device_t *dev;

//函数中新建一个结构体指针指向led_device_t开辟的空间中

//1.分配描述被打开的设备device结构体

dev = (struct led_device_t *)malloc(sizeof(struct led_device_t));

if (!dev) {

LOGE("alloc device memory failed\n");

return -EFAULT;

}

memset(dev, 0, sizeof(struct led_device_t));

//对其结构体成员进行赋值

//2.填充设备结构体的成员

dev->common.tag = HARDWARE_DEVICE_TAG;.//必须为这个

dev->common.version = 0;

dev->common.module = (struct hw_module_t *)module;

dev->common.close = led_close;

dev->led_on = led_on;

dev->led_off = led_off;

strcat(devpath, name);//devpath设备文件路径 /dev/name

//通过结构体赋值close、led_on、led_off

//3.调用系统调用open,open再通过linux内核调用到驱动代码中的led_open函数

dev->fd = open(devpath, O_RDWR);

if (dev->fd == -1) {

free(dev);

LOGE("open device failed\n");

return -1;

}

//4.返回打开的具体设备的描述结构体指针

*device = &(dev->common);

return 0;

}

④led_on、led_off、led_close的实现

主要是调用了系统调用,通过系统调用将设备的信息传递给结构体

static int led_on(struct hw_device_t* device)

{

struct led_device_t *dev = (struct led_device_t *)device;

if (dev->fd != -1)

return ioctl(dev->fd, LED_ON); //调用系统调用ioctl,ioctl回去通过linux内核调用到驱动中unlocked_ioctl

else

return -1;

}

static int led_off(struct hw_device_t* device)

{

struct led_device_t *dev = (struct led_device_t *)device;

if (dev->fd != -1)

return ioctl(dev->fd, LED_OFF);

else

return -1;

}

static int led_close(struct hw_device_t* device)

{

struct led_device_t *dev = (struct led_device_t *)device;

if (dev->fd != -1) {

close(dev->fd);//调用系统调用close,close回去通过linux内核调用到驱动中led_close

free(dev);

}

return 0;

}

lib中

使用hal提供的框架接口 hw_get_module();//获取到对应hal里模块的结构体地址

通过访问这个结构体的methods里的led_open即可调用到底层的open

touch可以更新对应的代码时间戳,使编译的时候重新编译

整体一个流程:

①app上 通过led_open会调用coreLib库

②库中使用hal接口函数hw_get_module();

来通过LED_HARDWARE_MODULE_ID进行获取到对应的模块module,

③通过获取模块中的第一个成员hw_modules_t 中的mothods方法集合里面的open方法将对应的模块传入进入到hal中

④在hal中通过上面传入下来的name,将器设备路径给获取后,通过打开设备驱动的open函数即完成获取,并且创建一个led_dev结构体,通过结构体进行初始化,将其传递给在coreLib中的device对象,便于后续的操作使用,虽然传递过去的不是led_dev对应的结构体,但是由于hw_device_t与对应结构体的首地址一致,所以通过强转即可获取到led_dev对应的方法

Android硬件抽象层框架代码在 hardware目录

Dalvik虚拟机作用:

①Dalvik基于虚拟机,JVM基于栈,所以效率更高

②Dalvik可运行压缩过,针对内存优化过的dex,减少文件尺寸,提高查找类的速度

③Dalvik每一个应用程序运行与一个独立进程

android系统中,使用 make ramdisk命令 只编译文件系统

android系统app framework层,HAL层,core Libs 代码编译之后在system.img

通过GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V")找到对应的方法应该

①([Ljava/lang/String;)V") ===》 [ 数组 String =》tring 类型,参数 V ===》void 返回值

②main为函数名 即可推出 void main (String [] )

init.rc包含5个部分

①import导入 ②commond命令 ③service服务 ④action行为 ⑤option选项

简述linux内核编译步骤

①进入lichee ./build.sh config //编译选择配置

②./build.sh 编译内核和uboot

简述android源码的编译步骤(以fspad-733配套androidL源码为例)进入androidL

①source build/envsetup.sh 添加配置调试命令到shell进程

②lunch 选择编译的产品好

③extract-bsp 将内核生成的bImage和模块拷贝到androidL/device/softwinner/fspad-733/下

④make -j2 编译

androidL ===>生成 system.img(非内核部分) + ramdisk.img(rootfs) + recovery.img(恢复出厂镜像)

lichee ===>生成 uboot.bin + uImage === > bImage

简单介绍一下Android系统中的HAL,并说出旧HAL架构libhardware_legacy和新HAL架构libhardware的区别

①屏蔽下层硬件差异,为上层提供统一接口

②保护硬件的知识产权

③硬件设备不全都有linux kernel接口

旧的没有经过封装,上层可以直接操走硬件

新的通过hal层将硬件分类,一个类一个stub,通过stub再进行访问

请介绍Android系统架构,并简单说明每层架构的作用

①linux内核 在内核基础上添加了android特有的驱动,binder驱动(进程间通讯)

②HAL 屏蔽下层差异,为上层提供统一接口

③核心库 libs 一些三方库和c/c++库的提供,

runtime Dalvik虚拟机和对应的运行环境

④app framework 让应用开发更为方便

⑤app 一些与用户进行交互的程序

android源码树添加新产品目录支持,必须有哪几个文件?

①vendorsetup.sh 添加产品编译的选项

②BoardConfig.mk 硬件方面定制

③AndroidProduct.mk 产品文件列表

④产品名.mk 定制软件系统的配置

上一篇:c语言指针详解,学霸课堂记录

下一篇:C++学习总结,学霸课堂笔记

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部