<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Android on lategege 的技术博客</title><link>https://lategege.com/categories/android/</link><description>Recent content in Android on lategege 的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 06 Apr 2026 06:07:10 +0000</lastBuildDate><atom:link href="https://lategege.com/categories/android/index.xml" rel="self" type="application/rss+xml"/><item><title>谷歌手机bootloader模式下fastboot命令不生效的问题</title><link>https://lategege.com/p/%E8%B0%B7%E6%AD%8C%E6%89%8B%E6%9C%BAbootloader%E6%A8%A1%E5%BC%8F%E4%B8%8Bfastboot%E5%91%BD%E4%BB%A4%E4%B8%8D%E7%94%9F%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98/</link><pubDate>Mon, 06 Apr 2026 06:07:10 +0000</pubDate><guid>https://lategege.com/p/%E8%B0%B7%E6%AD%8C%E6%89%8B%E6%9C%BAbootloader%E6%A8%A1%E5%BC%8F%E4%B8%8Bfastboot%E5%91%BD%E4%BB%A4%E4%B8%8D%E7%94%9F%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98/</guid><description>&lt;p&gt;最近给谷歌pixel6升级了android 16系统，本来想刷一个面具root.img,但是fastboot devices无论如何都没响应，可明明我手机已经进入了fastboot模式界面。&lt;/p&gt;
&lt;p&gt;我mac系统不应该有usb驱动问题，adb一切正常，我切换到windows，在设备管理器中没有看到设备，换了好几根数据线(被这里坑了，我都是华为数据线)，都不行。&lt;/p&gt;
&lt;p&gt;我都怀疑是不是bootloader内部的usb协议驱动出现了问题，但转念一想，这玩意出问题的话，大概率bootloader都启动不了，然后我尝试网上搜索找谷歌手机进入edl的办法，看看能不能通过一些芯片级线刷工具来刷一下bootloader，搞了半天搜不到谷歌手机进入edl的办法。&lt;/p&gt;
&lt;p&gt;第二天，我回顾昨天的经历，还是不死心，我使用谷歌的c to c线试一下，因为我的台式机没有c口，所以只能连上笔记本的c口，这一试，当我敲下fastboot devices的时候，屏幕上完好的出现了设备名称，搞了半天原来是数据线的问题，我又尝试了小米的数据线，发现小米的usb c数据线居然也可以用，华为的数据线在谷歌手机上，使用adb 没问题，但是进入fastboot模式后就无法使用命令了。&lt;/p&gt;
&lt;p&gt;总结: 谷歌手机用fastboot 不能使用华为数据线。&lt;/p&gt;</description></item><item><title>彻底搞懂init进程、init.rc(基于android aosp 15)</title><link>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82init%E8%BF%9B%E7%A8%8B-init-rc-%E5%9F%BA%E4%BA%8Eandroid-aosp-15/</link><pubDate>Sun, 28 Sep 2025 13:33:11 +0000</pubDate><guid>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82init%E8%BF%9B%E7%A8%8B-init-rc-%E5%9F%BA%E4%BA%8Eandroid-aosp-15/</guid><description>&lt;p&gt;上电加载—&amp;gt;init进程启动可以参考&lt;a href="https://lategege.com/p/android-最新最全面启动流程分析-含-tee-安全验证等/" title=" Android 最新最全面启动流程分析（含 TEE、安全验证等）"&gt; Android 最新最全面启动流程分析（含 TEE、安全验证等）&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;一、init程序源码和构建&lt;/h4&gt;
&lt;p&gt;在源码中init程序代码位于/system/core/init，其编译文件Android.bp: init.rc等文件都在/system/core/rootdir中.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//这里init_second_stage容易迷惑人,实际上它包含了first_stage逻辑
cc_defaults {
 name: "init_second_stage_defaults",
 stem: "init",
 defaults: ["init_defaults"],
 srcs: ["main.cpp"],
 symlinks: ["ueventd"],
}
cc_binary {
 name: "init_second_stage",
 defaults: ["init_second_stage_defaults"],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;构建系统会将rootdir下的rc文件和init程序打包进init_boot.img镜像中，内核启动后就会加载init_boot执行其中的init程序。&lt;/p&gt;
&lt;hr/&gt;
&lt;h4&gt;二、init程序执行流程&lt;/h4&gt;
&lt;h5&gt;1、init程序的入口main.cpp(/system/core/init/main.cpp)&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;//init进程分阶段,刚开始,可以看到有五条分支，第一次进入内核不带参数，所以进入FirstStageMain
int main(int argc, char** argv) {
 setpriority(PRIO_PROCESS, 0, -20);
 if (!strcmp(basename(argv[0]), "ueventd")) {
 return ueventd_main(argc, argv);
 }
 if (argc &amp;gt; 1) {
 if (!strcmp(argv[1], "subcontext")) {
 //.....
 return SubcontextMain(argc, argv, &amp;amp;function_map);
 }
 if (!strcmp(argv[1], "selinux_setup")) {
 return SetupSelinux(argv);
 }
 if (!strcmp(argv[1], "second_stage")) {
 return SecondStageMain(argc, argv);
 }
 }
 return FirstStageMain(argc, argv);
} &lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2、FirstStageMain 第一阶段初始化(/system/core/init/first_stage_init.cpp)&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int FirstStageMain(int argc, char** argv) {
 //设置环境变量
 setenv("PATH", _PATH_DEFPATH, 1)
 //构建初始文件系统tmpfs(内存中),创建基础文件目录
 mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")
 ...
 //获取cmdline、bootconfig
 android::base::ReadFileToString("/proc/cmdline", &amp;amp;cmdline);
 android::base::ReadFileToString("/proc/bootconfig", &amp;amp;bootconfig);
 ...
 //从cmdline、bootconfig中获取BOOT模式(充电、recovery,正常)
 BootMode boot_mode = GetBootMode(cmdline, bootconfig);
 //根据模式去加载不同内核模块
 LoadKernelModules(boot_mode, want_console,
 want_parallel, module_count) 
 //执行第一阶段文件系统挂载，最主要是/system挂载
 fsm-&amp;gt;DoFirstStageMount(); 
 ...
 const char* path = "/system/bin/init";
 const char* args[] = {path, "selinux_setup", nullptr};
 ...
 execv(path, const_cast&amp;lt;char**&amp;gt;(args));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在/system挂载后，执行了/system/bin/init，其实这个init和init_boot中是同一个，只是放在了两个镜像中，本质都是同一份代码构建的，所以启动/system/bin/init，就相当于启动/init自身，execv 是一个系统调用，传入参数selinux_setup，当前进程的代码、数据、堆栈被替换，进程ID不变，可以理解为init自己调用自己。&lt;/p&gt;
&lt;h5&gt;3、执行selinux_setup (/system/core/init/main.cpp)&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-c++"&gt;int main(int argc, char** argv){
 ....
 return SetupSelinux(argv);
 ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;SetupSelinux执行(/system/core/init/selinux.cpp)&lt;/h6&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int SetupSelinux(char** argv) {
 ...
 //加载 selinux 策略
 //1、内部先挂在其他没有挂载好的分区MountMissingSystemPartitions();
 //2、读取所有分区的策略文件 ReadPolicy(&amp;amp;policy);
 //3、加载策略LoadSelinuxPolicy(policy);
 //配置selinux挂载路径
 // set_selinuxmnt("/sys/fs/selinux");
 //正式加载，会调用内核
 // security_load_policy(policy.data(), policy.size()
 LoadSelinuxPolicyAndroid();
 //配置selinux为enforce模式,强制模式
 SelinuxSetEnforcement();
 //重置init执行文件的selinux上下文
 selinux_android_restorecon("/system/bin/init", 0);
 //进入second_stage
 const char* path = "/system/bin/init";
 const char* args[] = {path, "second_stage", nullptr};
 execv(path, const_cast&amp;lt;char**&amp;gt;(args));
&lt;p&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;h5&gt;4、执行second_stage (/system/core/init/main.cpp)&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-c++"&gt;int main(int argc, char** argv){
 ....
 return SecondStageMain(argv);
 ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;SecondStageMain执行(/system/core/init/init.cpp)&lt;/h6&gt;
&lt;pre&gt;&lt;code class="language-c++"&gt;int SecondStageMain(int argc, char** argv) {
 //一、selinux属性上下文初始化：PropertyInit();
 //1、配置selinux权限检查回调
 selinux_set_callback(SELINUX_CB_AUDIT, cb);
 //2、创建属性设备节点 mkdir("/dev/__properties__")
 mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
 //3、加载各分区中的selinux属性上下文
 LoadPropertyInfoFromFile("分区/etc/selinux/plat_property_contexts"
 //4、将属性上下文写入/dev/__properties__/property_info),它自身的文件上下文为u:object_r:default_prop:s0
 WriteStringToFile(serialized_contexts, "/dev/__properties__/property_info", 0444, 0, 0, false)
 ----------------------------
 //二、系统属性系统
 //初始化系统属性区域，实际调用代码/bioinc/libc/system_properties/system_properties.cpp---&amp;gt;SystemProperties::AreaInit
 //内部会读取上面写入的property_info
 //然后mmap申请一块匿名内存，在这块内存中创建上下文节点列表
 //创建/dev/__properties__/properties_serial
 //将properties_serial通过mmap映射到内存
 __system_property_area_init();
&lt;pre&gt;&lt;code&gt;//提取设备树、cmdline、bootconfig中的属性信息
ProcessKernelDt();
ProcessKernelCmdline();
ProcessBootconfig();
//配置kernelbootprop属性信息，就是给上面没获取到的关键属性赋默认值，获取到的就忽略
ExportKernelBootProps();
//读取从各分区的xxx.prop，加入属性列表
PropertyLoadBootDefaults();
//读取自定义属性，加入属性列表
PropertyLoadDerivedDefaults();
//以上添加属性都是通过__system_property_find先查找
//如果存在就__system_property_update， 不存在就__system_property_add
//实际调用/bioinc/libc/system_properties/system_properties.cpp---&amp;amp;gt;SystemProperties::Update|Add
//实际添加到mmap内存区域，也就是/dev/__properties__/properties_serial中
----------------
//挂载扩展文件系统，主要是将tmpfs挂载到/apex，为后续挂载做准备
MountExtraFilesystems();

//selinux标签初始化，就是将所有关键目录加上selinux标签，并且恢复到policy定义的状态
SelabelInitialize();
SelinuxRestoreContext();

//注册信号监听处理，处理子进程的退出，还有fork后的回调
InstallSignalFdHandler(&amp;amp;amp;epoll);
//处理线程通知,通过eventfd(0)来处理其他线程发来的通知
InstallInitNotifier(&amp;amp;amp;epoll);

//启动属性服务
//调用property_service.cpp的StartPropertyService
 //1.初始化版本InitPropertySet(&amp;quot;ro.property_service.version&amp;quot;, &amp;quot;2&amp;quot;);
 //2.开启两个线程打开socket，放入epoll监听(一个给系统用property_service_for_system，一个给普通程序用property_service)
 //3.有写入属性的请求会回调handle_property_set_fd，还是通过上面的方式写入到/dev/__properties__/properties_serial。
 //4.在epoll中还监听了init自身的socket,处理函数HandleInitSocket，用来处理PersistentProperties这种重新也生效的属性
StartPropertyService(&amp;amp;amp;property_fd);

----------
//初始化init子环境，该方法会fork一个进程执行一些特权操作，这样不破坏主init进程环境
//对应入口main.cpp---&amp;amp;gt; return SubcontextMain(argc, argv, &amp;amp;amp;function_map); ，子进程通过socketpair和init主进程通信
InitializeSubcontext();

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

//创建动作管理和服务列表
ActionManager&amp;amp;amp; am = ActionManager::GetInstance();
ServiceList&amp;amp;amp; sm = ServiceList::GetInstance();
//加载启动脚本
 //创建解析器Parser parser = CreateParser(action_manager, service_list);
 //解析各个分区下 /分区/etc/init 目录中的rc文件
 //am将各种action按先后顺序入队列
LoadBootScripts(am, sm);

while(true){
 ....
 //处理关机命令
 HandlePowerctlMessage(*shutdown_command);
 //执行rc脚本
 am.ExecuteOneCommand()
 ....
 //epoll定时唤醒，或者有上面监听的fd写入也会唤醒
 auto epoll_result = epoll.Wait(epoll_timeout);
 if (!IsShuttingDown()) {
 //处理外部控制命令，如开启start、停止stop、重启resart 那些rc中注册的服务
 HandleControlMessages();
 //设置usb控制
 SetUsbController();
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;h5&gt;5、init.&lt;em&gt;.rc执行 (/system/core/rootdir/init.&lt;/em&gt;.rc)&lt;/h5&gt;
&lt;p&gt;后面所有进程的启动就需要查看init.*.rc了。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://img.lategege.com:30443/images/2025/09/28/androidrc.drawio.png"&gt;&lt;img alt="android启动rc文件.drawio" src="https://img.lategege.com:30443/images/2025/09/28/androidrc.drawio.png"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h6&gt;import示例&lt;/h6&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#init.rc
import /init.environ.rc
&lt;p&gt;#init.environ.rc
on early-init
export ANDROID_BOOTLOGO 1
&amp;hellip;..&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h6&gt;action示例&lt;/h6&gt;
&lt;p&gt;其中on是固定写法，early-init为触发条件，export …为命令行&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#init.environ.rc
on early-init
 export ANDROID_BOOTLOGO 1&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h6&gt;service示例&lt;/h6&gt;
&lt;p&gt;其中service固定写法， zygote名称 ，/…为程序路径，后面的是参数 第二行起就是程序的配置项&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
 class main
 priority -20
 user root&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;h6&gt;下面枚举了绝大部分触发条件和命令，以及service相关的配置&lt;/h6&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;aticon触发类型&lt;/th&gt;
&lt;th&gt;实际条目&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;boot阶段触发器&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on early-init&lt;/code&gt;- 在早期初始化阶段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on init&lt;/code&gt;- 在初始化阶段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on late-init&lt;/code&gt;- 在后期初始化阶段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on post-fs&lt;/code&gt;- 文件系统挂载后&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on post-fs-data&lt;/code&gt;- 数据分区挂载后&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on boot&lt;/code&gt;- 系统启动完成时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on charger&lt;/code&gt;- 充电模式启动时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​属性变化触发器&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on property:&amp;lt;key&amp;gt;=&amp;lt;value&amp;gt;&lt;/code&gt;- 当指定属性值匹配时触发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on property:&amp;lt;key&amp;gt;=*&lt;/code&gt;- 当属性值发生变化时触发（不检查具体值）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;设备相关触发器&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on device-added-&amp;lt;path&amp;gt;&lt;/code&gt;- 当设备节点添加时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on device-removed-&amp;lt;path&amp;gt;&lt;/code&gt;- 当设备节点移除时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;文件系统触发器&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on fs&lt;/code&gt;- 文件系统挂载时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on zygote-start&lt;/code&gt;- Zygote进程启动时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​其他触发器​&lt;/strong&gt;​&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on early-boot&lt;/code&gt;- 早期启动阶段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on nonencrypted&lt;/code&gt;- 当设备未加密时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on load_persist_props&lt;/code&gt;- 加载持久属性时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on load_all_props&lt;/code&gt;- 加载所有属性时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;on firmware_mounts_complete&lt;/code&gt;- 固件挂载完成时&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;可用命令类型&lt;/th&gt;
&lt;th&gt;实际命令&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;进程管理​&lt;/strong&gt;​&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start &amp;lt;service&amp;gt;&lt;/code&gt;- 启动指定服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stop &amp;lt;service&amp;gt;&lt;/code&gt;- 停止指定服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;restart &amp;lt;service&amp;gt;&lt;/code&gt;- 重启指定服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;exec [ &amp;lt;seclabel&amp;gt; [ &amp;lt;user&amp;gt; [ &amp;lt;group&amp;gt;\* ] ] ] -- &amp;lt;command&amp;gt; [ &amp;lt;argument&amp;gt;\* ]&lt;/code&gt;- 执行命令并替换当前进程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;文件系统操作&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkdir &amp;lt;path&amp;gt; [mode] [owner] [group]&lt;/code&gt;- 创建目录&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;symlink &amp;lt;target&amp;gt; &amp;lt;path&amp;gt;&lt;/code&gt;- 创建符号链接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;write &amp;lt;path&amp;gt; &amp;lt;content&amp;gt;&lt;/code&gt;- 向文件写入内容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;copy &amp;lt;src&amp;gt; &amp;lt;dst&amp;gt;&lt;/code&gt;- 复制文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chown &amp;lt;owner&amp;gt; &amp;lt;group&amp;gt; &amp;lt;path&amp;gt;&lt;/code&gt;- 更改文件所有者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chmod &amp;lt;mode&amp;gt; &amp;lt;path&amp;gt;&lt;/code&gt;- 更改文件权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;restorecon &amp;lt;path&amp;gt;&lt;/code&gt;- 恢复SELinux上下文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;restorecon_recursive &amp;lt;path&amp;gt;&lt;/code&gt;- 递归恢复SELinux上下文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​属性操作&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setprop &amp;lt;name&amp;gt; &amp;lt;value&amp;gt;&lt;/code&gt;- 设置系统属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;trigger &amp;lt;event&amp;gt;&lt;/code&gt;- 触发另一个事件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;设备操作&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;insmod &amp;lt;path&amp;gt;&lt;/code&gt;- 加载内核模块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rmmod &amp;lt;name&amp;gt;&lt;/code&gt;- 移除内核模块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setrlimit &amp;lt;resource&amp;gt; &amp;lt;cur&amp;gt; &amp;lt;max&amp;gt;&lt;/code&gt;- 设置资源限制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;系统控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class_start &amp;lt;classname&amp;gt;&lt;/code&gt;- 启动指定类别的所有服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class_stop &amp;lt;classname&amp;gt;&lt;/code&gt;- 停止指定类别的所有服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class_reset &amp;lt;classname&amp;gt;&lt;/code&gt;- 重置指定类别的所有服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;domainname &amp;lt;name&amp;gt;&lt;/code&gt;- 设置域名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hostname &amp;lt;name&amp;gt;&lt;/code&gt;- 设置主机名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mount &amp;lt;type&amp;gt; &amp;lt;device&amp;gt; &amp;lt;dir&amp;gt; [ &amp;lt;flag&amp;gt;\* ] [&amp;lt;options&amp;gt;]&lt;/code&gt;- 挂载文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;umount &amp;lt;path&amp;gt;&lt;/code&gt;- 卸载文件系统&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;swapon_all &amp;lt;fstab&amp;gt;&lt;/code&gt;- 启用所有交换分区&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait &amp;lt;path&amp;gt; [ &amp;lt;timeout&amp;gt; ]&lt;/code&gt;- 等待文件存在&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait_for_prop &amp;lt;name&amp;gt; &amp;lt;value&amp;gt;&lt;/code&gt;- 等待属性值匹配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;strong&gt;日志和调试&lt;/strong&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;loglevel &amp;lt;level&amp;gt;&lt;/code&gt;- 设置内核日志级别&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;load_all_props&lt;/code&gt;- 加载所有属性文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;load_persist_props&lt;/code&gt;- 加载持久属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;​&lt;/strong&gt;​安全相关****&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setcon &amp;lt;context&amp;gt;&lt;/code&gt;- 设置当前SELinux上下文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setenforce &amp;lt;0\|1&amp;gt;&lt;/code&gt;- 设置SELinux强制模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setkey &amp;lt;keycode&amp;gt; &amp;lt;value&amp;gt;&lt;/code&gt;- 设置键盘映射&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****电源管理​&lt;strong&gt;​&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;powerctl&lt;/code&gt;- 电源控制命令&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;service配置类型&lt;/th&gt;
&lt;th&gt;实际options&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;执行控制选项&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;disabled&lt;/code&gt; - 服务不随class自动启动，必须显式启动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;oneshot&lt;/code&gt; - 服务退出后不自动重启&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;onrestart&lt;/code&gt; - 当服务重启时执行一个命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;critical&lt;/code&gt; - 关键服务，频繁崩溃会触发系统恢复&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;restart_periodic &amp;lt;seconds&amp;gt;&lt;/code&gt; - 周期性重启策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;shutdown &amp;lt;shutdown_behavior&amp;gt;&lt;/code&gt; - 设置关机行为&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;onrestart&lt;/code&gt; - 服务重启时执行的命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;用户与权限类&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用户和组配置&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user &amp;lt;username&amp;gt;&lt;/code&gt; - 运行身份（如 &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;root&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;group &amp;lt;group&amp;gt; [附加组...]&lt;/code&gt; - 主组和附加组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;supplementarygids &amp;lt;gid...&amp;gt;&lt;/code&gt; - 补充组ID（如 &lt;code&gt;supplementarygids 1000 1001&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;capabilities &amp;lt;cap...&amp;gt;&lt;/code&gt; - Linux Capabilities（如 &lt;code&gt;capabilities NET_ADMIN SYS_ADMIN&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;seclabel &amp;lt;SELinux上下文&amp;gt;&lt;/code&gt; - 强制SELinux标签（如 &lt;code&gt;seclabel u:r:hal_bluetooth:s0&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;namespace &amp;lt;pid|mnt|net&amp;gt;&lt;/code&gt; - 进入命名空间（如 &lt;code&gt;namespace net&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;资源限制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rlimit &amp;lt;resource&amp;gt; &amp;lt;cur&amp;gt; &amp;lt;max&amp;gt;&lt;/code&gt; - 设置资源限制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;常见资源类型: 1. &lt;code&gt;cpu&lt;/code&gt; - CPU时间（秒） 2. &lt;code&gt;nofile&lt;/code&gt; - 文件描述符数量 3. &lt;code&gt;memlock&lt;/code&gt; - 锁定内存大小 4. &lt;code&gt;nproc&lt;/code&gt; - 最大进程数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;安全配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;priority &amp;lt;priority&amp;gt;&lt;/code&gt; - 设置调度优先级（-20到19）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ioprio &amp;lt;class&amp;gt; &amp;lt;0-7&amp;gt;&lt;/code&gt; - I/O调度优先级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;class类型: 0:none 1:realtime 2:best-effort(默认) 3:idle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;文件与通信类&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;socket &amp;lt;名称&amp;gt; &amp;lt;类型&amp;gt; &amp;lt;权限&amp;gt; [user] [group] [seclabel]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;类型：&lt;code&gt;stream&lt;/code&gt;/&lt;code&gt;dgram&lt;/code&gt;/&lt;code&gt;seqpacket&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：&lt;code&gt;socket mysocket dgram 660 root system&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file &amp;lt;路径&amp;gt; &amp;lt;r|w|rw&amp;gt;&lt;/code&gt; - 预打开文件（如 &lt;code&gt;file /data/log.txt rw&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;writepid &amp;lt;文件...&amp;gt;&lt;/code&gt; - 写入PID到文件（如 &lt;code&gt;writepid /dev/cpuset/foreground/tasks&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;环境配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;env &amp;lt;key&amp;gt; &amp;lt;value&amp;gt;&lt;/code&gt; - 设置环境变量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;console&lt;/code&gt; - 允许服务使用控制台（已废弃，Android 10+移除）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;logd&lt;/code&gt; - 重定向输出到logd（替代 &lt;code&gt;console&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;其他重要选项&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;重启控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class &amp;lt;name&amp;gt;&lt;/code&gt; - 指定服务类别&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;task_profiles &amp;lt;配置文件...&amp;gt;&lt;/code&gt; - 关联cgroup配置文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;override&lt;/code&gt; - 覆盖同名的已定义服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;memcg.swappiness &amp;lt;值&amp;gt;&lt;/code&gt; - 控制内存交换行为&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reboot_on_failure &amp;lt;目标&amp;gt;&lt;/code&gt; - 自定义崩溃时重启目标（如 &lt;code&gt;reboot_on_failure bootloader&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;class &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​core​&lt;/strong&gt;​ - 核心系统服务&lt;/td&gt;
&lt;td&gt;最基本的系统服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;通常在早期启动阶段启动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：ueventd（设备节点管理）、logd（日志服务）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​main​&lt;/strong&gt;​ - 主要系统服务&lt;/td&gt;
&lt;td&gt;系统正常运行所需的主要服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;在核心服务之后启动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：servicemanager（Binder IPC）、surfaceflinger（图形合成)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;late_start​&lt;/strong&gt;​ - 延迟启动服务&lt;/td&gt;
&lt;td&gt;在系统基本功能就绪后启动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;通常用于不紧急的服务或第三方服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：一些厂商定制服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****charger​&lt;strong&gt;​ - 充电模式服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;仅在设备处于充电模式时启动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;用于显示充电界面和状态&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：charger服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;​&lt;strong&gt;​boot​&lt;/strong&gt;​ - 启动类服务&lt;/td&gt;
&lt;td&gt;与系统启动过程相关的服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：bootanim（启动动画）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****post-boot​&lt;strong&gt;​ - 启动后服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;在系统完成主要启动后运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;用于优化或维护任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****hal​&lt;strong&gt;​ - 硬件抽象层服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;与硬件交互的服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;示例：各种HIDL/ AIDL HAL服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****apex​&lt;strong&gt;​ - APEX模块服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Android APEX模块提供的服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;****vnd​&lt;strong&gt;​ - 供应商特定服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;设备制造商提供的专有服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;**​early_hal` - 早期HAL服务&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;需要在其他服务之前启动的HAL服务&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h5&gt;6、核心rc文件解析 (/system/core/rootdir/init.rc)&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#配置一些必要的环境变量，导入硬件驱动服务
import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc
.....
on init 
 .....
 # 也就是上面导入的其中三个servicemanager服务，在init阶段会启动，这三个服务是binder核心管理服务
 start servicemanager
 start hwservicemanager
 start vndservicemanager
&lt;p&gt;#在late-init阶段执行fs文件系统的初始化
#early-init→ init→ late-init(early-fs→ fs→ post-fs→ post-fs-data&amp;hellip;)
on late-init
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger load-bpf-programs
trigger bpf-progs-loaded
##触发zygote服务
trigger zygote-start
trigger firmware_mounts_complete
trigger early-boot
trigger boot
on early-fs
##存储系统的基石，Volume Daemon 卷守护进程
start vold&lt;br&gt;
on late-fs
#启动class为early_hal的所有服务
&amp;hellip;..
class_start early_hal
&amp;hellip;..
#上面在late-init阶段被执行
on zygote-start
wait_for_prop odsign.verification.done 1
exec_start update_verifier
start statsd
#启动64位zygote
start zygote
#启动32位zygote(忽略)
start zygote_secondary
on boot
&amp;hellip;..
#启动所有class为hal服务
class_start hal
#启动所有class为core的服务
class_start core&lt;/p&gt;
&lt;p&gt;#核心服务包括ueventd,这个服务和init程序是同一个程序，只不过拷贝到了/system/bin/ueventd下，实际调用/system/core/init/main.cpp中的ueventd_main(argc,argv)分支，该守护进程负责设备节点管理，以及后面的设备热插拔管理， 它是android硬件访问的基石
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
user root
shutdown critical &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#在late-init阶段，zygote进程被启动
#app_process64源码路径(/frameworks/base/cmds/app_process/app_main.cpp)
#传入了6个参数
#-Xzygote \ # argv[1]：虚拟机参数
#/system/bin \ # argv[2]：父目录路径
#--zygote \ # argv[3]：标志位（启动zygote模式）
#--start-system-server \ # argv[4]：标志位（启动系统服务）
#--socket-name=zygote # argv[5]：指定socket名称
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
 class main #class类型是main
 priority -20 #优先级-20
 user root #以root用户启动
 group root readproc reserved_disk #以root readproc reserved_disk用户组
 socket zygote stream 660 root system #socket流的权限
 socket usap_pool_primary stream 660 root system
 #服务重启时执行vdc,并重启audioserver、cameraserver、media、netd等进程
 onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse 
 onrestart write /sys/power/state on
 onrestart write /sys/power/wake_lock zygote_kwl
 onrestart restart audioserver
 onrestart restart cameraserver
 onrestart restart media
 onrestart restart --only-if-running media.tuner
 onrestart restart netd
 onrestart restart wificond
 #为 Zygote 进程分配高性能资源策略，确保其孵化的应用进程能获得充足的系统资源
 task_profiles ProcessCapacityHigh MaxPerformance
 #定义 Zygote 进程的崩溃监控策略，防止频繁崩溃导致系统不稳定。
 critical window=${zygote.critical_window.minute:-off} target=zygote-fatal&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后面就正式进入android framework了。&lt;/p&gt;</description></item><item><title>Android 最新最全面启动流程分析（含 TEE、安全验证等）</title><link>https://lategege.com/p/android-%E6%9C%80%E6%96%B0%E6%9C%80%E5%85%A8%E9%9D%A2%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90-%E5%90%AB-tee-%E5%AE%89%E5%85%A8%E9%AA%8C%E8%AF%81%E7%AD%89/</link><pubDate>Thu, 25 Sep 2025 15:52:22 +0000</pubDate><guid>https://lategege.com/p/android-%E6%9C%80%E6%96%B0%E6%9C%80%E5%85%A8%E9%9D%A2%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90-%E5%90%AB-tee-%E5%AE%89%E5%85%A8%E9%AA%8C%E8%AF%81%E7%AD%89/</guid><description>&lt;p&gt;最近整理了下最新android系统从上电开始的启动流程，这应该是网上最全面的从上电开机到launcher执行的整个启动流程，虽然说还有很多细节没提到，但基本已经是android系统启动的全貌了。&lt;/p&gt;
&lt;p&gt;真正想要理解android系统的启动流程需要有一些知识铺垫，包括但不限于操作系统原理、文件系统、密码学、指令集等。&lt;/p&gt;
&lt;p&gt;学过操作系统的人都知道x86架构的CPU，它有很多历史包袱，包括实模式到保护模式、段寄存器。这些都是为了兼容老旧cpu而不得不存在。它的指令集比较复杂，一条指令可能包含几条cpu操作，从传统BIOS到现代EFI BIOS依然没有摆脱历史包袱，所以x86系统的启动，最开始必须从16位实模式开始到32位保护模式再到64位长模式层层递进。&lt;/p&gt;
&lt;p&gt;而ARM芯片则没有负担，现代ARM自V9.2开始已经不再支持32位指令集，也就是从开机启动cpu处于64位，而arm作为移动端cpu，它不像桌面端cpu那样纯粹，桌面端cpu非常规范，它只要跑计算，不用过多考虑功耗，插在主板上就跑起来了，而移动端芯片功能却要比桌面cpu更多，且功耗要求尽量低，所以很多外设就封装进了芯片内，就变成了SOC，SOC内部包含了gpu、cpu、npu、pmu(电源管理)、音视频编解码器、dsp、通信基带、wifi、蓝牙、gpio、rom、sram等众多模块，这部分芯片厂商会提供BSP板级支持包，包含了集成在SOC内部的BootRom，输入UFS的bootloader以及带有soc内部设备驱动的linux内核、te os安全操作系统，然后提供基础android系统源码，OEM厂商（比如小米）收到芯片厂商的BSP后，在此基础上定制自己的产品，负责编写vendor层的驱动代码，以及定制系统UI和增加新的系统服务或者应用程序，由于谷歌的新架构，分区越来越细，android系统代码不再严重依赖于soc厂商的驱动，因为有兼容性要求，使得小米公司升级上层的android系统代码越来越简单，厂商的代码基本兼容android新系统，所以几乎不需要动，这也就造就了今天android系统的整个组成结构，如下图所示。&lt;/p&gt;
&lt;body&gt;&lt;div class="mxgraph" data-mxgraph='{"highlight":"#0000ff","nav":true,"resize":true,"dark-mode":"auto","toolbar":"zoom layers tags lightbox","edit":"_blank","xml":"&amp;lt;mxfile host=\"draw.lategege.com\" agent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36\" version=\"28.2.3\"&amp;gt;\n &amp;lt;diagram name=\"Page-1\" id=\"TF82WJkRjdgSPtmqIexC\"&amp;gt;\n &amp;lt;mxGraphModel dx=\"1999\" dy=\"1772\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"827\" pageHeight=\"1169\" math=\"0\" shadow=\"0\"&amp;gt;\n &amp;lt;root&amp;gt;\n &amp;lt;mxCell id=\"0\" /&amp;gt;\n &amp;lt;mxCell id=\"1\" parent=\"0\" /&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-2\" value=\"\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"-206.5\" y=\"-70\" width=\"1240\" height=\"810\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-26\" value=\"SOC\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;labelBorderColor=light-dark(#00000A,#EDEDED);fillColor=#b1ddf0;strokeColor=#10739e;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry y=\"70\" width=\"240\" height=\"270\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-27\" value=\"DRAM(内存)\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;fillColor=#e6d0de;gradientColor=#d5739d;strokeColor=#996185;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"270\" y=\"70\" width=\"430\" height=\"270\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-28\" value=\"UFS\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"2.5\" y=\"350\" width=\"707.5\" height=\"290\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-70\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-29\" target=\"_umvmRDYWOriw4nXjXCY-30\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-71\" value=\"1.上电执行\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-70\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.0146\" y=\"1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-103\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-29\" target=\"_umvmRDYWOriw4nXjXCY-90\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"78\" y=\"50\" /&amp;gt;\n &amp;lt;mxPoint x=\"640\" y=\"50\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-104\" value=\"10.cpu跳转执行安全内核\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-103\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.0806\" y=\"3\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-29\" value=\"CPU\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"10\" y=\"97.5\" width=\"90\" height=\"35\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-75\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-30\" target=\"_umvmRDYWOriw4nXjXCY-32\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-76\" value=\"3.获取根公钥\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-75\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.1667\" y=\"-1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-77\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-30\" target=\"_umvmRDYWOriw4nXjXCY-33\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-78\" value=\"4.读取ufs boot分区的header,验证bootloader签名\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-77\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.4792\" y=\"-4\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint x=\"9\" y=\"8\" as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-30\" value=\"BOOTROM\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"80\" y=\"170\" width=\"70\" height=\"40\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-32\" value=\"EFUSE\" style=\"ellipse;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"10\" y=\"220\" width=\"50\" height=\"50\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-33\" value=\"BOOT分区(存放bootloader)&amp;amp;lt;br&amp;amp;gt;&amp;amp;lt;br&amp;amp;gt;fastboot代码也在这里\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"10\" y=\"380\" width=\"70\" height=\"240\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-34\" value=\"RPMB分区\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;fillColor=#1ba1e2;fontColor=#ffffff;strokeColor=#006EAF;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"90\" y=\"380\" width=\"50\" height=\"240\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-35\" value=\"User分区(GPT分区表，部分分区支持a/b)\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;fillColor=#60a917;fontColor=#ffffff;strokeColor=#2D7600;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"148.5\" y=\"375\" width=\"530\" height=\"240\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-42\" value=\"kernel\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"170\" y=\"410\" width=\"50\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-45\" value=\"tz\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"359\" y=\"410\" width=\"41\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-46\" value=\"misc\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"410\" y=\"410\" width=\"50\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-47\" value=\"vbmeta\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"470\" y=\"410\" width=\"60\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-48\" value=\"recovery\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"540\" y=\"410\" width=\"60\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-50\" value=\"dtbo\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"170\" y=\"458.13\" width=\"50\" height=\"20\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-51\" value=\"modem\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"230\" y=\"457.5\" width=\"50\" height=\"20\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-53\" value=\"super分区(meta分区表，支持a/b分区)\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"168.5\" y=\"490\" width=\"501.5\" height=\"110\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-54\" value=\"system\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"180\" y=\"525\" width=\"50\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-55\" value=\"vendor\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"240\" y=\"525\" width=\"50\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-56\" value=\"product\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"299\" y=\"525\" width=\"51\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-57\" value=\"system_ext\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"359\" y=\"525\" width=\"71\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-58\" value=\"&amp;amp;lt;div&amp;amp;gt;odm&amp;amp;lt;/div&amp;amp;gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"440\" y=\"525\" width=\"60\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-59\" value=\"&amp;amp;lt;div&amp;amp;gt;vendor_dlkm&amp;amp;lt;/div&amp;amp;gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"515\" y=\"525\" width=\"65\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-60\" value=\"&amp;amp;lt;div&amp;amp;gt;odm_dlkm&amp;amp;lt;/div&amp;amp;gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"590\" y=\"525\" width=\"65\" height=\"55\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-61\" value=\"userdata\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"293\" y=\"454.38\" width=\"54\" height=\"23.75\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-62\" value=\"cache\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"353\" y=\"452.5\" width=\"57\" height=\"27.5\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-64\" value=\"pvmfw\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"420\" y=\"453.75\" width=\"50\" height=\"22.5\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-67\" value=\"非安全内存\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;fillColor=#ffcc99;strokeColor=#36393d;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"284\" y=\"97.5\" width=\"302\" height=\"222.5\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-68\" value=\"安全内存\" style=\"rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=9;fillColor=#cce5ff;strokeColor=#36393d;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"590\" y=\"97.5\" width=\"100\" height=\"222.5\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-73\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.007;entryY=0.052;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-30\" target=\"_umvmRDYWOriw4nXjXCY-27\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-74\" value=\"2.初始化外部DRAM\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-73\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.1922\" y=\"-1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-86\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-47\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-87\" value=\"7.加载vbmeta验证签名&amp;amp;lt;br&amp;amp;gt;计算各分区哈希和vbmeta&amp;amp;lt;br&amp;amp;gt;中比对等\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-86\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.5169\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint x=\"10\" as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-88\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-45\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-89\" value=\"8.加载tz安全系统内核\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-88\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.1864\" y=\"1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-91\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-90\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-92\" value=\"9.将tz 载入安全内存\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-91\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.222\" y=\"1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-94\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-42\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"330\" y=\"150\" /&amp;gt;\n &amp;lt;mxPoint x=\"330\" y=\"330\" /&amp;gt;\n &amp;lt;mxPoint x=\"195\" y=\"330\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-95\" value=\"11.获取android内核\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-94\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.5177\" y=\"-2\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-79\" value=\"bootloader\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"299\" y=\"120\" width=\"91\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-80\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.088;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-30\" target=\"_umvmRDYWOriw4nXjXCY-79\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint x=\"140\" y=\"215\" as=\"sourcePoint\" /&amp;gt;\n &amp;lt;mxPoint x=\"297.5\" y=\"150\" as=\"targetPoint\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-81\" value=\"5.将bootloader载入内存\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-80\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.4432\" y=\"2\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint x=\"18\" y=\"2\" as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-82\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-29\" target=\"_umvmRDYWOriw4nXjXCY-79\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-83\" value=\"6.cpu跳转执行bootloader执行\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-82\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.4984\" y=\"2\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint x=\"14\" y=\"2\" as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-90\" value=\"\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"597.5\" y=\"130\" width=\"85\" height=\"180\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-96\" value=\"\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"436\" y=\"150\" width=\"144\" height=\"120\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-97\" value=\"android内核\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"440\" y=\"160\" width=\"130\" height=\"40\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-133\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-98\" target=\"_umvmRDYWOriw4nXjXCY-124\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-134\" value=\"15.用户态执行\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-133\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.5881\" y=\"3\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-98\" value=\"android用户空间\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"441.25\" y=\"210\" width=\"127.5\" height=\"50\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-107\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-99\" target=\"_umvmRDYWOriw4nXjXCY-32\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"690\" y=\"170\" /&amp;gt;\n &amp;lt;mxPoint x=\"690\" y=\"650\" /&amp;gt;\n &amp;lt;mxPoint x=\"-10\" y=\"650\" /&amp;gt;\n &amp;lt;mxPoint x=\"-10\" y=\"245\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-108\" value=\"安全内核可访问EFUSE\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-107\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.1094\" y=\"2\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-109\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-99\" target=\"_umvmRDYWOriw4nXjXCY-34\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"750\" y=\"180\" /&amp;gt;\n &amp;lt;mxPoint x=\"750\" y=\"670\" /&amp;gt;\n &amp;lt;mxPoint x=\"115\" y=\"670\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-110\" value=\"安全内核可访问RPMB\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-109\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.5074\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-99\" value=\"tz内核空间\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"605\" y=\"150\" width=\"65\" height=\"40\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-100\" value=\"tz用户空间\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"607.5\" y=\"220\" width=\"65\" height=\"40\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-101\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-97\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-102\" value=\"12.载入内核\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-101\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.0167\" y=\"3\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-105\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=0;exitDx=0;exitDy=0;fontSize=9;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-29\" target=\"_umvmRDYWOriw4nXjXCY-97\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"33\" y=\"30\" /&amp;gt;\n &amp;lt;mxPoint x=\"505\" y=\"30\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-106\" value=\"13.cpu跳转执行android内核\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-105\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.1768\" y=\"3\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-43\" value=\"boot_init\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"230\" y=\"410\" width=\"50\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-44\" value=\"vendor_init\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"290\" y=\"410\" width=\"60\" height=\"30\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-122\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-118\" target=\"_umvmRDYWOriw4nXjXCY-43\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"735\" y=\"360\" /&amp;gt;\n &amp;lt;mxPoint x=\"268\" y=\"360\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-123\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-118\" target=\"_umvmRDYWOriw4nXjXCY-44\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"760\" y=\"380\" /&amp;gt;\n &amp;lt;mxPoint x=\"320\" y=\"380\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-118\" value=\"1.内核初始化内存管理，中断向量，内核调度、进程管理，核心驱动初始化&amp;amp;lt;br&amp;amp;gt;2.文件系统初始化，&amp;amp;lt;br&amp;amp;gt;3. 加载网络、GPU、音频等驱动&amp;amp;lt;br&amp;amp;gt;4.读取boot_init,vendor_init合并执行init进程进入android用户空间&amp;amp;lt;br&amp;amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;amp;lt;br&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;verticalAlign=middle;align=center;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"710\" y=\"-20\" width=\"100\" height=\"160\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-120\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.006;entryY=0.138;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-97\" target=\"_umvmRDYWOriw4nXjXCY-118\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-126\" value=\"14.内核初始化\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-120\"&amp;gt;\n &amp;lt;mxGeometry x=\"0.3905\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-135\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;dashed=1;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-124\" target=\"_umvmRDYWOriw4nXjXCY-53\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-136\" value=\"系统调用挂载\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-135\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.396\" y=\"-1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-137\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;dashed=1;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-124\" target=\"_umvmRDYWOriw4nXjXCY-61\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"790\" y=\"500\" /&amp;gt;\n &amp;lt;mxPoint x=\"569\" y=\"500\" /&amp;gt;\n &amp;lt;mxPoint x=\"569\" y=\"498\" /&amp;gt;\n &amp;lt;mxPoint x=\"334\" y=\"498\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-140\" value=\"系统调用挂载\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-137\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.1867\" y=\"1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-138\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-124\" target=\"_umvmRDYWOriw4nXjXCY-62\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"790\" y=\"460\" /&amp;gt;\n &amp;lt;mxPoint x=\"600\" y=\"460\" /&amp;gt;\n &amp;lt;mxPoint x=\"600\" y=\"500\" /&amp;gt;\n &amp;lt;mxPoint x=\"382\" y=\"500\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-139\" value=\"系统调用挂载\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"_umvmRDYWOriw4nXjXCY-138\"&amp;gt;\n &amp;lt;mxGeometry x=\"-0.2056\" y=\"-1\" relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;mxPoint as=\"offset\" /&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-124\" value=\"1.init进程启动后，会通过系统调用挂载其他所有分区，加载selinux文件，加载soc和odm厂商的特有驱动&amp;amp;lt;br&amp;amp;gt;2.zygote进程启动&amp;amp;lt;br&amp;amp;gt;3.system_server启动&amp;amp;lt;br&amp;amp;gt;4.核心服务启动&amp;amp;lt;br&amp;amp;gt;5&amp;amp;lt;span style=&amp;amp;quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&amp;amp;quot;&amp;amp;gt;.进入android世界&amp;amp;lt;/span&amp;amp;gt;\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"770\" y=\"380\" width=\"120\" height=\"290\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-129\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-127\" target=\"_umvmRDYWOriw4nXjXCY-50\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"-150\" y=\"468\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-130\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-127\" target=\"_umvmRDYWOriw4nXjXCY-48\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"-120\" y=\"450\" /&amp;gt;\n &amp;lt;mxPoint x=\"570\" y=\"450\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-131\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-127\" target=\"_umvmRDYWOriw4nXjXCY-46\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"-90\" y=\"400\" /&amp;gt;\n &amp;lt;mxPoint x=\"435\" y=\"400\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-132\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-127\" target=\"_umvmRDYWOriw4nXjXCY-51\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\"&amp;gt;\n &amp;lt;Array as=\"points\"&amp;gt;\n &amp;lt;mxPoint x=\"-180\" y=\"440\" /&amp;gt;\n &amp;lt;mxPoint x=\"255\" y=\"440\" /&amp;gt;\n &amp;lt;/Array&amp;gt;\n &amp;lt;/mxGeometry&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-127\" value=\"1.bootloader会读取dtbo分区，传递给后面的内核&amp;amp;lt;br&amp;amp;gt;2.bootloader会读取misc分区中标志位决定是否加载recovery执行&amp;amp;lt;br&amp;amp;gt;3.bootloader也负载识别modem分区，将它载入基带芯片的sram中执行\" style=\"rounded=0;whiteSpace=wrap;html=1;fontSize=9;\" vertex=\"1\" parent=\"1\"&amp;gt;\n &amp;lt;mxGeometry x=\"-180\" y=\"140\" width=\"120\" height=\"210\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;mxCell id=\"_umvmRDYWOriw4nXjXCY-128\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0.363;entryY=-0.001;entryDx=0;entryDy=0;entryPerimeter=0;\" edge=\"1\" parent=\"1\" source=\"_umvmRDYWOriw4nXjXCY-79\" target=\"_umvmRDYWOriw4nXjXCY-127\"&amp;gt;\n &amp;lt;mxGeometry relative=\"1\" as=\"geometry\" /&amp;gt;\n &amp;lt;/mxCell&amp;gt;\n &amp;lt;/root&amp;gt;\n &amp;lt;/mxGraphModel&amp;gt;\n &amp;lt;/diagram&amp;gt;\n&amp;lt;/mxfile&amp;gt;\n"}' style="max-width:100%;border:1px solid transparent;"&gt;&lt;/div&gt;
&lt;script src="https://viewer.diagrams.net/js/viewer-static.min.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;p&gt;AOSP代码，GMS套件-------谷歌&lt;/p&gt;
&lt;p&gt;Soc芯片、BSP板级支持包(Bootloader、原始Kernel、TE OS、在AOSP基础上改造的整个系统源码等)---------芯片公司提供(高通、联发科等）&lt;/p&gt;
&lt;p&gt;DRAM、UFS选型，基于BSP支持包做特有驱动开发，功能定制---------设备制造商开发(如小米、oppo、vivo等)&lt;/p&gt;
&lt;p&gt;上面讲述了android系统的每个部分分别属于产业链的哪个位置，这有助于我们理解为什么android系统会演进到图片上所示的架构。&lt;/p&gt;
&lt;p&gt;首先ARM芯片有四种执行状态，分别是(EL0(android用户态),EL1(android内核态),EL2(虚拟化),EL3(安全态))，四种状态可以理解为cpu层级的硬件权限限制，它是系统安全的基石。&lt;/p&gt;
&lt;p&gt;下面是对整个启动流程的详细说明。&lt;/p&gt;
&lt;p&gt;一、手机开机上电，CPU复位就处于EL3这个安全状态，PC指针直接指向SOC内部的ROM(BootRom)，在安全状态下，BootRom可以访问eFuse获取到保存在其中的根公钥，同时BootRom会初始化外部DRAM，使得DRAM可以正常工作。&lt;/p&gt;
&lt;p&gt;二、BootRom从UFS磁盘中的固定位置(Boot分区)读取Bootloader Header，从中获取到Bootloader签名信息，使用根公钥解密签名，得到Bootloader的哈希值，然后将Bootloader程序载入非安全内存，并使用哈希算法计算出Bootloader摘要信息，与前面解密出来得到的哈希值做比对，如果不一致，就Boot Failed，引导失败。&lt;/p&gt;
&lt;p&gt;三、当哈希比对一致，BootRom就跳转到Bootloader在内存中的位置进行执行，此时CPU还是处于安全模式，Bootloader会从eFuse中获取OEM_UNLOCK_ENABLE标志，该标志决定是否可以解锁Bootloader,使Bootloader可以加载第三方的内核，一般不允许，所以很多手机都是无法解锁Bootloader也就不能刷入第三方的内核镜像，如果允许，Bootloader会读取misc分区的，是否已解锁标志，该标志就是通过fastboot unlock写入，一旦允许，bootloader将不在对后面的内核镜像做验签操作。&lt;/p&gt;
&lt;p&gt;四、假设没有解锁，就是正常流程，Bootloader使用代码中内置的公钥去验证vbmeta分区签名信息，验证通过后再次验证vbmeta分区中存储的其他分区的签名信息。&lt;/p&gt;
&lt;p&gt;五、验证全部通过后先将TZ这个安全系统载入到DRAM中的安全内存中，CPU执行TZ系统，此时还是安全状态，TZ安全系统会从efuse中获取密钥种子形成密钥保存在安全内存中，同时可能会读取ufs的RPMB分区来获取一些安全信息，然后执行TZ系统的用户态程序。&lt;/p&gt;
&lt;p&gt;六、Bootloader还负责读取modem分区的基带固件，将固件载入基带芯片的SRAM中，基带系统在基带芯片中执行。&lt;/p&gt;
&lt;p&gt;七、接着Bootloader读取dtbo分区，形成设备树，然后载入kernel进DRAM的非安全内存，将内核启动参数和dtb设备树传入kernel执行kernel代码，此时cpu状态已经切换为EL1内核态，kernel代码分为头部未压缩的代码和后面被压缩的kernel代码，先被执行的是kernel未被压缩的代码，然后在这部分代码中执行对后面压缩kernel的解压然后自运行。&lt;/p&gt;
&lt;p&gt;八、kernel执行后，先初始化内存管理模块，中断向量、内核调度器，还有进程管理模块，核心驱动的初始化，接着文件系统初始化，加载网络、GPU、音视频驱动等，完成上述操作后，kernel读取boot_init和vendor_init分区，将分区合并最终挂载成根文件系统，执行它其中的init程序，此时CPU状态从EL1转为EL0变成普通用户态。&lt;/p&gt;
&lt;p&gt;九、init进程启动后，它会加载meta分区表信息，验证各个文件系统分区的完整性，防篡改，由于内核已经完成了内存、文件系统初始化，这个时候已经具备了加载文件系统(ext4等）的能力，进程会通过syscall 会先后挂载其他剩余分区(userdata、cache、system、vendor、product等等)，初始化各分区中的selinux文件，内核的seliunx模块运作开始运作。&lt;/p&gt;
&lt;p&gt;十、接着init会执行init.rc文件，在init.rc中配置的服务进程全部都会fork出来并启动，其中就包含zygote进程,surfaceflinger，service_manager,mediaserver,audioserver,audioflinger等进程，各种hal驱动服务进程等，接着最核心的system_server进程启动，其中就涵盖了android系统中最核心的AMS,PMS,WMS等核心服务，AMS拉起SystemUI、Launcher进程，自此进入了android桌面环境，整个系统启动过程结束。&lt;/p&gt;
&lt;p&gt;补充知识：&lt;/p&gt;
&lt;p&gt;我们所有的代码无论引导还是操作系统都存放在UFS磁盘中，CPU要执行，必须将UFS中 的代码载入内存，然后跳转执行，载入过程需要读去UFS的扇区，UFS设计了Boot Lun(相当于Boot分区)，bootloader以二进制形式存放在其中，RPMB Lun(RPMB分区）存放安全密钥必须cpu安全模式才能读写，User Lun(用户分区) 这部分才是通过GPT分区表来管理的，其中kernel、tz、misc、recovery、misc、dtbo、modem、pvmfw基本都是以二进制形式存放在对应的gpt分区中，不存在文件系统的概念，而其他分区是在内核启动后，文件系统驱动执行后才能挂载，因为其他分区都是以文件系统的形式刷入对应的分区，所以在启动的android系统中你看不到这些二进制形式刷入的分区，因为它们不是文件系统，无法以文件系统的形式挂载到linux文件树中。&lt;/p&gt;</description></item><item><title>彻底搞懂android selinux系列三 (android selinux实战)</title><link>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%B8%89-android-selinux%E5%AE%9E%E6%88%98/</link><pubDate>Mon, 22 Sep 2025 15:49:58 +0000</pubDate><guid>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%B8%89-android-selinux%E5%AE%9E%E6%88%98/</guid><description>&lt;p&gt;假设我们已经写了一个AIDL HAL驱动模块，AIDL接口包名为android.hardware.aidltest，ITest, 输出的instance为default，AIDL实现模块为服务进程，编译在vendor分区，服务程序名称为android.hardware.aidltest.service，那么在/vendor/bin/hw目录会生成android.hardware.aidltest.service的可执行程序，我们配置成init进程在启动后加载。&lt;/p&gt;
&lt;p&gt;在这种情况下，如果不配置selinux策略，init进程无法拉起进程，服务也无法注册进server_manager,客户端程序也无法调用。这受到selinux严格限制，每一步都需要有对应的策略以及上下文。&lt;/p&gt;
&lt;p&gt;一、在BoardConfig.mk中配置selinux目录，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BOARD_VENDOR_SEPOLICY_DIRS += vendor/soc/sphone/sepolicy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;二、在vendor/soc/sphone/sepolicy 中创建file_contexts, service_contexts,hal_aidltest_default.te三个文件&lt;br/&gt;
在上一篇中已经说明file_contexts是linux selinux内核支持的，而service_contexts则是android扩展的，hal_aidltest_default.te则是策略文件，是selinux规定的语法。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#file_contexts内容如下，声明了vendor或者system/vendor目录。
/(vendor|system/vendor)/bin/hw/android\.hardware\.aidltest\.service u:object_r:hal_aidltest_default_exec:s0
#两个都是vendor分区，只不过system分区会将vendor分区连接到自身的vendor文件夹，本质就是同一个分区
#声明/vendor/bin/hw/android.hardware.aidltest.service这个可执行程序的上下文为u:object_r:hal_aidltest_default_exec:s0
#只是打了一个标签，其中标签类型是hal_aidltest_default_exec，这是通用写法&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;pre&gt;&lt;code&gt;#service_contexts文件内容如下
android.hardware.aidltest.ITest/default u:object_r:hal_aidltest_service:s0
#声明了aidl服务android.hardware.aidltest.ITest/default
#它的标签是u:object_r:hal_aidltest_service:s0,所以类型为hal_aidltest_service&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;pre&gt;&lt;code&gt;#hal_aidltest_default.te 文件内容如下,后面有对每一行的解释
hal_attribute(aidltest);
type hal_aidltest_default, domain, mlstrustedsubject;
hal_server_domain(hal_aidltest_default, hal_aidltest);
type hal_aidltest_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_aidltest_default);
binder_call(hal_aidltest_client, hal_aidltest_default)
type hal_aidltest_service, service_manager_type;
add_service(hal_aidltest_default, hal_aidltest_service)
allow hal_aidltest_client hal_aidltest_service:service_manager find;
hal_client_domain(system_server, hal_aidltest)
allow hal_aidltest_default servicemanager:binder { call transfer };
allow { platform_app shell } hal_aidltest:binder {call};&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;语句&lt;/th&gt;
&lt;th style="text-align: left;"&gt;等价于&lt;/th&gt;
&lt;th style="text-align: left;"&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;hal_attribute(aidltest);&lt;/td&gt;
&lt;td style="text-align: left;"&gt;attribute hal_aidltest; attribute hal_aidltest_client; attribute hal_aidltest_server; neverallow { hal_aidltest_server -halserverdomain } domain:process fork;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;expandattribute 属性名 true 可以忽略，它是一种编译优化，决定编译时是否展开，hal_attribute这个宏其实就是定义了三个属性，这三个属性名称依据的是传入的参数名， neverallow指定了不允许任何hal_aidltest_server类型进程执行fork，除了halserverdomain类型&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;type hal_aidltest_default, domain, mlstrustedsubject;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;声明类型hal_aidltest_default ，并赋予domain,mlstrustedsubject属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;hal_server_domain(hal_aidltest_default, hal_aidltest);&lt;/td&gt;
&lt;td style="text-align: left;"&gt;typeattribute hal_aidltest_default halserverdomain; typeattribute hal_aidltest_default hal_aidltest_server; typeattribute hal_aidltest_default hal_aidltest;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;将类型hal_aidltest_default赋予halserverdomain，hal_aidltest_server，hal_aidltest三个属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;type hal_aidltest_default_exec, exec_type, vendor_file_type, file_type;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;声明hal_aidltest_default_exec类别，并赋予exec_type，vendor_file_type，file_type属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;init_daemon_domain(hal_aidltest_default);&lt;/td&gt;
&lt;td style="text-align: left;"&gt;domain_auto_trans(init, hal_aidltest_default_exec, hal_aidltest_default) 同时等价于 type_transition init hal_aidltest_default_exec:process hal_aidltest_default;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;init进程执行hal_aidltest_default_exec程序时，进程的安全上下文从hal_aidltest_default_exec转换为hal_aidltest_default安全上下文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;binder_call(hal_aidltest_client, hal_aidltest_default)&lt;/td&gt;
&lt;td style="text-align: left;"&gt;allow hal_aidltest_client hal_aidltest_default_exec:binder{call transfer} allow hal_aidltest_default hal_aidltest_client:binder transfer; allow hal_aidltest_client hal_aidltest_default:fd use;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;允许具备hal_aidltest_client属性的所有类型进程对hal_aidltest_default类别的binder对象执行 call transfer 允许hal_aidltest_default(服务)类别进程对所有具备hal_aidltest_client属性的binder对象执行transfer 允许具备hal_aidltest_client属性的所有类型进程 使用hal_aidltest_default类型的fd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;type hal_aidltest_service, service_manager_type;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;声明hal_aidltest_service类型，并赋予service_manager_type属性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;add_service(hal_aidltest_default, hal_aidltest_service)&lt;/td&gt;
&lt;td style="text-align: left;"&gt;allow hal_aidltest_default hal_aidltest_service:service_manager { add find }; neverallow { domain -hal_aidltest_default }hal_aidltest_service:service_manager add;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;允许hal_aidltest_default类型进程对hal_aidltest_service类型的service_manager对象进程add和find操作 不允许除了hal_aidltest_default类型的进程对hal_aidltest_service类型的service_manager对象进行add操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;allow hal_aidltest_client hal_aidltest_service:service_manager find;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;允许具备hal_aidltest_client属性的所有进程对hal_aidltest_service类型的service_manager对象执行find操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;hal_client_domain(system_server, hal_aidltest)&lt;/td&gt;
&lt;td style="text-align: left;"&gt;typeattribute system_server halclientdomain; typeattribute system_server hal_aidltest_client; not_full_treble(` typeattribute system_server hal_aidltest; allow hal_aidltest system_file:dir r_dir_perms; allow hal_aidltest vendor_file:dir r_dir_perms; allow hal_aidltest vendor_file:file { read open getattr execute map };&lt;/td&gt;
&lt;td style="text-align: left;"&gt;将system_server类型赋予halclientdomain和hal_aidltest_client属性 将system_server 赋予hal_aidltest属性 允许具备hal_aidltest属性的所有类型(进程）对system_file类型的目录对象，执行读操作 允许具备hal_aidltest属性的所有类型(进程）对vendor_file类型的目录对象，执行读操作允许具备hal_aidltest属性的所有类型(进程）对vendor_file类型的文件对象，执行read open gettattr excute map操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;allow hal_aidltest_default servicemanager:binder { call transfer };&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;允许hal_aidltest_default类型进程对servicemanager类型的binder对象执行call transfer操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;allow { platform_app shell } hal_aidltest:binder {call};&lt;/td&gt;
&lt;td style="text-align: left;"&gt;&lt;/td&gt;
&lt;td style="text-align: left;"&gt;允许平台app进程和shell进程对具备hal_aidltest属性的所有binder对象执行call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr/&gt;
&lt;p&gt;最终selinux策略会经过编译系统编译进/vendor/etc/selinux目录中，然后打包成镜像程序就能正确运行了，在写selinux规则的时候会有点绕，不过总体而言就是定义类型，声明类型有哪些属性，然后写允许的规则，要写出这份策略文件需要对android selinux定义的原生属性类型对象比较熟悉，这个事情非常考验耐心。&lt;/p&gt;</description></item><item><title>彻底搞懂android selinux系列二 (android selinux概述)</title><link>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%BA%8C-android-selinux%E6%A6%82%E8%BF%B0/</link><pubDate>Tue, 16 Sep 2025 15:20:53 +0000</pubDate><guid>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%BA%8C-android-selinux%E6%A6%82%E8%BF%B0/</guid><description>&lt;p&gt;在说android selinux之前，我们先来问一个问题，selinux究竟在解决什么问题？&lt;/p&gt;
&lt;p&gt;我看到网上文章一上来就是各种概念，却不曾讲本质，任何事物的出现都是在解决问题。首先我们抛开所有安全相关策略，在一个裸内核上来讨论。&lt;/p&gt;
&lt;h6&gt;没有DAC(linux用户权限系统)的内核系统：&lt;/h6&gt;
&lt;p&gt;从手机上电启动，arm芯片中的boot程序被加载，它将手机中的uboot引导程序载入内存执行 ，uboot又将linux最小内核载入内存执行，直到小内核将完整的linux内核镜像载入并执行，此时init进程被启动，内核引入虚拟内存管理，将进程映射到不同的物理内存中去，这个时候不同进程是使用不同的物理内存页的。看似进程间的的确确是隔离了内存，但是内存是隔离了，文件系统中的文件呢？一切数据总不能在内存中一直呆着吧，进程总得把数据持久化才有意义吧？而现在所有进程都能通过文件系统访问到所有文件，那我们只要写段程序可以胡乱修改文件了，系统不崩才怪。&lt;/p&gt;
&lt;h6&gt;引入DAC(linux用户权限系统)的内核系统:&lt;/h6&gt;
&lt;p&gt;DAC自主访问控制模型，这名词相当别扭，DAC的本质就是引入用户的概念，称为主体，还有客体(文件)，操作(读写执行)，首先内核提供创建用户接口，并且程序执行期间有了用户概念，操作系统有了在哪个用户环境下执行的感知能力，内核默认会自建一个root身份来启动用户态的一号进程，至于内核要进入其他用户，那就只要创建即可，当切入哪个环境，就以哪个身份执行程序，首先程序在没有运行之前就是一个文件，它在磁盘中保存着，是文件就有规则，它是谁创建的，有哪些权限，所以root用户可以直接启动任意程序，只要授予他执行权限，当程序被载入内存就变成了一个进程，这个进程以什么用户来启动的就标志着它的权限范围由多大。不过只要我保护好root的密码，那么进程我可以通过创建新的用户来启动，这样权限范围就自然缩小了，这解决了上面没有DAC系统带来的所有进程可以修改所有文件的问题，但也带来了新的问题，假如一个程序通过某个漏洞修改了进程的用户，直接进入root身份，那么这个程序就能无法无天了。&lt;/p&gt;
&lt;h6&gt;引入MAC(selinux)的内核系统：&lt;/h6&gt;
&lt;p&gt;针对上面DAC没有解决的问题，selinux给出了解决方案，它引入的一套粒度更细的权限系统 ，包含上篇提到的用户、角色、类型、等级，它脱离于DAC的用户系统，给每个文件又标上了一个规则，然后又给进程套上了一把枷锁，也就是哪个进程允许访问哪个文件，事先已经规定好了。比如http_t进程只能访问http_file_t这个类型所对应的目录，它在selinux的用户身份是user_t，http_t进程即使被提权到root用户，此时selinux中的域的身份并未改变还是user_t，它还是无法访问其他文件。&lt;/p&gt;
&lt;p&gt;为什么android adb shell 权限很大，因为adb shell 是在adbd域中，为了调试方便，它的selinux权限自然会很大，但并不是为所欲为的，因为它不是dac中的root身份，也不是selinux中的无限制身份。userdebug和eng模式下，adb shell 默认临时关闭selinux，同时它也能切换为dac的root身份，所以他能做几乎所有事。&lt;/p&gt;
&lt;h6&gt;所以selinux所做的事是将进程套上了一把锁，dac是第一把锁，mac是第二把锁，root提权只开了第一把锁，第二把锁只要没开就是安全的。&lt;/h6&gt;
&lt;hr/&gt;
&lt;h4&gt;android中的selinux&lt;/h4&gt;
&lt;p&gt;android 的selinux 本质上还是使用的linux内核的selinux，不过android并没有完全使用到selinux的所有特性，它唯一使用的是类型。&lt;/p&gt;
&lt;p&gt;我们都知道一条安全上下文包含用户:角色:类型:权限等级，而android中
所有selinux用户都是u
所有主体都是r
所有客体都是object_r
所有权限等级都为s0
唯一变化的就是类型t
所以一条android selinux 上下文固定为 u:[r|object]:类型:s0&lt;/p&gt;
&lt;p&gt;另外android还在其上下文基础上做了扩展。在linux selinux中基本的规则是针对文件、端口、套接字的，而android扩展了对binder、server_manager、app进程的支持。&lt;/p&gt;
&lt;p&gt;android为什么要扩展呢？直接使用selinux对文件的操作行吗？
如果只针对文件，那么app进程想要使用某个服务，那必然要添加对那个服务进程的文件目录授权，这不乱套了吗？每个进程都直接访问服务进程的文件无疑是危险操作。而android系统中使用某个功能都是跨进程调用服务来实现的。所以android增加了对服务、系统属性的上下文。比如某个系统服务要调用另一个系统服务，那么我只要申明对目标服务的权限即可。property_contexts、service_contexts是特殊的上下文，它不同于file_contexts,file_contexts是内核selinux原生支持的，所以需要用户空间来加载property_contexts和service_contexts来做好映射。其中property_contexts是由init进程处理，service_contexts由servicemanager 进程处理，android系统中还有有个特殊的上下文配置seapp_contexts，它指定了用什么签名的文件适用哪些规则，这个很特殊的上下文是由zygote进程来处理的，它根据应用的seinfo（签名信息），和uid来将应用进程映射到具体的规则中，上面这些上下文在检查权限的时候都是用户态进程主动调用selinux_check_access()完成的，用户态通过 selinux_check_access() 把 (调用者sid, 目标sid, class, perm) 提交给内核，内核做完检查返回。&lt;/p&gt;
&lt;p&gt;有了上面的特殊扩展就够了吗？那我普通应用要访问相机，就能访问吗？虽然应用selinux中有对camera_service的允许，但直接让它获取，显然是有问题的，下面的工作就是android中另一套权限系统，做android开发的都知道要在清单文件中声明权限，敏感权限还需要动态获取，这都是PMS来授权的。对于camera_service服务，它的selinux规则已经写死，它被允许访问相机，而普通应用在selinux层面允许访问camera_service服务，但是camera_service服务不是随随便便提供服务的，它要去询问pms，这个应用有没有权限访问相机服务，如果有，那么camera_service才会将相机数据通过非特权资源fd方式返回给应用程序，这弥补了android权限中的最后一环。&lt;/p&gt;</description></item><item><title>彻底搞懂android selinux系列一 (linux selinux)</title><link>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%B8%80-linux-selinux/</link><pubDate>Tue, 16 Sep 2025 09:27:14 +0000</pubDate><guid>https://lategege.com/p/%E5%BD%BB%E5%BA%95%E6%90%9E%E6%87%82android-selinux%E7%B3%BB%E5%88%97%E4%B8%80-linux-selinux/</guid><description>&lt;h4&gt;一、selinux是什么&lt;/h4&gt;
&lt;p&gt;selinux是linux内核的一个安全策略模块,本质就是一个控制进程对资源(文件)访问的系统，它由内核加载，运行在内核空间中。
selinux系统由编译器(编译策略代码)、执行程序(selinux引擎)、用户态管理程序(如id、semanage等)、 策略规则代码(由用户指定.te、.fc、.if、.cil)构成。&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;--------------------------
# 传统编译方式
#.te 文件是描述策略规则文件
#.fc 文件是指定资源(文件)的上下文标签
#.if 文件是宏定义和接口
#.pp 单个最终策略文件
# policy.31整合的策略文件，被放入/etc/selinux/targeted/policy/，由selinux加载
# 1. 将 .te 文件编译为 .mod 文件
checkmodule -M -m -o my_app.mod my_app.te
# 2. 将 .mod 和 .fc 文件打包成 .pp 策略包
semodule_package -o my_app.pp -m my_app.mod -f my_app.fc
--------------------------
#现代方式
#.cil 新的策略文件
#.te可以编译成.cil 
#.cil也可以最终编译成.pp
sudo semodule -i my_app.cil&lt;/code&gt;&lt;/pre&gt;
&lt;hr/&gt;
&lt;h4&gt;二、selinux 规则有哪些组成部分&lt;/h4&gt;
&lt;h6&gt;组成&lt;/h6&gt;
&lt;p&gt;是否允许(role) 主体subject 对某个客体 object 做 某个动作 action
主体 subject、动作action、对象 object
一般一条或一组规则被写入后缀名为.te的文件中，等待selinux编译系统编译&lt;/p&gt;
&lt;h6&gt;role：&lt;/h6&gt;
&lt;p&gt;访问控制规则：默认(拒绝一切)、allow(允许)
编译策略规则: neverallow(绝对不允许出现允许A干B的规则)
审计日志规则: dontaudit(不记录拒绝的日志)、auditallow(允许的日志也记录)&lt;/p&gt;
&lt;p&gt;role + subject(主体，通常是进程(在selinux中也称域)) + object(客体)+object类别(可以是文件、目录、端口、套接字等任何系统资源)+ action(做什么，读写创建、设置读取属性、执行)&lt;/p&gt;
&lt;h6&gt;object类别:&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;file、dir、lnk_file、sock_file、tcp_socket、udp_socket、port&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;action：&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;针对file ----read, write, create, getattr, setattr, execute, append, unlink 等&lt;/li&gt;
&lt;li&gt;针对tcp_socket-----name_bind, connectto, send_msg, recv_msg&lt;/li&gt;
&lt;li&gt;针对port------name_bind&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h4&gt;三、一些完整的selinux规则示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;//-----访问规则-------
//允许httpd_t进程对于httpd_log_t这个文件进行读写、追加内容、获取文件属性 
allow httpd_t httpd_log_t : file { read write append getattr };
//允许httpd_t进程对http_port_t这个tcp_socket进行端口绑定
allow httpd_t http_port_t : tcp_socket name_bind;
&lt;p&gt;//&amp;mdash;&amp;ndash;编译策略规则&amp;mdash;&amp;ndash;
//绝对不允许出现user_t进程对lib_t、user_t这些文件进行写入的规则
neverallow user_t {lib_t user_t}: file write;&lt;/p&gt;
&lt;p&gt;//&amp;mdash;&amp;ndash;日志规则&amp;mdash;&amp;mdash;-
//审计日志保存在/var/log/audit/audit.log中
//系统日志保存在/var/log/messages中
//1. 首先，允许某个域（如sysadmin_t）写shadow文件
allow sysadmin_t shadow_t : file { write };
// 2. 强制记录任何成功的写操作
auditallow sysadmin_t shadow_t : file write;&lt;/p&gt;
&lt;p&gt;//一条拒绝记录日志如下,大量的拒绝记录
AVC denied: read access by bad_app_t to shadow_t
// 告诉SELinux：拒绝bad_app_t读shadow_t是正常的，别记日志了
dontaudit bad_app_t shadow_t : file read;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h4&gt;四、selinux高级特性-属性和宏&lt;/h4&gt;
&lt;p&gt;实际编写selinux策略中，为了简化和模块化，很少直接编写上面那种最基础的规则，而是大量使用属性和宏
domain 所有进程都有这个属性，宏是对客体和action的封装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//这一条意味着所有进程域都可以读取 httpd_log_t文件。
allow domain httpd_log_t : file read;
&lt;p&gt;//下面是一个宏，它封装客体http_port_t和action(name_bind)
corenet_tcp_bind_http_port(httpd_t)
等同于
allow httpd_t http_port_t : tcp_socket name_bind;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h4&gt;五、linux中selinux安全模块执行逻辑&lt;/h4&gt;
&lt;img height="260" src="https://img.lategege.com:30443/images/2025/09/16/d5cdfa644ddf.png" width="529"/&gt;
&lt;img height="270" src="https://img.lategege.com:30443/images/2025/09/16/54b28b5d27a1.png" width="630"/&gt;
&lt;p&gt;系统启动会挂载一个selinuxfs伪文件系统到/sys/fs/selinux中，伪文件系统保存着策略
avc模块:Access Vector Cache 访问策略缓存
用户空间的se管理工具:semanage seinfo sesearch sediff等&lt;/p&gt;
&lt;img height="267" src="https://img.lategege.com:30443/images/2025/09/16/fa029e917380.png" width="582"/&gt;
&lt;hr/&gt;
&lt;h4&gt;六、selinux 标签&lt;/h4&gt;
&lt;p&gt;selinux标签可以理解为是主体(subject)和客体(object)的属性，有了策略规则，但是你不能识别主体和客体也是没有用的，所以主体和客体就必须有标签。&lt;/p&gt;
&lt;p&gt;标签的组成:
用户:角色:类型:级别&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;用户:selinux user,以_u结尾
角色:selinux role,以_r结尾
类型:selinux type,类型标识符,以_t结尾
级别:sensitivity level, ,MLS(多级别安全),MCS(多类别安全)&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;User和Role&lt;/h6&gt;
&lt;p&gt;这里的用户角色是selinux不是linux系统中的用户和角色，它们属于两套系统
预定义的 SELinux 用户：
user_u: 用于普通登录用户。
staff_u: 用于有部分特权（如可以使用 sudo）的用户。
sysadm_u: 用于系统管理员。
system_u: 用于系统进程和系统对象（文件）。
root: 用于 root 用户。
unconfined_u: 用于不受限制的进程（几乎禁用 SELinux 保护）。
xguest_u: 用于极受限制的来宾用户（例如，只能使用浏览器）。&lt;/p&gt;
&lt;p&gt;预定义的 SELinux 角色：
object_r: 几乎所有客体（文件、端口等）都使用的角色。
system_r: 用于系统进程。
user_r: 用于普通用户进程。
staff_r: 用于 staff_u 用户的角色。
sysadm_r: 用于 sysadm_u 用户的角色。
unconfined_r: 用于 unconfined_u 用户的角色。
这些预定义的用户和角色及其基本关系是策略的一部分，是相对“固定”的。&lt;/p&gt;
&lt;p&gt;semanage user -l 可以查看用户&lt;/p&gt;
&lt;h6&gt;Type类别&lt;/h6&gt;
&lt;p&gt;selinux预定义的标识符，比如上面进程的标识http_t，客体的标识符httpd_log_t&lt;/p&gt;
&lt;h6&gt;MLS(多级别安全)&lt;/h6&gt;
&lt;p&gt;层次化等级 sensitivity:category（灵敏度：类别）
例如 s0, s0:c0.c1023 ,s0代表灵敏度，c0.c1023代表类别
这是一个层次化的等级，例如 s0（无分类）、s1（秘密）、s2（机密）、s3（绝密）。&lt;/p&gt;
&lt;p&gt;核心规则： 一个主体（进程）只能读取同级或更低级别的客体（文件），但只能写入同级或更高级别的客体。这被称为 “不读向上，不写向下”（No read up, no write down）。&lt;/p&gt;
&lt;p&gt;linux系统中默认都使用s0级别，所以级别这个概念可以忽略&lt;/p&gt;
&lt;p&gt;####### MCS:多类别安全
通常表示为 c0, c1, c2, ... 或一个范围 c0.c1023。
核心规则： 主体必须拥有客体所要求的所有类别，才能访问它。&lt;/p&gt;
&lt;p&gt;示例上下文：
一个运行在容器中的进程可能具有这样的上下文：
system_u:system_r:container_t:s0:c1,c2
这意味着它属于类别 c1 和 c2。它只能访问那些也被标记为 s0:c1,c2（或其子集，取决于策略）的文件。&lt;/p&gt;
&lt;h6&gt;标签的存储&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;普通文件存储在文件系统中该文件的扩展属性中。&lt;/li&gt;
&lt;li&gt;进程,内核动态管理(fork 继承父进程的安全上下文标签， exec 会进程转换，存放在内核的进程结构体中)&lt;/li&gt;
&lt;li&gt;端口，静态指定，策略中规定&lt;/li&gt;
&lt;li&gt;网络接口，和端口一样&lt;/li&gt;
&lt;li&gt;网络节点，同样是策略中规定&lt;/li&gt;
&lt;li&gt;内核对象，内核维护在对应内核对象中&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h4&gt;七、selinux bool值&lt;/h4&gt;
&lt;p&gt;在selinux中什么是bool值？它其实是一组策略规则的总开关。
在编写.te文件的时候，往往一个进程需要一组规则，下面httpd_use_nfs 就是一个bool值
selinux通过setsebool getsebool semanage boolean来控制这个值，对应/sys/fs/selinux/booleans 目录&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;allow httpd_t nfs_t : file { read write };
&lt;h1 id="这是一个条件规则如果-httpd_use_nfs-为真则启用内部的规则"&gt;这是一个条件规则：如果 &amp;lsquo;httpd_use_nfs&amp;rsquo; 为真，则启用内部的规则
&lt;/h1&gt;&lt;p&gt;if (httpd_use_nfs) {
allow httpd_t nfs_t : file { read write };
allow httpd_t mount_t : process { sigchld };
# &amp;hellip; 可能还有另外 5-10 条让 Apache 能正常使用 NFS 所必需的规则
}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h4&gt;八、selinux 上下文标签修正&lt;/h4&gt;
&lt;h6&gt;1、文件上下文修改&lt;/h6&gt;
&lt;p&gt;对任何文件的操作都可能会影响文件的上下文。&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#查看selinux上下文标签
#文件的创建和复制会继承父文件的上下文，移动保留原来上下文
ls -Z 文件名
&lt;p&gt;#修改上下文，下面这种方式修改后，restorecon会还原成原来的上下文。
chcon -u -r -t 文件名
-u 指定用户
-r 指定角色名
-t 指定类型
-v 打印输出
-R 递归操作
-h 修改软链接，不加则直接修改链接的文件
&amp;ndash;reference=文件名 参考该文件的上下文来修改&lt;/p&gt;
&lt;h1 id="默认上下文预定义的-etcselinuxtargetedcontextsfilesfile_contexts"&gt;默认上下文，预定义的 /etc/selinux/targeted/contexts/files/file_contexts
&lt;/h1&gt;&lt;h1 id="恢复上下文"&gt;恢复上下文
&lt;/h1&gt;&lt;p&gt;restorecon -F 文件名
-F 不加F则只恢复type，加-F可恢复所有&lt;/p&gt;
&lt;p&gt;#这条命令可以管理上下文
semanage fcontext -a -t -e 文件目录正则
-a 添加
-t 类型
-e 参考某个目录&lt;/p&gt;
&lt;p&gt;#正确修改上下文的方式是先用semanage fcontext为某个目录添加上下文，然后再restorecon某个文件或目录。&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;h6&gt;2、进程和端口上下文修改&lt;/h6&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#查看进程上下文
ps -eZ | grep 名称
&lt;p&gt;#查看所有端口的策略规则
semanage port -l&lt;/p&gt;
&lt;p&gt;#添加删除端口规则
semanage port -a -t -p 协议(tcp|udp) 端口
-a 添加
-t 类型
-p 指定端口
-d 删除
-m 修改
-l 列表
-C 列出自定义的，非预定义的端口项&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;</description></item><item><title>android 闲置手机如何变废为宝(云手机服务器)</title><link>https://lategege.com/p/android-%E9%97%B2%E7%BD%AE%E6%89%8B%E6%9C%BA%E5%A6%82%E4%BD%95%E5%8F%98%E5%BA%9F%E4%B8%BA%E5%AE%9D-%E4%BA%91%E6%89%8B%E6%9C%BA%E6%9C%8D%E5%8A%A1%E5%99%A8/</link><pubDate>Sat, 06 Apr 2024 14:26:47 +0000</pubDate><guid>https://lategege.com/p/android-%E9%97%B2%E7%BD%AE%E6%89%8B%E6%9C%BA%E5%A6%82%E4%BD%95%E5%8F%98%E5%BA%9F%E4%B8%BA%E5%AE%9D-%E4%BA%91%E6%89%8B%E6%9C%BA%E6%9C%8D%E5%8A%A1%E5%99%A8/</guid><description>&lt;p&gt;闲置android手机尤其是具备root权限的手机作为一台服务器是比较不错的选择。&lt;/p&gt;
&lt;p&gt;既然是云手机服务器，那就要解决两个核心痛点。&lt;/p&gt;
&lt;p&gt;一、电池问题，作为服务器，长时间充电是不可取的，虽然现在手机都有过冲保护，但随着手机电量不断消耗，手机还是会在不停的充电，电池要不了多久就会废掉，那拆除电池可行吗？可行是可行，成本太高，费时费力，那有没有别的方法，经过亲自实验，最佳的方法是使用智能插座，而不是安装magisk模块来控制，我装过很多控制充电的模块，效果都不理想，根本无法有效阻断冲断电，可能是兼容性不佳。利用一个智能插座设置定时开关合理安排手机充电和断电，每天设定固定时间段即可，和自己使用手机充电没什么区别，这种方法最简单有效。&lt;/p&gt;
&lt;p&gt;二、远程控制问题，作为服务器，不可能还拿着个屏幕去操作，肯定是要远程操作，局域网互联是必须的，广域网的话有公网ip，只要端口映射即可。我实践过rustdesk，效果不理想，远控流畅度不行，流畅性最佳的是android 的scrcpy 投屏，在电脑上连接，延迟就跟玩触屏似的，不建议使用任何第三方远控软件，一是不安全，二是流畅性不行。scrcpy在android11以后能支持声音的，所以很完美。但是scrcpy是建立在adb 的基础上的，难道手机还要usb插电脑上？肯定不是，我们使用无线adb即可。但是问题又来了，无线adb 需要adb 执行一条指定端口的命令。每次重启后就失效了，人在外面的话发生重启的话就失去连接了，必须要开机 让他执行，如果手机已经root装上了面具magisk，那这事就会非常容易。
只要在/data/adb/post-fs-data.d 新建一个脚本 adb-net.sh,内容如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stop adbd
setprop service.adb.tcp.port 5555&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你是LG手机，可以参考我的这篇来开启root安装magisk，其他手机其实也差不多
&lt;a href="https://lategege.com/p/android-抓包系列一-开启root/" title="android 抓包系列一(开启root)"&gt;android 抓包系列一(开启root)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;进入/data/adb/post-fs-data.d 目录 赋予权限chmod +xr ./adb-net.sh
那么开机就会设置adb 端口，我们通过adb connect 手机ip:5555 就能连接手机，接着执行scrcpy就能投屏到电脑上
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/ce87526f0a77.png"/&gt;&lt;/p&gt;
&lt;p&gt;三、解决完电脑控制手机，手机怎么控制手机，还好有一个开源项目叫&lt;a href="https://gitlab.com/las2mile/scrcpy-android/raw/master/release/scrcpy-release.apk" title="scrcpy-android"&gt;scrcpy-android&lt;/a&gt;
安装它，打开输入要控制手机的ip地址即可，虽然没有电脑端那么流畅，但是也够用，比起其他android端控制软件好太多了。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/bbd25d6d2643.png"/&gt;&lt;/p&gt;
&lt;p&gt;四、如果要在外网控制家里的手机，你需要把手机的5555端口映射出去，比如ikuai路由就去网络设置--&amp;gt;端口映射中去添加，添加完成后外网访问和内网只是ip要换成你公网ip的地址，其他都一样，如果你的家庭宽带没有公网ip，又想在外网访问，你可以尝试使用手机的ipv6地址或者借助一些内网穿透软件来实现。&lt;/p&gt;
&lt;p&gt;五、解决完上面这些问题，还有个问题是有些应用在投屏的时候会检测出来，给你展示一个黑屏，比如一些敏感的二维码信息，如支付宝的付款码，paypal的二维码等等，如果看不到这些信息，那就不完美了，在android 10以前是没问题的，之后就加入了录屏、投屏的安全检测机制，这时候需要关闭这个机制才行。在LXPOSED中有这么一个模块，下载地址：&lt;a href="https://github.com/Xposed-Modules-Repo/com.varuns2002.disable_flag_secure/releases"&gt;https://github.com/Xposed-Modules-Repo/com.varuns2002.disable_flag_secure/releases&lt;/a&gt;，
安装重启就可以解决这个问题，前提是已经安装了Magisk和LXPOSED,没有安装的可以参考我的这篇文章：
&lt;a href="https://lategege.com/p/android-抓包系列二-安装magisk模块隐藏root/" title="android 抓包系列二(安装magisk模块隐藏root)"&gt;android 抓包系列二(安装magisk模块隐藏root)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>android 抓包系列五(过代理检测)</title><link>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%BA%94-%E8%BF%87%E4%BB%A3%E7%90%86%E6%A3%80%E6%B5%8B/</link><pubDate>Sat, 06 Apr 2024 13:43:57 +0000</pubDate><guid>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%BA%94-%E8%BF%87%E4%BB%A3%E7%90%86%E6%A3%80%E6%B5%8B/</guid><description>&lt;p&gt;如果还未过证书锁定，可以参考前篇 &lt;a href="https://lategege.com/p/android-抓包系列四-过证书锁定ssl-pinning/" title="android 抓包系列四(过证书锁定SSL Pinning)"&gt;android 抓包系列四(过证书锁定SSL Pinning)&lt;/a&gt;
有些应用会检测系统是否设置了代理，如果设置就不让使用，还有些如flutter项目压根不经过代理，除非自己设置，像这些应用的包是抓取不到的，针对这种问题需要过代理检测，那只能使用vpn，通过vpn将数据转发出去，这里通过 clash meta来解决,google play下载 Clash Meta for Android，你也可以去github下载：
&lt;a href="https://github.com/MetaCubeX/ClashMetaForAndroid/releases"&gt;https://github.com/MetaCubeX/ClashMetaForAndroid/releases&lt;/a&gt;
然后新建一份config.yaml,自定义规则如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;#tproxy-port: 7890
bind-address: '*'
mixed-port: 7892
redir-port: 7893
allow-lan: true
mode: Global
log-level: silent
ipv6: false
external-controller: 0.0.0.0:9090
&lt;p&gt;profile:
store-selected: true
store-fake-ip: false&lt;/p&gt;
&lt;p&gt;tun:
enable: true
device: Meta
stack: system #or gvisor
dns-hijack:
- &amp;lsquo;any:53&amp;rsquo;
auto-route: true #加上
auto-detect-interface: true #加上&lt;/p&gt;
&lt;p&gt;dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: redir-host
nameserver:
- 114.114.114.114
- 8.8.8.8&lt;/p&gt;
&lt;p&gt;proxies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name: Proxy_HTTP
&lt;h1 id="server-处修改为你的抓包软件设备的-ip"&gt;server 处修改为你的抓包软件设备的 IP
&lt;/h1&gt;server: 192.168.x.x
&lt;h1 id="抓包软件的端口"&gt;抓包软件的端口
&lt;/h1&gt;port: xxxx
&lt;h1 id="抓包软件的代理类型"&gt;抓包软件的代理类型
&lt;/h1&gt;type: http&lt;/li&gt;
&lt;li&gt;name: Proxy_Socks5
server: 192.168.x.x
port: xxxx
type: socks5&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;proxy-groups:&lt;/p&gt;
&lt;p&gt;rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DOMAIN-SUFFIX,ip6-localhost,DIRECT&lt;/li&gt;
&lt;li&gt;DOMAIN-SUFFIX,ip6-loopback,DIRECT&lt;/li&gt;
&lt;li&gt;DOMAIN-SUFFIX,lan,DIRECT&lt;/li&gt;
&lt;li&gt;DOMAIN-SUFFIX,localhost,DIRECT&lt;/li&gt;
&lt;li&gt;IP-CIDR,0.0.0.0/8,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,10.0.0.0/8,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,100.64.0.0/10,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,127.0.0.0/8,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,172.16.0.0/12,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,192.168.0.0/16,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,198.18.0.0/16,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR,224.0.0.0/4,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR6,::1/128,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR6,fc00::/7,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR6,fe80::/10,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;IP-CIDR6,fd00::/8,DIRECT,no-resolve&lt;/li&gt;
&lt;li&gt;MATCH,Proxy_HTTP
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面修改成你的抓包软件的地址和端口，然后上传到clash-meta中，或者你可以保存到服务器如nginx,github,gitlab等等，通过url的方式加载都可以。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/26a2fd0a0ab1.png"/&gt;
代理规则选则Proxy_HTTP ，然后运行&lt;/p&gt;
&lt;p&gt;这个时候 你所有的流量以VPN的形式全部流向你的抓包软件，即使是flutter应用，也无法逃脱。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/cfc5e866bb88.png"/&gt;&lt;/p&gt;
&lt;p&gt;还有很多app如果是双向证书验证，那就没办法了，更有甚者vpn也会被检测到幕后使用了代理，具体怎么检测未知，不过我想经过了代理的流量和不经过代理肯定会有区别，也许就是这小小的区别让他们检测出来的吧，正所谓道高一尺魔高一丈，具体问题还需要具体分析。&lt;/p&gt;
&lt;p&gt;本篇是android抓包系列的完结篇，抓包不仅可以作为测试用途，还能够查看手机哪些软件在偷偷上报数据，只要走http、https协议，普通抓包软件都能让他们现行，然后配合上家庭路由器的域名黑名单屏蔽它们的上报,统计和埋点这些请求无疑会泄漏隐私和偷跑流量，只要在dns层面拦截，它们就访问不了服务器，而且这些玩意都是耗电大户，无论是国内和国外都如此，谷歌框架上报的接口也非常多，只是频次没有国内软件那么频繁。&lt;/p&gt;</description></item><item><title>android 抓包系列四(过证书锁定SSL Pinning)</title><link>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E5%9B%9B-%E8%BF%87%E8%AF%81%E4%B9%A6%E9%94%81%E5%AE%9Assl-pinning/</link><pubDate>Sat, 06 Apr 2024 13:21:45 +0000</pubDate><guid>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E5%9B%9B-%E8%BF%87%E8%AF%81%E4%B9%A6%E9%94%81%E5%AE%9Assl-pinning/</guid><description>&lt;p&gt;还未处理信任证书的小伙伴可以看前篇&lt;a href="https://lategege.com/p/android-抓包系列三-信任证书/" title="android 抓包系列三(信任证书)"&gt;android 抓包系列三(信任证书)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;有些应用使用SSL Pinning，也就是将服务器证书内置到客户端，也就是不需要去信任系统证书，自己和自己玩，它不管系统证书是什么，要绕过它需要安装一个 LSPOSED模块。
&lt;a href="https://github.com/mobile46/TrustMeAlready/releases" title="TrustMeAlready "&gt;TrustMeAlready &lt;/a&gt;
安装完成在LXPOSED --&amp;gt;模块中启用即可。&lt;/p&gt;
&lt;p&gt;&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/a03c5b40c63f.png"/&gt;&lt;/p&gt;
&lt;p&gt;TrustMeAlreay通过hook方式解决证书信任问题。&lt;/p&gt;
&lt;p&gt;下一篇：&lt;a href="https://lategege.com/p/android-抓包系列五-过代理检测/" title="android 抓包系列五(过代理检测)"&gt;android 抓包系列五(过代理检测)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>android 抓包系列三(信任证书)</title><link>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%B8%89-%E4%BF%A1%E4%BB%BB%E8%AF%81%E4%B9%A6/</link><pubDate>Sat, 06 Apr 2024 13:10:09 +0000</pubDate><guid>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%B8%89-%E4%BF%A1%E4%BB%BB%E8%AF%81%E4%B9%A6/</guid><description>&lt;p&gt;还未安装magisk和隐藏root的小伙伴可以参考系列第二篇: android 抓包系列二(安装magisk模块隐藏root)&lt;a href="https://lategege.com/p/android-抓包系列二-安装magisk模块隐藏root/" title="android 抓包系列二(安装magisk模块隐藏root)"&gt;android 抓包系列二(安装magisk模块隐藏root)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;首先，抓包软件的证书我们要把它们变成系统证书，android7.0+无法信任用户证书，有什么办法将用户证书放入系统内部呢？我的系统是android12,直接push是无法push的，不过还是有办法。&lt;/p&gt;
&lt;p&gt;先获取到抓包软件证书，
Fiddler: wifi--&amp;gt;修改代理，连接你的代理,打开手机浏览器访问 ip:端口号，在页面中选择下载证书。
Charles : 同理，访问chls.pro/ssl 下载证书
Mitmproxy: &lt;a href="http://mitm.it"&gt;http://mitm.it&lt;/a&gt; 下载证书
其他软件大同小异，下载的证书会在手机的/sdcard/Download文件夹内，将证书导出&lt;/p&gt;
&lt;p&gt;在电脑上获取到证书的哈希值
openssl x509 -inform PEM -subject_hash_old -in 证书文件
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/1edd339d614d.png"/&gt;
然后将证书重命名为 e5c3944b.0 具体多少看你的值，下面脚本中也替换下你自己的文件名
将该证书导入手机/data/local/tmp/目录
然后新建一个inject-system-cert.sh脚本，脚本内容如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-shell"&gt;# inject-system-cert.sh
set -e # Fail on error
# Create a separate temp directory, to hold the current certificates
# Without this, when we add the mount we can't read the current certs anymore.
mkdir -m 700 /data/local/tmp/ca-copy
# Copy out the existing certificates
cp /system/etc/security/cacerts/* /data/local/tmp/ca-copy/
# Create the in-memory mount on top of the system certs folder
mount -t tmpfs tmpfs /system/etc/security/cacerts
# Copy the existing certs back into the tmpfs mount, so we keep trusting them
mv /data/local/tmp/ca-copy/* /system/etc/security/cacerts/
# Copy our new cert in, so we trust that too
cp /data/local/tmp/e5c3944b.0 /system/etc/security/cacerts/
# Update the perms &amp;amp; selinux context labels, so everything is as readable as before
chown root:root /system/etc/security/cacerts/*
chmod 644 /system/etc/security/cacerts/*
chcon u:object_r:system_file:s0 /system/etc/security/cacerts/*
# Delete the temp cert directory &amp;amp; this script itself
rm -r /data/local/tmp/ca-copy
# rm ${injectionScriptPath}
echo "System cert successfully injected"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将inject-system-cert.sh 文件导入手机/data/adb/post-fs-data.d目录，该目录是开机启动的
执行chmod +rx ./inject-system-cert.sh 修改为可读可执行，开机的时候就会将你的证书安装进系统。&lt;/p&gt;
&lt;p&gt;重启后，你的抓包软件证书就成为系统证书了，如果不需要成为系统证书，只要将inject-system-cert.sh的权限改为不可执行就可以了，不需要删除脚本。chmod -x ./inject-system-cert.sh&lt;/p&gt;
&lt;p&gt;因为安装了shamiko，所以不要安装网上的那个可以自动安装证书的模块，会不可用，采用这种脚本的方式是最好的。&lt;/p&gt;
&lt;p&gt;下一篇&lt;a href="https://lategege.com/p/android-抓包系列四-过证书锁定ssl-pinning/" title=" android 抓包系列四(过证书锁定SSL Pinning) "&gt; android 抓包系列四(过证书锁定SSL Pinning) &lt;/a&gt;&lt;/p&gt;</description></item><item><title>android 抓包系列二(安装magisk模块隐藏root)</title><link>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%BA%8C-%E5%AE%89%E8%A3%85magisk%E6%A8%A1%E5%9D%97%E9%9A%90%E8%97%8Froot/</link><pubDate>Sat, 06 Apr 2024 12:48:46 +0000</pubDate><guid>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%BA%8C-%E5%AE%89%E8%A3%85magisk%E6%A8%A1%E5%9D%97%E9%9A%90%E8%97%8Froot/</guid><description>&lt;p&gt;如果手机还没有root安装magisk的小伙伴，可以参考第一篇文章 &lt;a href="https://lategege.com/p/android-抓包系列一-开启root/" title="android 抓包系列一(开启root)"&gt;android 抓包系列一(开启root)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;安装了magisk之后，想要完美的抓包，首先需要隐藏root,因为很多应用会检测手机是不是root了，隐藏root分为隐藏magisk自身、隐藏root、隐藏非常明显的root应用。&lt;/p&gt;
&lt;p&gt;一、针对隐藏magisk自身，它magisk本身自带的功能，进入设置--&amp;gt;Magisk--&amp;gt;在Zygisk中运行Magisk勾选，24版本以后支持的，开启是因为后面的隐藏root模块需要它开启，然后先隐藏自身---&amp;gt;App-&amp;gt;隐藏Magisk应用，随便起个不起眼的名字就行。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/af905bd53f46.png"/&gt;&lt;/p&gt;
&lt;p&gt;二、安装shamiko模块
&lt;a href="https://github.com/LSPosed/LSPosed.github.io/releases/tag/shamiko-300"&gt;https://github.com/LSPosed/LSPosed.github.io/releases/tag/shamiko-300&lt;/a&gt; 下载压缩包&lt;/p&gt;
&lt;p&gt;进入magdisk 从本地安装
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/7733a2bd53b7.png"/&gt;&lt;/p&gt;
&lt;p&gt;建议开启白名单模式，就是默认全部隐藏，但是一旦全部隐藏就不能赋予新的root权限，旧的还是会保留，所以如果有应用需要root权限，先授予，然后再打开白名单模式，打开方式是进入手机/data/adb/shamiko，创建 whitelist 文件，不是文件夹！创建方式可以使用adb ,赋予adb root权限即可，或者使用&lt;a href="https://www.coolapk.com/apk/bin.mt.plus" title="MT管理器"&gt;MT管理器&lt;/a&gt; 同样要授予root权限，重启生效。&lt;/p&gt;
&lt;p&gt;三、安装LSPosed模块
&lt;a href="https://github.com/LSPosed/LSPosed/releases"&gt;https://github.com/LSPosed/LSPosed/releases&lt;/a&gt; 下载压缩包再Magisk中安装，安装完成后安装隐藏应用列表
&lt;a href="https://github.com/Xposed-Modules-Repo/com.tsng.hidemyapplist/releases"&gt;https://github.com/Xposed-Modules-Repo/com.tsng.hidemyapplist/releases&lt;/a&gt; 就是一个app，不过是LSPosed模块的app,安装后LSPosed会自动识别，然后进入模块内针对所有app都隐藏&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/e30858d50bca.png"/&gt;，然后重启。&lt;/p&gt;
&lt;p&gt;至此，隐藏root结束。&lt;/p&gt;
&lt;p&gt;下一篇 ： &lt;a href="https://lategege.com/p/android-抓包系列三-信任证书/" title="android 抓包系列三(信任证书)"&gt;android 抓包系列三(信任证书)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>android 抓包系列一(开启root)</title><link>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%B8%80-%E5%BC%80%E5%90%AFroot/</link><pubDate>Sat, 06 Apr 2024 12:13:45 +0000</pubDate><guid>https://lategege.com/p/android-%E6%8A%93%E5%8C%85%E7%B3%BB%E5%88%97%E4%B8%80-%E5%BC%80%E5%90%AFroot/</guid><description>&lt;p&gt;众所周知，android7.0+后，用户证书不被信任，因此使用抓包软件无法有效获取https的数据，下面是常用的抓包软件下载地址:
Fiddler &lt;a href="https://www.telerik.com/download/fiddler-everywhere"&gt;https://www.telerik.com/download/fiddler-everywhere&lt;/a&gt;
Charles &lt;a href="https://www.charlesproxy.com/download/"&gt;https://www.charlesproxy.com/download/&lt;/a&gt;
Mitmproxy &lt;a href="https://mitmproxy.org/downloads/"&gt;https://mitmproxy.org/downloads/&lt;/a&gt;
Wireshark &lt;a href="https://www.wireshark.org/"&gt;https://www.wireshark.org/&lt;/a&gt;
Burp Suite &lt;a href="https://portswigger.net/burp/releases"&gt;https://portswigger.net/burp/releases&lt;/a&gt;
我是mac电脑，所以使用的是Charles,你可以按需选择。&lt;/p&gt;
&lt;p&gt;在android7.0+以上想要抓包，首先需要做的事那就是root,root后你就能无限制地修改系统配置，包括将用户证书变成系统证书，从而使应用信任证书来实现抓包。&lt;/p&gt;
&lt;p&gt;我使用的手机是LGV50韩版，骁龙855处理器，自带谷歌全家桶，更新到了android 12系统，目前用来做云手机用，为什么我不选择虚拟机，而是选择实体机呢？其实虚拟机我也玩过，什么网易mumu，包括云手机redroid，想要完美绕过虚拟机检测是不存在的，而且都是x86模拟的，即使有gpu加速，性能损失是必然，主要原因是折腾到最后可能未必能满足将来未知的需求，比如过虚拟机检测等。大厂的云手机方案大多基于华为鲲鹏、ft2000等 arm服务器处理器，这些玩意对个人而言没有完整的开源解决方案，个人去架设费时费力还费钱。一块板子就上千，海鲜市场一两百捡个洋垃圾手机不香么？个人建议骁龙845+以上都可以，如国内品牌的海外版，LG,一加,Pixel系列等，注意必须是无锁，而非运营商定制款，不然不能root，可玩性就几乎没有了。&lt;/p&gt;
&lt;p&gt;目前主流的root方案就是安装magisk来接管root权限，在安装magisk之前，需要解锁bootloader, LG的解锁方式还是有些麻烦的，闲麻烦的小伙伴直接买台解锁过的就行了，如果没有解锁，大致流程如下：
一、解开OEM---&amp;gt;进开发者模式
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/eda0eba1ee0a.png"/&gt;
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/bd00d376af06.png"/&gt;&lt;/p&gt;
&lt;p&gt;二、找台windows电脑，安装高通9008驱动和软件，mac系统可以使用parallels desktop 安装windows虚拟机来使用，所有软件到这里下载：
&lt;a href="https://wwk.lanzouj.com/b00rmw7qhi"&gt;https://wwk.lanzouj.com/b00rmw7qhi&lt;/a&gt;
密码:d9ju
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/90b086140f5f.png"/&gt;&lt;/p&gt;
&lt;p&gt;三、安装完成，打开QFIL这个软件，手机进入9008模式
进入9008:
手机usb连接电脑，如果windows是虚拟机，将usb端口映射给虚拟机后操作，按住音量减和电源键，在看到屏幕黑屏后不松手再点按音量加键，直到软件识别到端口号
SelectPort选择连接的手机端口
Select Build Type选择Flat Build
Select Programmer选择下载好的LGE855 Firehose文件
右下角Storage Type选择ufs
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/06ae797f11ef.png"/&gt;&lt;/p&gt;
&lt;p&gt;先把下面分区先备份一下：
Tools-&amp;gt;Partition Manager 弹出分区包含下面6个分区，全部先备份，备份选择Read Data,备份好会有备份路径显示，将文件拷贝出来保存。
刷入工程文件(Load Image 找到对应的工程文件刷入):
将 xbl_a.image刷入 xbl_a、xbl_b 这两个分区
将xbl_config_a 刷入 xbl_config_a/xbl_config_b 这两个分区
将V500ES_abl_a.image 刷入abl_a/abl_b 这两个分区&lt;/p&gt;
&lt;p&gt;四、进入fastboot模式(同时按住音量减和电源键)
windows下安装fastboot驱动，打开adb工具箱目录，在此目录下用cmd打开，输入fastboot devices,看到设备证明设备连接着，输入fastboot oem unlock 手机跳转到解锁界面，音量键选择解锁后确认，至此解锁完成，重启会进入警告页面。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/ba6a7d34374e.png"/&gt;&lt;/p&gt;
&lt;p&gt;五、继续进入QFIL软件，手机进入9008模式，可以将之前备份的6个分区全部写回去，顺便把boot_a ,boot_b这两个分区备份出来，面具magisk要处理。&lt;/p&gt;
&lt;p&gt;六、重启手机进入android系统，安装面具到android系统
&lt;a href="https://github.com/topjohnwu/Magisk/releases"&gt;https://github.com/topjohnwu/Magisk/releases&lt;/a&gt; 下载个最新版
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/6f609675f0db.png"/&gt;
点击安装(修补boot分区),将刚刚备份的boot_a,boot_b导入手机，然后都让面具修补，两个都修补完成后，再次进入9008模式，将修补好的分别刷入boot_a,boot_b分区中，当然你也可以通过fastboot命令刷，效果是一样的，你也可以选择安装twrp分区，我没安装是因为怕进不了download模式，毕竟进入download模式可以救砖。&lt;/p&gt;
&lt;p&gt;七、重启后，面具的超级权限就解锁了，至此root过程完成。
&lt;img alt="file" src="https://img.lategege.com:30443/images/2024/04/06/99eadf7d8e41.png"/&gt;&lt;/p&gt;
&lt;p&gt;其他品牌的手机root可能会方便很多，比如pixel系列、一加手机，不需要刷这刷那的，执行几行命令就解锁了，具体可以搜索相关教程。&lt;/p&gt;
&lt;p&gt;下一篇&lt;a href="https://lategege.com/p/android-抓包系列二-安装magisk模块隐藏root/" title=" android 抓包系列二(安装magisk模块隐藏root)"&gt; android 抓包系列二(安装magisk模块隐藏root)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>pve-ubuntu20.04+vgpu+docker+redroid(实现android 云手机)</title><link>https://lategege.com/p/pve-ubuntu20-04-vgpu-docker-redroid-%E5%AE%9E%E7%8E%B0android-%E4%BA%91%E6%89%8B%E6%9C%BA/</link><pubDate>Sat, 16 Sep 2023 14:29:24 +0000</pubDate><guid>https://lategege.com/p/pve-ubuntu20-04-vgpu-docker-redroid-%E5%AE%9E%E7%8E%B0android-%E4%BA%91%E6%89%8B%E6%9C%BA/</guid><description>&lt;p&gt;PVE环境下如何开启VGPU可以参考之前的博文-----&lt;a href="https://lategege.com/p/pve8开启vgpu显卡虚拟化-基于tesla-p4/" title="PVE8开启vgpu显卡虚拟化(基于Tesla P4)"&gt;PVE8开启vgpu显卡虚拟化(基于Tesla P4)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;1.在PVE开启VGPU的前提下，创建一个ubuntu20.04 server虚拟机，过程略，注意选择VGPU型号尽量选择Q系列，因为像B系列没有计算功能，也就无法硬件加速，我多次实验也验证了这一点，如果无法使用硬件加速，android云主机就会占用非常多的cpu资源。&lt;/p&gt;
&lt;p&gt;2.ubuntu虚拟机安装openssh-server 略&lt;/p&gt;
&lt;p&gt;3.通过你的工作电脑运行scp xxxxx 用户名@虚拟机ip:/home/虚拟机用户名将驱动传入ubuntu虚拟机下，安装ubuntu下vgpu客户端驱动，可以选择deb结尾或者run结尾的驱动，区别在于deb可以直接安装,run需要编译，需要提前安装好gcc和make工具，任选其一即可。&lt;/p&gt;
&lt;p&gt;&lt;img alt="file" src="https://img.lategege.com:30443/images/2023/09/16/90a9d29251c2.png"/&gt;&lt;/p&gt;
&lt;p&gt;4.安装完成安装授权文件(前提是你已经部署了docker授权服务，在之前的博文中有教你怎么部署授权服务)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl --insecure -L -X GET https://&amp;lt;dls-hostname-or-ip&amp;gt;/-/client-token -o /etc/nvidia/ClientConfigToken/client_configuration_token_$(date '+%d-%m-%Y-%H-%M-%S').tok
#重启nvidia-gridd服务
service nvidia-gridd restart
#查看授权情况
nvidia-smi -q | grep "License"
#提示如下字样就代表授权成功
## vGPU Software Licensed Product
## License Status : Licensed (Expiry: YYYY-M-DD hh:mm:ss GMT)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5.安装docker&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo apt install docker.io&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;6.安装redroid&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#参考 https://github.com/remote-android/redroid-doc
##安装内核模块
sudo apt install linux-modules-extra-`uname -r`
sudo modprobe binder_linux devices="binder,hwbinder,vndbinder"
sudo modprobe ashmem_linux
&lt;p&gt;##启动redroid容器
sudo docker run -itd &amp;ndash;rm &amp;ndash;privileged &lt;br&gt;
&amp;ndash;pull always &lt;br&gt;
-v ~/data:/data &lt;br&gt;
-p 5555:5555 &lt;br&gt;
redroid/redroid:11.0.0-latest&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;7.工作机连接&lt;/p&gt;
&lt;p&gt;&lt;code&gt;scrcpy -s ip地址:5555 --audio-codec=raw&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;8.安装b站app,测试视频输出，声音输出没问题。&lt;/p&gt;
&lt;p&gt;&lt;img alt="file" src="https://img.lategege.com:30443/images/2023/09/16/7d57064528e4.png"/&gt;&lt;/p&gt;
&lt;p&gt;似乎调用了硬解，我不确定手机中播放视频是否真的调用了电脑的gpu，我只能确定手机界面的渲染肯定是gpu加速的，因为操作手机过程中，cpu占用一直不高。&lt;/p&gt;
&lt;p&gt;&lt;img alt="file" src="https://img.lategege.com:30443/images/2023/09/16/ba59782e21cc.png"/&gt;&lt;/p&gt;
&lt;p&gt;测试播放nas中的4K的《八角笼中》，确定调用了硬解，不然PVE的cpu占用不可能这么低。&lt;/p&gt;
&lt;p&gt;&lt;img alt="file" src="https://img.lategege.com:30443/images/2023/09/16/a222768e39aa.png"/&gt;&lt;/p&gt;
&lt;p&gt;总结：以上redroid云手机是vgpu的另一个实际的应用，在android开发调试上可以提高开发效率，非常实用。&lt;/p&gt;</description></item><item><title>android studio开发系统应用配置</title><link>https://lategege.com/p/android-studio%E5%BC%80%E5%8F%91%E7%B3%BB%E7%BB%9F%E5%BA%94%E7%94%A8%E9%85%8D%E7%BD%AE/</link><pubDate>Wed, 07 Dec 2022 08:17:55 +0000</pubDate><guid>https://lategege.com/p/android-studio%E5%BC%80%E5%8F%91%E7%B3%BB%E7%BB%9F%E5%BA%94%E7%94%A8%E9%85%8D%E7%BD%AE/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、配置系统签名,正常在build.gradle中配置即可（略)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、将系统应用import进android studio变成gradle工程。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三 、分析系统应用mk文件，查看依赖。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、在build.gradle中添加mk中的依赖，可以远程依赖，也可以去android系统编译目录查找，android系统编译目录为:out/soong/.intermediates.&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;五、将framework.jar拷贝进工程libs目录，framework.jar在android编译目录 out/soong/.intermediates/frameworks/base/framework/android_common/combined，这个jar可以不限制使用android的系统api.&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;六，配置framework.jar的gradle依赖，compileOnly只编译即可。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;七、在最外层build.gradle根目录中配置脚本，这一步是为了编译的时候以framework中的api优先编译.&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;allprojects {
 repositories {
 maven { url 'https://maven.aliyun.com/repository/public' }
 google()
 }
&lt;pre&gt;&lt;code&gt;gradle.projectsEvaluated {
 tasks.withType(JavaCompile) {
 Set&amp;amp;lt;File&amp;amp;gt; fileSet = options.bootstrapClasspath.getFiles()
 List&amp;amp;lt;File&amp;amp;gt; newFileList = new ArrayList&amp;amp;lt;&amp;amp;gt;();
 //&amp;quot;../framework.jar&amp;quot; 为相对位置，需要参照着修改，或者用绝对位置
 newFileList.add(new File(&amp;quot;./app/libs/framework.jar&amp;quot;))
 newFileList.addAll(fileSet)
 options.bootstrapClasspath = files(
 newFileList.toArray()
 )
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;八、在最外层配置脚本,这一步告诉android studio 将android.jar配置到依赖末尾，使得android studio去优先使用framework.jar中的api，这个和上一步的用途是不一样的，上一步是为了编译，而这一步是为了android studio这个开发工具来配置的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;gradle.buildFinished {
 pushDownAndroidSDK('./.idea/modules/app/xxxxx.app.iml')
}
&lt;p&gt;def pushDownAndroidSDK(iml){
def imlFile = file(iml)
try {
def parsedXml = (new XmlParser()).parse(imlFile)
def jdkIndexOf = parsedXml.component[1].orderEntry.findIndexOf { it.&amp;rsquo;@type&amp;rsquo; == &amp;lsquo;jdk&amp;rsquo; }
if (jdkIndexOf &amp;lt;= 1) {
def jdkNode =parsedXml.component[1].orderEntry.find { it.&amp;rsquo;@type&amp;rsquo; == &amp;lsquo;jdk&amp;rsquo; }
parsedXml.component[1].remove(jdkNode)
new Node(parsedXml.component[1], &amp;lsquo;orderEntry&amp;rsquo;,[&amp;rsquo;type&amp;rsquo;: &amp;lsquo;jdk&amp;rsquo;, &amp;lsquo;jdkName&amp;rsquo;: &amp;lsquo;Android API 28 Platform&amp;rsquo;, &amp;lsquo;jdkType&amp;rsquo;: &amp;lsquo;Android SDK&amp;rsquo;])
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
imlFile.text = writer.toString()
println &amp;ldquo;Push File: $iml jdk priority ok&amp;rdquo;
groovy.xml.XmlUtil.serialize(parsedXml,new FileOutputStream(imlFile))
}
} catch (Exception e) {
// do nothing
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;</description></item><item><title>mac 使用docker编译android9/10...</title><link>https://lategege.com/p/mac-%E4%BD%BF%E7%94%A8docker%E7%BC%96%E8%AF%91android9-10/</link><pubDate>Fri, 02 Dec 2022 02:07:58 +0000</pubDate><guid>https://lategege.com/p/mac-%E4%BD%BF%E7%94%A8docker%E7%BC%96%E8%AF%91android9-10/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、安装mac版docker,地址:https://www.docker.com/&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、docker run -td --name m8 --privileged=true -v 源路径:挂载路径 -p2210:22 ubuntu:20.04&lt;br/&gt;(直接启动ubuntu:20.04容器，没有下载会自动下载，将主机内的android源码路径映射到容器内部路径下面)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、进入容器的bash环境 docker exec -it aosp /bin/bash&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、修改ubuntu源&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;docker cp 容器名:/etc/apt/sources.list 宿主机路径(拷贝出来配置文件）&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;修改成以下内容：&lt;br/&gt;deb http://mirrors.163.com/ubuntu/ focal main restricted universe multiverse&lt;br/&gt;deb http://mirrors.163.com/ubuntu/ focal-security main restricted universe multiverse&lt;br/&gt;deb http://mirrors.163.com/ubuntu/ focal-updates main restricted universe multiverse&lt;br/&gt;deb http://mirrors.163.com/ubuntu/ focal-proposed main restricted universe multiverse&lt;br/&gt;deb http://mirrors.163.com/ubuntu/ focal-backports main restricted universe multiverse&lt;br/&gt;deb-src http://mirrors.163.com/ubuntu/ focal main restricted universe multiverse&lt;br/&gt;deb-src http://mirrors.163.com/ubuntu/ focal-security main restricted universe multiverse&lt;br/&gt;deb-src http://mirrors.163.com/ubuntu/ focal-updates main restricted universe multiverse&lt;br/&gt;deb-src http://mirrors.163.com/ubuntu/ focal-proposed main restricted universe multiverse&lt;br/&gt;deb-src http://mirrors.163.com/ubuntu/ focal-backports main restricted universe multiverse&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;docker cp 宿主机路径 容器名:/etc/apt/sources.list (拷贝回去）&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;五、执行 sudo apt-get update 更新源&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;六、sudo apt-get install curl python python3 git wget vim (为了使用repo,源码已在宿主机上拉取，但是docker容器也需要拉取更新，这样针对android源码操作完全可由容器操作)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;curl https://storage.googleapis.com/git-repo-downloads/repo -o /bin/repo 安装repo&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;chmod 777 /bin/repo&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;七、在容器内更新源码repo sync -j8&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;八、安装必须得依赖：&lt;br/&gt;//谷歌官网推荐&lt;br/&gt;sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig bc libssl-dev&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;九、mac版docker开启virtiofs需要 更新sed版本&lt;br/&gt;wget https://ftp.gnu.org/gnu/sed/sed-4.9.tar.gz&lt;br/&gt;tar xzvf ./sed-4.9.tar.gz&lt;br/&gt;cd ./sed-4.9&lt;br/&gt;./configure&lt;br/&gt;make install&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;十、有些项目还会依赖rsync cpio dpmod工具，可自行安装&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;sudo apt-get install rsync cpio kmod&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>Android源码导入android studio</title><link>https://lategege.com/p/android%E6%BA%90%E7%A0%81%E5%AF%BC%E5%85%A5android-studio/</link><pubDate>Thu, 04 Aug 2022 09:52:33 +0000</pubDate><guid>https://lategege.com/p/android%E6%BA%90%E7%A0%81%E5%AF%BC%E5%85%A5android-studio/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;#初始化编码环境变量&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;1、 source build/envsetup.sh&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;#编码idegen模块生成idegen.jar&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;2、mmm development/tools/idegen/ &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;#执行idegen.sh脚本&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;3、sudo development/tools/idegen/idegen.sh &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;4、根目录出现android.ipr、 android.iml &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;#修改权限 屏蔽不需要载入 模块 excludeFolder&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;5、sudo chmod777 android.iml sudo chmod777 android.ipr&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#例&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/.repo" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/art" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/bionic" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/bootable" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/build" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/compatibility" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/dalvik" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/cts" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/developers" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/developers/samples" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/development" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/device" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/docs" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/external" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/flashing-files" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/frameworks/base/docs" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/hardware" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/kernel" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/libcore" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/libnativehelper" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/out" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/pdk" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/platform_testing" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/prebuilt" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/prebuilts" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/shortcut-fe" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/sdk" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/system" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/test" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/toolchain" /&amp;gt;&lt;br/&gt;&amp;lt;excludeFolder url="file://$MODULE_DIR$/tools" /&amp;gt;&lt;br/&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;6、android studio 打开 android.ipr &lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>android 刷机知识汇总</title><link>https://lategege.com/p/android-%E5%88%B7%E6%9C%BA%E7%9F%A5%E8%AF%86%E6%B1%87%E6%80%BB/</link><pubDate>Sat, 02 Jul 2022 06:25:05 +0000</pubDate><guid>https://lategege.com/p/android-%E5%88%B7%E6%9C%BA%E7%9F%A5%E8%AF%86%E6%B1%87%E6%80%BB/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;bootloader:相当于电脑中的efi/bios环境，一个独立于操作系统的引导系统，手机厂商会上锁。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot:bootloader环境下使用的命令&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;bl锁:也就是bootloader锁，在bootloader环境下手机厂商上锁，防止破坏操作系统分区&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;旧版android系统分区包含：boot分区(kernel、ramdisk)、recovery分区(简易的系统)、system分区(anroid系统、系统软件)、data分区(用户软件和数据分区)、cache分区(缓存，比如apk对应的机器码)、Vendor分区(厂商自定义的系统级应用或者库）&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;新版(7.0以后)android系统(A/B system )分区包含: SlotA主(Boot (包含recovery)、System、vendor) 、SlotB备用(Boot(包含recovery)、System、vendor)，data分区、cache分区 可后台升级，比较占用空间&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;最新版(11以后）android系统(Vitural A/B)分区:对a/b system的优化，类似于差分包，引入版本管理，降低存储占用。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;线刷(usb刷机）也就是进入手机bootloader环境，通过电脑连接手机，使用fastboot命令将刷机包刷入手机。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;卡刷：下载完整的镜像包，导入手机，进入到手机的recovery环境，通过recovery提供的更新功能来刷固件。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot命令(需要在bootloader模式下才能使用,一般通过在系统启动后通过adb reboot bootloader进入或者关机状态下按电源键+音量键进入)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot flashing unlock 解锁oem&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot reboot 重启&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot erase 分区名(system、boot、cache等）&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot flash system system.img 刷入系统镜像&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot flash boot boot.img 刷入内核镜像&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot flash radio radio.img 刷入基带分区&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;fastboot -w 清空data\cache&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>android应用架构设计浅谈</title><link>https://lategege.com/p/android%E5%BA%94%E7%94%A8%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1%E6%B5%85%E8%B0%88/</link><pubDate>Fri, 08 Apr 2022 09:43:48 +0000</pubDate><guid>https://lategege.com/p/android%E5%BA%94%E7%94%A8%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1%E6%B5%85%E8%B0%88/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在android app开发过程中，经常会遇到代码写着写着就非常臃肿，有时候改动了一处，引发了关联性灾难，这不由得让我们思考如何才能把代码写的更具有扩展性和可维护性，架构这个东西并没有最好，只有最合适。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;网上很多文章在写MVC、MVP、MVVM，组件化、插件化、单一职责、单例模式、工厂模式等，但是没有一篇是综合上述思想或者模式来谈架构设计。它们都属于架构的范畴，但是关注的维度不同。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、针对MVC、MVP、MVVM，它关心的是数据从获取到展示的代码逻辑，所要解决的核心问题是数据和界面之间如何更好联系的问题，它的侧重点是界面和数据。然而这三种设计模式在每个开发人员心中都有各自的理解，现在这些模式与发明人当初发明时所想表达思想已经不尽相同，即使在当下，后台开发人员和前端开发人员的理解也不一样，甚至在android开发中，每个人都有不同的见解，但是我们的目的都是一样的，那就是解耦和复用。下面会以大部分人认同的理解来阐述这三种模式的异同。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;MVC(model view controller) 在android中，model模型--默认就是业务数据层(包括业务数据的获取、以及数据本身)，view 默认指xml文件 ，controller默认指activity，这种理解方式多少受到了android框架本身的影响，activity这个和界面强相关的实体，数据展示逻辑以及事件处理逻辑放在其中是顺理成章的事，由此如果这个activity业务非常复杂，那么它必然会变得很臃肿，但是反过来诸如以下代码：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;public class GuidActivity : AppCompatActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 //简单的逻辑
 startActivity(Intent(this,MainActivity.javaClass))
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这是一个app启动界面GuidActivity，如果这个界面没有复杂的逻辑，这个时候采用默认的MVC又有何不可呢？配上注释，它的可读性非常强，针对这种场景，谁能说这样的代码不够优雅？所以做架构千万不要陷入误区，不要为了架构而架构。另外MVC在android中的上述解释其实非常牵强，因为activity严格来说也算做界面的一部分，所以这个view和controller的界限很模糊，基本上都融合在一起了，称它为M-VC一点都不为过，基于这个事实才诞生了MVP这种模式。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;MVP(model view presenter)MVP针对上面这种activity和xml界限模糊的问题做出了解决方案，它单独抽出了Presenter层，其本质还是Controller，但是这种模式将activity彻底抛到了view的一边，也就是无论是xml、自定义空间还是activity，它都是属于view层，而presenter层单独创建了，它替代了原来在activity中的职责，负责向model层拿数据，也负责将数据塞给view层，从而将model和view隔离开来，所以android中大部分人认为的MVP在本质上才是真正的MVC模式，就相当于原来的MVC其实是假冒伪劣产品一样，以下代码就是一个完整的MVP结构。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;class MVPActivity : AppCompatActivity() {
 var persenter: Presenter? = null;
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main)
 persenter = Presenter(this)
 persenter!!.requestData()
 }
 fun show(data: String) {
 findViewById&amp;lt;TextView&amp;gt;(R.id.text).text = data
 }
}
class Presenter constructor(view: MVPActivity) {
 var model: Model = Model()
 var view: MVPActivity
 init {
 this.view = view
 }
 fun requestData() {
 view.show(model.getDataFromWork())
 }
}
class Model {
 fun getDataFromWork(): String {
 return "data"
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;当view和model不再联系时，MVP所带来的好处是view和model的可替换性，以及单元测试的便利性，还做到了presenter的重用，因为presenter单独抽离了，由于项目中在不同页面可能需要相同的业务，这个时候preseneter的代码就可以复用了，但是要做复用上面的代码还不够，必须要将view接口化，使得presenter和activity彻底解绑，presenter持有抽象的view就足够了。而对于model的接口化，它的目的是里氏替换，数据源可以从网络获取，同样可以从缓存获取，这个时候策略模式就可以上场了，而presenter的接口化更多的目的是业务的重用，比如一个业务中包含业务A和业B，其实并不需要重写一个Presenter，只要实现这两个Presenter接口，并让它们对应的实现去干活就可以了，如果该业务有新增就在其中加入独有的业务逻辑。MVP设计模式在android中的缺点也很明显，那就是内存泄漏风险，当presenter持有了activity的引用，一个presenter中的model做了耗时操作的同时activity关闭了，此时activity就泄漏了，所以要更好的使用MVP还要加上activity生命周期的维护，还有就是如果项目中的业务相对独立又没有任何重叠，MVP这种模式就非常鸡肋了，它会造成接口泛滥，一套MVP接口只用一次的尴尬在这种情景下显得非常突出，它唯一起到的作用就是控制层彻底分离了，针对这种情景就不需要面向接口了，因为你定义的接口就用了一次，失去了接口本来的意义，还不如直接了当的写代码，会让整体代码显得更加清爽，针对MVP的内存泄漏这一点，其实也是谷歌推崇MVVM的原因之一。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;MVVM(model view viewmodel) 在MVVM中viewmodel有了感知生命周期的能力，它的本质还是控制层，只不过它没有持有view的引用，在这种模式多了一个数据绑定概念，谷歌为我们开发者提供好了绑定工具api--databinding，databinding(数据绑定)使得数据实体和xml绑定在一起，更新数据实体中的数据，xml界面就更新，它是视图绑定(viewbinding)和可观察数据对象(observable)两者合一的应用，而livedata是obervable的一种具备生命周期感知的应用，本质上它们是实现android mvvm的一整套工具，所以android中MVVM注重对谷歌工具的使用，感知这个概念贯穿整个模式中，这个感知包括activity、fragment的生命周期感知，也包含数据变化的感知，在具体的MVVM实现中，viewmodel通过model获取数据，获取到的数据包装成可感知的数据，view层对数据变化做监听，无论使用databinding也好，还是使用livedata也好，都是监听数据变化来更新UI，它和MVP的区别在于P将数据主动喂给V，而MVVM中的view是去监听数据的变化来触发更新。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在MVVM中，正因为谷歌为我们提供了工具，所以代码量才没有MVP这种接口实现来的多，同时规避了MVP自动管理生命周期的尴尬，但是诸如使用databinding这种工具增加了代码的调试难度，同时编码过程中频繁在xml和java中切换，让xml文件含有数据逻辑处理本身就是一种耦合的表现，它并不利于xml文件的复用，databinding的使用场景应该是数据展示层变化较小同时数据展示纯粹(一个xml中绑定的实体数不多，但是实体中的数据数目较多时)的业务环境，大部分业务变化较快、数据结构多变的时候就不适合使用databinding，viewmodel+livedata+viewbinding是一个不错的选择。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、组件化、插件化在架构层面的侧重点是业务功能拆分，其中组件化是将应用按功能拆分不同组件或者模块，组件之间不再相互依赖，组件可以依赖同一套libray，组件化最大的好处是有利于团队开发，在团队开发中不需要等待别人的代码，自己可以进行独立测试，写库的写库，写模块的写模块，互不干涉，在android中一个组件对应着一个module，组件化还有一个好处就是可以提高编译效率。在大型项目中使用组件化是必要的，然而在一些独立开发的小型项目中使用组件化反而得不偿失，因为组件化多少会带来代码和配置增多。插件化核心在于动态加载模块，在庞大的工程中，可以按需下载功能包，它不是官方支持的，是一种取巧的技术，在国内盛行，因为android的开源，framework层代码对程序员透明，插件化是android开发者解决一个又一个的问题后诞生的，首先解决的是加载dex文件，android PathClassLoader就能解决，其次是资源文件的加载(res、assets、so) AssetsManager能解决，在资源加载的过程中还要考虑资源id重复或者找不到的问题，再次要解决activity清单文件注册问题，在分析framework后可以采用hook，也就是在调用ams时和ams返回后两个地方进行移花接木，插件化解决了初安装包体太大的问题，按需加载也解决了功能动态下发的问题，但是维护性很高，比如每个android新版本都要做兼容，甚至完全要看谷歌让不让用。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;组件化和插件化都是业务架构范畴，如何拆分更好的拆分业务才是核心。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、单一职责、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则是程序设计的六大原则，它是代码层面的编程思想，其中各种代码示例在我的另一篇文章有完整的体现。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;a href="https://blog.csdn.net/qq_28029345/article/details/68941819?spm=1001.2014.3001.5501" rel="noreferrer noopener" target="_blank"&gt;设计模式六大原则&lt;/a&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这些原则是一种编程指导思想，目的是让程序员写出更容易扩展和维护的代码，如果程序设计比作做人，那么六大设计原则就是做人的道德规范，而23种设计模式就好比行为准则一样。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、23种设计模式&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:list {"ordered":true} --&gt;
&lt;ol&gt;&lt;li&gt;创建型设计模式&lt;br/&gt;与对象创建有关；共5种：单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式；&lt;br/&gt;单例模式&lt;br/&gt;6种单例模式.&lt;/li&gt;&lt;li&gt;结构型设计模式&lt;br/&gt;共7种：适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。&lt;/li&gt;&lt;li&gt;行为型设计模式&lt;br/&gt;共11种：策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。、&lt;/li&gt;&lt;/ol&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;不管是设计原则还是设计模式，它们在架构设计中无处不在，每一种设计模式都有它适用的场景，比如项目中的管理类，管理大总管一般适合采用单例模式，一旦内存中出现了两个管理者，很可能产生混乱，建造者模式它的目的按需实例化一个对象，工厂模式的侧重点是屏蔽实例化对象的复杂性，原型模式侧重点是解决创建对象的成本问题。装饰者模式是为了增强类的能力而存在，代理模式其实是迪米特原则的体现，就是对象之间不能胡乱调用等等。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;架构切不可为了设计模式而设计模式，设计模式一定有它适用的场景，在实际开发中一定要随机应变，这才是真正的架构设计。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>音视频知识总结</title><link>https://lategege.com/p/%E9%9F%B3%E8%A7%86%E9%A2%91%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/</link><pubDate>Sat, 02 Apr 2022 04:29:52 +0000</pubDate><guid>https://lategege.com/p/%E9%9F%B3%E8%A7%86%E9%A2%91%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、常见的流媒体文件.mp4 .mkv .avi .flv都是音视频封装格式，这些封装格式由视频流、音频流、参数信息组成。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、原始音频数据是PCM数据，音频原始数据在计算机中的计算方式为采样率*位深*声道数，采样率单位赫兹(HZ)，指每秒钟对声音的采集次数，位深表示单个采样声音的精度或者声音强度范围，当位深作为一个值存在的时候所表示的是精度，而作为位深这个概念本身存在的时候表示的是声音的强度范围，比如16bit位深是16个二进制位能表示65535个不同强度的声音信息，就像标尺上有65535个刻度，如果提升到20bit，意味着刻度数增加。刻度数增加意味着可以使原有的刻度更精确，或者让刻度的范围变的更广。声道数这个概念本质就是多份声音采集，如果录音时声道数为2，那么录制时就有采集两份音频数据，由此得出PCM原始音频数据占用的内存大小。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;音频压缩分有损压缩和无损压缩，无论哪种压缩方式，最终大小肯定比PCM数据小，无损有AAL、APE、FLAC等，有损压缩有mp3、aac、ogg等。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;其中mp3压缩在高码率128kbps以上的音频中还原度更好，码率是指一秒钟声音的大小是多少位二进制，由于位深和声道基本都是固定的，相当于常量，由此得出，码率越高也意味着采样率越高，码率越低意味着采样率越低，mp3的适合场景是普通音乐文件。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;aac压缩在低码率下表现较好，在视频文件中大多采用这种格式，视频的音频流对声音的要求没有比纯音乐那么高。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;ogg在各种范围的码率下表现都非常好，缺点就是普及度不够，兼容性不行，常用语音视频通话中。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、原始视频数据格式为YUV格式，也称YCrCb，Y是亮度分量，U和V是色度分量，Cr表示红色分量与亮度分量的差异，Cb表示蓝色分量与亮度分量的差异，对计算机中对视频数据的算法处理大多都是基于YUV的，诸如H264、H265都是在处理YUV数据，如果要在显示器中显示，必须要转换为RGB数据，因为显示器只认RGB，显示器中一个像素有三个发光点，分别表示红、绿、蓝三种颜色，所有颜色都由这三种颜色组成。从YUV数据转RGB不是固定的算法，而是有多套标准，这也是为什么opengl在将YUV数据渲染到屏幕中需要一个矩阵信息，这个矩阵就是这套标准。YUV有很多名称，如YUV420\YUV420P\YUV444\YUV422\I420\NV21\NV12，无论怎么变化，这些名称都只表示两个维度上YUV数据在计算机中的区别，其中一个维度是YUV三个分量的比例信息，420、21、12字眼的代表4个Y分量共用一个UV分量，422代表两个 Y分量共用一个UV分量，444就表示一个Y使用一个U、一个V，有点类似RGB，但是YUV和RGB不是一个概念，但是他们都是表达的单个像素信息，另一个维度是YUV数据在计算机中的排列方式，其中后面不代P的，在计算机中YUV数值交错存储，带P或者NV开头的表示的是水平存储，意味着计算机中Y先存储，后面放U和V,NV21和NV12的区别就是U在前还是V在前。&lt;strong&gt;&lt;mark class="has-inline-color has-accent-color" style="background-color:rgba(0, 0, 0, 0)"&gt;不管怎么样，他们的区别就是在计算机中的个数比例和数值排列顺序的差异。&lt;/mark&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;大多数视频都是采用420格式数据，也就是YUV比例4:1:1，也就是1个字节的Y+四分之1个字节的U和四分之1字节的V，也就是1.5个字节就表示了一个像素，而对应的RGB数据需要3个字节表示，所以RBG大小是YUV420数据的两倍，相当于YUV舍弃了人眼不敏感的色度信息来"压缩"了像素数据，对于这种"压缩"，因为人眼是感知是弱的，相当于就是变相的无损压缩，但这也不能称之为压缩，因为图像采集的原始数据本身就是YUV格式。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、常见的视频压缩算法为h264、h265，谷歌的vp8，vp9，微软的av1，最为常用的是h264、h265压缩算法，h264在软件中的名称为mpeg4/avc，简称avc，h265简称hevc。h264、h265它们是一种标准，是一套算法的集合，因为这些算法比较固定，所以在计算机中设计了独立的芯片dsp，dsp芯片专门负责编码或者解码h264\h265格式的视频，这种解码方式称为硬件解码，黑苹果、黑群晖jellyfin中所谓的硬件加速其实就是调用专用dsp芯片对音视频进行编解码处理，从而不占用cpu资源，除了硬件解码外，h264\h265算法本质就是计算，交给cpu计算称为软解码，软解码会耗费cpu资源，不过cpu性能过剩的今天也变得无所谓了，至于dsp芯片内部工作原理就是编码算法的具体实现，内部就是对一帧一帧的数据进行编码，它编码输出和解码输入的单位为一帧数据。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;h264压缩方式分为帧内压缩和帧间压缩，帧内压缩原理来源于渐变，科学家发现一幅图像相邻像素之间的差异不会很大，也就是相邻像素之间有一定的连续性，它们大多是线性的关系。所以科学家们将一张图片像素进行分组，拆分为一个个小单元，其中有4*4，16*16等像素单元，这些单元称为宏块，基于像素之间色值的连续性，它们将宏块的第一行和第一列像素值保留，宏块其他的值舍弃，同时记录一个向量，向量具有方向，根据向量的方向和第一行第一列的像素值就能推倒出其他像素的信息，这就是基于宏块的帧内压缩原理。也就是一帧可以分为很多个宏块。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;帧间压缩就是在视频中连续的画面中，前一帧和后一帧的宏块大多都是相同的，唯一不同的区别是宏块的位置，所以h264将帧分为i、b、p三种类型的帧，i帧为关键帧，保留了完整的宏块信息，而b帧既要参考前面的i帧，又要参考后面的p帧，它保存的大部分是宏块的位移信息，但也有少部分是宏块的完整信息，而p帧参考的是前面的i帧，也是保存了宏块的唯一信息，从而达到了帧间压缩的效果。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;h264在计算机中编码后的数据除了帧数据还有对帧和视频的描述信息，这些信息也以一帧的形式出现，称为sps和pps，每一个网络帧称之为一个NAL，NAL之间以0x0000001分隔，后面一个字节是NAL的类型，后面就是具体的NAL数据了，由于NAL之间大多数是像素数据，像素数据范围是0-255，所以这种小而多的数据非常适合采用哥伦布编码，哥伦比编码的原理就是在前面补0，前面有几个零表示后面的数据是多少位的，它是一种变长的编码方式。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;h265和h264的有相同的地方，也有不同的地方，相同的是它们都是采用了帧内、帧间预测方式，只不过具体的方式有差异，h265的宏块最大是64*64，所以更具压缩比，但h265同时能保证视频细节更好，h265对色彩差异大和色彩差异小的像素区域分别对待，像素差异大的区域会继续将大的宏块切分，直到最小切分位4*4，同时增加预测方向数目，这样保证了细节又保证了压缩比，帧间预测和h264相同。因为h265对于i帧需要更多的配置信息，所以h265的i帧比h264大，b帧和p帧会比h264小非常多，同样的，h265的网络帧NAL，如sps\pps和h264有很大不同。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、音视频通信解决方案主要有两种，一种是以延迟性较高但是支持并发数高的直播技术，以RTMP协议为主，另一种是延迟较低的音视频通话或者音视频会议，以webrtc为主。之所以出现这两种方案，是因为需求不一样，直播面对的是一对N的场景，画面的要求也不高，这种场景适合采用服务器转发技术，而音视频会议是一对一或者几个对几个的问题，注重实时性，所以需要客户端直接连接，采用p2p技术。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;rtmp本身是一个协议，它支持h264\h265\aac等视频编码协议，在其数据上增加了一些协议定义的字节，经服务器流转到各终端，来实现直播，rtmp直播过程为:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:group --&gt;
&lt;div class="wp-block-group"&gt;&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;1、服务端开启RTMP服务&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;2、直播推流客户端采集音视频数据--&amp;gt;编码成h264\aac数据--&amp;gt;本地预览的同时编码成RTMP包数据--&amp;gt;发送给RTMP服务器&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;3、直播拉流客户端--&amp;gt;拉取RTMP数据-&amp;gt;解成H264数据和aac数据--&amp;gt;解码成YUV和PCM数据--&amp;gt;渲染和播放声音。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;&lt;/div&gt;
&lt;!-- /wp:group --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;webrtc解决方案的核心是p2p互联，所以这种架构下的服务端有两个角色，一个角色是负责前期沟通的信令服务器，还有一个角色是负责打通两端的ice服务器，信令服务器负责房间的创建、负责各端进入离开房间的消息通知，也负责各端之间媒体协商信息的传递(sdp传递)，也就是在媒体协商阶段，各端是没有直接连接的，媒体协商主要对音视频编解码的各种参数达成一致，为后面互通做准备，ice服务器主要负责建立通道，建立通道有前期的各端nat信息的获取(candidate)，通过信令服务器转发，然后建立连接后，通过RTP/RTCP来传输流媒体数据，其中rtcp是控制协议，因为RTP主要基于udp协议，udp协议是不可靠的，所以要rtcp来控制。一个简单的webrtc的流程为:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:group --&gt;
&lt;div class="wp-block-group"&gt;&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;1、服务端开启信令服务器、ICE服务器&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;2、客户端A发送加入房间信息到信令服务器--&amp;gt;信令服务器返回加入成功--&amp;gt;客户端创建RTCPeerConnection大总管--&amp;gt;在大总管下配置音视频源信息、音视频编解码器、开启本地预览、配置本地媒体参数信息等--&amp;gt;成功后发送SDP数据--&amp;gt;等待接收其他客户端SDP数据- &amp;gt;收到客户端B的SDP数据--&amp;gt;协商完毕&amp;lt;---&amp;gt;协商过程中通过ice服务器得到icecadiate数据通过信令服务器交换icecadiate--&amp;gt; 建立p2p通道--&amp;gt;通过p2p通道使用rtp\rtcp协议实现音视频通话&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph {"textColor":"accent","fontSize":"small"} --&gt;
&lt;p class="has-accent-color has-text-color has-small-font-size"&gt;3、客户端A发送加入房间信息到信令服务器--&amp;gt;信令服务器返回加入成功--&amp;gt;客户端创建RTCPeerConnection大总管--&amp;gt;在大总管下配置音视频源信息、音视频编解码器、开启本地预览、配置本地媒体参数信息等--&amp;gt;成功后发送SDP数据--&amp;gt;收到客户端A的SDP数据--&amp;gt;协商完毕&amp;lt;---&amp;gt;协商过程中通过ice服务器得到icecadiate数据通过信令服务器交换icecadiate--&amp;gt; 建立p2p通道--&amp;gt;通过p2p通道使用rtp\rtcp协议实现音视频通话&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;&lt;/div&gt;
&lt;!-- /wp:group --&gt;</description></item><item><title>一张图告诉你华为多屏协同怎么使用</title><link>https://lategege.com/p/%E4%B8%80%E5%BC%A0%E5%9B%BE%E5%91%8A%E8%AF%89%E4%BD%A0%E5%8D%8E%E4%B8%BA%E5%A4%9A%E5%B1%8F%E5%8D%8F%E5%90%8C%E6%80%8E%E4%B9%88%E4%BD%BF%E7%94%A8/</link><pubDate>Thu, 16 Dec 2021 13:21:29 +0000</pubDate><guid>https://lategege.com/p/%E4%B8%80%E5%BC%A0%E5%9B%BE%E5%91%8A%E8%AF%89%E4%BD%A0%E5%8D%8E%E4%B8%BA%E5%A4%9A%E5%B1%8F%E5%8D%8F%E5%90%8C%E6%80%8E%E4%B9%88%E4%BD%BF%E7%94%A8/</guid><description>&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/12-16/20.png"/&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这张图基本涵盖了华为多屏协同的大部分功能，下面列举具体功能。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;1、电脑端可以备份手机相册、视频，同时可以浏览查看手机文件&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;2、电脑端可以使用使用手机网络上网、类似与手机开wifi热点&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;3、电脑端快捷键ctrl+alt+q可以打开智慧搜索，输入关键字可以搜索手机中的文件、视频、相片&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;4、电脑端操作部分app，可以开启多窗口，截图中的b站app和今日头条app就是多窗口模式下打开的。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;5、手机端可以对电脑截屏和录频&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;6、手机端可以获取电脑的文件&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;以下是图片中没有表现出来的功能：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;7、手机文件或者照片长按可以拖拽复制到电脑中。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;8、同样电脑中的文件或者相册可以拖动到手机中，打开文件管理或者相册app就可以看到&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;9、电脑中拖动图片到手机微信聊天窗口可以直接发送&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;10、另外当手机通过app播放音乐时默认使用了电脑的声音输出&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;.....其他功能待发掘...&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/12-16/21.png"/&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>android 通过adb配置代理方式</title><link>https://lategege.com/p/android-%E9%80%9A%E8%BF%87adb%E9%85%8D%E7%BD%AE%E4%BB%A3%E7%90%86%E6%96%B9%E5%BC%8F/</link><pubDate>Mon, 18 Oct 2021 08:32:06 +0000</pubDate><guid>https://lategege.com/p/android-%E9%80%9A%E8%BF%87adb%E9%85%8D%E7%BD%AE%E4%BB%A3%E7%90%86%E6%96%B9%E5%BC%8F/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;设置代理：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb shell settings put global http_proxy ip:port&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;如：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb shell settings put global http_proxy 192.168.0.5:8888&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;移除代理（需三条指令全部执行，部分机型可能需要重启手机才能完全移除代理）：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb shell settings delete global http_proxy&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb shell settings delete global global_http_proxy_host&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb shell settings delete global global_http_proxy_port&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;adb reboot&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>android新一代模拟器cuttlefish</title><link>https://lategege.com/p/android%E6%96%B0%E4%B8%80%E4%BB%A3%E6%A8%A1%E6%8B%9F%E5%99%A8cuttlefish/</link><pubDate>Thu, 07 Oct 2021 11:06:41 +0000</pubDate><guid>https://lategege.com/p/android%E6%96%B0%E4%B8%80%E4%BB%A3%E6%A8%A1%E6%8B%9F%E5%99%A8cuttlefish/</guid><description>&lt;!-- wp:heading --&gt;
&lt;h2 id="what_is_cuttlefish"&gt;什么是 Cuttlefish？&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/"&gt;Cuttlefish&lt;/a&gt; 是一种可配置的虚拟 Android 设备，既可以远程运行（使用第三方云产品，如 Google Cloud Engine），又可以在本地运行（在 Linux x86 机器上）。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2 id="cuttlefish_goals"&gt;Cuttlefish 的目标&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:list --&gt;
&lt;ul&gt;&lt;li&gt;使平台和应用开发者不再依赖于物理硬件来开发和验证代码更改。&lt;/li&gt;&lt;li&gt;通过与核心框架保持高度一致，以&lt;strong&gt;高保真度&lt;/strong&gt;为重点来复制真实设备的基于框架的行为。&lt;/li&gt;&lt;li&gt;支持 API 级别 28 之后的所有 API 级别。&lt;/li&gt;&lt;li&gt;在各个 API 级别达到&lt;strong&gt;一致的&lt;/strong&gt;功能水平，与物理硬件上的行为保持一致。&lt;/li&gt;&lt;li&gt;实现规模化：&lt;ul&gt;&lt;li&gt;能够并行运行多台设备。&lt;/li&gt;&lt;li&gt;能够并发执行测试，实现高保真度且入门成本较低。&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;提供可配置的设备，能够调整设备类型、RAM、CPU 等。&lt;/li&gt;&lt;/ul&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2 id="comparing_cuttlefish_to_other_devices"&gt;Cuttlefish 与其他设备的对比情况&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:heading {"level":3} --&gt;
&lt;h3 id="cuttlefish_and_android_emulator"&gt;Cuttlefish 和 Android 模拟器&lt;/h3&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;Cuttlefish 与 &lt;a href="https://source.android.google.cn/setup/create/avd"&gt;Android 模拟器&lt;/a&gt;有许多相似之处，但 Cuttlefish 可以保证 Android 框架（无论这是纯 AOSP，还是您自己的树中的自定义实现）实现全保真。在实际应用中，这意味着 Cuttlefish 应该会在操作系统级别响应您的互动，就像使用相同的自定义或纯 Android 操作系统源代码构建的实体手机目标一样。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;Android 模拟器围绕简化应用开发的用例构建而成，它包含许多功能钩子来迎合 Android 应用开发者的用例。如果您要使用您的自定义 Android 框架来构建模拟器，这可能会带来一些挑战。如果您需要能够代表您的自定义平台/框架代码或 Android 树形结构的虚拟设备，那么 Cuttlefish 虚拟设备是理想的选择。它是用于表示当前 AOSP 开发状态的规范设备。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading {"level":3} --&gt;
&lt;h3 id="cuttlefish_and_physical_device"&gt;Cuttlefish 和物理设备&lt;/h3&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;Cuttlefish 虚拟设备与物理设备之间的主要区别在于硬件抽象层 (HAL) 级别，以及与任何自定义硬件互动的任何软件。除了硬件专用实现之外，您应该会发现 Cuttlefish 和物理设备表现出在功能上等效的行为。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2 id="how_can_cuttlefish_help"&gt;Cuttlefish 有哪些益处？&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;您可以像与任何可能用于调试的其他 Android 设备互动一样与 Cuttlefish 互动。它会通过 adb 将自身注册为正常设备，您可以像与物理设备互动一样通过远程桌面与之互动。Cuttlefish 的用例非常广泛，涵盖应用测试、自定义系统构建测试等。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;由于 Cuttlefish 力求实现框架全保真，因此可用于对您的框架和/或应用进行功能测试，在测试中没有无法模拟的物理硬件依赖项。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading {"level":3} --&gt;
&lt;h3 id="how_is_cuttlefish_commonly_used_for_testing_today"&gt;目前 Cuttlefish 通常如何用于测试？&lt;/h3&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;Cuttlefish 在测试方面的一些常见应用包括：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:list --&gt;
&lt;ul&gt;&lt;li&gt;CTS&lt;/li&gt;&lt;li&gt;框架合规性&lt;/li&gt;&lt;li&gt;持续集成测试&lt;/li&gt;&lt;li&gt;自定义测试套件&lt;/li&gt;&lt;/ul&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2 id="can_i_host_cuttlefish_in_the_cloud"&gt;我是否可以在云端托管 Cuttlefish？&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;可以，Cuttlefish 本身支持 Google Cloud，并计划支持其他云平台。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading {"level":1} --&gt;
&lt;h1&gt;Cuttlefish Getting Started&lt;/h1&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#Try-Cuttlefish"&gt;&lt;/a&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#try-cuttlefish"&gt;&lt;/a&gt;Try Cuttlefish&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:list {"ordered":true} --&gt;
&lt;ol&gt;&lt;li&gt;Make sure virtualization with KVM is available. grep -c -w "vmx\|svm" /proc/cpuinfo This should return a non-zero value. If running on a cloud machine, this may take cloud-vendor-specific steps to enable. For Google Compute Engine specifically, see the &lt;a href="https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances"&gt;GCE guide&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;ARM specific steps:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:list --&gt;
&lt;ul&gt;&lt;li&gt;When running on an ARM machine, the most direct way is to check for the existence of &lt;code&gt;/dev/kvm&lt;/code&gt;. Note that this method can also be used to confirm support of KVM on any environment.&lt;/li&gt;&lt;li&gt;Before proceeding to the next step, please first follow &lt;a href="https://android.googlesource.com/device/google/cuttlefish/+/HEAD/multiarch-howto.md"&gt;the guide&lt;/a&gt; to adjust APT sources.&lt;/li&gt;&lt;/ul&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:list {"ordered":true,"start":2} --&gt;
&lt;ol start="2"&gt;&lt;li&gt;Download, build, and install the host debian package:sudo apt install -y git devscripts config-package-dev debhelper-compat git clone https://github.com/google/android-cuttlefish cd android-cuttlefish debuild -i -us -uc -b sudo dpkg -i ../cuttlefish-common_*_*64.deb || sudo apt-get install -f sudo usermod -aG kvm,cvdnetwork $USER sudo reboot The reboot will trigger installing additional kernel modules and applying udev rules.&lt;/li&gt;&lt;li&gt;Go to &lt;a href="http://ci.android.com/"&gt;http://ci.android.com/&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Enter a branch name. Start with &lt;code&gt;aosp-master&lt;/code&gt; if you don‘t know what you’re looking for&lt;/li&gt;&lt;li&gt;Navigate to &lt;code&gt;aosp_cf_x86_64_phone&lt;/code&gt; and click on &lt;code&gt;userdebug&lt;/code&gt; for the latest build&lt;/li&gt;&lt;li&gt;Click on &lt;code&gt;Artifacts&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Scroll down to the OTA images. These packages look like &lt;code&gt;aosp_cf_x86_64_phone-img-xxxxxx.zip&lt;/code&gt; -- it will always have &lt;code&gt;img&lt;/code&gt; in the name. Download this file&lt;/li&gt;&lt;li&gt;Scroll down to &lt;code&gt;cvd-host_package.tar.gz&lt;/code&gt;. You should always download a host package from the same build as your images.&lt;/li&gt;&lt;li&gt;On your local system, combine the packages:mkdir cf cd cf tar xvf /path/to/cvd-host_package.tar.gz unzip /path/to/aosp_cf_x86_64_phone-img-xxxxxx.zip&lt;/li&gt;&lt;li&gt;Launch cuttlefish with:&lt;/li&gt;&lt;/ol&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;code&gt;$ HOME=$PWD ./bin/launch_cvd&lt;/code&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:list {"ordered":true,"start":11} --&gt;
&lt;ol start="11"&gt;&lt;li&gt;Stop cuttlefish with:&lt;/li&gt;&lt;/ol&gt;
&lt;!-- /wp:list --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;code&gt;$ HOME=$PWD ./bin/stop_cvd&lt;/code&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#Debug-Cuttlefish"&gt;&lt;/a&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#debug-cuttlefish"&gt;&lt;/a&gt;Debug Cuttlefish&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;You can use &lt;code&gt;adb&lt;/code&gt; to debug it, just like a physical device:&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;code&gt;$ ./bin/adb -e shell&lt;/code&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#Launch-Viewer-WebRTC"&gt;&lt;/a&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#launch-viewer-webrtc"&gt;&lt;/a&gt;Launch Viewer (WebRTC)&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;When launching with &lt;code&gt;---start_webrtc&lt;/code&gt; (the default), you can see a list of all available devices at &lt;code&gt;https://localhost:8443&lt;/code&gt; . For more information, see the WebRTC on Cuttlefish &lt;a href="https://source.android.com/setup/create/cuttlefish-ref-webrtc"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:heading --&gt;
&lt;h2&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#Launch-Viewer-VNC"&gt;&lt;/a&gt;&lt;a href="https://android.googlesource.com/device/google/cuttlefish/#launch-viewer-vnc"&gt;&lt;/a&gt;Launch Viewer (VNC)&lt;/h2&gt;
&lt;!-- /wp:heading --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;When launching with &lt;code&gt;--start_vnc_server=true&lt;/code&gt; , You can use the &lt;a href="https://www.tightvnc.com/download.php"&gt;TightVNC JViewer&lt;/a&gt;. Once you have downloaded the &lt;em&gt;TightVNC Java Viewer JAR in a ZIP archive&lt;/em&gt;, run it with&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;code&gt;$ java -jar tightvnc-jviewer.jar -ScalingFactor=50 -Tunneling=no -host=localhost -port=6444&lt;/code&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;Click “Connect” and you should see a lock screen!&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;webRTC 下运行效果：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/10-7/1.png"/&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;</description></item><item><title>ubuntu20.04编译android 11(R)</title><link>https://lategege.com/p/ubuntu20-04%E7%BC%96%E8%AF%91android-11-r/</link><pubDate>Sun, 03 Oct 2021 03:31:44 +0000</pubDate><guid>https://lategege.com/p/ubuntu20-04%E7%BC%96%E8%AF%91android-11-r/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;因为我有一台12核24线程 64g内存的1.5T固态+1T机械的服务器，服务器安装了PVE虚拟机，昨天刚把PVE从6.4升级为7.0，然后就想着要编译android 11的源码，编译源码当然选择最新的长期支持版ubuntu，也就是目前的ubuntu20.04，于是官网下载了镜像文件，下载地址：&lt;a href="https://ubuntu.com/download/desktop"&gt;https://ubuntu.com/download/desktop&lt;/a&gt;，装桌面版是因为可以应对后面在图形化界面中很方便解决的问题。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我给虚拟机分配了10个核心，16g内存，300g硬盘(固态)，安装过程略。因为我的家庭网络采用了双软路由方案（爱快+LEDE)，所以，你懂得，我不需要设置什么软件源，因为我畅通无阻，但是android 11 源码将近90g，走国外的话，这流量费用。。。所以下源码的时候我还是决定使用国内镜像下载。其他比如软件更新什么的那点流量就忽略不计了。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;PVE中装完ubuntu20.04，第一件事：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#不管三七二十一，所有软件更新到最新再说
sudo apt-get update &amp;amp;&amp;amp; upgrade
#安装vim 方便后面在终端编辑
sudo apt-get install vim
#安装ssh server 安装完后可以在本地电脑上ssh连接操作了
sudo apt-get install openssh-server
# 编辑ssh_config 将这个注释去掉 PasswordAuthentication yes 保存
vim /etc/ssh/ssh_config
#重启下ssh
sudo /etc/init.d/ssh restart&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;接着，在本地电脑的终端上&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#ssh登录ubuntu
ssh 用户名@ip
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;安装编译android所有需要的依赖(以下的操作建议先安装screen，然后在screen中操作，因为这样可以保证退出终端后任务还继续执行，下次进来想要展示之前的命令窗口也非常方便，具体的screen用法参考我的 另一篇文章&lt;a href="https://lategege.com/p/mac终端下使用screen/" rel="noreferrer noopener" target="_blank"&gt;https://www.lategege.com/?p=254&lt;/a&gt; mac、linux下操作一摸一样)&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g++-multilib
sudo apt-get install -y git flex bison gperf build-essential libncurses5-dev:i386
sudo apt-get install tofrodos python python-markdown libxml2-utils xsltproc zlib1g-dev:i386
sudo apt-get install dpkg-dev libsdl1.2-dev libesd0-dev
sudo apt-get install git-core gnupg flex bison gperf build-essential
sudo apt-get install zip curl zlib1g-dev gcc-multilib g++-multilib
sudo apt-get install libc6-dev-i386
sudo apt-get install lib32ncurses5-dev x11proto-core-dev libx11-dev
sudo apt-get install libgl1-mesa-dev libxml2-utils xsltproc unzip m4
sudo apt-get install lib32z-dev ccache
sudo apt-get install libssl-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这里要注意有坑，第一个坑是libesd0-dev 这个软件包 在ubuntu默认源中是没有的，所以要添加源然后更新下软件列表再安装&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;sudo vim /etc/apt/sources.list //在行尾添加下面这个源保存
deb http://archive.ubuntu.com/ubuntu/ trusty main universe restricted multiverse
&lt;p&gt;#安装libesd0-dev
sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install libesd0-dev&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;第二个坑是关于lib32ncurses5-dev的，这个包安装不上。在安装的时候发现要安装libncurses5-dev，但是我软件包都更新为最新版本的了，也就是libncurses5-dev 最新版本是6.2的，我查看了下/usr/lib/x86_64-linux-gnu  这个目录下的动态库，发现只有libncurses.so.6，这就是因为libncurses5-dev这个包太新了，已经更新到6.2了，所以没有libncurses.so.5，因为后面编译需要有libncurses.so.5，不然编译会报错，我想了下有两种方案:一种libncurses5-dev回退到5.9，但是其他也有可能有依赖，又因为libncurses.so.6依赖了libtinfo.so.6，所以干脆如下暴力处理(后面的编译报错问题解决)：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;cd /usr/lib/x86_64-linux-gnu
sudo cp libncurses.so.6 libncurses.so.5
sudo cp libtinfo.so.6 libtinfo.so.5&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#装完软件需要设置下git的用户名密码,可以随意，但是这步是必须的，因为拉android源码的时候会验证git是否设置的email和name 不设置就不让拉取，http.sslverify https.sslverify 也配置下，后面拉取https仓库有时候报验证证书失败，配了这两个后就不会报错了。
git config --global user.email "xxx@xxx.com"
git config --global user.name "xxx"
git config --global http.sslverify false
git config --global https.sslverify false&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;接着就是拉取源码了，谷歌建议在home目录建一个bin文件夹，源码下载到这里，当然这只是建议，这边就按照谷歌的建议来操作。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#在当前用户家目录创建bin文件夹
mkdir ~/bin
#将该目录写入环境变量
echo "PATH=~/bin:\$PATH" &amp;gt;&amp;gt; ~/.bash_profile
#刷新环境变量
source ~/.bash_profile&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;补充知识：环境变量分为/etc目录下全局的和每个用户下面独立的，每个用户独立的又有两处可以放，.bashrc 或者.bash_profile 因为.bashrc本身有很多代码，所以建议建立独立的.bash_profile文件存放环境变量。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#拉取谷歌的代码管理脚本repo
curl https://storage.googleapis.com/git-repo-downloads/repo &amp;gt; ~/bin/repo
#不能科学上网的 从清华镜像获取 两个都是一样的 curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o repo
#授权执行权限,repo本质就是一个python2的脚本
chmod a+x ~/bin/repo&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#初始化repo 就是设置url为镜像地址 android-11版本在不断更新，可以去https://source.android.google.cn/setup/start/build-numbers#source-code-tags-and-builds这个官网地址查看最新版本是多少，针对的设备是哪些，再决定拉取哪个代码。
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-11.0.0_r43
&lt;h2 id="如果提示无法连接到-gerritgooglesourcecom可以编辑-binrepo把-repo_url-一行替换成下面的"&gt;如果提示无法连接到 gerrit.googlesource.com，可以编辑 ~/bin/repo，把 REPO_URL 一行替换成下面的：
&lt;/h2&gt;&lt;h2 id="repo_url--"&gt;REPO_URL = &amp;lsquo;&lt;a class="link" href="https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/%27" target="_blank" rel="noopener"
 &gt;https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'&lt;/a&gt;
&lt;/h2&gt;&lt;p&gt;#由于清华大学镜像对并发是有限制的，这里就看自己测试下来能最大几个就填-jX
repo sync -j4 &amp;ndash;fail-fast &amp;ndash;force-sync&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;#拉完代码就是编译过程 固定的三个命令 按顺序执行 lunch 根据自己需要选择不通平台
. build/envsetup.sh
lunch aosp_x86-eng
make -j16&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在我编译过程中还遇到一个坑 如下：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;merge_zips.go:752: prebuilts/gradle-plugin/com/android/tools/lint/lint-api/26.5.0/lint-api-26.5.0.jar: zip: not a valid zip file
12:05:58 ninja failed with: exit status 1&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我查找prebuilts/gradle-plugin下面的文件，发现文件都是0kb，也就是这个文件夹中的文件根本没有同步下来，所以我直接删掉gradle-plugin这个文件夹，然后再一次执行repo sync，它会将gradle-plugin重新下载，接着编译就没问题了。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>jenkins构建apk并发布到企业微信</title><link>https://lategege.com/p/jenkins%E6%9E%84%E5%BB%BAapk%E5%B9%B6%E5%8F%91%E5%B8%83%E5%88%B0%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1/</link><pubDate>Wed, 29 Sep 2021 06:25:17 +0000</pubDate><guid>https://lategege.com/p/jenkins%E6%9E%84%E5%BB%BAapk%E5%B9%B6%E5%8F%91%E5%B8%83%E5%88%B0%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;目前公司内部的通讯工具是企业微信，由于现在提测的apk都是手动打包发送到群里，为了解放双手，打算部署自动化打包工具。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我接触过的自动化打包工具主要有两种，一种是jenkins，还有一种是gitlab-runner，后者由于我在公司gitlab上没有开启gitlab-runner的权限，所以放弃了，于是乎我在公司分配给我的macbook上搭建了一套jenkins。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在mac上搭建jenkins非常方便，在这里没有采用docker的方式，而是通过homebrew来安装。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一、首先安装homebrew，安装过程中有可能会让你去下载command line tools，有的电脑安装会提示网络连接失败，那是因为苹果默认不提供自动下载的服务了，需要你去&lt;a href="https://developer.apple.com/download/more/"&gt;https://developer.apple.com/download/more/&lt;/a&gt; 这个地址手动下载对应系统版本的command line tools，这里需要登录一下apple账户，下载安装。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;二、下载完homebrew后，安装jenkins就一行命令，非常方便&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;brew install jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;三、安装完毕，如果需要修改端口，vim是一个命令行下的编辑工具，如果不习惯用，可以直接访问该目录，通过诸如sublime这种工具修改。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;vim /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;修改httpListenAddress=0.0.0.0 意思是不管本机还是局域网中任意ip电脑都能访问&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;修改httpPort=8090 是访问的端口，只要不冲突，你也可以保持默认的8081不变&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
&amp;lt;dict&amp;gt;
	&amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
	&amp;lt;string&amp;gt;homebrew.mxcl.jenkins&amp;lt;/string&amp;gt;
	&amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
	&amp;lt;array&amp;gt;
		&amp;lt;string&amp;gt;/usr/local/opt/openjdk@11/bin/java&amp;lt;/string&amp;gt;
		&amp;lt;string&amp;gt;-Dmail.smtp.starttls.enable=true&amp;lt;/string&amp;gt;
		&amp;lt;string&amp;gt;-jar&amp;lt;/string&amp;gt;
		 &amp;lt;string&amp;gt;/usr/local/opt/jenkins/libexec/jenkins.war&amp;lt;/string&amp;gt;
		&amp;lt;string&amp;gt;--httpListenAddress=0.0.0.0&amp;lt;/string&amp;gt;
		&amp;lt;string&amp;gt;--httpPort=8090&amp;lt;/string&amp;gt;
	&amp;lt;/array&amp;gt;
	&amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
	&amp;lt;true/&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;补充: .plist文件是苹果系统独有的程序执行单元的配置文件，如果你把该文件放在mac系统的/Library/LaunchAgents这个目录下，那么开机的时候就会加载运行，也就是开机启动。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;四、修改完成后执行，开启jenkins，以下命令是固定写法，我猜想他的本质就是执行了mac系统的 launchctl load /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist 这个命令，去加载运行了这个配置文件，根据这个配置文件又执行了/usr/local/opt/openjdk@11/bin/java -jar /usr/local/opt/jenkins/libexec/jenkins.war -Dmail.smtp.starttls.enable=true --httpListenAddress=0.0.0.0 --httpPort=809，从而运行了jenkins的war包，jenkins的本质就是一个javaweb程序。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;brew services start jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;五、安装完成访问127.0.0.1:8090就能进入jenkins管理界面，期间会提示输入密码，初始密码会提示你在哪个目录下，去这个目录下找到输入即可，然后推荐你安装的插件，点击安装下，最后创建一个用户即可使用，这些操作很常规，这里就不放图了。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;六、因为我希望发送企业微信的时候将git的提交日志也带上，所以需要安装一个changelog插件，我已经上传到百度网盘中，在jenkins中选择系统管理--&amp;gt;插件管理--&amp;gt;高级--&amp;gt;上传插件--&amp;gt;选择文件即可安装，安装完成后重启下jenkins&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:preformatted --&gt;
&lt;pre class="wp-block-preformatted"&gt;链接: https://pan.baidu.com/s/1Z9rGxzy-dSPUyibpazdbuw 提取码: lfi3
--来自百度网盘超级会员v5的分享&lt;/pre&gt;
&lt;!-- /wp:preformatted --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;七、在正式建立构建任务前，先做一件事，保证下载了sdk，你不需要单独去下载，只要去android studio官网https://developer.android.google.cn/studio/ 下载安装android studio，然后打开就会提示你下载sdk，在mac下android sdk路径一般为 /Users/你的用户名/Library/Android/sdk&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;八、去jenkins建立任务&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/1.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/1.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;定义一个名称，选择构建一个自由风格的软件项目，点击确定&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/2.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/2.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;输入你们项目的git地址&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/7.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/7.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;输入分支名称，勾选轮询SCM&lt;br/&gt;轮询方式. 第一种配置一个token生成一个url，当访问该url时触发。第二种，第三种其他工程构建后触发、定时构建很好理解。第四种github-hook 需要github那边触发，企业默认使用gitlab较多。第五种，轮询 SCM，用的比较多，因为不知道代码什么时候提交，所以不断轮询检测是比较靠谱的方式，我一般都会使用这种方式。设置：H/2 * * * * 表示每两分钟检查下git有没有提交，有提交就触发构建&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/3.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/3.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;勾选add changelog 就是刚才安装好了changelog插件才能看到，这个插件的目的是为了获取到git的提交记录，entry format固定写法 %3$s&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/4.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/4.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;选择invoke gradle 选择你项目构建需要用到的gradle版本，如果没有，就去jenkins系统管理去下载对应的版本，tasks脚本，这里输入的脚本和在android studio中 gradlew task 一致，一行代表一个task 会顺序执行，如果需要clean就在第一行输入clean，assembleDebug表示构建debug包&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/5.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-29/5.png"/&gt;&lt;/a&gt;&lt;figcaption&gt;脚本的意思是获取到本机ip地址，然后拼接一个可下载的路径，需要在本地上部署web服务，我使用的是nginx,也就是通过nginx访问了我的apk，$SCM_CHANGELOG 这个字段中保存了changelog插件找到的提交记录信息&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;ip=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"​`
&lt;p&gt;echo $ip&lt;/p&gt;
&lt;p&gt;echo SCM_CHANGELOG $SCM_CHANGELOG&lt;/p&gt;
&lt;p&gt;curl &amp;lsquo;这里输入企业微信机器人webhook地址&amp;rsquo; &lt;br&gt;
-H &amp;lsquo;Content-Type: application/json&amp;rsquo; &lt;br&gt;
-d '
{
&amp;ldquo;msgtype&amp;rdquo;: &amp;ldquo;text&amp;rdquo;,
&amp;ldquo;text&amp;rdquo;: {
&amp;ldquo;mentioned_list&amp;rdquo;:[&amp;ldquo;wangqing&amp;rdquo;,&amp;quot;@all&amp;quot;],
&amp;ldquo;content&amp;rdquo;: &amp;ldquo;自动化打包完成\n下载地址:http://&amp;rsquo;${ip}&amp;rsquo;:8080/jenkins-build-out/toc_out/\n 本次更新:&amp;rsquo;${SCM_CHANGELOG}&amp;rsquo;&amp;rdquo;,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&amp;rsquo;&lt;/p&gt;
&lt;p&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:image {"width":745,"height":331,"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large is-resized"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-29/6.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" height="331" src="https://img.lategege.com:30443/images/web/2021/9-29/6.png" width="745"/&gt;&lt;/a&gt;&lt;figcaption&gt;最后执行的shell脚本的意思是将构建好的apk文件复制到可供下载的文件路径下，需要根据自己的实际情况修改&lt;/figcaption&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;
rm -rf /Users/用户名/Public/jenkins-build-out/toc_out/*.apk
cp /Users/用户名/.jenkins/workspace/构建名称/app/build/outputs/apk/*/*/*.apk /Users/用户名/Public/jenkins-build-out/toc_out/
chmod 777 /Users/用户名/Public/jenkins-build-out/toc_out/&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;如果你也使用nginx提供访问apk下载的服务，可以在nginx.conf文件夹中这样配置，在mac中安装nginx和jenkins一致，brew install nginx即可,然后去/usr/local/etc/nginx 这个目录修改配置文件，然后brew services start nginx 开启即可&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt; location / {
 	 root /Users/用户名/Public;
 autoindex on;
 autoindex_exact_size off;
 autoindex_localtime on;
 charset utf-8,gbk;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;完成后打包就可以自动发送企业微信群里了，测试根据你提供的web地址下载对应的安装包，从此，告别了手动发包的操作。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>Android dex ClassNotFoundException的特殊引发</title><link>https://lategege.com/p/android-dex-classnotfoundexception%E7%9A%84%E7%89%B9%E6%AE%8A%E5%BC%95%E5%8F%91/</link><pubDate>Fri, 24 Sep 2021 00:25:16 +0000</pubDate><guid>https://lategege.com/p/android-dex-classnotfoundexception%E7%9A%84%E7%89%B9%E6%AE%8A%E5%BC%95%E5%8F%91/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;一般情况下 引发 android dex 某个类未找到异常 是由于没有引用类的包所导致，但是如果反编译包发现该类存在，就要考虑是不是使用了ASM字节码插装的插件，在处理字节码的时候插入了不合规的代码，引起了DEX文件验证失败，如 Invalid type descriptor 这类错误，这种错误我看到一篇博客的博主在长达一年的时间也没发现原因。近期我在工作中遇到过，同事写了一个字节码插件，由于注入的文件路径写的不具备兼容性，导致我mac编译的dex是有误的，造成了开启就闪退，并且爆出某些类未找到异常。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>gitlab-ci-runner 流程分析</title><link>https://lategege.com/p/gitlab-ci-runner-%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/</link><pubDate>Mon, 20 Sep 2021 09:21:53 +0000</pubDate><guid>https://lategege.com/p/gitlab-ci-runner-%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;以前构建android工程都是使用jenkins，最近想试试gitlab-ci-runner，于是一发不可收拾，忙活了一个晚上，也遇到了一些坑，总的来说，它和gitlab的结合比jenkins更紧密了，jenkins毕竟是gitlab之外的一套ci工具，和gitlab的配合上是不如gitlab-ci的，但不可否认，jenkins同样是一款强大的ci工具，两者并没有孰优孰劣之分。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;本篇文章不是讲如何搭建环境之类的，这些资料网上比比皆是，我写这篇文章的目的是想分析下gitlab-ci是如何运作的，于是我花了点时间画了下面这张图。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:image {"sizeSlug":"large"} --&gt;
&lt;figure class="wp-block-image size-large"&gt;&lt;a href="https://img.lategege.com:30443/images/web/2021/9-20/2.png" rel="noopener noreferrer" target="_blank"&gt;&lt;img alt="" src="https://img.lategege.com:30443/images/web/2021/9-20/2.png"/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;!-- /wp:image --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;首先早期的gitlab是没有gitlab-ci-runner的，也就是在早期它就是一个纯粹的代码仓库，后面gitlab引入了gitlab-ci工具(gitlab-ci-runner)，这个工具它本身不具备构建的功能，它类似于zookeeper这种服务治理中心，真正的干活的是gitlab-runner-client，这些client需要在这个gitlab-ci-runner(server)中进行注册，gitlab-ci-runner(server）就可以对它们发号施令，这其实就是一种cs结构，runner server可以分三种，group runner、shared runner、specific runner 三种runner 本质是一样的，就是作用域不同，group runner作用在工程组上的，利用这个特征，我们企业开发过程中可以划分不同开发小组，因为android、ios、web、h5 它们的构建是完全不同的，正好使用不同的runner server，而shared runner可以作用在任何一个工程上，实际上，没有哪个构建runner会是通用的，specific runner是特定的工程上的，一些构建差异比较大的工程可以采用这种方式。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;所以开发团队在gitlab使用中分组是非常有必要的，分组后可以定义各组专有的gitlab-ci-runner，互不干涉。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;我们以构建android 工程为例，分析gitlab-ci从代码提交到构建经过了哪些步骤&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;1、部署远程或者本机的一套gitlab-runner-client 包括android编译环境，这里就采用docker方式，先安装docker，后安装gitlab-runner也就是gitlab-runner-client，这里以centos7为例&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;# 更新yum
sudo yum update
# 卸载旧版本
sudo yum remove docker docker-common docker-selinux docker-engin
# 安装需要的软件包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 设置yum源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装docker最新版
sudo yum install docker-ce
# 启动docker 设置开机启动
sudo systemctl start docker
sudo systemctl enable docker&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;# 获取gitlab-runner脚本运行(脚本作用是添加gitlab-runner源)
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
# 安装最新版本的gitlab-runner
sudo yum install gitlab-runner
# 注册gitlab-runner到gitlab-ci-runner server 需要填写gitlab上提供的信息
sudo gitlab-runner register
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;进入gitlab android组页面--&amp;gt;设置---&amp;gt;CI/CD--&amp;gt;展开Runner 获取到url 和token，在上面的注册步骤中填入，executor执行环境填docker，镜像名称填 jangrewe/gitlab-ci-android 该镜像包含了android sdk等一系列android构建所需的环境，最后要求填tags自定义一个并且记住，比如填入android。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;2、注册成功后在gitlab android组的设置--&amp;gt;CI/CD--&amp;gt;展开Runner 可以看到一个可用的runner客户端。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;3、在android工程根目录下创建一个文件.gitlab-ci.yaml 文件名必须是这个，当然也可以自定义，但需要在gitlab上做相应更改。然后贴入以下脚本。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;image: jangrewe/gitlab-ci-android
# 用来编译 android 项目的镜像
variables:
 GRADLE_OPTS: "-Dorg.gradle.daemon=false" # 禁用 gradle 守护进程
before_script:
# 配置 gradle 的缓存目录
 - export GRADLE_USER_HOME=/cache/.gradle
# 获取权限
 - chmod +x ./gradlew
stages:
 - build
# 提交代码自动编译
build:
 stage: build
 only:
 - master
 script:
 - ./gradlew assembleDebug
 artifacts:
 paths:
 - app/build/outputs/apk/debug/
 tags:
 - android&lt;/code&gt;&lt;/pre&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;image指的是你要gitlab-runner使用哪个镜像构建，only: master是指针对master分支的提交才有效,artifacts:paths是指将打包完成的该目录下的文件上传回gitlab工程中，tags就是在client注册过程中填写的android，这里指定tags就是指定使用哪个gitlab-runner-client。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;4、android工程有代码提交，gitlab-ci-runner中的android group runner 按照.gitlab-ci.yaml脚本中的流水线配置向已经注册在gitlab-ci-runner(server)中的 gitlab-runner-client(centos7中的runner)发布构建命令。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;5、centos7中 gitlab-runner-client收到命令，拉取名为jangrewe/gitlab-ci-android 的 docker镜像，拉取gitlab-runner的docker镜像，运行这两个镜像， 执行git clone 工程，克隆完毕执行构建，构建完成后上传构建完成的包到gitlab &lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;6、gitlab在工程中可以下载打包好的android包&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;以上是我对gitlab-ci-runner的流程分析总结，在搭建过程中遇到了一个坑，在centos7中的docker 执行 git clone拉代码失败，经过分析，是因为docker容器内无法连接网络所致，解决办法是&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;重新建立docker桥接，执行以下命令&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:code --&gt;
&lt;pre class="wp-block-code"&gt;&lt;code&gt;service docker stop
&lt;p&gt;ip link set dev docker0 down&lt;/p&gt;
&lt;p&gt;brctl delbr docker0&lt;/p&gt;
&lt;p&gt;brctl addbr docker0&lt;/p&gt;
&lt;p&gt;ip addr add 172.17.42.2/24 dev docker0&lt;/p&gt;
&lt;p&gt;ip link set dev docker0 up&lt;/p&gt;
&lt;p&gt;service docker start
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:code --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;完成后这个坑解决了，解决之后顺利完成打包作业。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item><item><title>kotlin五种单例的写法</title><link>https://lategege.com/p/kotlin%E4%BA%94%E7%A7%8D%E5%8D%95%E4%BE%8B%E7%9A%84%E5%86%99%E6%B3%95/</link><pubDate>Mon, 13 Sep 2021 12:04:07 +0000</pubDate><guid>https://lategege.com/p/kotlin%E4%BA%94%E7%A7%8D%E5%8D%95%E4%BE%8B%E7%9A%84%E5%86%99%E6%B3%95/</guid><description>&lt;!-- wp:preformatted --&gt;
&lt;pre class="wp-block-preformatted"&gt;object 饿汉&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;class 懒汉 private constructor() {&lt;br/&gt; companion object {&lt;br/&gt; private var instance: 懒汉? = null&lt;br/&gt; get() {&lt;br/&gt; if (&lt;strong&gt;field &lt;/strong&gt;== null) {&lt;br/&gt; &lt;strong&gt;field &lt;/strong&gt;= 懒汉()&lt;br/&gt; }&lt;br/&gt; return &lt;strong&gt;field&lt;br/&gt;&lt;/strong&gt;&lt;strong&gt; &lt;/strong&gt;}&lt;br/&gt; fun getIns(): 懒汉 {&lt;br/&gt; return instance!!&lt;br/&gt; }&lt;br/&gt; }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;class 线程安全懒汉 private constructor() {&lt;br/&gt; companion object {&lt;br/&gt; private var instance: 线程安全懒汉? = null&lt;br/&gt; get() {&lt;br/&gt; if (&lt;strong&gt;field &lt;/strong&gt;== null) {&lt;br/&gt; &lt;strong&gt;field &lt;/strong&gt;= 线程安全懒汉()&lt;br/&gt; }&lt;br/&gt; return &lt;strong&gt;field&lt;br/&gt;&lt;/strong&gt;&lt;strong&gt; &lt;/strong&gt;}&lt;br/&gt;&lt;br/&gt; @Synchronized&lt;br/&gt; fun getIns(): 线程安全懒汉 {&lt;br/&gt; return instance!!&lt;br/&gt; }&lt;br/&gt; }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;class 双重校验懒汉 private constructor() {&lt;br/&gt; companion object {&lt;br/&gt; val instance: 双重校验懒汉 by &lt;em&gt;lazy&lt;/em&gt;(mode = LazyThreadSafetyMode.&lt;em&gt;SYNCHRONIZED&lt;/em&gt;) &lt;strong&gt;{ &lt;/strong&gt;双重校验懒汉() &lt;strong&gt;}&lt;br/&gt;&lt;/strong&gt;&lt;strong&gt; &lt;/strong&gt;}&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;class 静态内部类 private constructor() {&lt;br/&gt; companion object {&lt;br/&gt; val instance: 静态内部类 = Holder.holder&lt;br/&gt; }&lt;br/&gt;&lt;br/&gt; private object Holder {&lt;br/&gt; val holder = 静态内部类()&lt;br/&gt; }&lt;br/&gt;}&lt;/pre&gt;
&lt;!-- /wp:preformatted --&gt;</description></item><item><title>Java类加载机制的本质</title><link>https://lategege.com/p/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</link><pubDate>Tue, 17 Aug 2021 06:33:51 +0000</pubDate><guid>https://lategege.com/p/java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E7%9A%84%E6%9C%AC%E8%B4%A8/</guid><description>&lt;!-- wp:paragraph --&gt;
&lt;p&gt;在编程领域，很多名词都非常奇怪，这就非常容易造成软件工程师不少困惑与不解，就像java中的类加载机制，网上成篇大论都谈到一个名词-----双亲委派机制，下面就是类加载的最关键代码。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:preformatted --&gt;
&lt;pre class="wp-block-preformatted"&gt;&lt;code&gt;protected Class&amp;lt;?&amp;gt; loadClass(String name, boolean resolve)
 throws ClassNotFoundException
{
 // First, check if the class has already been loaded
 Class&amp;lt;?&amp;gt; c = findLoadedClass(name);
 if (c == null) {
 try {
 if (parent != null) {
 c = parent.loadClass(name, false);
 } else {
 c = findBootstrapClassOrNull(name);
 }
 } catch (ClassNotFoundException e) {
 // ClassNotFoundException thrown if class not found
 // from the non-null parent class loader
 }
&lt;pre&gt;&lt;code&gt; if (c == null) {
 // If still not found, then invoke findClass in order
 // to find the class.
 c = findClass(name);
 }
 }
 return c;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;!-- /wp:preformatted --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;看到上面的代码，我会想双亲是谁？委派又从何而来？中文虽然博大精深，但是要将双亲委派和这段代码联系起来，未免太牵强。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这段代码我用一个例子来说明：&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;一、儿子在家找身份证，先看了看自己周围有没有，如果没有，就打电话找他爸问问。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;二、儿子发现他周围没有，然后打电话问他爸爸，说爸爸，看见我的身份证了吗？&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;三、爸爸在他自己周边找了一圈，没有发现，爸爸就打电话给了爷爷。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;四、爷爷在他边上翻找了一圈，也没有发现，爷爷觉得这可不行，我得仔细找找，爷爷去自己的房间找了一圈没有发现，他就只能和孙子的爸爸说没找到。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;五、无奈的爸爸只能去他自己的房间找了一圈，最终也没找到，他就训斥孩子，身份证是你自己的，又不是别人的，你自己想办法。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-cyan-blue-color"&gt;六、儿子无奈地回了房间，发现身份证就在他自己的房间。&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;strong&gt;这个故事虽然有点假，但是已经将java类加载机制通过叙事的手段说清楚了。&lt;/strong&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;儿子就是AppClassLoader. 爸爸就是ExtClassLoader 爷爷就是BootStrapClassLoader&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;身份证就是要加载的类，儿子的周边就是他自己的缓存空间，儿子的房间是属于他管理类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;爸爸的周边就是爸爸的缓存空间，爸爸的房间是属于爸爸所管理的类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;&lt;span class="has-inline-color has-vivid-red-color"&gt;爷爷的周边就是爷爷的缓存空间，爷爷的房间是属于爷爷所管理的类加载路径&lt;/span&gt;&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;这个故事核心就是找东西，至于怎么找，先找离自己最近范围的，没有就打电话给亲人，让他们帮忙找找，最后这么一圈下来，如果最终都没找到，就报一个类未找到异常，故事中找的是儿子的身份证，那多半是在儿子自己的生活圈中，但是儿子记不清了，就像程序一样，谁都不知道哪些类是谁加载的一样。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;如果是爷爷的身份证，那就好比java中的String类一样，属于爷爷的生活范围，也只有爷爷能找到，爸爸和儿子不可能找得到，假如爷爷的东西都是非常贵重的，就像java中的核心包一样。如果有人要弄个赝品给到儿子手里，依照这个流程，只要爷爷手中的是真的，那这个赝品不会被找到，因为爷爷优先查找。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;
&lt;!-- wp:paragraph --&gt;
&lt;p&gt;所以java类加载机制---双亲委托机制这个说法站不住脚，但是我也没能找到一个很好的词汇来描述这套机制，因为它和我们的生活不太一样，所以我只能以上面的一个故事来讲明白这套机制。&lt;/p&gt;
&lt;!-- /wp:paragraph --&gt;</description></item></channel></rss>