Android 开发笔记(更新中....)

基础
  • 版本
    Android 作業系統有預發行的內部版本,分別為原子小金剛(Astro)與機器人班亭(Bender,电视动画《乃出個未來》的角色)。 從2009年5月開始,Android 的版本代號改以甜點來命名,且每個代號間的字首以英文字母序接續排列: Cupcake (紙杯蛋糕)、Donut(甜甜圈)、Eclair(閃電泡芙)、 Froyo(優格冰淇淋)、Gingerbread(薑餅)、Honeycomb(蜂巢)[來源請求]、Ice Cream Sandwich(冰淇淋三明治)、Jelly Bean(雷根糖)、KitKat(奇巧巧克力)、 Lollipop(棒棒糖)、Marshmallow(棉花糖)、Nougat(牛轧糖)、Oreo(奧利奧)、Pie(派)、Red Velvet Cake(11.0——紅色天鵝絨蛋糕)、Snow cone(12.0——杯套裝碎冰)。 唯一不使用以甜品命名的版本為 Android 10,2019 年 8 月 23 日,Google 宣布從 Android Q 開始不再以甜品命名,且直接稱 Android QAndroid 10 。 -- 摘自 WIKI-PEDIA
    名称代号
    Android 10 Q
    Android 11 R
    Android 12 S
    Android 13 T
  • NDK 下载
    NDK 的下载请访问:这里
AS 开发
  • AndroidAPP 中指定 SDK and NDK
    工程 local.properties 文件中增加以下两行,其中路径该成你安装 SDK 和 NDK 的路径。
    sdk.dir=D\:\\AndroidSDK
    ndk.dir=D\:\\AndroidSDK\\ndk\\24.0.8215888
    
  • 乱码
    方法1: 双击 shift -> 搜索 Edit Custom VM Options -> 回车,没有则创建之 , 在打开的文件中加入配置: -Dfile.encoding=UTF-8, 然后 双击 shift -> 搜索 restart IED -> 回车 ,
  • 配置代理
    在工程的 gradle.properties 文件中,或者 ~/.gradle/gradle.properties 中加入下列配置,如果不支持可以使用 # 注释。
    systemProp.http.proxyHost=www.somehost.org
    systemProp.http.proxyPort=8080
    systemProp.http.proxyUser=user
    systemProp.http.proxyPassword=password
    systemProp.http.nonProxyHosts=localhost
    systemProp.http.auth.ntlm.domain=domain
    
    systemProp.https.proxyHost=www.somehost.org
    systemProp.https.proxyPort=8080
    systemProp.https.proxyUser=user
    systemProp.https.proxyPassword=password
    systemProp.https.nonProxyHosts=localhost
    systemProp.https.auth.ntlm.domain=domain
        
  • Could not resolve all artifacts for configuration ':classpath'.
    如果你出现类似下面的 BUG, 那么很有可能你无法访问 maven 仓库,这时你可能需要设置一下代理,或者直接在浏览器中访问一下报错链接:
    > Could not resolve all artifacts for configuration ':classpath'.
       > Could not resolve com.android.tools.build:gradle:4.1.0.
       ...
                   > Could not GET 'https://maven.aliyun.com/nexus/content/groups/public/com/android/tools/build/gradle/4.1.0/gradle-4.1.0.pom'.
       ...
        

    但是还有一种可能,你设置了 aliyun 的代理仓库。但是老的代理仓库已经不支持了,比如下面这样的老配置:
    buildscript {
        repositories {
            maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/' }
            maven{ url 'https://maven.aliyun.com/repository/google' }
            maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/'}
            google()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:4.1.0'
        }
    }
    
    allprojects {
        repositories {
            maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/' }
            maven{ url 'https://maven.aliyun.com/repository/google' }
            maven{ url 'https://maven.aliyun.com/nexus/content/groups/public/'}
            google()
        }
    }
        

    这个时候你需要参考阿里云最新的 maven 仓库使用规范: https://developer.aliyun.com/mvn/guide, 来更新你的配置,但我自己没有搞定。 因此我还是用回了原来的仓库,解决问题:如下所示:
    buildscript {
        repositories {
            google()
            jcenter()
            maven { url "https://maven.google.com" }
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:4.1.0'
        }
    }
    
    allprojects {
        repositories {
            google()
            jcenter()
            maven { url "https://maven.google.com" }
        }
    }
        
  • 在命令行编译 Android APP
    在命令行可以直接使用 gradle,或者工程下的 gradlew 来直接编译,不过你先需要使用 chmod +x 确保你的 gradlew 有可执行权限。
    使用 ./gradlew task 可以查看当前工程中支持的子任务,如下所示:
    > ./gradlew task               
    
    > Task :tasks
    
    ------------------------------------------------------------
    Tasks runnable from root project
    ------------------------------------------------------------
    ...
    
    Build tasks
    -----------
    assemble - Assemble main outputs for all the variants.
    assembleAndroidTest - Assembles all the Test applications.
    ...
    
        

    这时你可以直接使用 ./gradlew TASK 的形式来进行相关 task 的执行。
  • 命令行编译 Android 应用工程和升级 Android SDK
    首先你可以使用如下命令来编译 Android 工程,而且有可能你编译的时候会报错,如下所示:
    > ./gradlew compileDebugSources
    Exception while marshalling /opt/android-sdk/tools/package.xml. Probably the SDK is read-only
    ...
    FAILURE: Build failed with an exception.
    
    * What went wrong:
    Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'.
    > Failed to install the following Android SDK packages as some licences have not been accepted.
         build-tools;30.0.3 Android SDK Build-Tools 30.0.3
         platforms;android-29 Android SDK Platform 29
        

    这时你可以通过 sdkmanager --install "build-tools;30.0.3" 命令和 sdkmanager --install platforms;android-29 来安装相关的哦依赖;
    当然你首先要确保 sdkmanager 存在才行,这请参考: https://developer.android.com/studio/command-line
    其次你还需要确保你的 Android studio 工程使用的 SDK 是你安装的 SDK 目录才行。 像我系统默认的 SDK 目录是 /opt/android/sdk, 但是如果你没有安装 SDK 在该目录或者,你没有权限,那么此时大概率也会报告上述问题。 假如你安装 Android SDK 的目录在 /home/USER/android/sdk, 那么你需要在 local.properties 中声明你使用的 SDK 的实际目录。 如下所示:
    > cat local.properties
    ## This file must *NOT* be checked into Version Control Systems,
    # as it contains information specific to your local configuration.
    #
    # Location of the SDK. This is only used by Gradle.
    # For customization when using a Version Control System, please read the
    # header note.
    #Sun Jul 24 16:58:18 CST 2022
    sdk.dir=/home/USER/android/sdk
            
  • 给你的 Android 工程制定 SDK 可 NDK 目录
    你可以在 local.properties 中声明你使用的 Android SDK 或者 NDK 的实际目录。 如下所示:
    > cat local.properties
    sdk.dir=/home/USER/android/sdk
    ndk.dir=/home/USER/android/sdk/ndk-bundle
        

    注意:这里的路径不能是相对路径,一定要得是相对路径,而且像 sdk.dir=~/android/sdk 这样的路径也是不行的。
    注意: Windows 下的反斜杠需要使用 \\ 取代,防止反转译。比如: sdk.dir=D:\\android\\sdk
命令行工具
  • screencap 源码
    位置: frameworks/base/cmds/screencap/
  • dumpsys
    查询系统服务运行状态:命令格式 dumpsys 服务名, 服务名可以通过 dumpsys -L 或者 service List 查看。
    查看可以的 display dumpsys SurfaceFlinger --display-id
Android 系统编译框架
Android 一些基本知识
  • KITA
    参考文档: Android中的Kati
    KITA 的仓库地址:https://github.com/google/kati
    基本上里面初步对 KITA 进行了介绍,但是为了避免原文失效,我将原文中部分内容进行摘抄。
    KITA 的作用: kati 是 Google 专门为了 Android 而开发的一个小项目,基于 Golang 和 C++ 。 目的是为了把 Android 中的 Makefile ,转换成 Ninja 文件。
  • Ninjia
    参考文档: Android中的Ninja简介
    基本上里面初步对 KITA 进行了介绍,但是为了避免原文失效,我将原文中部分内容进行摘抄。
    二者最核心的区别,在于设计哲学。 Makefile 是设计来给人手写的,而 Ninja 设计出来是给其它程序生成的。 如果说 Makefile 是 C 语言,那么 Ninja 就是汇编语言。 如果说 Makefile 是一个 DSL ,那么 Ninja 就是一种配置文件。 Makefile 支持分支、循环等流程控制,而 Ninja 只支持一些固定形式的配置。
    二者的相同点是,都是为了控制编译流程而设计。 所以,他们的核心功能,都是指定目标,以及目标之间的依赖关系,自动计算执行顺序。
    与 Makefile 相比,由于 Ninja 仅仅专注于核心的功能,所以有轻巧、速度快的优点。
  • Android 中 Makefile, Android.mk, Android.bp, Soong, Ninja, Blueprint 的关系
    kita 编译出 ckita 可执行文件, ckita 可将 Makefile 转换成 build.ninja 文件,用于 ninja 建构;
    Soong 包含一个 androidmk 的命令,可自动将 Android.mk 转换成 Android.bp, 这只对无选择、循环等复杂流程控制的 Android.mk 生效。
    Android.bp 则使用 Blueprint 框架来解析,最终转换成 Ninja 文件。
    Android.bp 是用来替换 Android.mk 的配置文件。
    Blueprint 和 Soong 都是由 Golang 写的项目。 从 Android Nougat 开始, prebuilts/go/ 目录下新增了 Golang 所需的运行环境,在编译时使用。
    这几个模块之间的关系也可表示成下面这样:
    Android.bp --> Blueprint --> Soong --> Ninja
    Makefile or Android.mk --> kati --> Ninja
    (Android.mk --> Soong --> Blueprint --> Android.bp)
        
Android build/envsetup.sh 提供的指令(函数)

一般编译 Android 会首先要执行 source build/envsetup.sh, 该命令会将脚本中的一些脚本函数(操作流程)加载到当前的环境中。 你可以通过直接调用函数名称来执行该过程。该脚本加载的函数包括:

  • gettop(经常用到)
    获取当前工程的绝对路径
  • get_build_var(非常有用,有助于分析编译架构)
    获取某一个宏(变量 or 环境变量)对应对值。这个函数其实调用的是 build/soong/soong_ui.bash --dumpvar-mode $1
    而 soong 则是变量 device 目录下的 AndroidProducts.mk 目录来获得该列表的。
  • hmm
    打印以下命令的帮助信息
  • lunch
    选择板级,你可以直接在该函数后带目标板级,也可以直接执行该函数。
    直接调用该函数之后,它会直接打印出当前 SDK 支持的板级,你选择好编号之后,也可选择初始化对应的板级。
    一般你在编译之前,都需要运行该命令,确定你编译的目标平台。
    执行完该命令之后,一般会设置下列几个主要的环境变量:TARGET_PRODUCT, TARGET_BUILD_VARIANT, TARGET_PLATFORM_VERSION
    BUILD_TYPE 一般有三个可选的值: useruserdebugeng。 其分别代表,开放给用限制权限的版本;带有开放 root 权限的可调式版本;带有开发工具/调试工具的版本。
  • tapas
    主要用于配置之前没有配置到板级中的应用。执行 tapas 或者 hmm 可以了解详情。
  • banchan
    和 tapas 类似,不过该命令用于配置模块。
  • croot
    cd 到工程顶部目录。
  • m
    在 top 目录进行建构,其可指定特定的名称来建构指定模块
    这些名称在哪里找?
  • mmmma
    构建当前目录中的工程
  • mmmmmma
    构建和安装指定目录中的所有 modules 以及它们的依赖。可用 mmm dir/:target1,target2 来限制构建的对象。
  • provision
    Flash device with all required partitions. Options will be passed on to fastboot.
    (这个我也不知道是什么意思,因为我的工程编译出来也没有 provision-device 这个可执行文件。我从参考脚本中理解到的解释是清除当前编译的目标,不包括 fastboot)
  • cgrep
    Greps on all local C/C++ files
    在工程的所有 C/C++ 程序中查找包含目标字符串
    实际执行如下命令: find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) -exec grep --color -n "$@" {} +
  • ggrep
    Greps on all local Gradle files.
    这些命令与上叙类似,可以查看 build/envsetup.sh 中的具体实现。 比如这个,命令的具体实现是: find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" -exec grep --color -n "$@" {} +
  • gogrep Greps on all local Go files
  • jgrep Greps on all local Java files.
  • ktgrep Greps on all local Kotlin files
  • resgrep Greps on all local res/*.xml files
  • mangrep Greps on all local AndroidManifest.xml files
  • mgrep Greps on all local Makefiles and *.bp files
  • owngrep Greps on all local OWNERS files
  • rsgrep Greps on all local Rust files
  • sepgrep Greps on all local sepolicy files
  • sgrep Greps on all local source files
  • godir
    搜索并跳转到包含某个文件的目录
  • allmod
    列举所有的模块
  • gomod
    跳转到包含某个模块的目录
  • pathmod
    打印包含某个模块的路径(绝对路径),比如执行 pathmod Camera2,就会输出 /home/xxx/xxx/packages/apps/Camera2.
  • outmod
    打印某个模块安装到 out 目录的所有文件的绝对路径。比如执行 outmod toybox
  • dirmods
    输出某个指定路径下包括的所有模块名称(注意不是模块的目录名称)
  • installmod
    利用 adb 安装一个构建的 APK
  • refreshmod
    刷新所有的模块列表,包括 allmod/gomod/pathmod/outmod/installmod
  • syswrite
    重新以可写的方式挂载分区(比如:system.img)(如果有可能会 reboot)

TARGET_DEVICE_DIR 保存当前板级编译配置的路径。 比如我编译的板级为 rk3588_firefly_itx_3588j-userdebug, 那么 TARGET_DEVICE_DIR 的值为 device/rockchip/rk3588/rk3588_firefly_itx_3588j

当你再执行完 source build/envsetup.shlunch PLT 之后,再运行 make 这时编译系统做了哪些事情?

make 命令会自动加载 Makefile 中的内容,而可能你看到的工程根目录中的 Makefile 只有一行内容,即 include build/make/core/main.mk

而再你打开 include build/make/core/main.mk 文件之后,再文件的前部会看到 main.mk 将 make 的默认 target 改成了 droid, 而 droid target 又进而执行了 droid_targets target。

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

你可能已经发现了 droid_targets 最终调用到了 blueprint_tools target, 但你找遍整个 core 都找不到 blueprint_tools target 再哪里。 别着急,

关于 Android.mk

PS: 本章以下内容大多取自 极客笔记 相关页面。

  • CLEAR_VARS
    CLEAR_VARS 指向一个特殊的 Makefile(可能在这个路径 ./build/make/core/clear_vars.mk ),该命令将会为你清除很多 LOCAL_ 开头的变量, 例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。
    不过 GNU Makefile 不会清除 LOCAL_PATH。此变量必须保留其值, 因为系统在单一 GNU Make 执行上下文(其中的所有变量都是全局变量)中解析所有构建控制文件。在描述每个模块之前,您必须声明(重新声明)此变量。
    使用方式 include $(CLEAR_VARS)
  • include $(BUILD_SHARED_LIBRARY)
    这一行会运行一个特殊的 Makefile 构建出该模块。
    该变量指向的文件在 ./build/make/core/shared_library.mk
  • LOCAL_PATH
    LOCAL_PATH 每个模块都在 Android.mk 里定义, 表示模块所在目录。
    此变量用于指定当前文件的路径。必须在 Android.mk 文件开头定义此变量。
  • LOCAL_MODULE
    LOCAL_MODULE 此变量用于存储模块名称。 LOCAL_MODULE 将在每个模块的 makefile 里定义,如果未定义,编译系统会报错。
    指定的名称在所有模块名称中必须唯一,并且不得包含任何空格。
    您必须先定义该名称, 然后才能添加任何脚本 ( CLEAR_VARS 的脚本除外 )。
    无需添加 lib 前缀或 .so 或 .a 文件扩展名;构建系统会自动执行这些修改。
    在整个 Android.mk 和 Application.mk 文件中,请用未经修改的名称引用模块。
    如果您希望生成的模块使用除 "lib + LOCAL_MODULE 的值" 以外的名称,可以使用 LOCAL_MODULE_FILENAME 变量为生成的模块指定自己选择的名称。
  • LOCAL_MODULE_FILENAME
    LOCAL_MODULE_FILENAME 此可选变量使您能够替换构建系统为其生成的文件默认使用的名称。
  • LOCAL_MODULE_TAGS
    LOCAL_MODULE_TAGS 模块的 tag, 为 debug / eng / tests / optional / samples / shell_ash / shell_mksh 等 tag 的组合, 一个模块可有多个Tag。
    • user: 指该模块只在 user 版本下才编译
    • eng: 指该模块只在 eng 版本下才编译
    • tests: 指该模块只在 tests 版本下才编译
    • optional: 指该模块在所有版本下都编译

    如果某个模块没有通过 LOCAL_MODULE_TAGS 指定标记,则其标记默认设置为 optional。仅当 PRODUCT_PACKAGES 的产品配置需要可选模块时,系统才会安装可选模块。
    该变量在 build/target/product/base.mkbuild/target/product/core.mk 里有赋值,这是所有产品都将继承的基础配置, 另外每个设备可在自己的产品配置文件 device_*.mk 里设置该变量,添加更多的模块。
    如果当前目录或者父目录有*_GPL*的文件,那么将自动添加gnu的tag。
  • LOCAL_MODULE_CLASS
    LOCAL_MODULE_CLASS 将用于决定编译时的中间文件存放的位置。其可选的值有: RECOVERY_EXECUTABLES, UTILITY_EXECUTABLES, ETC, STATIC_LIBRARIES, EXECUTABLES, FAKE, JAVA_LIBRARIES, SHARED_LIBRARIES, APPS
    使用该变量的过程可参考:build/core/base_rules.mk
  • LOCAL_PROPRIETARY_MODULE
    Vendor 模块是特定于供应商的可执行文件或共享库(必须将这些模块安装到供应商分区中)。
    在 Android.bp 文件中,供应商模块必须将 vendor 或 proprietary 属性设为 true。
    在 Android.mk 文件中,供应商模块必须将 LOCAL_VENDOR_MODULE 或 LOCAL_PROPRIETARY_MODULE 设为 true。
  • LOCAL_MODULE_PATH
    LOCAL_MODULE_PATH 表示模块生成的目标将最终存放的目录,也即目标的安装路径。
    使用该变量的过程可参考:build/core/base_rules.mk, 其常用取值有:
    原始表达式例子
    TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj out/target/product/i9100/obj
    TARGET_OUT_HEADERS:= $(TARGET_OUT_INTERMEDIATES)/include out/target/product/i9100/obj/include
    TARGET_OUT_INTERMEDIATE_LIBRARIES := $(TARGET_OUT_INTERMEDIATES)/lib out/target/product/i9100/obj/lib
    TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj out/target/common/obj
    TARGET_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_SYSTEM) out/target/product/i9100/system
    TARGET_OUT_EXECUTABLES:= $(TARGET_OUT)/bin out/target/product/i9100/system/bin
    TARGET_OUT_OPTIONAL_EXECUTABLES:= $(TARGET_OUT)/xbin out/target/product/i9100/system/xbin
    TARGET_OUT_SHARED_LIBRARIES:= $(TARGET_OUT)/lib out/target/product/i9100/system/lib
    TARGET_OUT_JAVA_LIBRARIES:= $(TARGET_OUT)/framework out/target/product/i9100/system/framework
    TARGET_OUT_APPS:= $(TARGET_OUT)/app out/target/product/i9100/system/app
    TARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout out/target/product/i9100/system/usr/keylayout
    TARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars out/target/product/i9100/system/usr/keychars
    TARGET_OUT_ETC := $(TARGET_OUT)/etc out/target/product/i9100/system/etc
    TARGET_OUT_NOTICE_FILES:=$(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES out/target/product/i9100/obj/NOTICE_FILES
    TARGET_OUT_FAKE := $(PRODUCT_OUT)/fake_packages out/target/product/i9100/fake_packages
    TARGET_OUT_DATA := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DATA) out/target/product/i9100/data
    TARGET_OUT_DATA_EXECUTABLES:= $(TARGET_OUT_EXECUTABLES) out/target/product/i9100/system/bin
    TARGET_OUT_DATA_SHARED_LIBRARIES:= $(TARGET_OUT_SHARED_LIBRARIES) out/target/product/i9100/system/lib
    TARGET_OUT_DATA_JAVA_LIBRARIES:= $(TARGET_OUT_JAVA_LIBRARIES) out/target/product/i9100/system/framework
    TARGET_OUT_DATA_APPS:= $(TARGET_OUT_DATA)/app out/target/product/i9100/data/app
    TARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT) out/target/product/i9100/system/usr/keylayout
    TARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS) out/target/product/i9100/system/usr/keychars
    TARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC) out/target/product/i9100/system/etc
    TARGET_OUT_DATA_NATIVE_TESTS := $(TARGET_OUT_DATA)/nativetest out/target/product/i9100/data/nativetest
    TARGET_OUT_CACHE := $(PRODUCT_OUT)/cache out/target/product/i9100/cache
    TARGET_OUT_VENDOR := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR) out/target/product/i9100/system/vendor
    TARGET_OUT_VENDOR_EXECUTABLES:= $(TARGET_OUT_VENDOR)/bin out/target/product/i9100/system/vendor/bin
    TARGET_OUT_VENDOR_OPTIONAL_EXECUTABLES:= $(TARGET_OUT_VENDOR)/xbin out/target/product/i9100/system/vendor/xbin
    TARGET_OUT_VENDOR_SHARED_LIBRARIES:= $(TARGET_OUT_VENDOR)/lib out/target/product/i9100/system/vendor/lib
    TARGET_OUT_VENDOR_JAVA_LIBRARIES:= $(TARGET_OUT_VENDOR)/framework out/target/product/i9100/system/vendor/framework
    TARGET_OUT_VENDOR_APPS:= $(TARGET_OUT_VENDOR)/app out/target/product/i9100/system/vendor/app
    TARGET_OUT_VENDOR_ETC := $(TARGET_OUT_VENDOR)/etc out/target/product/i9100/system/vendor/etc
    TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols out/target/product/i9100/system/symbols
    TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin out/target/product/i9100/sysmbols/system/bin
    TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib out/target/product/i9100/sysmbols/system/lib
  • LOCAL_SRC_FILES
    该变量包含要建构到该模块中的 C 或者 C++ 源文件, 每一个文件用空格分隔
    请注意,您可以使用相对 ( 相对于 LOCAL_PATH ) 和绝对文件路径。不过应该避免使用绝对文件路径;相对路径可以提高 Android.mk 文件的移植性。
  • LOCAL_MODULE_RELATIVE_PATH
    LOCAL_MODULE_RELATIVE_PATH 指定动态库的相对路径,以各自编译对象放置的目录为参考。
    通知编译系统来将一个模块放置到它的类型通常需要放到的目录的子目录下。
    比如: LOCAL_MODULE_RELATIVE_PATH := ak, 生成的库将会存在于 xxx/ak/ 目录下, xxx 取决于 LOCAL_MODULE_PATH。
  • LOCAL_CPP_EXTENSION
    指定 .cpp 以外的文件扩展名。比如: LOCAL_CPP_EXTENSION := .cxx .cpp .cc
  • LOCAL_CPP_FEATURES
    LOCAL_CPP_FEATURES 您可使用此可选变量指明您的代码依赖于特定 C++ 功能。
    它会在构建过程中启用正确的编译器标记和链接器标记。
    对于预构建的二进制文件,此变量还会声明二进制文件依赖于哪些功能,从而确保最终链接正常运行。
    我们建议您使用此变量,而不要直接在 LOCAL_CPPFLAGS 定义中启用 -frtti 和 -fexceptions。
    使用此变量可让构建系统对每个模块使用适当的标记。使用 LOCAL_CPPFLAGS 会导致编译器将所有指定的标记用于所有模块,而不管实际需求如何。
    比如: LOCAL_CPP_FEATURES := rtti features
  • LOCAL_C_INCLUDES
    LOCAL_C_INCLUDES 您可使用此可选变量指定相对于 NDK root 目录的路径列表, 以便在编译所有源文件 ( C、C++ 和 Assembly ) 时添加到 include 搜索路径中。 例如: LOCAL_C_INCLUDES := $(LOCAL_PATH)/xxx/foo
    请在通过 LOCAL_CFLAGS 或 LOCAL_CPPFLAGS 设置任何对应的包含标记前定义此变量。
  • LOCAL_CFLAGS
    LOCAL_CFLAGS 此可选变量用于设置在构建 C 和 C++ 源文件时构建系统要传递的编译器标记。这样,您就可以指定额外的宏定义或编译选项。可以使用 LOCAL_CPPFLAGS 仅为 C++ 指定标记。
    请勿尝试在 Android.mk 文件中更改优化/调试级别。构建系统可以使用 Application.mk 文件中的相关信息自动处理此设置。这样,构建系统就可以生成供调试期间使用的有用数据文件。
  • LOCAL_CPPFLAGS
    LOCAL_CPPFLAGS 只构建 C++ 源文件时将传递的一组可选编译器标记。
  • LOCAL_STATIC_LIBRARIES
    LOCAL_STATIC_LIBRARIES 此变量用于存储当前模块依赖的静态库模块列表。
    如果当前模块是共享库或可执行文件,此变量将强制这些库链接到生成的二进制文件。
    如果当前模块是静态库,此变量只是指出依赖于当前模块的其他模块也会依赖于列出的库。
  • LOCAL_SHARED_LIBRARIES
    LOCAL_SHARED_LIBRARIES 此变量会列出此模块在运行时依赖的共享库模块。
    此信息是链接时必需的信息,用于将相应的信息嵌入到生成的文件中。
  • LOCAL_WHOLE_STATIC_LIBRARIES
    LOCAL_WHOLE_STATIC_LIBRARIES 此变量是 LOCAL_STATIC_LIBRARIES 的变体,表示链接器应将相关的库模块视为完整归档。如需详细了解完整归档,请参阅有关 --whole-archive 标记的 GNU Id 文档。
    多个静态库之间存在循环依赖关系时,此变量十分有用。使用此变量构建共享库时,它将强制构建系统将静态库中的所有对象文件添加到最终二进制文件。但是,生成可执行文件时不会发生这种情况。
  • LOCAL_LDLIBS
    LOCAL_LDLIBS 此变量列出了在构建共享库或可执行文件时使用的额外链接器标记。
    利用此变量,您可使用 -l 前缀传递特定系统库的名称。比如: LOCAL_LDLIBS := -lz
  • LOCAL_LDFLAGS
    LOCAL_LDFLAGS表示链接时用的参数,此变量列出了构建系统在构建共享库或可执行文件时使用的其他链接器标记。
    若要在 ARM/X86 上使用 ld.bfd 链接器可以添加该行 LOCAL_LDFLAGS += -fuse-ld=bfd
  • LOCAL_ALLOW_UNDEFINED_SYMBOLS
    LOCAL_ALLOW_UNDEFINED_SYMBOLS 忽略链接错误,但是在运行中可能会出现错误,因为库文件找不到依赖的文件就会报错。
    默认情况下,如果构建系统在尝试构建共享库时遇到未定义的引用,将会抛出“未定义的符号”错误。此错误可帮助您捕获源代码中的错误。
    如需停用此检查,请将此变量设置为 true。请注意,此设置可能会导致共享库在运行时加载。
  • LOCAL_ARM_MODE
    LOCAL_ARM_MODE 默认情况下,构建系统会以 thumb 模式生成 ARM 目标二进制文件,其中每条指令都是 16 位宽,并与 thumb/ 目录中的 STL 库链接。
  • LOCAL_MULTILIB
    LOCAL_MULTILIB 可以指定特定模块编译32bit或64bit或都编译。
    默认值有 "both", "32", "64", "first", ""(由其他命令确定)
  • LOCAL_SDK_VERSION
    我也没找到该变量的具体说明,但目前从经验来看,该变量制指定的是当前编译 Android 的 SDK 版本。
    而且如果你编写了 JNI, 那么你必须指定该变量,否则编译的时候可能会报告 jni.h not found(出现在 Android 12 某芯片厂商的工程中)。
    一般赋值为 current 即可。
    其可选值有:currentsystem_current, test_current, core_current,以及具体的版本编号,
关于 Android.bp
APP 下的 gradle

大体框架如下:

apply plugin: 'com.android.application'
android{
    compileSdkVersion xx
    defaultConfig {
        applicationId 'com.xxx.xxx'
        minSdkVersion xx
        targetSdkVersion xx
        versionCode X
        versionName xx

        externalNativeBuild{
            cmake {

            }
            // or
            ndkBuild {

            }

        }
    }

    buildTypes {
        release {

        }
        debug {
            minifyEnable true/false
            proguarFiles
        }
    }

    externalNativeBuild {
        cmake {
            path '.../CMakeLists.txt'
            version '3.xx.x'
        }
    }
}

dependencies {
  ...
}
Android init
https://android.googlesource.com/platform/system/core/+/master/init/README.md http://gityuan.com/2016/02/05/android-init/ https://segmentfault.com/a/1190000023184321

init 程序的源码位于 system/core/init 中。而解析 init.rc 文件的函数入口为 parse_config_file。.rc 中的 service 信息会被存放在 server_list 中。

Android Init 文件中主要存在五类对象: Actions、Commands、Services、Options 和 Imports。init 文件中可用 # 进行注释,并且可以使用 ${property.name} 来使用系统属性 (如:import /init.recovery.${ro.hardware}.rc )。

init 程序会在加载之后第一时间加载 /system/etc/init/hw/init.rc 中的配置,随后会加载 /{system,system_ext,vendor,odm,product}/etc/init/ 中的 .rc 文件。

其中,/system/etc/init/ 中存放 Android 核心组件的一些启动配置文件;/vendor/etc/init/ 用于存放厂商的核心 SoC 功能所需的操作或守护程序。 而 /odm/etc/init/ 目录则存放设备制造商的启动文件,如外设或者传感器的守护进程或初始化动作。

Action 和 Service 指示一个 section 的开始,所有的命令或者选项都属于先前定义的最近一个 section , 第一个区块以前的命令和选项将会被忽略。

另外 service 的名字必须被忽略,如果出现第二个同名的 service 名字,那么这个 service 将会被忽略,并且会答应 error 日志。

之前对于 Legacy devices 没有 first stage mount 机制,因此可以在 mount_all 阶段去导入 init 脚本,但是在后续版本中该特性被作废了,并且不允许在 Q 之后启动设备。

system / vendor / odm 分区的所有服务的可执行二进制文件或者脚本都应该在与之相关联的 .rc 文件都应该安装到所在分区的 /etc/init 目录中,并且每一个 .rc 文件应该包含其申明的服务的所有操作。 文件可以申明在 Android.mk 的 LOCAL_INIT_RC 宏中,编译的时候会自动安装到相应的目录。Android.pb 的编译代码如下所示:

prebuilt_etc {
    name: "init_recovery.rc",
    filename: "init.rc",
    src: "etc/init.rc",
    sub_dir: "init/hw",
    recovery: true,
}

一个 logcat 例子, logcat 的代码位于 system/core/logcat 目录, logcatd.rc 也在其中。 在改目录下有一个 Android.mk 文件,文件中指定了 LOCAL_INIT_RC 宏,在编译的时候将 logcatd.rc 安装到 /system/etc/init 目录中。 init 将在 mount_all 期间加载 logcatd.rc ,而其将会在系统启动过程的适当时机被执行。

每个服务拥有自己 .rc 文件的方式优于以前一个大 init.rc 的方式,一方面在代码的管理方面,冲突会大大减少,另外 .rc 文件跟着工程走,这保证了我们可以非常方便的查找定位问题。

因为 APEX 会应用到多个 Android 主线版本上,此时不同版本的 init.rc 可能不一致,由此在 APEX 中以 init.#rc 的方式命名了各个不同版本的 init.rc 文件,其中 # 为支持的 SDK 版本号(比如 init.rc, init.32rc, init.35rc )。 而在编译的时候,编译器会找出最大的,最接近当前 SDK 版本的 init.rc 安装进去。(如果你的 SDK 为 32,33,34 则会选择 init.32rc, 而如果你的 SDK 版本 >= 35, 那么会选择 init.35rc)

Actions

Action 是一系列指令的名字, Action 中有一个 trigger 字段,该字段标识该 action 在何时被调用。 动作被添加到队列中并根据包含它们的文件被解析的顺序执行(参见 import 部分),然后在单个文件中按顺序执行。

Each action in the queue is dequeued in sequence and each command in that action is executed in sequence. Init handles other activities (device creation/destruction, property setting, process restarting) “between” the execution of the commands in activities.

Action 的通用形式如下所示:

on \trigger [&& \trigger]*
   \command
   \command
   \command

比如下面这个例子:

on boot
   setprop a 1
   setprop b 2

on boot && property:true=true
   setprop c 1
   setprop d 2

on boot
   setprop e 1
   setprop f 2

当 属性 true 的值为 true 时,执行的顺序如下所示:

    setprop a 1
    setprop b 2
    setprop c 1
    setprop d 2
    setprop e 1
    setprop f 2
Services

Services 是一个在开机时需要启动的一个程序,其可以指定是否在程序退出时,自动重启。 Service 的格式如下所示:

service \name \pathname [ \argument ]*
   \option
   \option
   ...
Options
  • class $name [ $name\* ]
    指定服务的 classname 。
    属于同一 classname 的服务可以一起停止或者启动。如果没有指定 classname, 那么该服务默认属于 default classname,
    一个 service 可以设置多个 classname, 除了第一个 classname 其他的 classname 用于对服务进行分组。
    animation 类的 class 包括 启动和关闭动画所需的所有服务,这些服务在系统早期就被启动,并且可以运行到关机的最后阶段,
  • socket $name $type $perm [ $user [ $group [ $seclabel ] ] ]
    该选项将会创建一个 unix domain socket 给 init 脚本。位置在 /dev/socket/name
    type 的值必须是 dgramstreamseqpacket 这几个选项 Create a UNIX domain socket named /dev/socket/name and pass its fd to the launched process. type must be “dgram”, “stream” or “seqpacket”. type may end with “+passcred” to enable SO_PASSCRED on the socket. User and group default to 0. ‘seclabel’ is the SELinux security context for the socket. It defaults to the service security context, as specified by seclabel or computed based on the service executable file security context. For native executables see libcutils android_get_control_socket().
参考文档
Service

了解 Service 你可能需要先了解 Binder, 这里有一篇解释 Binder 的文章写得还挺详细的:写给 Android 应用工程师的 Binder 原理剖析

Android 的所有系统服务都在 base/services/java/com/android/server/SystemServer.java 中加载。 SystemServer.java 中包含 java 的函数入口 public static void main(String[] args), zygote 启动的时候会 fork 出 SystemServer 进程,在最后通过反射机制调用到了 SystemServer.main() 函数。 SystemServer 类中的初始化过程如下所示: SystemServer.main -> SystemServer.run -> startBootstrapServices(t) && startCoreServices(t) && startOtherServices(t)具体的 SystemServer 启动过程可以参考本章文后参考文档 3。

其中 Android 的 core service 包括, SystemConfigService, BatteryService, UsageStatsService, WebViewUpdateService, CachedDeviceStateService, BinderCallsStatsService, LooperStatsService, RollbackManagerService, NativeTombstoneManagerService, BugreportManagerService, GpuService。 这些服务都是在 startOtherServices 函数中启动的。

startOtherServices 中启动的 service 有: KeyAttestationApplicationIdProviderService, KeyChainSystemService, SchedulingPolicyService, TelecomLoaderService, TelephonyRegistry, EntropyMixer( 可能算不上 service ), AccountManagerService$Lifecycle, ContentService$Lifecycle, DropBoxManagerService, RoleService, VibratorManagerService.Lifecycle, DynamicSystemService, ConsumerIrService, AlarmManagerService, InputManagerService, DeviceStateManagerService, CameraServiceProxy, WindowManagerService, VrManagerService, BluetoothService, PinnerService, IorapForwardingService, ProfcollectForwardingService, AppIntegrityManagerService, MultiClientInputMethodManagerService.Lifecycle, InputMethodManagerService.Lifecycle, AccessibilityManagerService$Lifecycle, StorageManagerService$Lifecycle, StorageStatsService$Lifecycle, UiModeManagerService, LockSettingsService$Lifecycle, PersistentDataBlockService, TestHarnessModeService, OemLockService, DeviceIdleController, DevicePolicyManagerService, StatusBarManagerService, MusicRecognitionManagerService, SpeechRecognitionManagerService, AppPredictionManagerService, ContentSuggestionsManagerService, SearchUiManagerService, SmartspaceManagerService, FontManagerService.Lifecycle, TextServicesManagerService.Lifecycle, SystemUpdateManagerService, UpdateLockService, RkDisplayDeviceManagementService, RkAudioSettingService, NotificationManagerService, DeviceStorageMonitorService, LocationManagerService, CountryDetectorService, TimeDetectorService$Lifecycle, TimeZoneDetectorService$Lifecycle, LocationTimeZoneManagerService$Lifecycle, GnssTimeUpdateService$Lifecycle, SearchManagerService$Lifecycle, WallpaperManagerService$Lifecycle, AudioService.Lifecycle, SoundTriggerMiddlewareService.Lifecycle, BroadcastRadioService, DockObserver, MidiService$Lifecycle, adb.AdbService$Lifecycle, usb.UsbService$Lifecycle, SerialService, HardwarePropertiesManagerService, TwilightService, ColorDisplayService, JobSchedulerService, SoundTriggerService, TrustManagerService, BackupManagerService$Lifecycle, appwidget.AppWidgetService, voiceinteraction.VoiceInteractionManagerService, apphibernation.AppHibernationService, GestureLauncherService, SensorNotificationService, ContextHubSystemService, DiskStatsService, RuntimeService, RulesManagerService$Lifecycle, NetworkTimeUpdateService, EmergencyAffordanceService, BlobStoreManagerService, DreamManagerService, GraphicsStatsService, CoverageService, PrintManagerService, CompanionDeviceManagerService, RestrictionsManagerService, MediaSessionService, HdmiControlService, TvInputManagerService, TunerResourceManagerService, MediaResourceMonitorService, TvRemoteService, MediaRouterService, FaceService, IrisService, FingerprintService, BiometricService, AuthService, BackgroundDexOptService, DynamicCodeLoggingService, PruneInstantAppsJobService, ShortcutService, LauncherAppsService, CrossProfileAppsService, PeopleService, MediaMetricsManagerService, SliceManagerService$Lifecycle, IoTSystemService, StatsCompanion$Lifecycle, RebootReadinessManagerService$Lifecycle, StatsPullAtomService, IncidentCompanionService, MmsServiceBroker, AutofillManagerService, TranslationManagerService, ClipboardService, AppBindingService.Lifecycle, TracingServiceProxy, PermissionPolicyService, R.array.config_deviceSpecificSystemServices 中指定的服务, GameManagerService$Lifecycle, uwb.UwbService, AppSearchManagerService, MediaCommunicationService, CarServiceHelperService

startOtherServices 中启动的和网络相关的 service 有: IpConnectivityMetrics, NetworkWatchlistService, NetworkManagementService, IpSecService, NetworkScoreService.Lifecycle, NetworkStatsService, NetworkPolicyManagerService, wifi.WifiService, wifi.scanner.WifiScanningService, wifi.rtt.RttService, wifi.aware.WifiAwareService, wifi.p2p.WifiP2pService, lowpan.LowpanService, ethernet.EthernetService, PacProxyService, ConnectivityServiceInitializer, VpnManagerService, VcnManagementService, NsdService,

startOtherServices 中启动的和手表相关的 service 有:ThermalObserver, MediaProjectionManagerService, WearPowerService, WearConnectivityService, WearDisplayService, WearTimeService, WearLeftyService, GlobalActionsService

publishBinderService 和 publishLocalService 区别

参考文档:publishBinderService和publishLocalService区别

摘要: 1. 通过 publishBinderService 方法发布的 Service 上层可以访问对应服务 Manager; 2. 通过 publishLocalService 方法发布的 Service, 上层不可以访问对应服务 Manager, 只能 System 进程访问。

查看 Android 系统中启动的 service 可以使用: service list

  • ./frameworks/base/core/java/android/os/ServiceManager.java
    public static IBinder getService(String name)
NetworkManagementService

NetworkManagementService 位于 frameworks/base/services/core/java/com/android/server/NetworkManagementService.java 位置。

framework/base/services/java/com/android/server/SystemServer.java 在初始化的时候在 private void startOtherServices(@NonNull TimingsTraceAndSlog t) 函数会初始化网络相关服务, 之后再初始化大多数基础模块之后会调用 NetworkManagementService 进行初始化。 如下所示:

    ...

    networkManagement = NetworkManagementService.create(context);
    ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
    ...
    final NetworkManagementService networkManagementF = networkManagement;
    ...
    if (networkManagementF != null) {
        networkManagementF.systemReady();
    }

SystemServer 调用 NetworkManagementService.systemReady 之后, systemReady 会调用 private void prepareNativeDaemon() 进行一系列的网络初始化工作。

NetworkManagementService 继承自 INetworkManagementService, INetworkManagementService 定义于 ./frameworks/base/core/java/android/os/INetworkManagementService.aidl 文件中。

NetworkManagementService 提供的接口包括:注册监听网络事件接口; get/set/config 网络接口的接口; 对网络接口进行 IPV6 设置的接口; 操作共享网络的接口; 为 Uid 设置网络接口的权限; 设置防火墙的一些接口; ...

如何打开 NetworkManagerService 的调试信息?方法之一是直接修改 NetworkManagementService.javaDBG 成员的值为 true。下面是一个自动编译安装的脚本。

ssh ${PRJ_HOST} "cd ${PRJ_HOME}; source build/envsetup.sh; lunch ${PRJ_PLT}-userdebug; make services -j 32"
scp ${PRJ_HOST}:${PRJ_HOME}/out/target/product/${PRJ_PLT}/system/framework/services.jar /tmp/services.jar
adb root && adb remount && adb push /tmp/services.jar //system/framework/services.jar && adb shell sync && adb reboot
rm -rf /tmp/services.jar
参考文档
  1. 图解 Android 系列(一)揭秘 Android 系统启动过程
  2. 图解 Android 系列(二)深入理解 init 与 zygote 进程
  3. 图解 Android 系列(三)探索 SystemServer 进程创建过程
Android NetD
简介
https://github.com/Dufre/Android-Settings-Ethernet

Netd 是 Android 系统中专门负责网络管理和控制的后台 daemon 程序,主要三块功能。

  • 网络设置: 设置防火墙 ( Firewall )、网络地址转换 ( NAT ) 、带宽控制、无线网卡软接入点 (Soft Access Point) 控制, 网络设备绑定 ( Tether ) 等。
  • Android 系统中 DNS 信息的缓存和管理。
  • 服务: 网络服务搜索 ( Net Service Discovery, 简称NSD ) 功能, 包括服务注册( Service Registration )、服务搜索( Service Browse )和服务名解析 ( Service Resolv )等。

Netd 启动的时候会创建这么几个 socket: netd / mdns -- Framework 层中的 NetworkManagementService 和 NsdService 将分别和 netd 及 mdns 监听 socket 建立链接并交互; dnsproxyd -- 每一个调用和域名解析相关的 socket API (如 getaddrinfo 或 gethostbyname 等) 的进程都会借由 dnsproxyd 监听 socket 与 netd 建立链接; fwmarkd -- fwmarkd 和底层 kernel 交互, 防火墙 firewall 会对进来的包做标记。 如下所示:

rk3588_firefly_itx_3588j:/system # cat etc/init/netd.rc
service netd /system/bin/netd
    class main
    capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER IPC_LOCK KILL NET_ADMIN NET_BIND_SERVICE NET_RAW SETUID SETGID
    socket dnsproxyd stream 0660 root inet
    socket mdns stream 0660 root system
    socket fwmarkd stream 0660 root inet
    onrestart restart zygote
    onrestart restart zygote_secondary
    # b/121354779: netd itself is not updatable, but on startup it dlopen()s the resolver library
    # from the DNS resolver APEX. Mark it as updatable so init won't start it until all APEX
    # packages are ready.
    updatable

Netd 接收并处理来自 Framework 层中 NetworkManagementService 或 NsdService 的命令。 这些命令最终由 Netd 中对应的 Command 对象去处理。Net 接收并解析来自 Kernel 的 UEvent 消息, 然后再转发给 Framework 层中对应 Service 去处理。

Netd 进程由 init 进程根据 init.rc 的对应配置项而启动。

NetdService

假定你已经熟悉 ServiceManager 相关知识, 假定你已经熟悉基本的网络知识: ethnet, wifi, 七层架构,四层架构,路由,常规网络管理知识, IPV4/IPV6, DNS, DHCP ...

  1. ./system/netd/server/binder/android/net/INetd.aidl
    ./system/netd 下定义了好几个版本的 INetd.aidl 请仔细甄别使用的是哪一个
  2. ./frameworks/base/services/net/java/android/net/util/NetdService.java
    返回 Netd service 实例: final INetd netdInstance = INetd.Stub.asInterface(ServiceManager.getService(Context.NETD_SERVICE));
  3. ./frameworks/libs/net/client-libs/netd/com/android/net/module/util/NetdUtils.java
    该类主要提供一些功能函数的封装,借由 netd 赖对 interface 进行一些基本的操作,其中包括: 获取和设置 interface 的配置 getInterfaceConfigParcel setInterfaceConfig, up/down interface setInterfaceUp, setInterfaceDown
  4. ./frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
    提供操作 Ethnet 的一些基础函数,包括: hasInterface, updateIpConfiguration, getIpAddress, getNetmask ...
    其中 updateIpConfiguration 可以直接设置 Ethnet 的一些配置,比如说 DHCP
  5. ./frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetTracker.java
    该类在创建之后调用 void start() 会调用 mNMService.registerObserver(new InterfaceObserver()); 向 INetworkManagementService 注册 BaseNetworkObserver 观察者。 该 Observer 主要用于监听网络的状态变化(如: interfaceLinkStateChanged, interfaceAdded, interfaceRemoved )。

为解决的问题: framework 各个目录中 net 的关系是如何的

NetD 小知识
  • netd 初始化防火墙
    初始化流程如下所示: main.cpp / gCtls->init(); -> Controllers.cpp / void Controllers::init() -> Controllers.cpp / initIptablesRules();
    initIptablesRules 会初始化 INPUT, FORWARD, PREROUTING, POSTROUTING, OUTPUT 的子链
  • netd 添加客制化 iptables 规则
    可将对应的 iptables 的添加命令编写到 /system/bin/oem-iptables-init.sh 文件中,并同时赋予该文件的可读可执行权限
    需要注意的是,你需要确保该脚本执行成功,否则 netd 会清空所有的 oem 的设置,如下所示:
    iptables -I  oem_fwd -p all -i eth0 -o eth1 -j ACCEPT; iptables -I  oem_fwd -p all -i eth1 -o eth0 -j ACCEPT;
    exit 0
        

    另外,与 oem 相关的表有: oem_fwd / oem_out, 如果还要增加则要改 netd 中 oem_iptables_hook.cpp 部分代码。
netd 如何提供服务的

netd 的服务存在一个类 class NetdNativeService, 该类继承自 public BinderService<NetdNativeService>, public BnNetd, 也意味着它是一个 binder service。

netd 启动的时候执行以下代码启动该服务:

    if ((ret = NetdNativeService::start()) != android::OK) {                                                                                                                                                                                                                         ALOGE("Unable to start NetdNativeService: %d", ret);
        exit(1);
    }
netd 提供的服务
获取 interface List

调用路径如下所示: @utf8InCpp String[] interfaceGetList(); -> NetdNativeService::interfaceGetList -> InterfaceController::getIfaceNames

InterfaceController::getIfaceNames 会打开 /sys/class/net 目录,并查看该目录下的接口有哪些。并将这些接口存储到 std::vector ifaceNames 中,然后返回给调用者。

获取某个 interface 的配置

调用路径如下所示: InterfaceConfigurationParcel interfaceGetCfg(in @utf8InCpp String ifName); -> NetdNativeService::interfaceGetCfg -> InterfaceController::getCfg

InterfaceController::getCfg 函数会获取到接口的 MAC 地址, IP 地址,掩码, UP/DOWN 等信息。为了获取这些信息,首先要打开一个 socket ( socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0) ), 然后调用 ioctl(fd, SIOCGIFHWADDR, &ifr) 获取接口的硬件地址,调用 ioctl(fd, SIOCGIFADDR, &ifr) 获取接口的 IP 地址以及类型,调用 ioctl(fd, SIOCGIFNETMASK, &ifr) 获取接口掩码, 调用 ioctl(fd, SIOCGIFFLAGS, &ifr) 获取目前接口状态,并通过检查 IFF_UP 查看接口是否被使能。同时还可以检查如下状态 IFF_BROADCAST, IFF_LOOPBACK, IFF_POINTOPOINT, IFF_RUNNING, IFF_MULTICAST

设置 interface

set 的接口和 get 的接口类似,调用流程如下所示: void interfaceSetCfg(in InterfaceConfigurationParcel cfg) -> NetdNativeService::interfaceSetCfg -> InterfaceController::setCfg

set 的时候 ioctl 带的 request id 分别为, SIOCSIFADDR ( 设置 IP 地址 ). SIOCSIFFLAGS ( 将接口 UP or DOWN )。

ndc

ndc 工具是属于 netd 的一部分,在调试 netd 的时候非常有帮助。 netd 工程地址在 system/netd

其中与 ndc 相关的配置在 system/netd/server/Android.pb 文件中,如下所示:

cc_binary {
    name: "ndc",                                                                                                                                                                                                                                                                 defaults: ["netd_defaults"],
    include_dirs: [
        "system/netd/include",
    ],
    header_libs: [
        "libnetd_client_headers",
    ],
    shared_libs: [
        "libbase",
        "libnetdutils",
        "libnetutils",
        "libcutils",
        "liblog",
        "libutils",
        "libbinder",
        "dnsresolver_aidl_interface-V7-cpp",
        "netd_aidl_interface-V6-cpp",
    ],
    srcs: [
        "ndc.cpp",
        "UidRanges.cpp",
        "NdcDispatcher.cpp",
    ],
    sanitize: {
        cfi: true,
        memtag_heap: true,
    },
}

server/ndc.cpp 中包含了 ndc 的入口,如下所示。

int main(int argc, char** argv){
    android::net::NdcDispatcher nd;
    exit(nd.dispatchCommand(argc - 1, argv + 1));
}

ndc 支持下表所列的一些指令。 这些指令从 server/NdcDispatcher.cpp 注册到每一个指令的专属类中,这些类的定义在 server/NdcDispatcher.h 中。 ndc 收到命令行输入的数据之后会直接调用 NdcDispatcher::dispatchCommand 方法,该方法会在注册到 NdcDispatcher::mCommands 的命令类中找到对应的命令类, 并调用该命令类的 NdcNetdCommand::runCommand 将指令分发出去。

NdcNetdCommand 为各个命令类的基类,每个派生类都会实现一次 NdcDispatcher::_xxx_::runCommand。 而 runCommand 会负责检查参数的合法性,并调用 sp<INetd> mNetd 通过 binder 将要操作的指令发给 netd 去处理。

指令说明
interface 支持的指令有 list, getcfg, setcfg, clearaddrs, ipv6privacyextensions, ipv6, enable, setmtu
ipfwd 支持的指令有 status, enable, add,
tether 支持的指令有 stop, status, interface, start, dns,
nat 支持的指令有 enable, disable
bandwidth 支持的指令有 removeiquota | riq, setiquota | siq, addnaughtyapps | ana, removenaughtyapps | rna, addniceapps | aha, removeniceapps | rha, setglobalalert | sga, setinterfacealert | sia, removeinterfacealert | ria
idletimer 支持的指令有 add, remove
firewall 支持的指令有 enable, set_interface_rule, set_uid_rule, enable_chain, disable_chain
clatd 支持的指令有 stop, start
strict 支持的指令有 set_uid_cleartext_policy
network 支持的指令有 route, interface, create, destroy, default, permission, users, protect

下面是一些例子:

  • 列举系统中存在的网络 interface ndc interface list
  • 获取 eth0 的配置 ndc interface getcfg eth0
  • 配置 eth0 down ndc interface setcfg eth0 down
  • 配置 eth0 up ndc interface setcfg eth0 up or ndc interface setcfg eth0 0.0.0.0 up
关于 interface 的操作

 

Camera Provider

camera.provider 位于 hardware/interface/camera/provider 目录。 原始仓库地址:https://android.googlesource.com/platform/hardware/interfaces。 关于 camera 的原始信息可以参考 google 官方仓库中的文档。( source.android.com

camera.provider 主要用于 camera service 发现,请求,和打开单个 camera 设备。 他还允许直接控制闪光灯,或者打开关闭手电筒模式等。 下面文中的资料也是我直接参考 google 官方的解释以及源码中的注释,如果大家想获得一手的资料,建议还是去查看官方文档。

2.4 是地一个 HIDL 版本, 该版本提供了 HAL 层的操作和回调接口。 之后的版本基本上都是继承字该接口。如下所示: ( HIDL 为 Android 为了描述 service 和 HAL 层调用的接口而设计的一套接口语言 )

// ./2.4/ICameraProvider.hal
interface ICameraProvider {
    ...
}

// ./2.5/ICameraProvider.hal
interface ICameraProvider extends @2.4::ICameraProvider {
    ...
}

// ../2.6/ICameraProvider.hal
interface ICameraProvider extends @2.5::ICameraProvider {
    ...
}

// ./2.7/ICameraProvider.hal
interface ICameraProvider extends @2.6::ICameraProvider {
    ...
}

下面是个版本 ICameraProvider.hal 提供的接口一览,其中 2.4 版本除了提供了注册 ICameraProviderCallback 毁掉函数的接口之外, 还提供了获取 VendorTag, CameraTags, CameraIdList 的接口,检查是否支持手电筒模式的接口,和获取 CameraDevice 接口的接口。 而后续的版本则补充了发送 notify 的接口,检测是否支持并发流模式的相关接口。在接下来的章节中,将针对各个部分逐一介绍。

> find -name "ICameraProvider.hal" | sort | while read F; do echo "---> $F"; grep "(.*)"  $F | grep -v "*" ; done
---> ./2.4/ICameraProvider.hal
setCallback(ICameraProviderCallback callback) generates (Status status);
getVendorTags() generates (Status status, vec<VendorTagSection> sections);
getCameraIdList()
            generates (Status status, vec<string> cameraDeviceNames);

isSetTorchModeSupported() generates (Status status, bool support);
getCameraDeviceInterface_V1_x(string cameraDeviceName) generates
            (Status status, android.hardware.camera.device@1.0::ICameraDevice device);

getCameraDeviceInterface_V3_x(string cameraDeviceName) generates
            (Status status, android.hardware.camera.device@3.2::ICameraDevice device);

---> ./2.5/ICameraProvider.hal
notifyDeviceStateChange(bitfield<DeviceState> newState);

---> ./2.6/ICameraProvider.hal
getConcurrentStreamingCameraIds() generates (Status status, vec<vec<string>> cameraIds);

isConcurrentStreamCombinationSupported(vec<CameraIdAndStreamCombination> configs)
    generates (Status status, bool queryStatus);

---> ./2.7/ICameraProvider.hal
isConcurrentStreamCombinationSupported_2_7(vec<CameraIdAndStreamCombination> configs)
    generates (Status status, bool queryStatus);
ICameraProvider.hal

位于 hardware/interfaces/camera/provider/x.x/ICameraProvider.hal 路径下,主要提供了 setCallback, getVendorTags, getCameraIdList, isSetTorchModeSupported, getCameraDeviceInterface_VN_x 这么几个方法。

Camera provider HAL 可用于枚举独立可用的 camera 设备,并且 provider 还提供更新这些 camera 设备状态的方式。 这些状态包括 connection / disconnection 和手电筒模式的启用和禁用。

provider 有责任提供一个 camera device service 名的 list, 而这些名字对应的 service 必须能被 hardware service manager 打开。

多个 camera provider HAL 也许被集成在一个系统中。为了系统能发现这些 service 的名字和 process 的名字, provider 必须以 android.hardware.camera.provider@<major>.<minor>/<type>/<instance>< 的形式命名。 这里的 <major>.<minor> 为 HAL HIDL 的版本,比如 2.4 / 2.5 ... 而 <type> 则为 provider 的类型,目前已定义的类型有 internallegacyexternalremote ... 而 camera framework 必须直到各个版本间的差异性。 最后 <instance>, 是一个从 0 开始的非负整数,用于消除相同类型的多个 HAL 之间的歧义。

其中"legacy" 类型的 provider 提供的 device 只能以传统 HAL 模式访问, 而且一定不能被 standalone binderized HAL 所使用。

设备实例名字必须能被 getCameraIdList() 或者 ICameraProviderCallback::cameraDeviceStatusChange() 枚举。 的获取的格式必须服从 "device@<major>.<minor>/<type>/<id><" 其中 <major>/<minor> 为对应的 HIDL 的版本号。 id 是 "internal" 设备类型的一个小的递增整数,0 是主后置摄像头,1 是主前置摄像头(如果存在)。 或者,对于外部设备,一个唯一的序列号(如果可能)可用于在设备断开连接和重新连接时可靠地识别设备。

需要注意的是,多个 provider 一定不能枚举出同一个 device ID。

setCallback

原型:setCallback(ICameraProviderCallback callback) generates (Status status);

该接口用于设置一个回调函数,该回调函数用于同步底层 camera 子系统的事件, camera service 必须在其启动期间,在调用其他 provider 方法前,调用以下该函数。 (当然在 camera service 重启之后也应该重新注册)。

需要注意的是返回值 status 返回的可能值有 OK, INTERNAL_ERROR, ILLEGAL_ARGUMENT

getVendorTags

原型:getVendorTags() generates (Status status, vec<VendorTagSection> sections);

camera service 可通过该接口获取 所有的 vendor tag。 这些 tag 将会通过 section 字段返回。

需要注意的是返回值 status 返回的可能值有 OK, INTERNAL_ERROR

getCameraIdList

原型:getCameraIdList() generates (Status status, vec<string> cameraDeviceNames);

该接口给应用层返回一个列表,该列表为 provider 知道的 camera 设备名字。 这些设备都必须能被 hardware service 所管理。

外接摄像头设备必须通过设备状态变化回调上报,并不出现在此列表中。 此处必须仅列出前后摄像头设备。

需要注意的是返回值 status 返回的可能值有 OK, INTERNAL_ERROR。 需要注意的是,如果返回值为 INTERNAL_ERROR 那么很有可能是 camera 子系统初始化失败导致。

isSetTorchModeSupported

原型:isSetTorchModeSupported() generates (Status status, bool support);

该接口不用太多解释,该接口用于检查 camera device 是否支持手电筒模式。 需要注意的是,该接口并不是非常准确,因为并不是每一个 camera 设备都知道自己是否拥有 flash 单元。

getCameraDeviceInterface_VN_x

原型:getCameraDeviceInterface_V1_x(string cameraDeviceName) generates(Status status, android.hardware.camera.device@1.0::ICameraDevice device);getCameraDeviceInterface_V3_x(string cameraDeviceName) generates(Status status, android.hardware.camera.device@3.2::ICameraDevice device);

这不会直接给摄像头设备上电,而只是获取查询设备静态信息的接口,或者额外打开设备进行活动使用。 每一个主版本,返回的相机设备 HAL 都是互相独立的,因为它们彼此不兼容。

提供程序的有效设备名称可以通过 getCameraIdList() 或通过来自 ICameraProviderCallback::cameraDeviceStatusChange() 的可用性回调获得。

返回接口的版本必须是主要版本下的最高(即主版本号相同,次版本最高)。而 HAL 客户端负责处理好次版本存在但实际不支持的方法。

notifyDeviceStateChange

原型:notifyDeviceStateChange(bitfield<DeviceState> newState);。该方法于 2.5 版本加入。

该接口的作用主要用于通知 HAL provider 某些 HAL 层面需要通知的状态变化。比如,物理隐私盖已经被关闭或者打开,又或者与 camera 相关的附加的按键已经按下或者弹起。 这里的状态是潜在状态的 bit 位的状态,一些物理配置可能对应多种不同状态位的组合。HAL 层必须过滤任何没有用到的状态位,一确定正确的配置相机。

例如,在某些设备上, FOLDED 状态可能意味着后向摄像头被折叠覆盖,因此 FOLDED 本身意味着 BACK_COVERED 。 但是其他设备可能支持折叠,但折叠时不会覆盖任何相机,因此对于那些 FOLDED 不会暗示任何其他标志。 由于这些关系是非常特定于设备的,因此很难指定一个全面的策略。 但作为一项建议,如果一个标志必然意味着也设置了其他标志,那么应该设置这些标志。 因此,从清楚准确的角度,即使 FOLDED 足以在某些设备上推断出 BACK_COVERED ,也应该设置 BACK_COVERED 标志。

该方法可能在任何阶段被调用,但我们必须保证其不会导致 camera 或者 session 被关闭,但可能动态的改变 physical camera 和 Logocal multi-camera 的某些属性。

该方法必须在 HAL client 调用 ICameraDevice::open 打开属于这个 provider 的 camera 设备前先调用一次,用来初始化设备的状态。

其中方法中的 newState 为 camera device 的新状态。

getConcurrentStreamingCameraIds

原型: getConcurrentStreamingCameraIds() generates (Status status, vec<vec<string>> cameraIds);, 该方法于 2.6 版本加入。

该接口用于获取一个开流组合的配置列表, 在同时打开多个流的情况下,组合中建议的每一个 camera device 至少要支持下表中的这些组合。

Target 1Target 2
Type Size Type Size
YUV s1440p    
JPEG s1440p    
PRIV s1440p    
YUV / PRIV s720p YUV / PRIV s1440p
YUV / PRIV s720p JPEG s1440p

这里的 s720p - min (max 最大支持的分辨率为 1280 X 720 ), s1440p - min (最大支持的分辨率为, 1920 X 1440)

如果设备支持 MONOCHROME 能力 ( 通过获取 capabilities 获取到的能力包含 ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME ), 并因此支持 Y8 格式输出,还必须支持上述的流组合,那么其中的 YUV 应该替换为 Y8。

如果 device 的 capabilities 并没有包含 ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE, 那么其必须至少支持单路的 Y16 Stream, sVGA 分辨率的 Dataspace::DEPTH

camera framework 必须在收到 cameraDeviceStatusChange 得知任何 device 被加入或者移除的时候调用该接口来获取设备的最新状态。 这是为了让相机框架可以即使的获取相机 ID 的出流新组合。

对于每一个从 getConcurrentStreamingCameraIds 获取的 camera 设备 ID 的组合: 如果每个设备只能同时支持强制组合,如果需要确保相机应用程序之间的仲裁按预期工作,则并发集的资源成本总和一定大于 100。 仅当资源足以以全部功能 运行一组摄像机(通过摄像机元数据公开的配置设置中可用的最大资源消 耗帧速率和流大小设置)时,组合的资源成本总和才应该 <= 100。

为了保证 camera 的并发操作,在对目标 camera 进行 stream 配置前, camera framwork 需要先对所有涉及的 camera 调用 ICameraDevice.open(), 这使相机 HAL 进程有机会在流配置之前更好地分配硬件资源。

由于物理相机设备内部切换的潜在硬件限制,设备的完整 ZOOM_RATIO_RANGE ( 如果支持 ) 在并发操作期间可能不适用。 如果支持 ZOOM_RATIO, 则相机 HAL 必须确保在并发操作期间该设备支持 [ 1.0, ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ] 的 ZOOM_RATIO_RANGE。

 

 

isConcurrentStreamCombinationSupported

原型: isConcurrentStreamCombinationSupported(vec<CameraIdAndStreamCombination> configs) generates (Status status, bool queryStatus);, 该方法于 2.6 版本加入。

当需要与其他设备一起开流的时候,可用该方法检查设备是否支持指定的组合方式。

每个设备的 streamList 必须包含至少一个可输出的流,但是不能包含多个 input stream。 与常规流配置相比, framwork 不创建或初始化任何实际流。 这意味着 Hal 不得使用或关注 “id” 的值。

下面是一些前提条件:

 

 

 

 

 

 

------------------------------------------------------------------------ * Preconditions: * The framework can call this method at any time before, during and after active session configuration per device. This means that calls must not impact the performance of pending camera requests in any way. In particular there must not be any glitches or delays during normal camera streaming. * The framework must not call this method with any combination of camera ids that is not a subset of the camera ids advertised by getConcurrentStreamingCameraIds of the same provider. * Performance requirements: This call is expected to be significantly faster than stream configuration. In general HW and SW camera settings must not be changed and there must not be a user-visible impact on camera performance. * @param configs a vector of camera ids and their corresponding stream configurations that need to be queried for support. * @return status Status code for the operation, one of: OK: On successful stream combination query. METHOD_NOT_SUPPORTED: The camera provider does not support stream combination query. INTERNAL_ERROR: The stream combination query cannot complete due to internal error. @return true in case the stream combination is supported, false otherwise.

isConcurrentStreamCombinationSupported_2_7

isConcurrentStreamCombinationSupported_2_7(vec configs) generates (Status status, bool queryStatus);

该函数与 @2.6::isConcurrentStreamCombinationSupported 版本中的同名函数相同,只是该函数采用的是 @3.7::StreamConfiguration vector。

configs 参数为一个与查询 camera stream 相关的 camera id 集合。

当返回的 status 值为 OK 的时候请求成功。 而当返回 status 值为 METHOD_NOT_SUPPORTED 的时候,查询的流中包含不支持 stream combination 查询请求。 而当返回 status 值为 INTERNAL_ERROR 的时候则是内部出现错误。

返回值 queryStatus 的值为 true 则代表操作成功, false 代表操作失败。

ICameraProviderCallback.hal

这里 provider 的 CB 函数用于通知 camera service camera 子系统的状态变更。

cameraDeviceStatusChange

原型: cameraDeviceStatusChange(string cameraDeviceName, CameraDeviceStatus newStatus);

cameraDeviceStatusChange: Callback to the camera service to indicate that the state of a specific camera device has changed. On camera service startup, when ICameraProvider::setCallback is invoked, the camera service must assume that all internal camera devices are in the CAMERA_DEVICE_STATUS_PRESENT state. The provider must call this method to inform the camera service of any initially NOT_PRESENT devices, and of any external camera devices that are already present, as soon as the callbacks are available through setCallback. @param cameraDeviceName The name of the camera device that has a new status. @param newStatus The new status that device is in.

torchModeStatusChange

原型: torchModeStatusChange(string cameraDeviceName, TorchModeStatus newStatus);

torchModeStatusChange: Callback to the camera service to indicate that the state of the torch mode of the flash unit associated with a specific camera device has changed. At provider registration time, the camera service must assume the torch modes are in the TORCH_MODE_STATUS_AVAILABLE_OFF state if android.flash.info.available is reported as true via the ICameraDevice::getCameraCharacteristics call. @param cameraDeviceName The name of the camera device that has a new status. @param newStatus The new status that device is in.

vendor tag
vendor tag
SELinux
简介

关于 SELinux 再 Android 中的简要介绍可以查看Android 官方关于 SELinux 的介绍。 AOSP 的 SELinux 仓库位于https://android.googlesource.com/platform/external/selinux/

如果想要查询 SELinux 的目的,架构,以及其他关于 SELinux 的一切,你可以查看 SELinux Notebook Github 工程, 该工程介绍了:

  • SELinux 的宗旨。
  • LSM / SELinux 架构、它的支持服务以及它们是如何在 GNU / Linux 中实现的。
  • SELinux 网络、虚拟机、X-Windows、PostgreSQL 和 Apache/SELinux-Plus SELinux 感知功能。
  • 关于 SELinux 的核心策略语言,以及如何为研究目的构建基本政策模块。
  • 关于新的 Common Intermediate Language (CIL) 语言的实现介绍。
  • SELinux 策略管理工具,以及其使用范例。
  • 参考的策略架构,它的支持服务以及如何实现的。
  • SELinux 在 Android 中的集成方式。

如果想跳过简介直接查看 SELinux 相关的介绍资料可以访问该文档 https://github.com/SELinuxProject/selinux-notebook/blob/main/src/toc.md

一些使用 SElinux 的场景

可以参考 Google 官方提供的参考用例 查看。(非完备清单)

一些经验
  • 单独编译 SELinux policy
    在经过 source build/envsetup.shlunch xxx-xxx 之后 执行 make selinux_policy 即可单独编译 SELinux 的 Policy
  • 如何查看进程和文件的安全标签
    查看进程的 SELinux 标签: ps -Z
    查看文件的 SELinux 标签: ls -Z
  • TE 文件格式的敏感性
    我们都知道 Windows 下的格式为 CRLF, 而 Linux 下位 LF, 我在编译的过程中发现 SElinux 的检查程序报告大量的 te 文件格式错误信息(如下所示), 这时你可能需要注意一下,你的文件是否为 LF 格式的。如果不是你可能需要使用 IDE 工具(比如说 vscode )将你的文件转换为 LF Unix 格式的。 如果你是在 Linux 服务器中,你可以直接使用 dos2unix 进行文件格式转换。
  • Android 下脚本文件格式的敏感性
    如果你启用的服务是一个脚本,
  • 对于 ioctl 的特殊性
    如果你的程序中使用了 ioctl 接口,即便你申明了访问 ioctl 的权限,那么你也同样会被内核报警告,如下所示:
    # te 中存在的规则        
    allow self_net self_net:udp_socket { create ioctl };
    
    # 报告的警告
    avc: denied { ioctl } for path="socket:[47164]" dev="sockfs" ino=47164 ioctlcmd=0x8914 scontext=u:object_r:self_net:s0 tcontext=u:object_r:self_net:s0 tclass=udp_socket permissive=1
    avc: denied { ioctl } for path="socket:[50134]" dev="sockfs" ino=50134 ioctlcmd=0x8916 scontext=u:object_r:self_net:s0 tcontext=u:object_r:self_net:s0 tclass=udp_socket permissive=1
        

    这是因为 ioctl 能够操作的东西太多, SELinux 对其做了进一步的限制,因此你需要针对 ioctl 做出额外的规则申明,如下所示:
    # te 中存在的规则    
    allowxperm ak_net ak_net:udp_socket ioctl { 0x8914 0x8916 };
        
  • SELinux 的宏
    官方定义的宏函数在 system/sepolicy/public/te_macros 路径下,其他和平台相关的宏可以在 device 目录使用 find -name "te_macros" 进行搜索。
  • SELinux 一键脚本
    #!/bin/bash
    
    set -e
    
    PRJ_HOST=l2
    PRJ_HOME=./firefly
    
    f_cp_selinux_server_2_device(){
    	TEMP_F_NAME=/tmp/$$$$
    	ssh ${PRJ_HOST} "cd ${PRJ_HOME}/out/target/product/rk3588_firefly_itx_3588j/ && ls $1"  | while read FILE
    	do
    		scp ${PRJ_HOST}:${PRJ_HOME}/out/target/product/rk3588_firefly_itx_3588j/$1/${FILE} ${TEMP_F_NAME}
    		adb push ${TEMP_F_NAME} //$1/${FILE}
    		rm -rf ${TEMP_F_NAME}
    	done
    }
    
    adb root && adb remount
    f_cp_selinux_server_2_device system/etc/selinux/mapping/
    f_cp_selinux_server_2_device system/etc/selinux
    f_cp_selinux_server_2_device vendor/etc/selinux
    f_cp_selinux_server_2_device odm/etc/selinux
    adb reboot
        
参考资料
  • Android SELinux介绍和配置
    简单介绍了 SELinux 的三种模式,如何开机关闭 SELinux, 如何查看进程以及 文件的 SELinux 权限,等等...
    文中对规则的解释我觉得非常形象,因此我摘抄到这里: 什么是规则? 规则就是针对 domain 对象的,上面我们说过,每一个进程都属于一个 domain (域), 规则就是设置哪个 domain (域)的对象(就是进程)能够对哪些 type (类型)的目标对象(文件或者属性或者设备)具有哪有操作(删除啊,访问啊等等),这就是规则!
    补充一下其中提到的但是不是很详细的一些信息:
    1. uboot 的关闭 selinux 的地方一般在 uboot 的板级配置中, 即 xxx_defconfig 中; kernel 中关闭的地方在板级的 dts 配置中;
  • SELinux 介绍
    本文介绍了 SELinux 的实现原理(简介),其中包括: SELinux 内部组成模块, SELinux 与 Linux 的关系, SELinux 与 Android 的关系; 以及 Security Context ( 安全上下文 ) 中的一些概念的定义,比如, Type Enforcement (TE) / Multi-Level Security (MLS) / Subject / Object / Subject, Object 与 Process, File 之间的关系,等等。
    该文基本上信息都能从 SELinux Notebook 工程找到更加权威的解释,如果需要深入了解,建议还是仔细阅读 SELinux Notebook 工程。
其他
  • 将 system 以读写的方式挂载
    adb root 
    adb disable-verity
    adb reboot
    adb root
    adb remount
        
  • 向 toybox 中添加我们所需支持的命令
    1. 工具在 external/toybox 中,打开 Android.bp 文件,找到你需要增加的命令(比如 brctl ),然后找到该命令关联的文件,find -name "brctl*" , 现在找到这个文件 brctl.c
    2. 将该文件添加到 Android.bp 的 device_srcs 中 -- 注意添加的顺序,这里面的文件都按照字母顺序排序。
    3. 并且在 toybox_symlinks 中加入 brctl ,让编译工具在编译的时候自动创建 brctl 这个软连接文件。
    4. 检查 Android.bp 中 cc_defaults,可以观察到与 Android 相关的配置头文件在 android/device 路劲中。
    5. 因此我们还要在 android/device 路径的 generated/config.h 中将我们想要的命令添加到 toybox 中。如下所示:
      -#define CFG_BRCTL 0
      -#define USE_BRCTL(...)
      +#define CFG_BRCTL 1         // 使能 brctl
      +#define USE_BRCTL(...) __VA_ARGS__         // 将 brctl 添加到 toybox 中
              
  • 向 Android 工程中加入一个我们自己的程序
    首先你需要将你的代码仓库迁移到 extern 目录,比如 brctl-utils
    其次你需要为该仓库编写 Android.bp 文件。Android.bp 如何编写可以参考本章下面 Android.bp 小节。
    如果你想将 brctl-utils 编进你的平台中,你需要在你平台相应的 AndroidBoard.mk 中的 PRODUCT_PACKAGES 变量加上该 package 名称。 文件可能在一个类似于这样 device/rockchip/rk3588/xxx 的路径中。
  • Android.bp 文件格式
    以模块类型开头,模块类型参考 该文档, 每个模块类型中需要设置相应的属性,点击参考文档里面相应的模块类型可以跳转到该类型的更加详细的说明。
    如下是一个集成 brctl-utils 的例子:
    all_srcs = [
        "libbridge/libbridge_devif.c",
        "libbridge/libbridge_misc.c",
        "libbridge/libbridge_init.c",
        "libbridge/libbridge_if.c",
        "brctl/brctl.c",
        "brctl/brctl_cmd.c",
        "brctl/brctl_disp.c",
    ]
    
    cc_binary {
        name: "brctl",
        // defaults: ["brctl-defaults"],
        host_supported: true,
        // recovery_available: true,
        vendor_ramdisk_available: true,
        srcs: all_srcs,
    
        cflags: [
            "-Os",
            "-Wall",
            "-Werror",
            "-Wno-unused-parameter",
            "-Wno-unused-result",
        ],
    
        // cppflags: [],
    
        local_include_dirs: [
            "libbridge"
        ],
    
        // header_libs: [ ],
        // static_libs: [ ],
        // shared_libs: [ ],
        // export_include_dirs: [ ],
    
        target: {
            android: {
                // local_include_dirs: [],
    
                // shared_libs: [],
    
                // symlinks: [],
            },
        },
    }
        
  • APEX
  • 内存测试
    stressapptest
  • Java 语言 ublic, protected, private, 不加修饰符 的区别
    修饰符类内部同个包(package)子类其他范围
    public Y Y Y Y
    protected Y Y Y N
    无修饰符 Y Y Y或者N(见说明) N
    private Y N N N

    说明:需要特别说明“无修饰符”这个情况,子类能否访问父类中无修饰符的变量/方法,取决于子类的位置。如果子类和父类在同一个包中,那么子类可以访问父类中的无修饰符的变量/方法,否则不行。
  • Android 的启动流程
    bootloader -> Linux 内核 -> init 进程 -> init 配置文件 -> zygote (JVM / JNI) -> SystemServer
    SystemServer -> 开 Binder 线程池 + SystemServiceManager -> (AMS / WMS / PMS / CS / SS ...)
    AMS -> Lancher.java ->
    应用启动: Lancher.java -> 冷启动/热启动 -> AMS -> Zygote ->fork -> ActivityThread.java

    init -> system/core/init -> init.cpp
    init.rc -> system/core/rootdir
    Android 的基础服务是从 init.zygote*.rc 配置中启动的
    Lancher.java -> packages/apps/Lancher*
    冷启动用 Instramentation, 热启动用 bindler
    AMS -> Instramentation -> Activity/fragment/application
  • Android 加固
    加固种类: DEX 加固, SDK 加固,资源加固, SO 加固, H5 加固
    SO 加固:代码/字符串加密,函数隐藏,动态加密,导入导出函数保护,防 HOOK ,对抗拖壳
    加固的方法:
 
参考文档
原创文章,版权所有,转载请获得作者本人允许并注明出处
我是留白;我是留白;我是留白;(重要的事情说三遍)
posted @ 2022-07-06 01:16  Mojies  阅读(2463)  评论(0编辑  收藏  举报