android源码树结构介绍

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nonmarking/article/details/70500100

最近在学习android源码,在这里分享学习过程中总结的一些知识。仅供参考,如有错误,还请指教。

无论是下载源码还是在AndroidXref 上在线阅读源码,我认为首先都要弄明白源码中各个目录下的代码所负责的工作,以nougat 7.1.1_r6为例,其源码树目录结构如下
这里写图片描述
下面就来介绍一下各部分的作用:

bionic

这是google专为android开发的一套c标准库,采用bsd许可形式开源,google开发它的目的是为了替代glibc(android基于linux kernel,glibc也是linux kernel上应用最广泛的c库,但是二者都是采用copyleft license的,google采用bsd许可开发的bionic相当于在copyleft的kernel和non-copyleft的应用之间建立了一个隔离层,这样无疑有利于android系统上应用生态的发展),并且为了适应移动设备的性能做到了轻量化和高运行速度。
bionic目录包含如下内容
这里写图片描述
在readme中就有对bionic的详细介绍,可以看到bionic中包含了
libc - c库,我们熟悉的fopen啥的就在这里
libm - 数学运算库,比如开方运算sqrt
libstdc++ - c++运行时支持库
上面这些库在编译后都能在手机的system/lib和lib64目录下找到对应的so。
libdl 和 linker - 都是与动态库链接有关的,例如dlopen等,libdl里只包含了接口,具体的实现都在linker目录下。libdl对应system/lib或system/lib64下的so,而linker则对应system/bin下的linker和linker64。
bionic支持aarch64, arm, mips, mips64, x86, 和 x86-64框架,相应的,在libc等库的目录下也能找到对应不同框架的实现。

bootable

引导和启动有关的内容,刷机时的recovery mode相关源码就在bootable/recovery目录下,用boot.img启动的linux系统就对应平时的正常模式,而recovery模式就对应用recovery.img启动的linux系统,在recovery模式下与普通模式最大的不同就是要把recovery程序作为服务启动,在bootable/recovery/etc/init.rc中就可以看到对应的内容
service recovery /sbin/recovery
recovery程序读取不同的命令即可完成安装ota包、擦除user data等工作,对应的代码就在bootable/recovery/recovery.cpp中。
如果你看的是高通平台的源码,可能还会在bootable目录下找到一个bootloader文件夹,其结构如下

-bootloader/LK
   -app            #功能实现,如 adb 命令
   -arch           #CPU架构
   -dev            #设备驱动
   -include      #头文件
   -kernel        #主文件,main.c
   -lib             #库文件
   -platform    #平台文件,如:msm8916
   -projiect     #mk文件
   -make       #mk文件
   -scripts      #脚本文件
   -target        #目标设备文件
   AndroidBoot.mk
   makefie

这就是所谓的Little Kernel(LK) based Android bootloader ,是高通为旗下系列芯片开发的开源的 android bootloader,作用是用来引导内核启动的,或者通过按键等控制进入recovery模式。这其中重要的一步就是如何解析boot.img和recovery.img的头部信息,提取这两部分的参数,传递给内核,另外值得注意的是,lk中还控制着内存的分布。

cts

Compatibility Test Suite的内容,是所有android设备都必须通过的一套测试,在开发过程中持续进行cts测试有助于保持与android标准的兼容性,除此之外,cts的测试代码也可以帮助我们学习android sdk的使用,例如cts/tests/tests/media/src/android/media/cts/目录下的内容全是与多媒体测试相关的代码,其中的EncoderTest.java就为我们展示了使用MediaCodec进行多媒体文件编码的方法。

pdk

Platform Development Kit相关的测试代码和工具,pdk本身是google在新版本android正式发布之前先release给部分签约的oem厂商和半导体厂商的android代码子集,这样做的目的是让oem厂商和半导体厂商可以提前开始适配新版本android,所以pdk本身的内容是不会公开的。

sdk

和pdk目录类似,这个目录下也不是android sdk的源码,而是一些sdk相关的工具,例如模拟器。

ndk

Android Native Development Kit,通过ndk,我们可以在android应用程序中使用native代码,这次和前面两个目录不一样了,真的是ndk的源码,有关ndk的使用可以参考我之前的文章

art&dalvik

art和dalvik相关的代码。Dalvik是Google公司自己设计用于Android平台的虚拟机,与pc上的jvm不同。art即Android Runtime。ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

libcore

核心java库,例如xml,json解析相关的库。

packages

android系统自带应用或服务等的源码,例如日历、camera、计算器等。

developers

包含了android sdk sample的源码。

development

与应用程序开发工作相关的各种内容,既有sample程序,也有copyright模板,甚至还有codestyle配置文件,还包括一些实用工具,比如tools/checkstyle就是一个可以检查java代码书写是否规范的工具,在实际开发repo upload步骤之前,就可以用它来检查一下。

docs

与android相关的各种文档。

external

AOSP中用到的各种开源项目的源代码,例如libxml,okhttp,opencv等。

frameworks

我们经常说的android framework层的代码就在这里了,既包括java库也包括C库层的内容,囊括了android的各个核心组件,比如surfaceflinger,media framework等

hardware

硬件抽象层(HAL)代码。其中不仅有android的libhardware库的代码,还包括一些硬件厂商开源的HAL代码,比如在hardware/qcom/media/msm8996/mm-video-v4l2目录下就能找到高通视频编解码器的HAL代码。

libnativehelper

包含了一些配合JNI使用的实用方法。

prebuilts

包含了各种以bin形式发布的编译好的工具,这其中就包括交叉编译工具链。

platform_testing

与系统测试相关的代码,例如app的smoke测试相关的java代码就在这个目录下。

system

系统底层应用和组件(蓝牙、连接等)的源码。在system/core目录下还可以找到init程序的代码以及init.rc,它们是android系统开机第一个运行的程序和对应的配置文件,此外,包括adb、logcat、fastboot等核心应用的源码也在这个目录下。

tools

包含了三个工具
fat32lib:用于操作FAT文件系统的Java库
gradle:项目自动化构建工具
acts(Android Comms Test Suite):一个用来测试设备连接性能(蓝牙、WiFi、蜂窝网络)的测试套件

toolchain

包含了GNU development tools。

device

设备配置相关的内容。如果想要在原生android中添加自己的定制产品的话,就需要在这个目录下做文章了。如果是在androidxref网站上看的代码的话,会发现该目录下有moto、huawei等厂商的文件夹,这其实就对应每一代nexus设备的信息。目录结构如下

-device
 -company公司名
   -product产品名
     -vendorsetup.sh
     -AndroidProducts.mk
     -BoardConfig.mk
     -device.mk
     -...

一款产品需要多个配置文件,对应的是真实产品的构建顺序,如下
1、芯片架构层(architecture):如arm,x86等
2、核心板层(board):硬件电路的核心板层配置,对应的是BoardConfig.mk,以华为nexus6p为例,该文件的内容包括目标架构、硬件设备属性、分区布局、boot地址等一系列参数,如下

TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_ABI := arm64-v8a
...
BOARD_BOOTIMAGE_PARTITION_SIZE := 33554432
BOARD_RECOVERYIMAGE_PARTITION_SIZE := 33554432
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 3221225472
...
BOARD_WLAN_DEVICE := bcmdhd
...

3、设备层(device):外围设备的配置,例如键盘、触摸屏
4、产品层(product):最终系统的软件模块和配置,例如默认国家语言,对应的是AndroidProducts.mk,以华为nexus6p为例,该文件的内容是

PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/aosp_angler.mk

它直接指向了aosp_angler.mk,该文件的内容是

#将配置文件复制到设备指定目录中
PRODUCT_COPY_FILES := device/huawei/angler/apns-full-conf.xml:system/etc/apns-conf.xml

# Inherit from the common Open Source product configuration
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk)
#设置系统属性,这些属性最终会写入设备的system/build.prop中,例如#PRODUCT_MANUFACTURER就对应ro.product.manufacturer
PRODUCT_NAME := aosp_angler
PRODUCT_DEVICE := angler
PRODUCT_BRAND := Android
PRODUCT_MODEL := AOSP on angler
PRODUCT_MANUFACTURER := Huawei
PRODUCT_RESTRICT_VENDOR_FILES := true

PRODUCT_COPY_FILES += device/huawei/angler/fstab.aosp_angler:root/fstab.angler
#继承下面两个makefile
$(call inherit-product, device/huawei/angler/device.mk)
$(call inherit-product-if-exists, vendor/huawei/angler/device-vendor.mk)
#代表系统需要预装的一系列程序
PRODUCT_PACKAGES += \
    Launcher3

PRODUCT_PACKAGES += \
    AOSPLinks

有了这些配置文件后,还需要告知编译系统该产品的存在,vendorsetup.sh就是完成这一工作的,其内容如下

add_lunch_combo aosp_angler-userdebug

add_lunch_combo是在envsetup.sh中定义的方法,将参数所描述的产品添加到系统相关变量中,后续lunch生成的列表就是基于这些变量,而vendorsetup.sh也是在envsetup.sh中被调用的,如下

# Execute the contents of any vendorsetup.sh files we can find.
for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
         `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \
         `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
do
    echo "including $f"
    . $f
done
unset f

可见,envsetup会去device\vendor\product这三个目录下找vendorsetup.sh。
除了上面提到的几个添加新设备所必须的配置文件外,device目录下还有很多与设备性能相关的配置文件,例如指明多媒体编解码能力的media_codecs.xml,与系统启动相关的Init.x.rc等,还有人会把针对不同运营商的不同开机动画放到这个目录下。

build

android编译系统的核心,很多关键的makefile都在该目录下,我们都知道编译android的第一步是执行下面的命令

source ./build/envsetup.sh

这个envsetup.sh也在该目录下,它记录了编译过程中需要的各种函数的实现,例如lunch, m,mm等,它还帮助我们设置了一些环境变量,并且扫描了device和vendor目录,寻找可以build的device,对应执行lunch命令时生成的列表。
编译android时最后一步是执行make命令,对应的是源码根目录下的makefile,这个文件的内容如下

### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###

可以看到,它只是一个简单的文件转向,转向了build/core/main.mk。
在main.mk中,只要做了以下几件事情:
1、检测编译环境,诸如jdk版本等
2、设置各类全局变量
3、定义各类编译时用到的函数
4、引用其他的makefile文件,main.mk中引用其他重要makefile文件的顺序如下图所示
这里写图片描述
各个主要makefile文件的作用如下
1、config.mk:产品配置的主导文件。根据 BoardConfig.mk 文件,配置产品相关的参数;定义编译器参数以及常见文件后缀,例如 .zip,.jar.apk;BUILD系列变量的定义,如BUILD_STATIC_LIBRARY等也都是在这里定义的。
2、envsetup.mk:配置 Build 系统需要的环境变量,例如:TARGET_PRODUCT,TARGET_BUILD_VARIANT,HOST_OS,HOST_ARCH 等。
3、version_defaults.mk:生成版本信息,例如BUILD_NUMBER
4、product_config.mk:产品级别的配置
5、cleanbuild.mk:clean操作的定义
6、definitions.mk:各种编译中用到的函数的定义
7、pdk_config.mk:针对 pdk(Platform Developement Kit)的配置文件
8、Android.mk:源码各处的Android.mk
9、MakeFile:辅助 main.mk 的一些额外内容

下面来分析一下main.mk的依赖树是怎样的,又是如何一步步把各个模块的内容全部包含进来的。首先,根节点是droid,它依赖于droid_targets

.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL): droid_targets

.PHONY: droid_targets
droid_targets:

droid_targets的依赖项随编译选项不同而变化

ifneq ($(TARGET_BUILD_APPS),)
  # If this build is just for apps, only build apps and not the full system by default.
  ...
.PHONY: apps_only
apps_only: $(unbundled_build_modules)

droid_targets: apps_only
...
else # TARGET_BUILD_APPS
...
# Building a full system-- the default is to build droidcore
droid_targets: droidcore dist_files

endif # TARGET_BUILD_APPS

如果选择了TARGET_BUILD_APPS,即编译出当前配置下不包含 user,userdebug,eng 标签的应用程序,则droid_targets依赖于apps_only,否则依赖于droidcore和dist_files,这也是我们常用的build full system的情况。
dist_files用来拷贝文件到 /out/dist 目录,略过不看。

# dist_files only for putting your library into the dist directory with a full build.
.PHONY: dist_files

droidcore的依赖如下

# Build files and then package it into the rom formats
.PHONY: droidcore
droidcore: files \ #仅仅是所依赖的几个目标的组合,其本身不做更多的处理
    systemimage \ #生成 system.img
    $(INSTALLED_BOOTIMAGE_TARGET) \ #生成 boot.img
    $(INSTALLED_RECOVERYIMAGE_TARGET) \ #生成 recovery.img
    $(INSTALLED_USERDATAIMAGE_TARGET) \ #生成 userdata.img
    $(INSTALLED_CACHEIMAGE_TARGET) \ #生成 cache.img
    $(INSTALLED_VENDORIMAGE_TARGET) \ #生成 vendor.img
    $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \ 
    $(INSTALLED_FILES_FILE) \ #生成 out/target/product/<product_name>/ installed-files.txt 文件,该文件中内容是当前系统镜像中已经安装的文件列表。
    $(INSTALLED_FILES_FILE_VENDOR) \
    $(INSTALLED_FILES_FILE_SYSTEMOTHER)

下面来分析其中关键的几个依赖项
1、file

# All the droid stuff, in directories
.PHONY: files
files: prebuilt \
        $(modules_to_install) \
        $(INSTALLED_ANDROID_INFO_TXT_TARGET)

①prebuilt 依赖于 $(ALL_PREBUILT),$(ALL_PREBUILT)的作用就是处理所有已编译好的文件,但是目前已经被废弃,如下

  $(warning * ALL_PREBUILT is a deprecated mechanism that)
  $(warning * should not be used for new files.)
  $(warning * As an alternative, use PRODUCT_COPY_FILES in)
  $(warning * the appropriate product definition.)

②modules_to_install的依赖项如下

modules_to_install := $(sort \
    $(ALL_DEFAULT_INSTALLED_MODULES) \
    $(product_FILES) \ #编译该产品所涉及的相关文件,依赖于PRODUCT_PACKAGES的结果,前面讲device时就有往PRODUCT_PACKAGES里面加东西的情况,总体来说,product_FILES就是指各个模块需要安装的文件列表
    $(foreach tag,$(tags_to_install),$($(tag)_MODULES)) \ #根据不同的tag,如user\debug\eng\tests来筛选要编译的模块
    $(CUSTOM_MODULES) \
  )

③ INSTALLED_ANDROID_INFO_TXT_TARGET对应out/target/product/xxx/android-info.txt,记录了当前 Build 配置的设备信息

# in build/target/board/Android.mk
INSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt

2、systemimage

# in /build/core/Makefile
$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
    @echo "Install system fs image: $@"
    $(copy-file-to-target)
    $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

systemimage: $(INSTALLED_SYSTEMIMAGE)

可见systemimage依赖于$(INSTALLED_SYSTEMIMAGE),而$(INSTALLED_SYSTEMIMAGE)又依赖于$(BUILT_SYSTEMIMAGE),如下

# in /build/core/Makefile
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
    $(call build-systemimage-target,$@)

将所有targets的集合传给build-systemimage-target方法,最终编译出system.img。其他几个$(INSTALLED_xxx_TARGET) 的依赖项也都在/build/core/Makefile中能够找到,比较简单,略过。
前面还提到,在main.mk中会把源码工程中所有的Android.mk添加进来,对应内容如下

#
# Include all of the makefiles in the system
#

# Can't use first-makefiles-under here because
# --mindepth=2 makes the prunes not work.
subdir_makefiles := \
    $(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) $(subdirs) Android.mk)

ifeq ($(USE_SOONG),true)
subdir_makefiles := $(SOONG_ANDROID_MK) $(call filter-soong-makefiles,$(subdir_makefiles))
endif

$(foreach mk, $(subdir_makefiles),$(info including $(mk) ...)$(eval include $(mk)))

整个android系统就是利用这样的依赖树让各个目标相互配合,最终编译得到的。

out

如果编译成功的话,会生成out目录,该目录包含如下内容:
host:该目录下包含了针对宿主机的 Android 开发工具的产物。即 SDK 中的各种工具,例如:emulator,adb,aapt 等。
target/common:该目录下包含了针对目标机的共通的编译产物,主要是 Java 应用代码和 Java 库。
target/product:包含了针对目标机的编译结果(包括各种img)以及平台相关的 C/C++ 库和二进制文件。
所谓宿主机(host)和目标机(target)都是交叉编译中的对象,我们知道android一般运行于arm这样的嵌入式平台上,而我们是在pc平台上进行android系统的研发工作,这时就需要交叉编译器来生成可运行与arm平台的系统包,这里宿主机就是指我们开发和编译代码所在的平台,而目标机就是指编译生成的系统包的目标平台。而交叉编译器就是运行于宿主机上,用于产生目标机可执行文件的编译器,例如arm-linux-gcc就是以arm为目标机、以linux为宿主机的交叉编译器。

out/target目录下生成的重要的img作用如下
boot.img:包含内核启动参数等
ramdisk.img:root根目录的镜像,是android系统启动的关键
system.img:android系统的运行程序包,framework代码就在这里,挂载到设备的/system节点下
userdata.img:各个应用程序的数据存储位置,挂载到设备的/data目录下
recovery.img:设备进入recovery mode时所需要的包
misc.img:杂项
cache.img:缓冲区,将被挂载到设备的/cache节点下

参考文献
Android Bootloader - Main system - Recovery
Android源码bootable解析之bootloader LK(little kernel)
嵌入式系统下BootLoader介绍
Describing the Android Source Code Folders
跟我一起写 Makefile(一)
Adding a New Device
理解 Android Build 系统
Android 的main.mk完整分析
GNU toolchain
ART and Dalvik
Android Runtime
Android 中的Dalvik和ART是什么,有啥区别?
art和dalvik的区别?

各位看官,如果您觉得本人的博客对您有所帮助,可以扫描如下二维码进行打赏,打赏多少您随意
这里写图片描述

猜你喜欢

转载自blog.csdn.net/nonmarking/article/details/70500100
今日推荐