├── English └── five_base_sentence.md ├── ML_doc ├── deep learning website.txt └── 深度学习概念.md ├── README.md ├── android_doc ├── ANR分析笔记.md ├── Android O 耗电统计.md ├── Android SystemServer学习笔记.md ├── Android.bp添加宏开关.md ├── Android从驱动到应用(1)添加驱动.md ├── Android从驱动到应用(2)测试freg驱动的功能内置C程程序.md ├── Android从驱动到应用(3)添加HAL层代码.md ├── Android从驱动到应用(4)添加JNI层.md ├── Android从驱动到应用(5)框架层添加硬件访问服务.md ├── App_Standby_Bucket.md ├── Hidden_API.md ├── JobScheduler(1).md ├── JobScheduler_2.md ├── MTK_PowerHal使用指北.md ├── MTK平台GAT抓取log.md ├── PKMS扫描安装包源码分析.md ├── Write_document.md ├── android_classloader.md ├── android_doze_series │ ├── Doze and app standby介绍.md │ └── Doze源码分析(一).md ├── androidstart.md ├── android添加系统服务.md ├── battery-historian │ ├── battary-historian-Mac-Docker.md │ └── res │ │ ├── battery-chrome.jpg │ │ ├── docker-image.jpg │ │ ├── docker-run.jpg │ │ └── docker-search.jpg ├── init进程启动源码分析.md ├── rootcheck源码分析.docx ├── zygote启动.md ├── 常用命令.md ├── 广播发送.docx ├── 广播接受者注册.docx └── 设置默认launcher.md ├── book ├── Linux设备驱动开发详解_宋宝华.pdf └── VNDK编译及HIDL.docx ├── res ├── FCM.png ├── Hidden_API_02.png ├── PMS_addshareuserid.jpg ├── android-boot.jpg ├── app_standby_bucket_001.png ├── classloader.png ├── doze.png ├── doze_mode_state.png ├── doze_stateMachine.png ├── doze_state_change.png ├── getAppStandbyBucket.png ├── getAppStandbyBucket.xml ├── hidden_API.asta ├── hidden_API.png ├── init_property.png ├── test.jpg ├── wms_relation.jpg ├── write_document_1.png ├── write_document_2.png ├── write_document_3.png ├── write_document_4.png ├── write_document_5.png ├── write_document_6.png ├── write_document_7.png └── zygote_1.png ├── temp ├── Binder_driver.md ├── Bugreport 源码分析.md ├── CFQ │ ├── CFQ代码逻辑分析.md │ └── CFQ代码逻辑分析.md~ ├── F2FS │ ├── assets │ │ ├── filestructure.png │ │ └── layoutF2FS.png │ ├── f2fs-CN.md │ ├── f2fs-EN.txt │ └── f2fs.md ├── Handler机制.md ├── IO栈 │ └── IO栈.md ├── Launcher启动源码分析.md ├── ListNode.java ├── NNAPI.pptx ├── PowerUsageSummay详解.md ├── README.md ├── Solution.java ├── Treble_VNDK.md ├── UsageStatsService.md ├── WindowManagerService启动.md ├── android 抓取log.md ├── android_input系统(一).md ├── app-debug-2.apk ├── app-debug.apk ├── broadcast │ ├── broadcast.md │ └── broadcast.md~ ├── class │ ├── _images │ │ ├── ASCII.jpg │ │ ├── access_flag.png │ │ ├── access_flag_2.png │ │ ├── class_detail.jpg │ │ ├── class_file.png │ │ ├── cp_struct.png │ │ └── methon_info.png │ └── class.md ├── decode_vdex │ ├── GUI_download.png │ ├── dex2jar_download.png │ ├── dex_version.png │ ├── gui_open_file.png │ ├── vdex.md │ └── vdex.png ├── dex │ ├── HelloWorld.class │ ├── HelloWorld.dex │ ├── HelloWorld.java │ ├── data_type.jpg │ ├── dex&class.jpg │ ├── dex.md │ ├── dex_header.png │ ├── dex_product.png │ ├── dexdump │ ├── dexdump_ubuntu │ └── dx ├── linux input subsystem.md ├── powerHal.drawio ├── settings.md ├── systrace │ ├── res │ │ ├── Chrome_open_systrace.png │ │ ├── Chrome_systrace.png │ │ ├── DDMS_systrace_01.png │ │ ├── DDMS_systrace_02.png │ │ ├── DDMS_systrace_03.png │ │ ├── Setting_systrace_01.png │ │ └── Setting_systrace_02.png │ └── systrace 的几种获取方式.md ├── tct-block.md ├── tctlogs.md ├── watchdog.md ├── 培训.png ├── 未命名绘图.drawio └── 转屏算法.md └── tools └── 反编译odex工具箱及文档.zip /English/five_base_sentence.md: -------------------------------------------------------------------------------- 1 | # 五种基本句型 2 | 3 | 英语按句型结构分为: 4 | 5 | - 简单句 6 | - 并行句 7 | - 复合句 8 | 9 | 简单句分为: 10 | 11 | - 主 + 谓 12 | - 主 + 谓 + 宾 13 | - 主 + 谓 + 宾 + 宾补 14 | - 主 + 谓 + 间宾 + 直宾 15 | - 主 + 系 + 表 16 | 17 | 18 | ## 1. 主 + 谓 19 | 20 | 谁,怎么了 谁 --> 主语 怎么了 --> 谓语 21 | 谁 --> 人/物/某种行为 22 | 怎么了 --> vi. 不及物动词 23 | 24 | ``` 25 | I dance. 26 | I sleep. 27 | I know. 28 | I see. 29 | ``` 30 | 31 | 主 + 谓 + 其他: 32 | ``` 33 | She learns English. 34 | 35 | She learns English hard. 36 | 37 | She learns English hard at school. 38 | 39 | She learns English hard at school on weekends. 40 | 41 | She learns English hard for the test at school onweekends. 42 | 43 | She learns English hard for the test with her classmates at school on weekends. 44 | ``` 45 | 46 | “其他”部分虽然不是句子的核心,但可以把句子描述得更加充分,更加生动准确.这些部分经常在句子中作状语. 47 | 常作状语的有:副词、介词词组、原因、地点和时间. 48 | 49 | ## 2. 主 + 谓 + 宾 50 | 51 | 谁对谁怎么了 52 | 53 | ``` 54 | I hate you. 55 | I love you. 56 | ``` 57 | 58 | ## 3. 主 + 谓 + 间宾 + 直宾 59 | 60 | 谓语后面跟了两个宾语,直接宾语,就是谓语动词直接作用的对象,而间接宾语,是指谓语需要先借助于一个间接的对象,再把动作传递到直接宾语身上. 61 | 双宾动词: award, buy, give, leave, lend, offer, pay, show, teach, tell,bring, do, make, pass, sell, send, sing, write,answer, deny, envy, refuse, save, spare 62 | 63 | ``` 64 | I give him a book. 65 | ``` 66 | 67 | ## 4. 主 + 谓 + 宾 + 宾补 68 | 69 | 宾补就是宾语补足语,用来补充说明宾语的,说明宾语怎么了. 该句型中,宾补可能不是一个单词,而是一个短语. 70 | 71 | ``` 72 | I want you to go with me. 73 | ``` 74 | 75 | ## 5. 主 + 系 + 表 76 | 77 | 系动词: 78 | 79 | - be动词 80 | 81 | - get become turn go grow 82 | 83 | - look sound smell taste feel 84 | 85 | - keep stay seem 86 | ``` 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ML_doc/deep learning website.txt: -------------------------------------------------------------------------------- 1 | PCA: 2 | http://www.cnblogs.com/fuleying/p/4458439.html 3 | 梯度下降法: 4 | https://www.cnblogs.com/pinard/p/5970503.html 5 | 矩阵求导: 6 | http://blog.csdn.net/u010976453/article/details/54381248 7 | 激活函数: 8 | https://www.cnblogs.com/steven-yang/p/6357775.html 9 | https://www.zhihu.com/question/67366051 10 | 激活函数在神经网络里面的作用: 11 | https://zhuanlan.zhihu.com/p/25279356 12 | 梯度消失与梯度爆炸 13 | http://blog.csdn.net/weixin_37933986/article/details/69255863 14 | 仿射变换: 15 | https://www.zhihu.com/question/20666664 16 | tensorflow CNN优质blog 所有层+概述+典型网络+code: 17 | http://blog.csdn.net/cxmscb/article/details/71023576 18 | 为什么ReLu: 19 | https://www.cnblogs.com/yangmang/p/7477802.html 20 | Batch Normalization: 21 | https://www.zhihu.com/question/38102762 22 | -------------------------------------------------------------------------------- /ML_doc/深度学习概念.md: -------------------------------------------------------------------------------- 1 | **argmax** 2 | argmax是一种函数,函数y=f(x),x0= argmax(f(x)) 的意思就是参数x0满足f(x0)为f(x)的最大值;换句话说就是argmax(f(x))是使得f(x)取得最大值所对应的变量x。 arg即argument,此处意为“自变量”。 3 | 4 | **one hot** 5 | 在分类问题的标签中,使用one hot形式进行标记 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # document 2 | 技术文档总结 3 | 4 | android_doc --- 关于android的技术文档 5 | ML_doc --- 关于机器学习的文档 6 | 7 | temp --- 正在书写的技术文档(未完成的) 8 | -------------------------------------------------------------------------------- /android_doc/ANR分析笔记.md: -------------------------------------------------------------------------------- 1 | 分析的第一个ANR问题,trace: 2 | ``` 3 | "Binder:876_B" prio=5 tid=100 Runnable 4 | | group="main" sCount=0 dsCount=0 obj=0x13d36f70 self=0x97c62b00 5 | | sysTid=1958 nice=0 cgrp=default sched=0/0 handle=0x94213920 6 | | state=R schedstat=( 50485139390 8705967032 26706 ) utm=4415 stm=633 core=2 HZ=100 7 | | stack=0x94117000-0x94119000 stackSize=1014KB 8 | | held mutexes= "mutator lock"(shared held) 9 | native: #00 pc 0034f0ad /system/lib/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiP12BacktraceMapPKcPNS_9ArtMethodEPv+128) 10 | native: #01 pc 0032f96d /system/lib/libart.so (_ZNK3art6Thread9DumpStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEEbP12BacktraceMap+632) 11 | native: #02 pc 003418cd /system/lib/libart.so (_ZN3art14DumpCheckpoint3RunEPNS_6ThreadE+620) 12 | native: #03 pc 003304fb /system/lib/libart.so (_ZN3art6Thread21RunCheckpointFunctionEv+146) 13 | native: #04 pc 003f24c1 /system/lib/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+52) 14 | native: #05 pc 003f27e1 /system/lib/libart.so (_ZN3art25JniMethodEndWithReferenceEP8_jobjectjPNS_6ThreadE+12) 15 | native: #06 pc 00015d7f /system/framework/arm/boot.oat (Java_java_lang_Throwable_nativeFillInStackTrace__+82) 16 | at java.lang.Throwable.nativeFillInStackTrace!(Native method) 17 | at java.lang.Throwable.fillInStackTrace(Throwable.java:774) 18 | - locked <0x09c5860b> (a java.lang.NumberFormatException) 19 | at java.lang.Throwable.(Throwable.java:257) 20 | at java.lang.Exception.(Exception.java:66) 21 | at java.lang.RuntimeException.(RuntimeException.java:62) 22 | at java.lang.IllegalArgumentException.(IllegalArgumentException.java:53) 23 | at java.lang.NumberFormatException.(NumberFormatException.java:55) 24 | at java.lang.Integer.parseInt(Integer.java:483) 25 | at java.lang.Integer.parseInt(Integer.java:556) 26 | at com.android.internal.util.XmlUtils.readIntAttribute(XmlUtils.java:1526) 27 | at android.content.res.Configuration.readXmlAttrs(Configuration.java:1948) 28 | at com.android.server.usage.UsageStatsXmlV1.loadEvent(UsageStatsXmlV1.java:111) 29 | at com.android.server.usage.UsageStatsXmlV1.read(UsageStatsXmlV1.java:214) 30 | at com.android.server.usage.UsageStatsXml.read(UsageStatsXml.java:99) 31 | at com.android.server.usage.UsageStatsXml.read(UsageStatsXml.java:63) 32 | at com.android.server.usage.UsageStatsDatabase.queryUsageStats(UsageStatsDatabase.java:449) 33 | - locked <0x0178af01> (a java.lang.Object) 34 | at com.android.server.usage.UserUsageStatsService.queryStats(UserUsageStatsService.java:275)//UserUsageStatsService用于搜集用户的使用app的个人信息 35 | at com.android.server.usage.UserUsageStatsService.queryUsageStats(UserUsageStatsService.java:302) 36 | at com.android.server.usage.UsageStatsService.queryUsageStats(UsageStatsService.java:722) 37 | - locked <0x017b01a6> (a java.lang.Object)//没有释放该锁导致别的线程ANR 38 | at com.android.server.usage.UsageStatsService$BinderService.queryUsageStats(UsageStatsService.java:1270) 39 | at android.app.usage.IUsageStatsManager$Stub.onTransact(IUsageStatsManager.java:61) 40 | at android.os.Binder.execTransact(Binder.java:570) 41 | ``` 42 | 是一个native方法导致的线程超时,而没有释放锁<0x017b01a6>: 43 | ``` 44 | at java.lang.Throwable.nativeFillInStackTrace!(Native method) 45 | ``` 46 | 通过addr2line工具分析; 47 | ``` 48 | native: #00 pc 0034f0ad /system/lib/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiP12BacktraceMapPKcPNS_9ArtMethodEPv+128) 49 | native: #01 pc 0032f96d /system/lib/libart.so (_ZNK3art6Thread9DumpStackERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEEbP12BacktraceMap+632) 50 | native: #02 pc 003418cd /system/lib/libart.so (_ZN3art14DumpCheckpoint3RunEPNS_6ThreadE+620) 51 | native: #03 pc 003304fb /system/lib/libart.so (_ZN3art6Thread21RunCheckpointFunctionEv+146) 52 | native: #04 pc 003f24c1 /system/lib/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+52) 53 | native: #05 pc 003f27e1 /system/lib/libart.so (_ZN3art25JniMethodEndWithReferenceEP8_jobjectjPNS_6ThreadE+12) 54 | native: #06 pc 00015d7f /system/framework/arm/boot.oat (Java_java_lang_Throwable_nativeFillInStackTrace__+82) 55 | ``` 56 | 找到`libart.so`的位置: 57 | ```shell 58 | qli3@tabletbuild13:~/code/A3AXL3G/out/target$ find -name "libart.so" 59 | ./product/a3a63g_gmo/obj/lib/libart.so 60 | ./product/a3a63g_gmo/obj/SHARED_LIBRARIES/libart_intermediates/PACKED/libart.so 61 | ./product/a3a63g_gmo/obj/SHARED_LIBRARIES/libart_intermediates/LINKED/libart.so 62 | ./product/a3a63g_gmo/symbols/system/fake-libs/libart.so 63 | ./product/a3a63g_gmo/symbols/system/lib/libart.so 64 | ./product/a3a63g_gmo/system/lib/libart.so 65 | ``` 66 | 选取symbols下面的对应的: 67 | ``` 68 | ./product/a3a63g_gmo/symbols/system/lib/libart.so 69 | ``` 70 | 使用命令解析; 71 | ```shell 72 | qli3@tabletbuild13:~/code/A3AXL3G/out/target$ addr2line -e ./product/a3a63g_gmo/symbols/system/lib/libart.so 003f27e1 73 | /proc/self/cwd/art/runtime/entrypoints/quick/quick_jni_entrypoints.cc:106 74 | ``` 75 | 找到对应的代码位置quick_jni_entrypoints.cc: 76 | ``` 77 | extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, 78 | Thread* self) { 79 | GoToRunnable(self); 80 | return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); 81 | } 82 | ``` 83 | 接着分析` native: #04 pc 003f24c1 /system/lib/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+52)`: 84 | ``` 85 | qli3@tabletbuild13:~/code/A3AXL3G$ addr2line -e ./out/target/product/a3a63g_gmo/symbols/system/lib/libart.so 003f24c1 86 | /proc/self/cwd/art/runtime/thread-inl.h:68 87 | ``` 88 | 89 | 90 | 91 | *** 92 | 如果没有addr2line工具,在android源码中可以找使用`find -name "addr2line"`: 93 | ```shell 94 | ./prebuilts/go/darwin-x86/src/cmd/addr2line 95 | ./prebuilts/go/darwin-x86/pkg/tool/darwin_amd64/addr2line 96 | ./prebuilts/go/linux-x86/src/cmd/addr2line 97 | ./prebuilts/go/linux-x86/pkg/tool/linux_amd64/addr2line 98 | ./prebuilts/tools/gcc-sdk/addr2line 99 | ``` 100 | 任意选取其中一个即可. 101 | *** 102 | -------------------------------------------------------------------------------- /android_doc/Android.bp添加宏开关.md: -------------------------------------------------------------------------------- 1 | # Android.bp 添加宏开关 2 | 3 | 平台: android 8.1 + mt6739 4 | 5 | 作者: 李强 日期: 2018-04-18 6 | 7 | 以前在android系统控制编译的Android.mk不是纯文本形式,里面还有流控制,而Android.bp是类似JSON的纯文本形式. 8 | 对于Android.mk里面流控制部分,在Android.bp里要借助使用go语言文件去进行控制. 9 | 10 | 这里的添加宏开关两种情况: 11 | 12 | - 无流控制的宏开关添加 13 | - 有流控制的宏开关添加 14 | 15 | ## 1.无流控制的宏开关添加Demo 16 | 17 | ### 1.1 在已有的Android.bp中添加宏 18 | 首先找要添加的Android.bp文件中是否有`cppflags`或者'cflags',基本上都是有的,例如: 19 | ``` 20 | cc_defaults { 21 | name: "fs_mgr_defaults", 22 | defaults: ["BBB"],// new add 23 | sanitize: { 24 | misc_undefined: ["integer"], 25 | }, 26 | local_include_dirs: ["include/"], 27 | cppflags: ["-Werror", "-DMTK_FSTAB_FLAGS"], 28 | } 29 | ``` 30 | 例如要添加的宏: 31 | ``` 32 | LOCAL_CFLAGS += -DTEST1 33 | LOCAL_CFLAGS += -DTEST2=1 34 | ``` 35 | 将上面的宏补在原有的'cc_defaults'里面的'cppflags'后面: 36 | ``` 37 | cc_defaults { 38 | name: "fs_mgr_defaults", 39 | sanitize: { 40 | misc_undefined: ["integer"], 41 | }, 42 | local_include_dirs: ["include/"], 43 | cppflags: ["-Werror", 44 | "-DMTK_FSTAB_FLAGS", 45 | "-DTEST1", 46 | "-DTEST2=1"], 47 | } 48 | ``` 49 | 50 | ### 1.2 androidmk命令 51 | 如果要转化的Android.mk内容没有流控制,可以使用Androidmk命令直接转换. 52 | 该命令在:`out/soong/host/linux-x86/bin/androidmk`,使用方法: 53 | 54 | ``` 55 | androidmk Android.mk > Android.bp 56 | ``` 57 | 如果要转换的`Android.mk`里面没有复杂结构,就可以转换成功,如果报错就可能有复杂结构.需要手动转换. 58 | 59 | ## 2. 有流控制的宏开关添加Demo 60 | 61 | 在Android.mk中添加的宏开关: 62 | ``` 63 | ifeq ($(ENABLE_USER2ENG),true) 64 | LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1 65 | LOCAL_CFLAGS += -DENABLE_USER2ENG=1 66 | endif 67 | ``` 68 | 69 | 如果要将以上的宏开关添加到Android.bp中去要通过使用go语言书写一个新文件: 70 | 71 | 比如我的修改是在`system/core/fs_mgr/Android.bp`,那么要在添加 72 | `system/core/fs_mgr/fs_mgr.go`: 73 | ``` 74 | package fs_mgr 75 | 76 | import ( 77 | "android/soong/android" 78 | "android/soong/cc" 79 | "fmt" 80 | ) 81 | 82 | func init() { 83 | // for DEBUG 84 | fmt.Println("init start") 85 | android.RegisterModuleType("AAA", fs_mgrDefaultsFactory) 86 | } 87 | 88 | func fs_mgrDefaultsFactory() (android.Module) { 89 | module := cc.DefaultsFactory() 90 | android.AddLoadHook(module, fs_mgrDefaults) 91 | return module 92 | } 93 | 94 | func fs_mgrDefaults(ctx android.LoadHookContext) { 95 | type props struct { 96 | Cflags []string 97 | } 98 | p := &props{} 99 | p.Cflags = globalDefaults(ctx) 100 | ctx.AppendProperties(p) 101 | } 102 | 103 | func globalDefaults(ctx android.BaseContext) ([]string) { 104 | var cppflags []string 105 | 106 | fmt.Println("ENABLE_USER2ENG:", 107 | ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG")) 108 | if ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG") { 109 | cppflags = append(cppflags, 110 | "-DALLOW_ADBD_DISABLE_VERITY=1", 111 | "-DENABLE_USER2ENG=1") 112 | } 113 | 114 | return cppflags 115 | } 116 | ``` 117 | 118 | `Android.bp`需要修改的地方: 119 | ``` 120 | /// add start 121 | bootstrap_go_package { 122 | // name and pkgPath need to according to your module 123 | name: "soong-fs_mgr", 124 | pkgPath: "android/soong/fs_mgr", 125 | deps: [ 126 | "blueprint", 127 | "blueprint-pathtools", 128 | "soong", 129 | "soong-android", 130 | "soong-cc", 131 | "soong-genrule", 132 | ], 133 | srcs: [ 134 | // include new add .go file 135 | "fs_mgr.go", 136 | ], 137 | pluginFor: ["soong_build"], 138 | } 139 | 140 | // AAA is a module 141 | AAA { 142 | name: "BBB", 143 | } 144 | /// add end 145 | 146 | cc_defaults { 147 | name: "fs_mgr_defaults", 148 | defaults: ["BBB"],// new add 149 | sanitize: { 150 | misc_undefined: ["integer"], 151 | }, 152 | local_include_dirs: ["include/"], 153 | cppflags: ["-Werror", "-DMTK_FSTAB_FLAGS"], 154 | } 155 | ``` 156 | 参照该例子修改时注意`AAA`,`BBB`的对应关系即可. 157 | 158 | ## 3. 一些相关的经验 159 | 160 | ### 3.1 161 | 在go文件中使用`fmt.Println`添加打印信息,可以调试go代码有没有按照正确的.使用方式参考上面的例子. 162 | 这些打印信息会在用`mmm`或者`make`命令编译是打印在屏幕上. 163 | 164 | ### 3.2 165 | 如果添加了*.go文件,可以在使用到宏的地方加入编译会报错的代码,例如上面的例子: 166 | ``` 167 | #ifdef ALLOW_ADBD_DISABLE_VERITY 168 | if (verity.disabled) { 169 | retval = FS_MGR_SETUP_VERITY_DISABLED; 170 | LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG"; 171 | goto out; 172 | } 173 | #endif 174 | ``` 175 | 改为: 176 | ``` 177 | #ifdef ALLOW_ADBD_DISABLE_VERITY 178 | 11111111111 179 | if (verity.disabled) { 180 | retval = FS_MGR_SETUP_VERITY_DISABLED; 181 | LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG"; 182 | goto out; 183 | } 184 | #endif 185 | ``` 186 | 这样就可以很快的验证自己添加的flag是否生效. 187 | 188 | ## 4. Android.bp的相关知识 189 | 该部分内容结合上面的例子,主要参考`android/build/soong/README.md` 190 | 191 | ### 4.1 注释 192 | 有两种形式:单行注释`//`和多行注释`/* */` 193 | 194 | ### 4.2 module 195 | 在上文的例子中: 196 | ``` 197 | AAA { 198 | name: "BBB", 199 | } 200 | ``` 201 | `AAA` 必须要在go文件中注册,`name`的值必须要是在所有Android.bp文件中是唯一的(建议按照所在的模块取名字). 202 | 每个module必须要有一个name.在module中的值都是用`name: value`的形式来,比如: 203 | ``` 204 | name: "libfstab", 205 | vendor_available: true, 206 | defaults: ["fs_mgr_defaults"], 207 | srcs: [ 208 | "fs_mgr_fstab.cpp", 209 | "fs_mgr_boot_config.cpp", 210 | "fs_mgr_slotselect.cpp", 211 | ], 212 | export_include_dirs: ["include_fstab"], 213 | header_libs: ["libbase_headers"], 214 | 215 | ``` 216 | 217 | ## 参考: 218 | 1. [Android编译系统中的Android.bp、Blueprint与Soong](http://note.qidong.name/2017/08/android-blueprint/) 219 | 2. [Android 编译系统之Android.bp ](https://blog.csdn.net/drageon_j/article/details/77336817) 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /android_doc/Android从驱动到应用(1)添加驱动.md: -------------------------------------------------------------------------------- 1 | # Android从驱动到应用(1)添加驱动 2 | 3 | 学习知识,光说不练假把式.系列文章按照罗升阳大神的blog进行实践.向罗大神致敬!实现的是在从驱动层,HAL层,系统添加,JNI方法添加,AIDL方法添加,配置SELinux策略. 4 | 由于android版本的差异,部分代码和配置需要修改,kernel和android源码版本不一样才能出错,这样才有试错,学习成长的机会.本文是参看<> 5 | 以及罗大神的blog和其他网友的blog而来. 6 | 7 | 平台: Android N + kernel 3.18 + MTK 8 | ``` 9 | android/kernel-3.18/drivers/ 10 | ----freg 11 | ----freg.c 12 | ----freg.h 13 | ----Kconfig 14 | ----Makefile 15 | ``` 16 | 17 | freg.c 18 | ``` 19 | nclude 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "freg.h" 30 | 31 | static int freg_major = 0; 32 | static int freg_minor = 0; 33 | 34 | static struct class* freg_class = NULL; 35 | static struct fake_reg_dev* freg_dev = NULL; 36 | 37 | static int freg_open(struct inode* inode, struct file* filp); 38 | static int freg_release(struct inode* inode, struct file* filp); 39 | static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos); 40 | static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); 41 | 42 | static struct file_operations freg_fops = { 43 | .owner = THIS_MODULE, 44 | .open = freg_open, 45 | .release = freg_release, 46 | .read = freg_read, 47 | .write = freg_write, 48 | }; 49 | 50 | #define SEQ_printf(m, x...) \ 51 | do { \ 52 | if (m) \ 53 | seq_printf(m, x); \ 54 | else \ 55 | pr_err(x); \ 56 | } while (0) 57 | 58 | static int freg_proc_show(struct seq_file *m, void *v) 59 | { 60 | SEQ_printf(m, "%d\n", freg_dev->val); 61 | return 0; 62 | } 63 | 64 | static int freg_proc_open(struct inode *inode, struct file *file) 65 | { 66 | return single_open(file, freg_proc_show, inode->i_private); 67 | } 68 | 69 | static ssize_t __freg_set_val(struct fake_reg_dev* dev, const char* buf, size_t count){ 70 | int val = 0; 71 | 72 | val = simple_strtol(buf, NULL, 10); 73 | 74 | if(down_interruptible(&(dev->sem))){ 75 | return -ERESTARTSYS; 76 | } 77 | 78 | dev->val = val; 79 | up(&(dev->sem)); 80 | 81 | return count; 82 | } 83 | 84 | /* 85 | static ssize_t freg_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data){ 86 | if(off >0 ){ 87 | *eof = 1; 88 | return 0; 89 | } 90 | 91 | return __freg_get_val(freg_dev, page); 92 | }*/ 93 | 94 | static ssize_t freg_proc_write(struct file *filp, const char *ubuf, size_t cnt, loff_t *data){ 95 | int err = 0; 96 | char* page = NULL; 97 | 98 | if(cnt > PAGE_SIZE){ 99 | printk(KERN_ALERT"The buff is too large: %lu.\n", cnt); 100 | return -EFAULT; 101 | } 102 | 103 | page = (char*) __get_free_page(GFP_KERNEL); 104 | if(!page){ 105 | printk(KERN_ALERT"Failed to alloc page.\n"); 106 | return -ENOMEM; 107 | } 108 | 109 | if(copy_from_user(page, ubuf, cnt)){ 110 | printk(KERN_ALERT"Failed to copy buff from user.\n"); 111 | err = -EFAULT; 112 | goto out; 113 | } 114 | 115 | err = __freg_set_val(freg_dev, page, cnt); 116 | 117 | out: 118 | free_page((unsigned long)page); 119 | return err; 120 | } 121 | 122 | static const struct file_operations freg_proc_fops = { 123 | .open = freg_proc_open, 124 | .write = freg_proc_write, 125 | .read = seq_read, 126 | .llseek = seq_lseek, 127 | .release = single_release, 128 | }; 129 | 130 | 131 | static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf); 132 | static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); 133 | 134 | static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, freg_val_show, freg_val_store); 135 | 136 | static int freg_open(struct inode * inode, struct file * filp){ 137 | struct fake_reg_dev * dev; 138 | 139 | dev = container_of(inode->i_cdev, struct fake_reg_dev, dev); 140 | filp->private_data = dev; 141 | 142 | return 0; 143 | } 144 | 145 | static int freg_release(struct inode* inode, struct file* filp){ 146 | return 0; 147 | } 148 | 149 | static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos){ 150 | ssize_t err = 0; 151 | struct fake_reg_dev* dev = filp->private_data; 152 | 153 | if(down_interruptible(&(dev->sem))){ 154 | return -ERESTARTSYS; 155 | } 156 | 157 | if(count < sizeof(dev->val)){ 158 | goto out; 159 | } 160 | 161 | if(copy_to_user(buf, &(dev->val), sizeof(dev->val))){ 162 | err = -EFAULT; 163 | goto out; 164 | } 165 | 166 | err = sizeof(dev->val); 167 | 168 | out: 169 | up(&(dev->sem)); 170 | return err; 171 | } 172 | 173 | static ssize_t freg_write(struct file * filp, const char __user * buf, size_t count, loff_t * f_pos){ 174 | struct fake_reg_dev* dev = filp->private_data; 175 | ssize_t err = 0; 176 | 177 | if(down_interruptible(&(dev->sem))){ 178 | return -ERESTARTSYS; 179 | } 180 | 181 | if(count != sizeof(dev->val)){ 182 | goto out; 183 | } 184 | 185 | if(copy_from_user(&(dev->val), buf, count)){ 186 | err = -EFAULT; 187 | goto out; 188 | } 189 | 190 | err = sizeof(dev->val); 191 | 192 | out: 193 | up(&(dev->sem)); 194 | return err; 195 | } 196 | 197 | static ssize_t __freg_get_val(struct fake_reg_dev* dev, char* buf){ 198 | int val = 0; 199 | 200 | if(down_interruptible(&dev->sem)){ 201 | return -ERESTARTSYS; 202 | } 203 | 204 | val = dev->val; 205 | up(&(dev->sem)); 206 | 207 | return snprintf(buf, PAGE_SIZE, "%d\n", val); 208 | } 209 | 210 | 211 | static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf){ 212 | struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev); 213 | 214 | return __freg_get_val(hdev, buf); 215 | } 216 | 217 | static ssize_t freg_val_store(struct device*dev, struct device_attribute* attr, const char* buf, size_t count){ 218 | struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev); 219 | 220 | return __freg_set_val(hdev, buf, count); 221 | } 222 | 223 | 224 | static void freg_create_proc(void){ 225 | proc_create(FREG_DEVICE_PROC_NAME, 0644, 0, &freg_proc_fops); 226 | } 227 | 228 | static void freg_remove_proc(void){ 229 | remove_proc_entry(FREG_DEVICE_PROC_NAME, NULL); 230 | } 231 | 232 | static int __freg_setup_dev(struct fake_reg_dev* dev){ 233 | int err; 234 | dev_t devno = MKDEV(freg_major, freg_minor); 235 | 236 | memset(dev, 0, sizeof(struct fake_reg_dev)); 237 | 238 | cdev_init(&(dev->dev), &freg_fops); 239 | dev->dev.owner = THIS_MODULE; 240 | dev->dev.ops = &freg_fops; 241 | 242 | err = cdev_add(&(dev->dev), devno, 1); 243 | if(err){ 244 | return err; 245 | } 246 | 247 | //init_MUTEX(&(dev->sem)); 248 | sema_init(&(dev->sem), 1); 249 | dev->val = 0; 250 | 251 | return 0; 252 | } 253 | 254 | static int __init freg_init(void){ 255 | int err = -1; 256 | dev_t dev = 0; 257 | struct device* temp = NULL; 258 | 259 | printk(KERN_ALERT"Initializing freg device.\n"); 260 | 261 | err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME); 262 | if(err < 0){ 263 | printk(KERN_ALERT"Failed to alloc char dev region.\n"); 264 | goto fail; 265 | } 266 | 267 | freg_major = MAJOR(dev); 268 | freg_minor = MINOR(dev); 269 | 270 | freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL); 271 | if(!freg_dev){ 272 | err = -ENOMEM; 273 | printk(KERN_ALERT"Failed to alloc freg device.\n"); 274 | goto unregister; 275 | } 276 | 277 | err = __freg_setup_dev(freg_dev); 278 | if(err){ 279 | printk(KERN_ALERT"Failed to setup freg device: %d.\n", err); 280 | goto cleanup; 281 | } 282 | 283 | freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME); 284 | if(IS_ERR(freg_class)){ 285 | err = PTR_ERR(freg_class); 286 | printk(KERN_ALERT"Failed to create freg device class.\n"); 287 | goto destroy_cdev; 288 | } 289 | 290 | temp = device_create(freg_class, NULL, dev, NULL, "%s", FREG_DEVICE_FILE_NAME); 291 | if(IS_ERR(temp)){ 292 | err = PTR_ERR(temp); 293 | printk(KERN_ALERT"Failed to create freg device.\n"); 294 | goto destroy_class; 295 | } 296 | 297 | err = device_create_file(temp, &dev_attr_val); 298 | if(err < 0){ 299 | printk(KERN_ALERT"Failed to create attribute val of freg device.\n"); 300 | goto destroy_device; 301 | } 302 | 303 | dev_set_drvdata(temp, freg_dev); 304 | 305 | freg_create_proc(); 306 | 307 | printk(KERN_ALERT"Succedded to initialize freg device.\n"); 308 | 309 | return 0; 310 | 311 | destroy_device: 312 | device_destroy(freg_class, dev); 313 | destroy_class: 314 | class_destroy(freg_class); 315 | destroy_cdev: 316 | cdev_del(&(freg_dev->dev)); 317 | cleanup: 318 | kfree(freg_dev); 319 | unregister: 320 | unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1); 321 | fail: 322 | return err; 323 | } 324 | 325 | static void __exit freg_exit(void){ 326 | dev_t devno = MKDEV(freg_major, freg_minor); 327 | 328 | printk(KERN_ALERT"Destory freg device.\n"); 329 | 330 | freg_remove_proc(); 331 | 332 | if(freg_class){ 333 | device_destroy(freg_class, MKDEV(freg_major, freg_minor)); 334 | class_destroy(freg_class); 335 | } 336 | 337 | if(freg_dev){ 338 | cdev_del(&(freg_dev->dev)); 339 | kfree(freg_dev); 340 | } 341 | 342 | unregister_chrdev_region(devno, 1); 343 | } 344 | 345 | MODULE_LICENSE("GPL"); 346 | MODULE_DESCRIPTION("Fake Register Driver"); 347 | 348 | module_init(freg_init); 349 | module_exit(freg_exit); 350 | ``` 351 | 在kernel添加一个寄存器的驱动程序,具体代码的含义可以参考罗升阳的blog. 352 | 353 | freg.h 354 | ``` 355 | #ifndef _FAKE_REG_H_ 356 | #define _FAKE_REG_H_ 357 | 358 | #include 359 | #include 360 | 361 | #define FREG_DEVICE_NODE_NAME "freg" 362 | #define FREG_DEVICE_FILE_NAME "freg" 363 | #define FREG_DEVICE_PROC_NAME "freg" 364 | #define FREG_DEVICE_CLASS_NAME "freg" 365 | 366 | struct fake_reg_dev { 367 | int val; 368 | struct semaphore sem; 369 | struct cdev dev; 370 | }; 371 | 372 | #endif 373 | ``` 374 | 375 | Kconfig 376 | ``` 377 | config FREG 378 | tristate "Fake Register Driver" 379 | default y 380 | help 381 | This is the freg driver for android system. 382 | ``` 383 | 这里设置为`default y`是让内核默认是编译的,若果设置成n就是默认不编译.按照罗大神的单编kernel没有成功,我有机器,没有使用模拟器 384 | 385 | Makefile 386 | ``` 387 | obj-$(CONFIG_FREG) += freg.o 388 | ``` 389 | 修改`android/kernel-3.18/drivers`下面的两个文件Kconfig和Makefile 390 | 在Kconfig文件 391 | ``` 392 | menu "Device Drivers" 393 | ``` 394 | 下添加: 395 | ``` 396 | source "drivers/freg/Kconfig" 397 | ``` 398 | 然后在android目录下进行make编译.如果在kernel-3.18目录下make过可能会导致整编不过,此时需要在把kernel-3.18下的目录下的.config文件删除. 399 | 编译成功进行刷机,`adb shell`进入机器,就可以看到里面有 400 | ``` 401 | proc/freg 402 | sys/class/freg 403 | dev/freg 404 | ``` 405 | dev查看需要root权限,可以先把机器root.dev目录下的访问需要权限,实现在添加HAL以及add到system_server时也会遇到SELinux的限制.这些在后续的相关文章有介绍. 406 | -------------------------------------------------------------------------------- /android_doc/Android从驱动到应用(2)测试freg驱动的功能内置C程程序.md: -------------------------------------------------------------------------------- 1 | # Android从驱动到应用(2)测试freg驱动的功能内置C程程序 2 | 3 | 在上一节驱动程序已经添加,可是还不知道该驱动程序是否可以正常工作,用该节的代码来验证一下. 4 | 5 | ``` 6 | android/external 7 | ----freg 8 | ----freg.c 9 | ----Android.mk 10 | ``` 11 | 12 | 13 | 在'android/external'下 14 | ``` 15 | mkdir freg 16 | ``` 17 | freg.c 18 | ``` 19 | #include 20 | #include 21 | #include 22 | 23 | #define FREG_DEVICE_NAME "/dev/freg" 24 | 25 | int main(int argc, char** argv) 26 | { 27 | int fd = -1; 28 | int val = 0; 29 | 30 | fd = open(FREG_DEVICE_NAME, O_RDWR); 31 | if(fd == -1) 32 | { 33 | printf("Failed to open device %s.\n", FREG_DEVICE_NAME); 34 | return -1; 35 | } 36 | 37 | printf("Read original value:\n"); 38 | read(fd, &val, sizeof(val)); 39 | printf("%d.\n\n", val); 40 | 41 | val = 5; 42 | printf("Write value %d to %s.\n\n", val, FREG_DEVICE_NAME); 43 | write(fd, &val, sizeof(val)); 44 | 45 | 46 | printf("Read the value again:\n"); 47 | read(fd, &val, sizeof(val)); 48 | printf("%d.\n\n", val); 49 | 50 | close(fd); 51 | 52 | return 0; 53 | } 54 | ``` 55 | freg.c是代码的逻辑实现,先读取'/dev/freg'里面的值,在将写入新值5,再读取出来 56 | Android.mk 57 | ``` 58 | LOCAL_PATH := $(call my-dir) 59 | include $(CLEAR_VARS) 60 | LOCAL_MODULE_TAGS := optional 61 | LOCAL_MODULE := freg 62 | LOCAL_SRC_FILES := $(call all-subdir-c-files) 63 | include $(BUILD_EXECUTABLE) 64 | ``` 65 | Android.mk里该bin文件命名为freg 66 | 67 | 单编: 68 | ``` 69 | mmm ./external/freg 70 | ``` 71 | 打包system image: 72 | ``` 73 | make snod 74 | ``` 75 | 刷入机器,`adb shell`进入机器,在`system/bin/`会出现名称叫freg的bin文件.在`system/bin/`里执行命令: 76 | ``` 77 | ./freg 78 | ``` 79 | 执行我们写的freg程序,会输出: 80 | ``` 81 | Read the original value: 82 | 83 | 0. 84 | 85 | Write value 5 to /dev/freg. 86 | 87 | Read the value again: 88 | 89 | 5. 90 | ``` 91 | 我们进入`proc/`,`cat freg`,发现值为5.说明修改成功.到此说明驱动的添加没有问题. 92 | -------------------------------------------------------------------------------- /android_doc/Android从驱动到应用(3)添加HAL层代码.md: -------------------------------------------------------------------------------- 1 | # Android从驱动到应用(3)添加HAL层代码 2 | 3 | 在前面,我们添加里成功了驱动层的代码,下来接着添加HAL层的代码.让我们的用户空间的代码也能使用到添加的新的虚拟寄存器设备. 4 | 5 | ``` 6 | hardware 7 | ----libhardware/include/hardware/freg.h 8 | ----libhardware/modules/freg 9 | |---freg.cpp 10 | |---Android.mk 11 | ``` 12 | freg.h: 13 | ``` 14 | #ifndef ANDROID_FREG_INTERFACE_H 15 | #define ANDROID_FREG_INTERFACE_H 16 | 17 | #include 18 | 19 | __BEGIN_DECLS 20 | 21 | /** 22 | * The id of this module 23 | */ 24 | #define FREG_HARDWARE_MODULE_ID "freg" 25 | 26 | /** 27 | * The id of this device 28 | */ 29 | #define FREG_HARDWARE_DEVICE_ID "freg" 30 | 31 | struct freg_module_t { 32 | struct hw_module_t common; 33 | }; 34 | 35 | struct freg_device_t { 36 | struct hw_device_t common; 37 | int fd; 38 | int (*set_val)(struct freg_device_t* dev, int val); 39 | int (*get_val)(struct freg_device_t* dev, int* val); 40 | }; 41 | 42 | __END_DECLS 43 | 44 | #endif 45 | ``` 46 | freg.h按照android对HAL层规范的要求,分别定义模块ID、模块结构体以及硬件接口结构体. 47 | 48 | freg.h: 49 | ``` 50 | #define LOG_TAG "FregHALStub" 51 | 52 | #include 53 | #include 54 | 55 | #include 56 | #include 57 | 58 | #include 59 | #include 60 | 61 | #include 62 | #include 63 | #include 64 | 65 | #include 66 | #include 67 | #include 68 | 69 | 70 | 71 | #define DEVICE_NAME "/dev/freg" 72 | #define MODULE_NAME "Freg" 73 | #define MODULE_AUTHOR "shyluo@gmail.com" 74 | 75 | static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device); 76 | static int freg_device_close(struct hw_device_t* device); 77 | static int freg_set_val(struct freg_device_t* dev, int val); 78 | static int freg_get_val(struct freg_device_t* dev, int* val); 79 | 80 | static struct hw_module_methods_t freg_module_methods = { 81 | open: freg_device_open 82 | }; 83 | 84 | struct freg_module_t HAL_MODULE_INFO_SYM = { 85 | common: { 86 | tag: HARDWARE_MODULE_TAG, 87 | version_major: 1, 88 | version_minor: 0, 89 | id: FREG_HARDWARE_MODULE_ID, 90 | name: MODULE_NAME, 91 | author: MODULE_AUTHOR, 92 | methods: &freg_module_methods, 93 | } 94 | }; 95 | 96 | static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) { 97 | if(!strcmp(id, FREG_HARDWARE_DEVICE_ID)) { 98 | struct freg_device_t* dev; 99 | 100 | dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t)); 101 | if(!dev) { 102 | ALOGE("Failed to alloc space for freg_device_t."); 103 | return -EFAULT; 104 | } 105 | 106 | memset(dev, 0, sizeof(struct freg_device_t)); 107 | 108 | dev->common.tag = HARDWARE_DEVICE_TAG; 109 | dev->common.version = 0; 110 | dev->common.module = (hw_module_t*)module; 111 | dev->common.close = freg_device_close; 112 | dev->set_val = freg_set_val; 113 | dev->get_val = freg_get_val; 114 | 115 | if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { 116 | ALOGE("Failed to open device file /dev/freg -- %s.", strerror(errno)); 117 | free(dev); 118 | return -EFAULT; 119 | } 120 | 121 | *device = &(dev->common); 122 | 123 | ALOGI("Open device file /dev/freg successfully."); 124 | 125 | return 0; 126 | } 127 | 128 | return -EFAULT; 129 | } 130 | 131 | static int freg_device_close(struct hw_device_t* device) { 132 | struct freg_device_t* freg_device = (struct freg_device_t*)device; 133 | if(freg_device) { 134 | close(freg_device->fd); 135 | free(freg_device); 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | static int freg_set_val(struct freg_device_t* dev, int val) { 142 | if(!dev) { 143 | ALOGE("Null dev pointer."); 144 | return -EFAULT; 145 | } 146 | 147 | ALOGI("Set value %d to device file /dev/freg.", val); 148 | write(dev->fd, &val, sizeof(val)); 149 | 150 | return 0; 151 | } 152 | 153 | static int freg_get_val(struct freg_device_t* dev, int* val) { 154 | if(!dev) { 155 | ALOGE("Null dev pointer."); 156 | return -EFAULT; 157 | } 158 | 159 | if(!val) { 160 | ALOGE("Null val pointer."); 161 | return -EFAULT; 162 | } 163 | 164 | read(dev->fd, val, sizeof(*val)); 165 | 166 | ALOGI("Get value %d from device file /dev/freg.", *val); 167 | 168 | return 0; 169 | } 170 | ``` 171 | freg.cpp相对罗大神blog的修改是include文件,由于free函数和malloc方法的头文件有变化,还有log系统,LOGE->ALOGE,LOGI->ALOGI.这两个改动是依据代码编译报错修改而来. 172 | 173 | Android.mk: 174 | ``` 175 | LOCAL_PATH := $(call my-dir) 176 | include $(CLEAR_VARS) 177 | LOCAL_MODULE_TAGS := optional 178 | LOCAL_PRELINK_MODULE := false 179 | LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw 180 | LOCAL_SHARED_LIBRARIES := liblog 181 | LOCAL_SRC_FILES := freg.cpp 182 | LOCAL_MODULE := freg.default 183 | include $(BUILD_SHARED_LIBRARY) 184 | ``` 185 | 编译该模块: 186 | ``` 187 | mmm hardware/libhardware/modules/freg 188 | ``` 189 | 这样就会在`out/target/product/generic/system/lib/hw`目录下看到freg.default.so文件. 190 | 打包system image :`make snod` 191 | HAL层书写完毕. 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /android_doc/Android从驱动到应用(4)添加JNI层.md: -------------------------------------------------------------------------------- 1 | # Android从驱动到应用(4)添加JNI层 2 | 3 | 为了使用java代码调用c代码.需要实现对应的JNI方法. 4 | 5 | ``` 6 | frameworks/base/services/core/jni/ 7 | ----com_android_server_FregService.cpp 8 | ----Android.mk 9 | ----onload.cpp 10 | system/core/rootdir/ueventd.rc 11 | ``` 12 | com_android_server_FregService.cpp 13 | ``` 14 | #define LOG_TAG "FregServiceJNI" 15 | 16 | #include "jni.h" 17 | #include "JNIHelp.h" 18 | #include "android_runtime/AndroidRuntime.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | namespace android 28 | { 29 | static void freg_setVal(JNIEnv* env, jobject clazz, jint ptr, jint value) { 30 | freg_device_t* device = (freg_device_t*)ptr; 31 | if(!device) { 32 | ALOGE("Device freg is not open."); 33 | return; 34 | } 35 | 36 | int val = value; 37 | 38 | ALOGI("Set value %d to device freg.", val); 39 | 40 | device->set_val(device, val); 41 | } 42 | 43 | static jint freg_getVal(JNIEnv* env, jobject clazz, jint ptr) { 44 | freg_device_t* device = (freg_device_t*)ptr; 45 | if(!device) { 46 | ALOGE("Device freg is not open."); 47 | return 0; 48 | } 49 | 50 | int val = 0; 51 | 52 | device->get_val(device, &val); 53 | 54 | ALOGI("Get value %d from device freg.", val); 55 | 56 | return val; 57 | } 58 | 59 | static inline int freg_device_open(const hw_module_t* module, struct freg_device_t** device) { 60 | return module->methods->open(module, FREG_HARDWARE_DEVICE_ID, (struct hw_device_t**)device); 61 | } 62 | 63 | static jint freg_init(JNIEnv* env, jclass clazz) { 64 | freg_module_t* module; 65 | freg_device_t* device; 66 | 67 | ALOGI("Initializing HAL stub freg......"); 68 | 69 | if(hw_get_module(FREG_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) { 70 | ALOGI("Device freg found."); 71 | if(freg_device_open(&(module->common), &device) == 0) { 72 | ALOGI("Device freg is open."); 73 | return (jint)device; 74 | } 75 | 76 | ALOGE("Failed to open device freg."); 77 | return 0; 78 | } 79 | 80 | ALOGE("Failed to get HAL stub freg."); 81 | 82 | return 0; 83 | } 84 | 85 | static const JNINativeMethod method_table[] = { 86 | {"init_native", "()I", (void*)freg_init}, 87 | {"setVal_native", "(II)V", (void*)freg_setVal}, 88 | {"getVal_native", "(I)I", (void*)freg_getVal}, 89 | }; 90 | 91 | int register_android_server_FregService(JNIEnv *env) { 92 | return jniRegisterNativeMethods(env, "com/android/server/FregService", method_table, NELEM(method_table)); 93 | } 94 | }; 95 | ``` 96 | Android.mk,在`LOCAL_SRC_FILES +=`里添加 97 | ``` 98 | $(LOCAL_REL_DIR)/com_android_server_FregService.cpp \ 99 | ``` 100 | onload.cpp,在`namespace android`里添加: 101 | ``` 102 | int register_android_server_FregService(JNIEnv* env); 103 | ``` 104 | 在`extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)`中添加: 105 | ``` 106 | register_android_server_FregService(env); 107 | ` 108 | 编译: 109 | ``` 110 | mmm frameworks/base/services/core/jni 111 | ``` 112 | 打包system image: 113 | ``` 114 | make snod 115 | ``` 116 | 添加硬件设备访问权限: 117 | 在system/core/rootdir/ueventd.rc添加: 118 | ``` 119 | /dev/freg 0666 root root 120 | ``` 121 | 这样JNI方法就添加完成了,如果你编译的是eng版本,SELinux是关闭的,那么该JNI方法是可以正常运行的.如果是user版本需要配置SELinux的策略文件才能访问. 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /android_doc/Android从驱动到应用(5)框架层添加硬件访问服务.md: -------------------------------------------------------------------------------- 1 | # Android从驱动到应用(5)框架层添加硬件访问服务 2 | 3 | 本节内容包括添加硬件访问服务,配置selinux权限,启动服务. 4 | 5 | ##1. 添加硬件访问服务 6 | 由于freg服务是在system_server进程中,而访问该服务的app是在自己的进程里,属于两个不同的进程,因此需要用到跨进程通讯Binder,freg服务是一个stub端,要实现AIDL方法. 7 | `frameworks/base/core/java/android/os/IFregService.aidl`: 8 | ``` 9 | package android.os; 10 | 11 | /** @hide */ 12 | interface IFregService { 13 | void setVal(int val); 14 | int getVal(); 15 | } 16 | ``` 17 | 该方法中注意要添加`/** @hide */`,否则编译会报错,提示更新更新API.加了hide相当于不对外公开,就不需要更新API 18 | 在`frameworks/base/Android.mk`的`LOCAL_SRC_FILES`添加 19 | ``` 20 | core/java/android/os/IFregService.aidl \ 21 | ``` 22 | 作用是添加`IFregService.aidl`源文件.需要注意的是在mk文件的书写中,每行的开头要用table键,而不是8个空格!!! 23 | 24 | 添加系统服务代码`frameworks/base/services/core/java/com/android/server/FregService.java`: 25 | ``` 26 | package com.android.server; 27 | 28 | import android.content.Context; 29 | import android.os.IFregService; 30 | import android.util.Slog; 31 | 32 | public class FregService extends IFregService.Stub { 33 | private static final String TAG = "FregService"; 34 | 35 | private int mPtr = 0; 36 | 37 | FregService() { 38 | mPtr = init_native(); 39 | 40 | if(mPtr == 0) { 41 | Slog.e(TAG, "Failed to initialize freg service."); 42 | } 43 | } 44 | 45 | public void setVal(int val) { 46 | if(mPtr == 0) { 47 | Slog.e(TAG, "Freg service is not initialized."); 48 | return; 49 | } 50 | 51 | setVal_native(mPtr, val); 52 | } 53 | 54 | public int getVal() { 55 | if(mPtr == 0) { 56 | Slog.e(TAG, "Freg service is not initialized."); 57 | return 0; 58 | } 59 | 60 | return getVal_native(mPtr); 61 | } 62 | 63 | private static native int init_native(); 64 | private static native void setVal_native(int ptr, int val); 65 | private static native int getVal_native(int ptr); 66 | }; 67 | ``` 68 | 调用native方法,实现对`dev/freg`的读写操作. 69 | 70 | ##2. 配置selinux权限 71 | 在device.te文件中添加: 72 | ``` 73 | type freg_device, dev_type; 74 | ``` 75 | 在domain.te文件中添加 76 | ``` 77 | allow domain freg_device:chr_file rw_file_perms; 78 | ``` 79 | 在file_contexts文件中添加 80 | ``` 81 | /dev/freg u:object_r:freg_device:s0 82 | ``` 83 | 在service.te中文件中添加 84 | ``` 85 | type freg_service, system_api_service, system_server_service, service_manager_type; 86 | ``` 87 | 在service_contexts文件中添加 88 | ``` 89 | freg u:object_r:freg_service:s0 90 | ``` 91 | 在system_server.te文件中添加 92 | ``` 93 | allow system_server freg_device:chr_file rw_file_perms; 94 | ``` 95 | 在untrusted_app.te文件中添加 96 | ``` 97 | allow untrusted_app freg_service:service_manager find; 98 | ``` 99 | 在system_app.te文件中添加 100 | ``` 101 | allow system_app freg_service:service_manager find; 102 | ``` 103 | 参考blog:[在/dev下添加新设备驱动下Selinux相关设置](http://blog.csdn.net/fantasy_wxe/article/details/52013922) 104 | ##3. 启动服务 105 | 在'frameworks/base/services/java/com/android/server/SystemServer.java'中的startOtherServices()添加如下代码: 106 | ``` 107 | try { 108 | Slog.i(TAG, "Freg Service"); 109 | ServiceManager.addService("freg", new FregService()); 110 | } catch (Throwable e) { 111 | Slog.e(TAG, "Failure starting Freg Service", e); 112 | } 113 | ``` 114 | 把new的实例FregService注册到ServiceManager,取名:freg.这个名字在配置selinux权限 115 | -------------------------------------------------------------------------------- /android_doc/JobScheduler(1).md: -------------------------------------------------------------------------------- 1 | # JobScheduler机制(一) 2 | 3 | ** Android P ** 4 | 5 | 在android p 上, google 提出了应用待机分组, 分析其代码,涉及到了JobScheduler, 对其实习想学习一下, 6 | 故有`JobScheduler机制` 系列. 7 | 8 | ## 1 JobScheduler 使用 9 | 为了学习JobScheduler机制,首先要了解JobScheduler的使用,本节就简单的介绍JobScheduler的使用. 10 | 提供一个简单使用的demo : [JobScheduler demo](https://github.com/lqktz/TctApplication) 仅供参考. 11 | 12 | JobScheduler 程序首先要继承`JobService`对象, 并必须实现其中的两个抽象方法 `onStartJob` 和 `onStopJob`: 13 | 14 | ``` 15 | public class MyJobService extends JobService { 16 | private static final String TAG = "MyJobService"; 17 | 18 | /** 19 | * false: 该系统假设任何任务运行不需要很长时间并且到方法返回时已经完成。 20 | * true: 该系统假设任务是需要一些时间并且当任务完成时需要调用jobFinished()告知系统。 21 | */ 22 | @Override 23 | public boolean onStartJob(JobParameters params) { 24 | Log.i(TAG, "Totally and completely working on job " + params.getJobId()); 25 | Log.d(TAG,"onStartJob"); 26 | return true; 27 | } 28 | 29 | /** 30 | * 当收到取消请求时,该方法是系统用来取消挂起的任务的。 31 | * 如果onStartJob()返回false,则系统会假设没有当前运行的任务,故不会调用该方法。 32 | */ 33 | @Override 34 | public boolean onStopJob(JobParameters params) { 35 | Log.i(TAG, "stop job " + params.getJobId()); 36 | return false; 37 | } 38 | 39 | } 40 | ``` 41 | 42 | 定义 Job ,并调用JobScheduler的schedule方法去执行: 43 | 44 | ``` 45 | jobService = new ComponentName(this, MyJobService.class); 46 | Intent service = new Intent(this, MyJobService.class); 47 | startService(service); 48 | 49 | JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); 50 | 51 | JobInfo jobInfo = new JobInfo.Builder(10087, jobService) //任务Id等于123 52 | .setMinimumLatency(12345)// 任务最少延迟时间 53 | .setOverrideDeadline(60000)// 任务deadline,当到期没达到指定条件也会开始执行 54 | .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 网络条件,默认值NETWORK_TYPE_NONE 55 | .setRequiresCharging(true)// 是否充电 56 | .setRequiresDeviceIdle(false)// 设备是否空闲 57 | .setPersisted(true) //设备重启后是否继续执行 需要权限android.permission.RECEIVE_BOOT_COMPLETED 58 | .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) //设置退避/重试策略 59 | .build(); // build 才是真正的执行创建 60 | 61 | Log.d(TAG,"scheduler.schedule"); 62 | scheduler.schedule(jobInfo); 63 | 64 | ``` 65 | 66 | 为了绑定`JobService` , 在AndroidManifest.xml中要添加service,并且定义权限`android.permission.BIND_JOB_SERVICE` 67 | ``` 68 | 71 | ``` 72 | 73 | 这里梳理一下流程: 74 | - 通过getSystemService获取JobSchedulerService的代理端 75 | - `new Intent(this, MyJobService.class)` 创建服务, 启动服务 76 | - 采用builder模式创建JobInfo对象 77 | - 调用`JobScheduler.schedule(JobInfo)` 启动`JobInfo` 78 | 79 | ## 2 查看定义的jobscheduler 80 | 81 | 当app设置的jobscheduler 之后,使用`adb shell dumpsys jobscheduler` 可以查看注册的jobscheduler, 82 | dumpsys 命令不需要使用root命令 : 83 | 84 | ``` 85 | ...... 86 | JOB #u0a174/10087: 86d97f7 com.lq.tct.tctapplication/.MyJobService 87 | u0a174 tag=*job*/com.lq.tct.tctapplication/.MyJobService 88 | Source: uid=u0a174 user=0 pkg=com.lq.tct.tctapplication 89 | JobInfo: 90 | Service: com.lq.tct.tctapplication/.MyJobService 91 | PERSISTED 92 | Requires: charging=true batteryNotLow=false deviceIdle=false 93 | Network type: 2 94 | Minimum latency: +12s345ms 95 | Max execution delay: +1m0s0ms 96 | Backoff: policy=0 initial=+10s0ms 97 | Has early constraint 98 | Has late constraint 99 | Required constraints: CHARGING TIMING_DELAY DEADLINE UNMETERED 100 | Satisfied constraints: CHARGING BATTERY_NOT_LOW APP_NOT_IDLE DEVICE_NOT_DOZING 101 | Unsatisfied constraints: TIMING_DELAY DEADLINE UNMETERED 102 | Tracking: BATTERY CONNECTIVITY TIME 103 | Enqueue time: -1s976ms 104 | Run time: earliest=+10s369ms, latest=+58s24ms 105 | Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true) 106 | ...... 107 | ``` 108 | 109 | 如果能获取root权限, 还可以查看`/data/system/job/jobs.xml` 文件: 110 | 111 | ``` 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | ..... 125 | 126 | ``` 127 | 128 | 两种查看jobscheduler方式,结果形式比一样,但是显示的内容都是相同的,eg : jobid: 10087是我们在前面的代码中定义的. 129 | 对于在定义job的时候的详细的参数,都在dumpsys或者job.xml中查看到.constraints 就是对应的限制条件,可参考: 130 | [Android省电的秘密(2)之adb解读JobScheduler](https://www.jianshu.com/p/10f9a26081be) 131 | 132 | ## 参看Blog 133 | 134 | - [Android省电的秘密(2)之adb解读JobScheduler](https://www.jianshu.com/p/10f9a26081be) 135 | - [【Android P】 JobScheduler服务源码解析(一) —— 如何使用Job](https://blog.csdn.net/u011311586/article/details/83027007) 136 | -------------------------------------------------------------------------------- /android_doc/MTK_PowerHal使用指北.md: -------------------------------------------------------------------------------- 1 | # MTK PowerHal 使用指北 2 | 3 | ## 0 PowerHal的作用 4 | 5 | PowerHal 是一套MTK提供的在用户空间可以调度cpu & gpu资源的接口. 提供java & C++接口. 6 | 本文demo是java接口. 7 | 8 | ## 1 使用demo 9 | 10 | ### 1.1 导包 11 | ``` 12 | import com.mediatek.powerhalmgr.PowerHalMgr; 13 | import com.mediatek.powerhalmgr.PowerHalMgrFactory; 14 | ``` 15 | 16 | ### 1.2 申明变量 17 | 18 | ``` 19 | private PowerHalMgr mPowerOnHalService; 20 | private int mPowerOnHandle = -1; 21 | ``` 22 | 23 | ### 1.3 调用逻辑 24 | 25 | ``` 26 | if (null == mPowerOnHalService) { 27 | mPowerOnHalService = PowerHalMgrFactory.getInstance().makePowerHalMgr(); 28 | } 29 | if (null != mPowerOnHalService && -1 == mPowerOnHandle) { 30 | mPowerOnHandle = mPowerOnHalService.scnReg(); 31 | } 32 | if (mPowerOnHalService != null && mPowerOnHandle != -1) { 33 | //big cluster: 将配置大核心里面的所有core, MTK的设计目前只支持拉一个CLUSTER里面的所有core 34 | mPowerOnHalService.scnConfig(mPowerOnHandle, 35 | PowerHalMgr.CMD_SET_CLUSTER_CPU_CORE_MIN, 0, 4, 0, 0); 36 | //3000000 代表3GHZ,底层会适配,因为没有频率能达到这么高,所以执行结果就是把频率拉倒最高 37 | mPowerOnHalService.scnConfig(mPowerOnHandle, 38 | PowerHalMgr.CMD_SET_CLUSTER_CPU_FREQ_MIN, 0, 3000000, 0, 0); 39 | //little cluster: 40 | mPowerOnHalService.scnConfig(mPowerOnHandle, 41 | PowerHalMgr.CMD_SET_CLUSTER_CPU_CORE_MIN, 1, 4, 0, 0); 42 | mPowerOnHalService.scnConfig(mPowerOnHandle, 43 | PowerHalMgr.CMD_SET_CLUSTER_CPU_FREQ_MIN, 1, 3000000, 0, 0); 44 | boost timeout(ms): 45 | mPowerOnHalService.scnEnable(mPowerOnHandle, 5000); // 5000 是5000ms, 是一个timeout参数. 46 | } 47 | ``` 48 | 拉频率是有一个开始点, 和结束点. 开始点即`public void scnEnable(int handle, int timeout)`, 结束点即`public void scnDisable(int handle)`. 49 | 将需要拉频率的逻辑代码包含在其中就行.timeout是该调度起作用的最长时间. 也可以值设置开始点,不设置结束点.这样就可以在timeout时间到的时候结束. 50 | 51 | ## 2 客制化使用总结 52 | 53 | - 添加客制化的hint & 配置config 54 | ``` 55 | PowerHalMgr mPowerOnHalService = PowerHalMgrFactory.getInstance().makePowerHalMgr(); 56 | int mPowerOnHandle = mPowerOnHalService.scnReg(); 57 | ``` 58 | - 使能Performance Boost 59 | ``` 60 | public void scnEnable(int handle, int timeout) 61 | ``` 62 | - 执行逻辑代码 63 | - 关闭Performance Boost 64 | ``` 65 | public void scnDisable(int handle) 66 | ``` 67 | 68 | ## 3 验证是否生效 69 | 70 | ### 3.1 方法一 直接查看cpu的当前频率 71 | 将timeout时间设置的长一些,在执行到相关逻辑时(机器需要root权限) 72 | 73 | ``` 74 | adb shell cat /sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_cur_freq //查看机器的实时频率 75 | adb shell cat /sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq //如果设置了最低频率,可以使用此方式去查看 76 | ``` 77 | 78 | ### 3.2 方法二 log中查看 79 | 在log中查看是否类似以下log输出: 80 | ``` 81 | 11-24 21:25:17.972 361 374 I libPowerHal: 36: legacy set freq: 1105000 -1 82 | ``` 83 | 84 | ## 参考文献 85 | - ALPS04863324 86 | - CS6765-BD8E-PGD-V1.0EN_MTK_P0_BSP+_PowerHalService_Programming_Guide_MT6765.pdf 87 | -------------------------------------------------------------------------------- /android_doc/MTK平台GAT抓取log.md: -------------------------------------------------------------------------------- 1 | #MTK平台GAT抓取log 2 | 3 | **平台:MTK** 4 | *** 5 | DESCRIPTION: 6 | 如何用GAT抓取log , 比如当MTKLogger出现异常,或SD卡不可用时 7 | 8 | 利用MTK 提供的GAT(官网可以下载)可以进行抓取: 9 | 一. 抓取问题复现的log 10 | 1.连接usb到pc,确认手机usb debugging选项是开启的 11 | 2.打开GAT 12 | 3.设定log存储路径,window->preferences->savelog 13 | 4.点击控制栏 L字样的按钮,开始录制 14 | 5.复现现象 15 | 6.再次点击L字样的按钮,停止录制 16 | 7.到第4步设定的路径上取log 17 | 18 | 二. 抓取开机log 19 | 1.连接usb到pc,确认手机usb debugging选项是开启的 20 | 2.手机关机 21 | 3.打开GAT 22 | 4.设定log存储路径,window->preferences->savelog 23 | 5.点击控制栏 L字样的按钮,开始录制 24 | 6.手机开机,进入待机5分钟以上 25 | 7.再次点击L字样的按钮,停止录制 26 | 8.到第4步设定的路径上取log 27 | *** 28 | 29 | **平台:MTK** 30 | *** 31 | DESCRIPTION: 32 | 33 | 如何抓取开机Log 34 | 一般分析开机失败或者开机过程异常问题,都牵涉到如何抓取开机log的问题,为了顺利抓取开机Log便于分析问题,参照如下抓取有效log: 35 | 1.如果开机过程还没有出现开机动画,就已经异常,直接抓取UART串口log; 36 | 2.如果开机动画已经显示,后面出现异常,可以首先check SD卡是否已经mount成功,如果SD卡mount成功,直接提供Mobilelog; 37 | 否则,可以通过adb logcat抓取log或者通过我司release的GAT Tool抓取log,其中logcat抓取log的command: 38 | `adb logcat -v time -b main -b events -b system>logcat.txt`。 39 | 40 | *** 41 | 42 | **打印CPP方法调用堆栈** 43 | 44 | 一、CPP打印堆栈: 45 | #include 46 | ... 47 | android::CallStack stack; 48 | stack.update(1, gettid()); 49 | stack.log("satcklog", ANDROID_LOG_ERROR, "stackdump:"); 50 | ... 51 | } 52 | 在mk文件中,加入: 53 | LOCAL_SHARED_LIBRARIES += libutils 54 | 55 | 这样设置打印出来的堆栈信息的Log tag为satcklog,信息格式类似于: 56 | 01-01 08:02:30.615 372 2158 E satcklog: stackdump:#00 pc 0002671b /system/lib/libcamera_client.so (_ZN7android16CameraParameters12setVideoSizeEii+70) 57 | 58 | 二、用addr2line工具进行具体的代码行数定位。 59 | 60 | 其中addr2line的使用,以PIXI5-8 TMO举例,在根目录使用如下命令可以定位地址信息对应的代码: 61 | ./prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-addr2line -C -f -e out/target/product/pixi584g/symbols/system/lib/libcamera_client.so 0002671b 62 | 输出为: 63 | android::CameraParameters::setVideoSize(int, int) 64 | /proc/self/cwd/frameworks/av/camera/CameraParameters.cpp:384 (discriminator 1) 65 | 66 | 即可定位具体的代码行数。 67 | *** 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /android_doc/Write_document.md: -------------------------------------------------------------------------------- 1 | # 写文档经验分享 2 | 3 | 作者: 李强 时间: 2018/05/16 4 | 5 | ## 1.概述 6 | 刚开始写文档的时候,我一直被一个问题困扰,看了代码.学了东西,要不要整理成文档,很多方面的文档, 7 | 别人都写过,而且还比自己写的完善,后来想明白了,书写文档分两种: 8 | 9 | - 此类文档别人写过,自己书写是对自己成长的一种记录,这类文档是写给自己看的; 10 | - 别人没有写过,或者自己比别人写的更好,这种文档是可以分享给别人多看的; 11 | 12 | 坚定了写文档的信念之后,问题就会转换成写什么,怎么写的问题,我的理解是: 13 | 14 | - 写什么在于学到了什么新知识,get 到了什么新技能; 15 | - 怎么写的问题,在网上会有非常多的介绍,比如:知乎.选择自己适合的就行; 16 | 17 | 我常用到的软件的分享: 18 | 19 | - git,Github 20 | - vim + bundle + python-vim-instant-markdown 21 | - Astah 22 | - draw.io 23 | - Atom 24 | 25 | ## 2.各种软件的使用 26 | 27 | ### 2.1 git,Github 28 | 29 | 使用git + Github 能够解决以下问题: 30 | 31 | - 文档的实时备份,不怕丢失; 32 | - 公司 + home 协同办公; 33 | - 如果你够牛,有很多人follow,这样可以上关注你的人及时看到你的最新文档; 34 | 35 | ### 2.2 vim + bundle + python-vim-instant-markdown 36 | 37 | 书写文档,首选markdown格式.推荐使用vim + bundle + python-vim-instant-markdown.bundle是插件管理器,vim + bundle也简称为`Vundle`. 38 | 它可以帮助实时查看文档显示情况. 39 | 如果你写的文档是这个样子: 40 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_1.png) 41 | 42 | 在vim里输入命令`:Instantmd`,在默认浏览器里面就可以看到,是这个样子的: 43 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_2.png) 44 | 45 | 当然为了能愉快的书写markdown文档,需要使用半个小时把markdown的基本语法学习一下. 46 | 47 | ### 2.3 Astah 48 | 49 | 作为一个程序员,免不了会画时序图之类的图,Astah就是用来干这个的.Astah是一款日本软件开发者的作品,分Community和Professional两款,Community提供基本功能,是免费的. 50 | 51 | Astah官网; [http://astah.net/](http://astah.net/) 52 | 53 | 软件打开界面是这个样子的: 54 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_5.png) 55 | 可以画出这样子的时序图来: 56 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_6.png) 57 | 58 | ### 2.4 draw.io 59 | 60 | 除了时序图,当然还可能会用到流程图,android界面图,draw.io是google推出的,有网页版,和客户端版本. 61 | 62 | draw.io网址: [https://www.draw.io/](https://www.draw.io/) 63 | 64 | 打开的界面: 65 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_3.png) 66 | 可以画Android的界面: 67 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_4.png) 68 | 69 | ### 2.5 Atom 70 | Atom是github推出的一款软件,据说是21世纪的编辑器,与vim,Emacs比肩.书写文档: 71 | ![](https://raw.githubusercontent.com/lqktz/document/master/res/write_document_7.png) 72 | 73 | Atom有丰富的插件,而且界面比vim也要好看不少,功能也是非常的强大. 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /android_doc/android_classloader.md: -------------------------------------------------------------------------------- 1 | # Android Classloader 2 | 3 | **Android N** 4 | 5 | 最近的工作遇到了运用反射来加载类,需要用到这块的知识,因此有了这篇文章. 6 | 7 | ##1. java类加载器 8 | Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程。 9 | 具体过程参考<<深入理解java虚拟机>>.在加载阶段,java虚拟机需要完成以下3件事: 10 | 11 | - 通过一个类的全限定名来获取定义此类的二进制字节流。 12 | 13 | - 将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构。 14 | 15 | - 在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口,一个类不管new多少实例,只有一个对应的class对象. 16 | 17 | 这个加载过程是使用类加载器完成的. 18 | 19 | 在java里面默认的有三个类加载器:BootStrap,ExtClassLoader,AppClassLoader.类加载器也是需要加载的,BootStrap是用C++书写,直接在java虚拟机的内核里面,用于加载类加载器, 20 | 以及rt.jar包,ExtClassLoader是用来加载在`Java/jre7/lib/ext/`下面的jar包. 21 | 22 | 每个ClassLoader必须有一个父ClassLoader,在装载Class文件时,子ClassLoader会先请求父ClassLoader加载该Class文件,只有当其父ClassLoader找不到该Class文件时, 23 | 子ClassLoader才会继续装载该类,这是一种安全机制。叫类加载器的委托机制. 24 | 25 | 在使用标准Java虚拟机时,我们经常自定义继承自ClassLoader的类加载器。Android中ClassLoader的defineClass方法具体是调用VMClassLoader的defineClass本地静态方法。 26 | 而这个本地方法除了抛出一个“UnsupportedOperationException”之外,什么都没做,甚至连返回值都为空.所以在android中继承ClassLoader定义自己的类加载器是行不通的. 27 | 为了在android中实现动态的加载类,Android从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader. 28 | 29 | - PathClassLoader是android中的默认加载器,只能加载/data/app中的apk,即安装在手机中的apk; 30 | - DexClassLoader可以加载任何路径的apk/dex/jar; 31 | 32 | ##2. android的类加载器 33 | ###2.1 DexClassLoader 34 | 加载器的代码在`libcore/dalvik/src/main/java/dalvik/system/`,DexClassLoader.java: 35 | ``` 36 | package dalvik.system; 37 | 38 | /** 39 | * A class loader that loads classes from {@code .jar} and {@code .apk} files 40 | * containing a {@code classes.dex} entry. This can be used to execute code not 41 | * installed as part of an application. 42 | * 43 | *

This class loader requires an application-private, writable directory to 44 | * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create 45 | * such a directory:

   {@code
 46 |  *   File dexOutputDir = context.getCodeCacheDir();
 47 |  * }
48 | * 49 | *

Do not cache optimized classes on external storage. 50 | * External storage does not provide access controls necessary to protect your 51 | * application from code injection attacks. 52 | */ 53 | public class DexClassLoader extends BaseDexClassLoader { 54 | /** 55 | * Creates a {@code DexClassLoader} that finds interpreted and native 56 | * code. Interpreted classes are found in a set of DEX files contained 57 | * in Jar or APK files. 58 | * 59 | *

The path lists are separated using the character specified by the 60 | * {@code path.separator} system property, which defaults to {@code :}. 61 | * 62 | * @param dexPath the list of jar/apk files containing classes and 63 | * resources, delimited by {@code File.pathSeparator}, which 64 | * defaults to {@code ":"} on Android 65 | * @param optimizedDirectory this parameter is deprecated and has no effect 66 | * @param librarySearchPath the list of directories containing native 67 | * libraries, delimited by {@code File.pathSeparator}; may be 68 | * {@code null} 69 | * @param parent the parent class loader 70 | */ 71 | public DexClassLoader(String dexPath, String optimizedDirectory, 72 | String librarySearchPath, ClassLoader parent) { 73 | super(dexPath, null, librarySearchPath, parent); 74 | } 75 | } 76 | ``` 77 | DexClassLoader构造器,只有dexPath,optimizedDirectory,librarySearchPath,parent: 78 | ###2.2 PathClassLoader 79 | PathClassLoader的源码也在`libcore/dalvik/src/main/java/dalvik/system/`,PathClassLoader.java: 80 | ``` 81 | public class PathClassLoader extends BaseDexClassLoader { 82 | /** 83 | * Creates a {@code PathClassLoader} that operates on a given list of files 84 | * and directories. This method is equivalent to calling 85 | * {@link #PathClassLoader(String, String, ClassLoader)} with a 86 | * {@code null} value for the second argument (see description there). 87 | * 88 | * @param dexPath the list of jar/apk files containing classes and 89 | * resources, delimited by {@code File.pathSeparator}, which 90 | * defaults to {@code ":"} on Android 91 | * @param parent the parent class loader 92 | */ 93 | public PathClassLoader(String dexPath, ClassLoader parent) { 94 | super(dexPath, null, null, parent); 95 | } 96 | 97 | /** 98 | * Creates a {@code PathClassLoader} that operates on two given 99 | * lists of files and directories. The entries of the first list 100 | * should be one of the following: 101 | * 102 | *

    103 | *
  • JAR/ZIP/APK files, possibly containing a "classes.dex" file as 104 | * well as arbitrary resources. 105 | *
  • Raw ".dex" files (not inside a zip file). 106 | *
107 | * 108 | * The entries of the second list should be directories containing 109 | * native library files. 110 | * 111 | * @param dexPath the list of jar/apk files containing classes and 112 | * resources, delimited by {@code File.pathSeparator}, which 113 | * defaults to {@code ":"} on Android 114 | * @param librarySearchPath the list of directories containing native 115 | * libraries, delimited by {@code File.pathSeparator}; may be 116 | * {@code null} 117 | * @param parent the parent class loader 118 | */ 119 | public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { 120 | super(dexPath, null, librarySearchPath, parent); 121 | } 122 | } 123 | ``` 124 | PathClassLoader同样是继承BaseDexClassLoader类,有两个构造方法,最终都是调用BaseDexClassLoader的构造方法进行构造.但是与DexClassLoader相比, 125 | 没有了optimizedDirectory参数,其他都一样.原因是PathClassLoader加载的是android安装的apk的类,这些class的dex有一个固定的目录`/data/dalvik-cache`: 126 | ``` 127 | Android:/data/dalvik-cache # cd arm/ 128 | system@app@ApplicationsProvider@ApplicationsProvider.apk@classes.dex 129 | system@app@ApplicationsProvider@ApplicationsProvider.apk@classes.vdex 130 | system@app@AtciService@AtciService.apk@classes.art 131 | system@app@AtciService@AtciService.apk@classes.dex 132 | system@app@AtciService@AtciService.apk@classes.vdex 133 | system@app@BasicDreams@BasicDreams.apk@classes.art 134 | system@app@BasicDreams@BasicDreams.apk@classes.dex 135 | system@app@BasicDreams@BasicDreams.apk@classes.vdex 136 | system@app@BatteryWarning@BatteryWarning.apk@classes.art 137 | system@app@BatteryWarning@BatteryWarning.apk@classes.dex 138 | system@app@BatteryWarning@BatteryWarning.apk@classes.vdex 139 | system@app@Bluetooth@Bluetooth.apk@classes.art 140 | system@app@Bluetooth@Bluetooth.apk@classes.dex 141 | system@app@Bluetooth@Bluetooth.apk@classes.vdex 142 | system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.art 143 | system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.dex 144 | system@app@BluetoothMidiService@BluetoothMidiService.apk@classes.vdex 145 | system@app@BookmarkProvider@BookmarkProvider.apk@classes.art 146 | system@app@BookmarkProvider@BookmarkProvider.apk@classes.dex 147 | system@app@BookmarkProvider@BookmarkProvider.apk@classes.vdex 148 | system@app@BuiltInPrintService@BuiltInPrintService.apk@classes.art 149 | system@app@BuiltInPrintService@BuiltInPrintService.apk@classes.dex 150 | system@app@BuiltInPrintService@BuiltInPrintService.apk@classes.vdex 151 | ...... 152 | ``` 153 | ###2.3 BaseDexClassLoader 154 | DexClassLoader和PathClassLoader的构造方法都是调用了BaseDexClassLoader,在BaseDexClassLoader.java里面: 155 | ``` 156 | public class BaseDexClassLoader extends ClassLoader{ 157 | ..... 158 | public BaseDexClassLoader(String dexPath, File optimizedDirectory, 159 | String librarySearchPath, ClassLoader parent) { 160 | super(parent); 161 | this.pathList = new DexPathList(this, dexPath, librarySearchPath, null); 162 | 163 | if (reporter != null) { 164 | reporter.report(this.pathList.getDexPaths()); 165 | } 166 | } 167 | ...... 168 | } 169 | ``` 170 | 说明这最后都是从ClassLoader来的,ClassLoader才是祖师爷呀.在BaseDexClassLoader的构造方法里创建了DexPathList,也就干了这么一件事. 171 | BaseDexClassLoader创建时的四个参数的意义: 172 | 173 | - dexPath: 是加载apk/dex/jar的路径,dexPath,当有多个路径则采用:分割; 174 | - optimizedDirectory: 是dex的输出路径(因为加载apk/jar的时候会解压除dex文件,这个路径就是保存dex文件的),优化后的dex文件存在的目录; 175 | - librarySearchPath: 是加载的时候需要用到的lib库; 176 | - parent: 给DexClassLoader指定父加载器; 177 | 178 | ####2.3.1 DexPathList 179 | 接着分析DexPathList代码,DexPathList.java在`libcore/dalvik/src/main/java/dalvik/system`, 180 | ``` 181 | /** 182 | * Constructs an instance. 183 | * 184 | * @param definingContext the context in which any as-yet unresolved 185 | * classes should be defined 186 | * @param dexPath list of dex/resource path elements, separated by 187 | * {@code File.pathSeparator} 188 | * @param librarySearchPath list of native library directory path elements, 189 | * separated by {@code File.pathSeparator} 190 | * @param optimizedDirectory directory where optimized {@code .dex} files 191 | * should be found and written to, or {@code null} to use the default 192 | * system directory for same 193 | */ 194 | public DexPathList(ClassLoader definingContext, String dexPath, 195 | String librarySearchPath, File optimizedDirectory) { 196 | 197 | if (definingContext == null) { 198 | throw new NullPointerException("definingContext == null"); 199 | } 200 | 201 | if (dexPath == null) { 202 | throw new NullPointerException("dexPath == null"); 203 | } 204 | 205 | if (optimizedDirectory != null) { 206 | if (!optimizedDirectory.exists()) { 207 | throw new IllegalArgumentException( 208 | "optimizedDirectory doesn't exist: " 209 | + optimizedDirectory); 210 | } 211 | 212 | if (!(optimizedDirectory.canRead() 213 | && optimizedDirectory.canWrite())) { 214 | throw new IllegalArgumentException( 215 | "optimizedDirectory not readable/writable: " 216 | + optimizedDirectory); 217 | } 218 | } 219 | this.definingContext = definingContext; 220 | 221 | ArrayList suppressedExceptions = new ArrayList(); 222 | // save dexPath for BaseDexClassLoader 223 | // 记录所有的dexFile文件 224 | this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, 225 | suppressedExceptions, definingContext); 226 | 227 | // Native libraries may exist in both the system and 228 | // application library paths, and we use this search order: 229 | // 230 | // 1. This class loader's library path for application libraries (librarySearchPath): 231 | // 1.1. Native library directories 232 | // 1.2. Path to libraries in apk-files 233 | // 2. The VM's library path from the system property for system libraries 234 | // also known as java.library.path 235 | // 236 | // This order was reversed prior to Gingerbread; see http://b/2933456. 237 | // app的native库 238 | this.nativeLibraryDirectories = splitPaths(librarySearchPath, false); 239 | // 系统的native库 240 | this.systemNativeLibraryDirectories = 241 | splitPaths(System.getProperty("java.library.path"), true); 242 | // 把app的native库和system的native库合并,汇总 243 | List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); 244 | allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); 245 | 246 | // 记录所有的native动态库 247 | this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories); 248 | 249 | if (suppressedExceptions.size() > 0) { 250 | this.dexElementsSuppressedExceptions = 251 | suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]); 252 | } else { 253 | dexElementsSuppressedExceptions = null; 254 | } 255 | } 256 | 257 | ``` 258 | 总结的构造的过程中主要干了两件事:记录DexFile(dexElements)和记录native动态库(nativeLibraryPathElements). 259 | 260 | ##2.4 ClassLoader 261 | 所有的类加载器最终都是在调用ClassLoader类,该类是类加载的核心,其他都只是进行封装,打包.ClassLoader.java在`libcore/ojluni/src/main/java/java/lang/ClassLoader.java`, 262 | 下面只是列出了主要代码部分: 263 | ``` 264 | public abstract class ClassLoader { 265 | 266 | static private class SystemClassLoader { 267 | public static ClassLoader loader = ClassLoader.createSystemClassLoader(); 268 | } 269 | 270 | /** 271 | * Encapsulates the set of parallel capable loader types. 272 | */ 273 | private static ClassLoader createSystemClassLoader() { 274 | String classPath = System.getProperty("java.class.path", "."); 275 | String librarySearchPath = System.getProperty("java.library.path", ""); 276 | 277 | // String[] paths = classPath.split(":"); 278 | // URL[] urls = new URL[paths.length]; 279 | // for (int i = 0; i < paths.length; i++) { 280 | // try { 281 | // urls[i] = new URL("file://" + paths[i]); 282 | // } 283 | // catch (Exception ex) { 284 | // ex.printStackTrace(); 285 | // } 286 | // } 287 | // 288 | // return new java.net.URLClassLoader(urls, null); 289 | 290 | // TODO Make this a java.net.URLClassLoader once we have those? 291 | return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance()); 292 | } 293 | 294 | public static ClassLoader getSystemClassLoader() { 295 | // 返回系统默认的加载器,appclassloader 296 | return SystemClassLoader.loader; 297 | } 298 | 299 | // 加载使用双亲委派模式 300 | protected Class loadClass(String name, boolean resolve) 301 | throws ClassNotFoundException 302 | { 303 | // First, check if the class has already been loaded 304 | // 首先,检查请求的类是否已经加载过 305 | Class c = findLoadedClass(name); 306 | if (c == null) { 307 | try { 308 | if (parent != null) {//委派父类加载器加载 309 | c = parent.loadClass(name, false); 310 | } else {//委派启动类加载器加载 311 | c = findBootstrapClassOrNull(name); 312 | } 313 | } catch (ClassNotFoundException e) {//父类加载器无法完成加载请求 314 | // ClassNotFoundException thrown if class not found 315 | // from the non-null parent class loader 316 | } 317 | 318 | if (c == null) {//本身加载器无法完成加载 319 | // If still not found, then invoke findClass in order 320 | // to find the class. 321 | c = findClass(name); 322 | } 323 | } 324 | return c; 325 | } 326 | 327 | /** 328 | * Returns the class with the given binary name if this 329 | * loader has been recorded by the Java virtual machine as an initiating 330 | * loader of a class with that binary name. Otherwise 331 | * null is returned. 332 | * 333 | * @param name 334 | * The binary name of the class 335 | * 336 | * @return The Class object, or null if the class has 337 | * not been loaded 338 | * 339 | * @since 1.1 340 | */ 341 | protected final Class findLoadedClass(String name) { 342 | ClassLoader loader; 343 | if (this == BootClassLoader.getInstance()) 344 | loader = null; 345 | else 346 | loader = this; 347 | return VMClassLoader.findLoadedClass(loader, name); 348 | } 349 | 350 | // 自定义ClassLoader需要重写该方法 351 | protected Class findClass(String name) throws ClassNotFoundException { 352 | throw new ClassNotFoundException(name); 353 | } 354 | ``` 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | -------------------------------------------------------------------------------- /android_doc/android_doze_series/Doze and app standby介绍.md: -------------------------------------------------------------------------------- 1 | # Doze and app standby介绍 2 | 3 | ## 1 简介 4 | 5 | 从android 6.0(API 23) 开始,android为了延长电池的使用时间,引入了两个新的省电特性.当手机没有进行充电时,通过管理app的行为到达省电目的. 6 | 当设备较长时间没有使用的时候,设备会进入Doze模式,此时,屏幕是灭屏的,app的cpu和网络的使用将会延时处理.app standby是将app没有处于前台, 7 | 该app的网络使用将会被限制,此时不需要灭屏. 8 | Doze and app standby 需要 API 23以及更高的版本. 9 | 10 | ## 2 理解Doze 11 | 12 | 在设备不充电,灭屏,较长一段时间用户没有使用移动设备的情况下,设备会进入Doze模式.在Doze模式下,系统会限制APP的网络和cup服务来达到省电的目的. 13 | 并且Doze模式还会延迟app的jobs, syncs, and standard alarms. 14 | 系统会定期退出一段时间让被阻止的APP去完成自己被延时的工作,这个时间片段被叫做maintenance window.在maintenance window期间所有app都可以去完成 15 | 被挂起的jobs, syncs, and standard alarms以及允许APP连接网络. 16 | ![Doze](https://raw.githubusercontent.com/lqktz/document/master/res/doze.png) 17 | 当一个maintenance window结束之后,设备又会进入Doze模式.挂起网络,延迟APP的jobs, syncs, and standard alarms.不同的是maintenance window之间的 18 | 间隔会越来越长.这样的策略是为了省电. 19 | 20 | ### 2.1 Doze模式的限制 21 | 22 | 处于Doze模式,APP会有以下限制 23 | 24 | - 网络访问被暂停 25 | - 系统会忽略wack lock 26 | - 标准的alarm(setExact() 和 setWindow())会被推迟到下一个maintenance window去执行 27 | 如果在设备处于Doze下,需要使用alarm去唤醒,需要使用setAndAllowWhileIdle() / setExactAndAllowWhileIdle(). 28 | - 系统不再进行wifi扫描 29 | - 系统不允许执行 sync adapters 30 | - 系统不允许执行 JobScheduler(也就是限制了jobs) 31 | 32 | ### 2.2 Doze的状态以及变化 33 | 34 | Doze的状态的变化是Doze的核心设计,下面介绍Doze的进入,状态变化,退出 35 | 36 | #### 2.2.1 Doze状态的进入 37 | 38 | Android M 满足以下条件,进入Doze: 39 | 40 | - 用户不操作设备一段时间 (通过动作监测来判断) 41 | - 屏幕关闭 42 | - 设备未连接电源充电 43 | 44 | 在Android N中不需要满足设备是否移动,分为浅度doze(light idle)和深度doze(deep idle) 45 | 46 | | 级别 | 进入条件 |对App的行为限制|退出条件|设备硬件要求 47 | | ---------- | ------------------- |---------------|---------|---------|---------| 48 | | 第一级别限制|浅度IDLE(Light IDLE)|1不能访问网络;
2推迟作业和同步|激活屏幕,设备充电|无| 49 | | 第二级别限制|深度IDLE(Deep IDLE) |1 不能访问网络;
2 wake lock失效;
3 禁止GPS/WIFI 扫描;
4 Alarms推迟;
5 作业,同步推迟。|激活屏幕,设备充电,有移动动作,Alarm 到时|要求具有SMD(Significant motion Dector),一种用于检测设备是否处于静止状态传感器| 50 | 51 | 注:Notifications的到来不会导致退出doze模式 52 | 53 | #### 2.2.2 Doze的五种状态 54 | Doze的核心就是使用了状态的变化,主要是使用控制机器在五种状态下面切换,下面对五种状态进行说明: 55 | 56 | - ACTIVE:手机设备处于激活活动状 57 | - INACTIVE:屏幕关闭进入非活动状态 58 | - IDLE_PENDING:每隔30分钟让App进入等待空闲预备状态 59 | - IDLE:空闲状态 60 | - IDLE_MAINTENANCE:处理挂起任务 61 | 下图是状态转移的图: 62 | ![doze_state_change:](https://raw.githubusercontent.com/lqktz/document/master/res/doze_state_change.png) 63 | 64 | #### 2.2.3 Doze的退出 65 | 退出Doze和进入相对应,只要进入条件的其中一个不满足就会退出Doze,对应如下: 66 | 67 | - 用户唤醒装置移动 68 | - 打开屏幕 69 | - 连接电源 70 | 71 | ### 2.3 在Doze下适配APP 72 | 73 | 由于在Doze模式下,APP的jobs, syncs, and standard alarms以及APP连接网络,会被收到限制,但是APP如果是真的要使用,有两种方式: 74 | 75 | - 使用白名单,在白名单有系统白名单,用户添加的白名单,临时白名单(10s);在白名单之中的APP能够在Doze模式下豁免; 76 | - 使用setAndAllowWhileIdle() / setExactAndAllowWhileIdle(),不过这也是有限制的,每个APP每9分钟只能触发一次,这两个方法的本质是将APP加入临时的白名单, 77 | 来达到在Doze模式下,仍然可以使用alarm下唤醒机器. 78 | - 使用 Firebase Cloud Messaging(FCM),之前是Google Cloud Messaging(GCM),该服务就是google的专用通道,在android中有较高的优先级.APP要执行的操作, 79 | ![FCM](https://raw.githubusercontent.com/lqktz/document/master/res/FCM.png) 80 | 81 | 82 | 在设备Doze模式下的设备,APP只要将要唤醒,更新等操作,从服务器发送到FCM是,由FCM统一发送即可,避免了每个 83 | APP都与设备建立一个长链接用于推送信息.并且FCM在android拥有高的优先级,能够在Doze模式下,不影响其他APP的前提下,唤醒APP,达到省电目的. 84 | 85 | 86 | ## 2 理解APP standby 87 | 88 | ### 2.1 APP standby策略 89 | 90 | 当用户不触摸使用APP,一段时间后,系统会判定进入APP进入空闲状态.当包含以下任意一种情况,App就会退出App Standby状态: 91 | 92 | - 用户主动启动App; 93 | - App有一个前台进程,或包含一个前台服务,或被另一个activity或前台service使用; 94 | - App生成一个用户所能在锁屏或通知托盘看到的Notification,而当用户设备插入电源时,系统将会释放App的待机状态,允许他们自由的连接网络及其执行未完成的工作和同步。如果设备空闲很长一段时间,系统将允许空闲App一天一次访问网络。 95 | 96 | ### 2.2 Doze和App Standby的区别 97 | 98 | Doze模式需要屏幕关闭(通常晚上睡觉或长时间屏幕关闭才会进入),而App Standby不需要屏幕关闭,App进入后台一段时间也会受到连接网络等限制。 99 | 100 | ## 3 低电耗模式测试app 101 | App要去测试,适配Doze,App Standby,避免一些功能不能使用. 102 | 103 | ### 3.1 测试Doze模式 104 | 105 | - 使用 Android 6.0(API 级别 23)或更高版本的系统映像配置硬件设备或虚拟设备 106 | - 将设备连接到开发计算机并安装应用 107 | - 运行应用并使其保持活动状态 108 | - 关闭设备屏幕。(应用保持活动状态。) 109 | - 通过运行以下命令强制系统在低电耗模式之间循环切换: 110 | ``` 111 | $ adb shell dumpsys battery unplug 112 | $ adb shell dumpsys deviceidle step 113 | ``` 114 | 可能需要多次运行第二个命令。不断地重复,直到设备变为空闲状态。 115 | 116 | - 在重新激活设备后观察应用的行为.确保应用在设备退出低电耗模式时正常恢复. 117 | 118 | ### 3.2 测试App Standby模式 119 | 120 | - 使用 Android 6.0(API 级别 23)或更高版本的系统映像配置硬件设备或虚拟设备 121 | - 将设备连接到开发计算机并安装应用 122 | - 运行应用并使其保持活动状态 123 | - 通过运行以下命令强制应用进入应用待机模式: 124 | ``` 125 | $ adb shell am set-inactive false 126 | $ adb shell am get-inactive 127 | ``` 128 | - 观察唤醒后的应用行为。确保应用从待机模式中正常恢复。 特别地,您应检查应用的通知和后台作业是否按预期继续运行 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /android_doc/android_doze_series/Doze源码分析(一).md: -------------------------------------------------------------------------------- 1 | # Doze源码分析(一) 2 | 3 | **平台: Android O** 4 | 5 | ## 1 简介 6 | 7 | 低电耗模式(Doze)和应用待机模式(App standby)模式.Doze应该叫device idle mode.DeviceIdleController.是一个系统服务,可以使用 8 | `adb shell service list | grep devicesidle`查看到该服务: 9 | ``` 10 | deviceidle: [android.os.IDeviceIdleController] 11 | ``` 12 | DeviceIdleController维护着白名单,位于白名单的list将受到App Standby的限制,使用`adb shell dumpsys deviceidle` 13 | 可以查看到该白名单,白名单分为系统应用白名单和第三方应用白名单. 14 | 其核心是`frameworks/base/services/core/java/com/android/server/DeviceIdleController.java`. 15 | 16 | ## 2 DeviceIdleController服务的启动 17 | 18 | DeviceIdleController服务是在SystemServer中启动的,`frameworks/base/services/java/com/android/server/SystemServer.java`: 19 | 20 | ``` 21 | traceBeginAndSlog("StartDeviceIdleController"); 22 | mSystemServiceManager.startService(DeviceIdleController.class); 23 | traceEnd(); 24 | 25 | ``` 26 | 27 | SystemServiceManager的startService使用反射来创建了DeviceIdleController对象,然后使用onstart初始化. 28 | 代码位于:`frameworks/base/services/core/java/com/android/server/DeviceIdleController.java` 29 | 30 | ``` 31 | public DeviceIdleController(Context context) { 32 | super(context); 33 | // 在创建data/system/deviceidle.xml,用于记录用户设置的白名单 34 | mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml")); 35 | 36 | if(FileUtils.copyFile(new File("/system/etc/permissions","deviceidle.xml"), 37 | new File(getSystemDir(),"deviceidle.xml"), 38 | false)){ 39 | if (DEBUG) Slog.d(TAG, "copy deviceidle.xml success"); 40 | } 41 | 42 | // 创建Handler来处理消息 43 | mHandler = new MyHandler(BackgroundThread.getHandler().getLooper()); 44 | } 45 | ``` 46 | 接下来看看onStart()方法的具体实现: 47 | ``` 48 | @Override 49 | public void onStart() { 50 | final PackageManager pm = getContext().getPackageManager(); 51 | 52 | synchronized (this) { 53 | // Doze模式分light doze 和deep doze,这里对这两个开关进行初始化 54 | mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean( 55 | com.android.internal.R.bool.config_enableAutoPowerModes); 56 | 57 | /// M: Config Doze and App Standby { 58 | if (SystemProperties.get(CONFIG_AUTO_POWER, "0").equals(ENABLE_LIGHT_DOZE)) { 59 | mDeepEnabled = false; 60 | mLightEnabled = true; 61 | } else if (SystemProperties.get(CONFIG_AUTO_POWER, "0").equals(ENABLE_DEEP_DOZE)) { 62 | mDeepEnabled = true; 63 | mLightEnabled = false; 64 | }else if (SystemProperties.get(CONFIG_AUTO_POWER, "0"). 65 | equals(ENABLE_LIGHT_AND_DEEP_DOZE)) { 66 | mDeepEnabled = true; 67 | mLightEnabled = true; 68 | } 69 | // Config Doze and App Standby } 70 | // sysConfig和PKMS有关,保存PKMS解析xml的信息 71 | SystemConfig sysConfig = SystemConfig.getInstance(); 72 | // 获取除了idle模式,其他省电模式能使用的 73 | ArraySet allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle(); 74 | for (int i=0; i allowPower = sysConfig.getAllowInPowerSave(); 87 | for (int i=0; i 126 | false 127 | ``` 128 | 一般这个都是false,google使用overlay机制在GMS包里面把这个值覆盖,改为true.位置在`vendor/partner_gms/products/gms_overlay/frameworks/base/core/res/res/values/config.xml` 129 | 130 | 131 | ## 3 Doze状态机的切换 132 | 133 | ### 3.1 DeviceIdleController的控制功能简介 134 | 上一篇文章我们介绍了Doze有5中模式切换,作为Doze模式的核心理所当然的是控制这5中状态之间的切换以及怎么控制的.这些功能的实现都在 135 | DeviceIdleController中实现,并且通知其他相关注册了AppIdleStateChangeListener接口的服务进行处理,而反过来这些服务也可以向DeviceIdleController查询device的状态,是一种交互的关系。 136 | 这种关系如下图所示: 137 | ![doze_stateMachine](https://raw.githubusercontent.com/lqktz/document/master/res/doze_stateMachine.png) 138 | 139 | ### 3.2 状态转换简介 140 | 141 | - 当设备亮屏或者处于正常使用状态时其就为ACTIVE状态; 142 | - ACTIVE状态下不插充电器或者usb且灭屏设备就会切换到INACTIVE状态; 143 | - INACTIVE状态经过30分钟,期间检测没有打断状态的行为Doze就切换到IDLE_PENDING的状态; 144 | - 然后再经过30分钟以及一系列的判断,状态切换到SENSING; 145 | - 在SENSING状态下会去检测是否有地理位置变化,没有的话就切到LOCATION状态; 146 | - LOCATION状态下再经过30s的检测时间之后就进入了Doze的核心状态IDLE; 147 | - 在IDLE模式下每隔一段时间就会进入一次IDLE_MAINTANCE,此间用来处理之前被挂起的一些任务; 148 | - IDLE_MAINTANCE状态持续5分钟之后会重新回到IDLE状态; 149 | - 在除ACTIVE以外的所有状态中,检测到打断的行为如亮屏、插入充电器,位置的改变等状态就会回到ACTIVE,重新开始下一个轮回。 150 | 151 | 状态转换如下: 152 | 153 | ![doze_stateMachine](https://raw.githubusercontent.com/lqktz/document/master/res/doze_mode_state.png) 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /android_doc/androidstart.md: -------------------------------------------------------------------------------- 1 | # Android开机总体流程 2 | 3 | ## 启动环节 4 | 1. **启动电源以及系统启动** 5 | 当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行. 6 | 2. **引导程序Bootloader** 7 | 引导程序是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。 8 | 3. **linux内核启动** 9 | 内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。 10 | 4. **init进程启动** 11 | Anroid系统的"天字号"进程--init进程,准确的说init进程是Linux系统的用户空间的第一个进程,所以init进程也是 12 | android系统用户空间的第一个进程. 13 | 5. **Zygote进程启动** 14 | init进程,那么Zygote进程就是二号进程,android启动之后的所有进程都是Zygote进程fork()出来的,Zygote进程是由 15 | init进程启动起来的. 16 | 6. **system_server进程启动** 17 | system_server进程其实是systemserver进程,只是在代码中把进程的名字修改了一下(为什么要修改?不知道...),该进程负责启动系统中重量级的服务,例如: 18 | ActivityManagerService,PackageManagerService.etc,system_server进程是Zygote进程的嫡长子,Zygote最重视该进程,其他进程crash,Zygote进程只是打印信息,system_server进程 19 | crash,Zygote进程会调用方法kill自己.android会重启... 20 | 7. **系统服务启动** 21 | 这些系统服务都还是在system_server进程中 22 | 23 | 24 | -------------------------------------------------------------------------------- /android_doc/android添加系统服务.md: -------------------------------------------------------------------------------- 1 | # android添加系统服务 2 | 3 | 内容: 在system server定义自己的系统服务,提供接口给app侧使用. 4 | 平台: Android P 5 | 6 | ## 1 添加系统接口 7 | 8 | ### 1.1 定义service name 9 | 添加service name. 10 | `frameworks/base/core/java/android/content/Context.java` 11 | 12 | ``` 13 | public static final String DEMO_SERVICE = "demoservice"; 14 | ``` 15 | demoservice 就是要定义的service的name. 16 | 17 | ### 1.2 定义service 18 | 19 | 添加`frameworks/base/services/core/java/com/android/server/DemoService.java`新文件 20 | 21 | ``` 22 | package com.android.server; 23 | 24 | import android.content.Context; 25 | import android.util.Slog; 26 | import android.app.IDemoManager; 27 | 28 | public class DemoService extends IDemoManager.Stub { 29 | private static final String TAG = "DemoService"; 30 | private Context mContext 31 | public DemoService(Context context) { 32 | mContext = context; 33 | Slog.d(TAG, "DemoService start success"); 34 | } 35 | public void sayHello(){ 36 | Slog.d(TAG,"hello world"); 37 | } 38 | } 39 | ``` 40 | 41 | 这里定义了一个`public void sayHello()`,供app侧调用 42 | 43 | ### 1.3 添加demo service到system server 44 | 45 | 首先需要导包 46 | ``` 47 | import com.android.server.DemoService; 48 | ``` 49 | 50 | 将demo service 注册到ServiceManager, 下面的代码添加到 `startOtherServices` 中 51 | ``` 52 | try { 53 | ServiceManager.addService(Context.DEMO_SERVICE, new DemoService(context)); 54 | Slog.i(TAG, "addService demo service is OK!"); 55 | } catch (Exception e) { 56 | Slog.e(TAG, "DemoService start fail" + e); 57 | } 58 | 59 | ``` 60 | 到这系统服务已经添加完毕, 不过还不能给app侧调用,调用需要使用添加binder通讯模块. 61 | 62 | ### 1.4 添加aidl文件 63 | 64 | 添加新文件 `frameworks/base/core/java/android/app/IDemoManager.aidl`,将demoservice的sayHello方法暴露给app侧. 65 | 66 | ``` 67 | package android.app; 68 | interface IDemoManager { 69 | void sayHello(); 70 | } 71 | ``` 72 | 73 | 将路径添加到`Android.bp`的 `srcs:`里面 74 | ``` 75 | "core/java/android/app/IDemoManager.aidl", 76 | ``` 77 | 78 | ### 1.5 添加app侧manager文件 79 | 80 | 添加新文件 `frameworks/base/core/java/android/app/DemoManager.java`,app可以调用此接口来使用demo service. 81 | 82 | ``` 83 | package android.app; 84 | import android.util.Log; 85 | import android.os.ServiceManager; 86 | import android.content.Context; 87 | import android.annotation.NonNull; 88 | public class DemoManager { 89 | private static final String TAG = "DemoManager"; 90 | private static DemoManager sInstance; 91 | private IDemoManager mDemoManagerService; 92 | private static Context mContext; 93 | private static IDemoManager mService; 94 | public DemoManager(@NonNull Context context, @NonNull IDemoManager service) { 95 | mContext = context; 96 | mService = service; 97 | } 98 | public void sayHello() { 99 | try { 100 | mService.sayHello(); 101 | } catch (Exception e) { 102 | Log.i(TAG, "exception sayHello()"); 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | 注册服务,在 `frameworks/base/core/java/android/app/SystemServiceRegistry.java`中的`static`添加如下代码: 109 | 110 | ``` 111 | registerService(Context.DEMO_SERVICE, DemoManager.class, new CachedServiceFetcher() { 112 | @Override 113 | public DemoManager createService(ContextImpl ctx) throws ServiceNotFoundException { 114 | IBinder b = ServiceManager.getServiceOrThrow(Context.DEMO_SERVICE); 115 | return new DemoManager(ctx.getOuterContext(), IDemoManager.Stub.asInterface(b)); 116 | } 117 | }); 118 | ``` 119 | 120 | ### 1.6 解决SELinux权限问题 121 | 122 | 在`service_contexts`中添加: 123 | ``` 124 | demoservice u:object_r:demo_service:s0 125 | ``` 126 | 127 | 在`service.te`中添加: 128 | ``` 129 | type demo_service, app_api_service, system_server_service, service_manager_type; 130 | ``` 131 | 132 | ### 1.7 编译 133 | 由于添加了public的接口给app侧, 需要使用 134 | ``` 135 | make update-api 136 | ``` 137 | 138 | 到此, 编译,刷机. 开机可以使用`adb shell service list | grep demoservice`, 可以查看到 139 | ``` 140 | 54 demoservice: [android.app.IDemoManager] 141 | ``` 142 | 说明已经添加成功. 143 | 144 | ## 2 APP 使用 145 | 在源码中的app代码中添加一下代码即可使用自己添加的service的api接口. 146 | ``` 147 | DemoManager mDemoManager = (DemoManager) getSystemService(Context.DEMO_SERVICE); 148 | try { 149 | mDemoManager.sayHello(); 150 | } catch (Exception e) { 151 | e.printStackTrace(); 152 | } 153 | 154 | ``` 155 | 156 | -------------------------------------------------------------------------------- /android_doc/battery-historian/battary-historian-Mac-Docker.md: -------------------------------------------------------------------------------- 1 | # Mac Docker安装battery-historian 2 | 3 | 4 | ## 环境说明 5 | ``` 6 | mac os 版本: 10.15.2 7 | Docker 版本: 2.2.0.0 8 | ``` 9 | 10 | 1. 如果使用github上的官方安装方法安装,需要vpn,尝试使用vpn,依然没有反应,不知道是不是网络问题,不去纠结。换其他方法。 11 | 2. 使用阿里云上images出现无法打开的bugtrport.zip的问题,如果有大神指导请指教。 12 | 13 | 下面说说我的办法,需要使用一点点vpn(其他方法也是需要的)。 14 | 15 | ## 安装docker环境 16 | 下载文件[Docker dmg](https://docs.docker.com/docker-for-mac/install/) , 回下载一个Docker.dmg文件,直接安装就行。如果在下载文件的过程中需要注册,就注册下。 17 | 可以在本地使用,而不是用云的相关功能。 18 | 19 | 在官方文档中有详细的方法,按步骤就行。 20 | 21 | 最后打开终端,check docker 是否安装成功: 22 | 23 | ```bash 24 | [user/home]: docker --version 25 | Docker version 19.03.5, build 633a0ea 26 | ``` 27 | 28 | ## 安装battery-historian 29 | 执行`docker search battery`: 30 | ![docker search battery](./res/docker-search.jpg) 31 | 选择一个结果,例如:下载 `docker pull bhaavan/battery-historian` , 此过程需要等待一会,我在使用wifi下载时会出现卡死的情况,换成手机热点就很快下载完成了。 32 | 33 | 查看一下结果`docker images`: 34 | ![docker image](./res/docker-image.jpg) 35 | 36 | 运行:`docker run -it -d --name MyBattery -p 8000:9999 bhaavan/battery-historian` 37 | ![docker run](./res/docker-run.jpg) 38 | 39 | 此时打开Docker app的Dashboard可以进行管理,当然也不需要这步,只是下次开机不需要输入那些命令,直接鼠标点击就可以管理 40 | 41 | 在chrome中输入 : `http://localhost:8000/`, 注意这里的8000和之间执行`docker run`时指定的命令需要是一样的。界面如下: 42 | ![battery chrome](./res/battery-chrome.jpg) 43 | 不过可能这个界面你无法显示,没有报错,一直是个白色界面,这时需要你打开vpn,可能是需要下载特定的js包,只需要使用这一次,以后就不需要了。 44 | 45 | ## 参考资料 46 | - [Docker for mac](https://docs.docker.com/docker-for-mac/) 47 | - [battery historian gihub](https://github.com/google/battery-historian) 48 | - [Mac OS使用docker安装ubuntu](https://blog.csdn.net/hjs218/article/details/98470287) -------------------------------------------------------------------------------- /android_doc/battery-historian/res/battery-chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/battery-historian/res/battery-chrome.jpg -------------------------------------------------------------------------------- /android_doc/battery-historian/res/docker-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/battery-historian/res/docker-image.jpg -------------------------------------------------------------------------------- /android_doc/battery-historian/res/docker-run.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/battery-historian/res/docker-run.jpg -------------------------------------------------------------------------------- /android_doc/battery-historian/res/docker-search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/battery-historian/res/docker-search.jpg -------------------------------------------------------------------------------- /android_doc/rootcheck源码分析.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/rootcheck源码分析.docx -------------------------------------------------------------------------------- /android_doc/广播发送.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/广播发送.docx -------------------------------------------------------------------------------- /android_doc/广播接受者注册.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/android_doc/广播接受者注册.docx -------------------------------------------------------------------------------- /android_doc/设置默认launcher.md: -------------------------------------------------------------------------------- 1 | #设置默认launcher 2 | 3 | ##平台:android O 4 | 5 | ###需求 6 | 手机中有多个launcher,需要给每个用户设置一个默认的Launcher(每类用户是同一个),考虑到android的多用户问题,不单是第一次开机要设置,每一次创建新的用户(user,guest,owner) 7 | 都要去设置一个默认的,并且要保证每个用户修改为自己喜欢的Launcher后,重启机器不能去覆盖用户的设置. 8 | 9 | ##思路 10 | 1.在手机的'/data/system/users/'目录下.有用户的列表,以及用户的文件夹,比如owner用户是0文件夹,在文件夹里有一个偏好设置保存的文件:package-restrictions.xml. 11 | 2.作为owner用户,可以在第一次开机的时候对其进行设置,作为其他用户可以在创建的时候去设置; 12 | 13 | 先把方案给出: 14 | 需要修改三个文件: 15 | `frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java` 16 | `frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java` 17 | `frameworks/base/packages/SystemUI/AndroidManifest.xml` 18 | UserManagerService.java-->createUserInternalUnchecked 19 | ``` 20 | } finally { 21 | Binder.restoreCallingIdentity(ident); 22 | } 23 | mPm.setDefaultLauncherAsUser(userId);//add 24 | return userInfo; 25 | } 26 | ``` 27 | 28 | PackageManagerService.java-->systemReady最后添加: 29 | `setDefaultLauncher();` 30 | 31 | 在PackageManagerService.java添加两个方法: 32 | ``` 33 | public void setDefaultLauncher(){ 34 | if(isFirstBoot()) { 35 | setDefaultLauncherAsUser(0);//owner用户第一次开机进行设置 36 | } 37 | } 38 | 39 | public void setDefaultLauncherAsUser(int userId){//给普通user用户和guest用户使用, 40 | final PackageManager mPm = mContext.getPackageManager(); 41 | 42 | Intent homeIntent=new Intent(); 43 | 44 | homeIntent.addCategory(Intent.CATEGORY_HOME); 45 | homeIntent.setAction(Intent.ACTION_MAIN); 46 | homeIntent.addCategory(Intent.CATEGORY_DEFAULT); 47 | String defaultLauncherPkg = "com.test.launcher"; 48 | String defaultLauncherClass = "com.test.launcher.Launcher"); 49 | 50 | ResolveInfo info = mPm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY); 51 | ComponentName DefaultLauncher=new ComponentName(defaultLauncherPkg,defaultLauncherClass); 52 | 53 | ArrayList homeActivities = new ArrayList(); 54 | ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 55 | ComponentName[]mHomeComponentSet = new ComponentName[homeActivities.size()]; 56 | for (int i = 0; i < homeActivities.size(); i++) { 57 | final ResolveInfo candidate = homeActivities.get(i); 58 | final ActivityInfo activityInfo= candidate.activityInfo; 59 | ComponentName activityName = new ComponentName(activityInfo.packageName, activityInfo.name); 60 | mHomeComponentSet[i] = activityName; 61 | } 62 | 63 | IntentFilter mHomeFilter = new IntentFilter(Intent.ACTION_MAIN); 64 | mHomeFilter.addCategory(Intent.CATEGORY_HOME); 65 | mHomeFilter.addCategory(Intent.CATEGORY_DEFAULT); 66 | mPm.addPreferredActivityAsUser(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY,mHomeComponentSet, DefaultLauncher, userId); 67 | } 68 | ``` 69 | PackageManagerService.java-->findPreferredActivity 70 | ``` 71 | // Okay we found a previously set preferred or last chosen app. 72 | // If the result set is different from when this 73 | // was created, we need to clear it and re-ask the 74 | // user their preference, if we're looking for an "always" type entry. 75 | if (always && !pa.mPref.sameSet(query)) { 76 | if (!(intent.getAction() != null 77 | && intent.getAction().equals(intent.ACTION_MAIN) 78 | && intent.getCategories() != null 79 | && intent.getCategories().contains(intent.CATEGORY_HOME))) {//add 添加过滤条件 80 | Slog.i(TAG, "Result set changed, dropping preferred activity for " 81 | + intent + " type " + resolvedType); 82 | if (DEBUG_PREFERRED) { 83 | Slog.v(TAG, "Removing preferred activity since set changed " 84 | + pa.mPref.mComponent); 85 | } 86 | pir.removeFilter(pa); 87 | // Re-add the filter as a "last chosen" entry (!always) 88 | PreferredActivity lastChosen = new PreferredActivity( 89 | pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false); 90 | pir.addFilter(lastChosen); 91 | changed = true; 92 | return null; 93 | } 94 | } 95 | ``` 96 | 97 | 在`frameworks/base/packages/SystemUI/AndroidManifest.xml`添加设置偏好的权限. 98 | ` ` 99 | 100 | 解决该问题遇到的坑以及参考的有价值的文章: 101 | ###1号坑 102 | 网上有不少方案修改`packages/apps/Provision`,该方案是可行的,但是会带来问题 103 | 1.会设置两次默认值,导致第一次开机解锁后会出现闪屏; 104 | 2.开机时间加长. 105 | 106 | ###2号坑 107 | 直接配置一个默认的xml,让其加载到默认设置`package-restrictions.xml`中. 108 | 很可惜,该方案对其他默认APP有效,对Launcher无效.具体分析参考: 109 | [1] "Android源码设置default application" 110 | [2] "为什么不能设置Launcher?" 111 | 112 | 113 | ###3号坑 114 | 如果不添加`SET_PREFERRED_APPLICATIONS`权限,在Launcher界面下拉菜单添加user或者guest导致system UI crash. 115 | 116 | 思考: 117 | 1.owner也是用户,可以将设置默认launcher的代码写在一个共同的节点代码.目前没有找到. 118 | 2.最好使用PackageManager.addPreferredActivityAsUser方法,PackageManager.replacePreferredActivityAsUser 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /book/Linux设备驱动开发详解_宋宝华.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/book/Linux设备驱动开发详解_宋宝华.pdf -------------------------------------------------------------------------------- /book/VNDK编译及HIDL.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/book/VNDK编译及HIDL.docx -------------------------------------------------------------------------------- /res/FCM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/FCM.png -------------------------------------------------------------------------------- /res/Hidden_API_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/Hidden_API_02.png -------------------------------------------------------------------------------- /res/PMS_addshareuserid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/PMS_addshareuserid.jpg -------------------------------------------------------------------------------- /res/android-boot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/android-boot.jpg -------------------------------------------------------------------------------- /res/app_standby_bucket_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/app_standby_bucket_001.png -------------------------------------------------------------------------------- /res/classloader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/classloader.png -------------------------------------------------------------------------------- /res/doze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/doze.png -------------------------------------------------------------------------------- /res/doze_mode_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/doze_mode_state.png -------------------------------------------------------------------------------- /res/doze_stateMachine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/doze_stateMachine.png -------------------------------------------------------------------------------- /res/doze_state_change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/doze_state_change.png -------------------------------------------------------------------------------- /res/getAppStandbyBucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/getAppStandbyBucket.png -------------------------------------------------------------------------------- /res/getAppStandbyBucket.xml: -------------------------------------------------------------------------------- 1 | 7Vlbk6I4FP41PGpBuKiPovbOVs1UdU3vZeYxQpRsR0KF0Lb76/cA4Wa0cdpLWd3ri+SQnJDzne/LIRj2bPP6m8BJ9I2HhBnIDF8Ne24gZJmODX+5ZVda3BEqDWtBQ9WpMTzRf0k1UlkzGpK001FyziRNusaAxzEJZMeGheDbbrcVZ91ZE7wmmuEpwEy3/k1DGZXWsWs29i+ErqNqZstUdza46qwMaYRDvm2Z7IVhzwTnsrzavM4Iy4NXxaUc93Dkbv1ggsTylAHzCPlj5sQ/Bt+D1Hv8iw7oYKC8vGCWqQX/meYBkFimGxzDpRj+g1+wWoLcVXGB1ST5ZbZhX+mKMBpDy0+IoBsiiYA7TJkfG5u/jagkTwkO8qFbyBiwRXLDoGXBJYAoMQwRdZsxnKR0WcxqgkWQIBMpfSHfSVrmSm7lmcxnmtU5kBvVyoiQ5PVoyKwaCMhgwuE5xQ66qAHeqByhctdWSG6bRLA8ZYtaSWBXOYBV8q1rzw0+cKEg+gW40BtwPRHxQgOSfmK8UEXLNwBzbwqYrQE2TRKAKw6XO1i+FBCxT00xB3l3xjHnEGS/h4x8oankYveJwfJMtxcs56ZguRpYGjIkDqd5IQCtJePBM4QCTA+UVVFuBZyEuZiWQ7mQEV/zGLNFY/Uxo+s4R5OsYHF+Hk4K1cJUmSVP6lDnzt4ONDwnz0RA+jdoicWayL6d4ShwlRtBGJaQLd0y6QAoytMjp/DAtZuaiQp/a7SHa7kcNapdi+w5slGPo3K9mqMiR+rlvT9tPC1tLC1vRM4o3FCtlSY9SbDkUvIN3GB4SZiPg+e14FkczjjjonBur4ofdFkBxVXVazUsVdWsmr2pIXvYe5wil8uMs+g61uIOKDc7op8Fz0RqSMCSZReAfWySPFGKh3V9w523IAogOIXU7oO0oWFYMPoEkI4G/xhOZ4JiDs2x15Xaals6k8GVm8qr23XAV6uUXIVxlr6t3oNSA0pi9yPPoqFbNX8WTa9qtvbr1j56irj3irZ9C9G2UTeT6lfrXxVtZ9zj6MqibembvZ5DH0W1S77ciWwjnbyfVLf7YDGHyB55lym2JrfSZnSfVXRbm22vq84jb3QDfb4wBw/j7JrdN2DkvVOfPa/H0ZX1GelVtf1h9RndU1mNRv/r82mwQF1nTlCHJpeisTt0D/q9gX7rr1Vv6XfMi7OpFuynSqYe19bJz6GD1cp2ZmyR0z0ucN5bwtaObiSJNtKQWdI4BOIsJsbYNCZTY+EY/oMx1c8fPiI7x33stEZudxMbnFlMXZ991SFo98AYDAW0C2M6ugy0meTqiPeEzbKsoNIEBzRe/5E35lDNXOQQ2JrsfWSpTip6tABd7RuL/hHTWHjGZGaM58bCNcbTnF4fEwzk3QwMaDYfq0vyNJ/87cV/ -------------------------------------------------------------------------------- /res/hidden_API.asta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/hidden_API.asta -------------------------------------------------------------------------------- /res/hidden_API.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/hidden_API.png -------------------------------------------------------------------------------- /res/init_property.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/init_property.png -------------------------------------------------------------------------------- /res/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/test.jpg -------------------------------------------------------------------------------- /res/wms_relation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/wms_relation.jpg -------------------------------------------------------------------------------- /res/write_document_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_1.png -------------------------------------------------------------------------------- /res/write_document_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_2.png -------------------------------------------------------------------------------- /res/write_document_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_3.png -------------------------------------------------------------------------------- /res/write_document_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_4.png -------------------------------------------------------------------------------- /res/write_document_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_5.png -------------------------------------------------------------------------------- /res/write_document_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_6.png -------------------------------------------------------------------------------- /res/write_document_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/write_document_7.png -------------------------------------------------------------------------------- /res/zygote_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/res/zygote_1.png -------------------------------------------------------------------------------- /temp/F2FS/assets/filestructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/F2FS/assets/filestructure.png -------------------------------------------------------------------------------- /temp/F2FS/assets/layoutF2FS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/F2FS/assets/layoutF2FS.png -------------------------------------------------------------------------------- /temp/Handler机制.md: -------------------------------------------------------------------------------- 1 | #Handler机制 2 | **android N** 3 | 4 | 对Handler的分析从java层和native层两方面来分析. 5 | *** 6 | ##Handler java层 7 | 主要使用到四个类: 8 | ``` 9 | aosp/frameworks/base/core/java/android/os/Handler.java 10 | aosp/frameworks/base/core/java/android/os/Message.java 11 | aosp/frameworks/base/core/java/android/os/Looper.java 12 | aosp/frameworks/base/core/java/android/os/MessageQueue.java 13 | ``` 14 | 下面分析这四个主要的类: 15 | ###Looper 16 | ####Looper的creat 17 | ```java 18 | /** Initialize the current thread as a looper. 19 | * This gives you a chance to create handlers that then reference 20 | * this looper, before actually starting the loop. Be sure to call 21 | * {@link #loop()} after calling this method, and end it by calling 22 | * {@link #quit()}. 23 | */ 24 | public static void prepare() { 25 | prepare(true);//true表示允许退出,非主线程的Looper都可以设置成true 26 | } 27 | 28 | private static void prepare(boolean quitAllowed) { 29 | if (sThreadLocal.get() != null) {//已经有Looper就不能再此创建 30 | throw new RuntimeException("Only one Looper may be created per thread"); 31 | } 32 | //sThreadLocal是一个ThreadLocal类型,每一个线程都有一个线程本地存储区域(Thread Local Storage,TLs), 33 | //ThreadLocal的get和set方法可以操作该区域,这里的Looper就是存储在该区域. 34 | sThreadLocal.set(new Looper(quitAllowed)); 35 | } 36 | 37 | /** 38 | * Initialize the current thread as a looper, marking it as an 39 | * application's main looper. The main looper for your application 40 | * is created by the Android environment, so you should never need 41 | * to call this function yourself. See also: {@link #prepare()} 42 | */ 43 | public static void prepareMainLooper() {//创建主线程里面的Loop 44 | prepare(false);//主线程的Looper不允许退出 45 | synchronized (Looper.class) { 46 | if (sMainLooper != null) { 47 | throw new IllegalStateException("The main Looper has already been prepared."); 48 | } 49 | sMainLooper = myLooper(); 50 | } 51 | } 52 | ``` 53 | 每个线程中Loop都是用以上三种方法的其中一种方法创建的.在`prepare(boolean)`方法里new了一个Looper: 54 | ```java 55 | private Looper(boolean quitAllowed) { 56 | mQueue = new MessageQueue(quitAllowed);//每个Looper中都用一个MessageQueue 57 | mThread = Thread.currentThread(); 58 | } 59 | ``` 60 | ####Looper进入loop 61 | ```java 62 | /** 63 | * Run the message queue in this thread. Be sure to call 64 | * {@link #quit()} to end the loop. 65 | */ 66 | public static void loop() { 67 | final Looper me = myLooper();//从TLS获取Looper对象 68 | if (me == null) { 69 | throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 70 | } 71 | final MessageQueue queue = me.mQueue; 72 | 73 | // Make sure the identity of this thread is that of the local process, 74 | // and keep track of what that identity token actually is. 75 | Binder.clearCallingIdentity(); 76 | final long ident = Binder.clearCallingIdentity(); 77 | 78 | for (;;) {//进入loop的循环方法 79 | //1.读取queue中的下一条message 80 | Message msg = queue.next(); // might block可能会阻塞 81 | if (msg == null) {//没有消息退出 82 | // No message indicates that the message queue is quitting. 83 | return; 84 | } 85 | 86 | // This must be in a local variable, in case a UI event sets the logger 87 | final Printer logging = me.mLogging; 88 | if (logging != null) { 89 | logging.println(">>>>> Dispatching to " + msg.target + " " + 90 | msg.callback + ": " + msg.what); 91 | } 92 | 93 | ///省略 M: ANR mechanism for Message Monitor Service代码 94 | final long traceTag = me.mTraceTag; 95 | if (traceTag != 0) { 96 | Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); 97 | } 98 | try { 99 | //2.将message 分发给对应的target,该target就是一个handle对象 100 | msg.target.dispatchMessage(msg);//分发消息 101 | } finally { 102 | if (traceTag != 0) { 103 | Trace.traceEnd(traceTag); 104 | } 105 | } 106 | 107 | if (logging != null) { 108 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 109 | } 110 | 111 | ///省略 M: ANR mechanism for Message Monitor Service 代码 112 | // Make sure that during the course of dispatching the 113 | // identity of the thread wasn't corrupted. 114 | final long newIdent = Binder.clearCallingIdentity(); 115 | if (ident != newIdent) { 116 | Log.wtf(TAG, "Thread identity changed from 0x" 117 | + Long.toHexString(ident) + " to 0x" 118 | + Long.toHexString(newIdent) + " while dispatching to " 119 | + msg.target.getClass().getName() + " " 120 | + msg.callback + " what=" + msg.what); 121 | } 122 | //3.将使用完的Message放回message池 123 | msg.recycleUnchecked();//使用消息池,可以避免重复的创建message对象消耗资源,message池大小是50 124 | } 125 | } 126 | ``` 127 | 在Looper.java中loop方法是一个核心方法,其他方法都是在围绕该方法. 128 | ####Looper的quit 129 | Looper的创建时,有一个quitAllowed的boolean值,接下来分析该值的使用: 130 | ```java 131 | public void quit() { 132 | mQueue.quit(false); 133 | } 134 | 135 | public void quitSafely() { 136 | mQueue.quit(true); 137 | } 138 | ``` 139 | 两个方法都调用了MessageQueue的quit方法: 140 | ```java 141 | void quit(boolean safe) { 142 | if (!mQuitAllowed) { 143 | throw new IllegalStateException("Main thread not allowed to quit."); 144 | } 145 | 146 | synchronized (this) { 147 | if (mQuitting) { 148 | return; 149 | } 150 | mQuitting = true; 151 | 152 | if (safe) { 153 | removeAllFutureMessagesLocked();//移除所有还没有触发的消息 154 | } else { 155 | removeAllMessagesLocked();//移除所有的消息 156 | } 157 | 158 | // We can assume mPtr != 0 because mQuitting was previously false. 159 | nativeWake(mPtr); 160 | } 161 | } 162 | ``` 163 | ####常用方法 164 | #####getMainLooper 165 | 获取所属进程主线程的Looper 166 | ```java 167 | /** 168 | * Returns the application's main looper, which lives in the main thread of the application. 169 | */ 170 | public static Looper getMainLooper() { 171 | synchronized (Looper.class) { 172 | return sMainLooper; 173 | } 174 | } 175 | ``` 176 | #####myLooper() 177 | 获取当前线程的Looper 178 | ```java 179 | /** 180 | * Return the Looper object associated with the current thread. Returns 181 | * null if the calling thread is not associated with a Looper. 182 | */ 183 | public static @Nullable Looper myLooper() { 184 | return sThreadLocal.get(); 185 | } 186 | ``` 187 | #####isCurrentThread() 188 | 判断当前线程是不是looper所属的线程 189 | ```java 190 | /** 191 | * Returns true if the current thread is this looper's thread. 192 | */ 193 | public boolean isCurrentThread() { 194 | return Thread.currentThread() == mThread; 195 | } 196 | ``` 197 | ###Handler 198 | ####Handler的creat 199 | ```java 200 | public Handler(Looper looper, Callback callback, boolean async) { 201 | mLooper = looper; 202 | mQueue = looper.mQueue; 203 | mCallback = callback; 204 | mAsynchronous = async; 205 | } 206 | ``` 207 | Handler最多有3个参数,这三个参数都可有可无,所以排列组合一下,总共有7中Handler的构造方法: 208 | ```java 209 | public Handler(); 210 | public Handler(Callback callback); 211 | public Handler(boolean async); 212 | public Handler(Callback callback, boolean async); 213 | public Handler(Looper looper); 214 | public Handler(Looper looper, Callback callback); 215 | public Handler(Looper looper, Callback callback, boolean async); 216 | ``` 217 | 前四种未指定Looper,表示是使用的当前线程TLS里存储的Looper; 218 | 未指定消息的处理方式是同步还是异步的(即async),默认是同步; 219 | callback没有设置,默认是null; 220 | ####Handler的dispatchMessage 221 | 在Looper.loop()中调用`msg.target.dispatchMessage(msg);`target就是一个Handler 222 | ```java 223 | /** 224 | * Handle system messages here. 225 | */ 226 | public void dispatchMessage(Message msg) { 227 | if (msg.callback != null) { 228 | //1.message有回调方法方法,调用message的callback.run()处理消息 229 | handleCallback(msg); 230 | } else { 231 | if (mCallback != null) { 232 | //2.Handler存在回调方法,调用handler的callback的handleMessage(msg) 233 | if (mCallback.handleMessage(msg)) { 234 | return; 235 | } 236 | } 237 | //3.调用Handler的callback方法 238 | handleMessage(msg); 239 | } 240 | } 241 | ``` 242 | 大多是都是使用3方法,在Handler的Handler的handleMessage方法中实现自己的逻辑. 243 | ```java 244 | /** 245 | * Subclasses must implement this to receive messages. 246 | */ 247 | public void handleMessage(Message msg) { 248 | } 249 | ``` 250 | handleMessage方法是空方法,Handler的子类要继承该类去在handleMessage里面实现自己的逻辑. 251 | ####Handler消息的发送 252 | #####sendMessage 253 | ``` 254 | public final boolean sendMessage(Message msg) 255 | { 256 | return sendMessageDelayed(msg, 0); 257 | } 258 | ``` 259 | ``` 260 | public final boolean sendEmptyMessage(int what) 261 | { 262 | return sendEmptyMessageDelayed(what, 0); 263 | } 264 | ``` 265 | ``` 266 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 267 | Message msg = Message.obtain(); 268 | msg.what = what; 269 | return sendMessageDelayed(msg, delayMillis); 270 | } 271 | ``` 272 | ``` 273 | 274 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 275 | Message msg = Message.obtain(); 276 | msg.what = what; 277 | return sendMessageAtTime(msg, uptimeMillis); 278 | } 279 | ``` 280 | ``` 281 | public final boolean sendMessageDelayed(Message msg, long delayMillis) 282 | { 283 | if (delayMillis < 0) { 284 | delayMillis = 0; 285 | } 286 | return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 287 | } 288 | ``` 289 | ``` 290 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 291 | MessageQueue queue = mQueue; 292 | if (queue == null) { 293 | RuntimeException e = new RuntimeException( 294 | this + " sendMessageAtTime() called with no mQueue"); 295 | Log.w("Looper", e.getMessage(), e); 296 | return false; 297 | } 298 | return enqueueMessage(queue, msg, uptimeMillis); 299 | } 300 | ``` 301 | ``` 302 | public final boolean sendMessageAtFrontOfQueue(Message msg) { 303 | MessageQueue queue = mQueue; 304 | if (queue == null) { 305 | RuntimeException e = new RuntimeException( 306 | this + " sendMessageAtTime() called with no mQueue"); 307 | Log.w("Looper", e.getMessage(), e); 308 | return false; 309 | } 310 | return enqueueMessage(queue, msg, 0); 311 | } 312 | ``` 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | *** 330 | ##Handler native层 331 | 332 | 333 | 334 | 335 | 336 | -------------------------------------------------------------------------------- /temp/IO栈/IO栈.md: -------------------------------------------------------------------------------- 1 | # IO栈 2 | 3 | ## 重要结构体 4 | bio --> include/linux/blk_types.h 5 | 6 | ## 0 脏页回写 7 | - 当空闲内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。 8 | - 当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘, 9 | 以确保脏页不会无限期地驻留在内存中。 10 | 11 | 而是先写到了系统cache里,随后由pdflush内核线程将系统中的脏页写到磁盘上,在下面几种情况下, 12 | 系统会唤醒pdflush回写脏页: 13 | 14 | 1 、定时方式: 15 | 定时机制定时唤醒pdflush内核线程,周期为/proc/sys/vm/dirty_writeback_centisecs ,单位 16 | 是(1/100)秒,每次周期性唤醒的pdflush线程并不是回写所有的脏页,而是只回写变脏时间超过 17 | /proc/sys/vm/dirty_expire_centisecs(单位也是1/100秒)。 18 | 注意:变脏的时间是以文件的inode节点变脏的时间为基准的,也就是说如果某个inode节点是10秒前变脏的, 19 | pdflush就认为这个inode对应的所有脏页的变脏时间都是10秒前,即使可能部分页面真正变脏的时间不到10秒, 20 | 细节可以查看内核函数wb_kupdate()。 21 | 22 | 2、 内存不足的时候: 23 | 这时并不将所有的dirty页写到磁盘,而是每次写大概1024个页面,直到空闲页面满足需求为止。 24 | 25 | 3 、写操作时发现脏页超过一定比例: 26 | 当脏页占系统内存的比例超过/proc/sys/vm/dirty_background_ratio 的时候,write系统调用会唤醒 27 | pdflush回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_background_ratio,但write系统调 28 | 用不会被阻塞,立即返回。当脏页占系统内存的比例超过/proc/sys/vm/dirty_ratio的时候, write系 29 | 统调用会被被阻塞,主动回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_ratio,这一点在 30 | 2.4内核中是没有的。 31 | 32 | 4 、用户调用sync系统调用: 33 | 这是系统会唤醒pdflush直到所有的脏页都已经写到磁盘为止。 34 | 35 | 36 | pdflush回写期间极其消耗磁盘IO,严重影响读性能。 37 | ## 1 F2FS 38 | ``` 39 | const struct address_space_operations f2fs_dblock_aops = { 40 | .readpage = f2fs_read_data_page, 41 | .readpages = f2fs_read_data_pages, 42 | .writepage = f2fs_write_data_page, 43 | .writepages = f2fs_write_data_pages, 44 | .write_begin = f2fs_write_begin, 45 | .write_end = f2fs_write_end, 46 | .set_page_dirty = f2fs_set_data_page_dirty, 47 | .invalidatepage = f2fs_invalidate_page, 48 | .releasepage = f2fs_release_page, 49 | .direct_IO = f2fs_direct_IO, 50 | .bmap = f2fs_bmap, 51 | #ifdef CONFIG_MIGRATION 52 | .migratepage = f2fs_migrate_page, 53 | #endif 54 | }; 55 | ``` 56 | 57 | ``` 58 | /* 59 | * A control structure which tells the writeback code what to do. These are 60 | * always on the stack, and hence need no locking. They are always initialised 61 | * in a manner such that unspecified fields are set to zero. 62 | */ 63 | struct writeback_control { 64 | long nr_to_write; /* Write this many pages, and decrement 65 | this for each page written */ 66 | long pages_skipped; /* Pages which were not written */ 67 | 68 | /* 69 | * For a_ops->writepages(): if start or end are non-zero then this is 70 | * a hint that the filesystem need only write out the pages inside that 71 | * byterange. The byte at `end' is included in the writeout request. 72 | */ 73 | loff_t range_start; 74 | loff_t range_end; 75 | 76 | enum writeback_sync_modes sync_mode; 77 | 78 | unsigned for_kupdate:1; /* A kupdate writeback */ 79 | unsigned for_background:1; /* A background writeback */ 80 | unsigned tagged_writepages:1; /* tag-and-write to avoid livelock */ 81 | unsigned for_reclaim:1; /* Invoked from the page allocator */ 82 | unsigned range_cyclic:1; /* range_start is cyclic */ 83 | unsigned for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ 84 | #ifdef CONFIG_CGROUP_WRITEBACK 85 | struct bdi_writeback *wb; /* wb this writeback is issued under */ 86 | struct inode *inode; /* inode being written out */ 87 | 88 | /* foreign inode detection, see wbc_detach_inode() */ 89 | int wb_id; /* current wb id */ 90 | int wb_lcand_id; /* last foreign candidate wb id */ 91 | int wb_tcand_id; /* this foreign candidate wb id */ 92 | size_t wb_bytes; /* bytes written by current wb */ 93 | size_t wb_lcand_bytes; /* bytes written by last candidate */ 94 | size_t wb_tcand_bytes; /* bytes written by this candidate */ 95 | #endif 96 | }; 97 | ``` 98 | 99 | ``` 100 | struct f2fs_io_info fio = 101 | { 102 | .sbi = sbi, 103 | .ino = inode->i_ino, 104 | .type = DATA, 105 | .op = REQ_OP_WRITE, 106 | .op_flags = wbc_to_write_flags(wbc), 107 | .old_blkaddr = NULL_ADDR, 108 | .page = page, 109 | .encrypted_page = NULL, 110 | .submitted = false, 111 | .need_lock = LOCK_RETRY, 112 | .io_type = io_type, 113 | .io_wbc = wbc, 114 | }; 115 | ``` 116 | ## 2 调度器层 117 | 118 | 119 | ## 120 | -------------------------------------------------------------------------------- /temp/Launcher启动源码分析.md: -------------------------------------------------------------------------------- 1 | #Launcher启动 2 | 3 | 流程: 4 | systemserver.java->main()->run()->startOtherService 5 | mActivityManagerService.systemReady(Runnable) 6 | RunnablemSupervisor.scheduleResumeTopActivities() 7 | ActivityManagerService.systemReady(final Runnable goingCallback) 8 | startHomeActivityLocked(mCurrentUserId, "systemReady") 9 | getHomeIntent() 10 | mActivityStarter.startHomeActivityLocked(intent, aInfo, reason) 11 | ActivityStarter.java->void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) 12 | mSupervisor.scheduleResumeTopActivities() 13 | ActivityStackSupervisor.java->scheduleResumeTopActivities() 14 | mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG) 15 | resumeFocusedStackTopActivityLocked() 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /temp/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.leecode; 2 | public class ListNode { 3 | int val; 4 | ListNode next; 5 | ListNode(int x) { val = x; } 6 | } -------------------------------------------------------------------------------- /temp/NNAPI.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/NNAPI.pptx -------------------------------------------------------------------------------- /temp/PowerUsageSummay详解.md: -------------------------------------------------------------------------------- 1 | # PowerUsageSummary详解 2 | 3 | 平台: Android O 4 | 5 | *** 6 | 7 | `packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java` 8 | 9 | PowerUsageSummary.java是在fuelgauge下的入口,也是核心方法. 10 | 功能: 完成在Settings模块的电池耗电情况的显示. 11 | 12 | 本文的分析重点是界面上的显示的耗电数据是怎么显示出来的. 13 | ``` 14 | protected void refreshUi() { 15 | final Context context = getContext(); 16 | if (context == null) { 17 | return; 18 | } 19 | 20 | cacheRemoveAllPrefs(mAppListGroup); 21 | mAppListGroup.setOrderingAsAdded(false); 22 | boolean addedSome = false; 23 | // 获取电源配置文件 24 | final PowerProfile powerProfile = mStatsHelper.getPowerProfile(); 25 | // 获取电池的状态,是一个BatteryStats对象 26 | final BatteryStats stats = mStatsHelper.getStats(); 27 | // 获取屏幕的单位耗电,具体意义参见屏幕耗电计算方法 28 | final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); 29 | 30 | final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000; 31 | Intent batteryBroadcast = context.registerReceiver(null, 32 | new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 33 | BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context, batteryBroadcast, 34 | mStatsHelper.getStats(), elapsedRealtimeUs, false); 35 | mBatteryHeaderPreferenceController.updateHeaderPreference(batteryInfo); 36 | 37 | final TypedValue value = new TypedValue(); 38 | context.getTheme().resolveAttribute(android.R.attr.colorControlNormal, value, true); 39 | final int colorControl = context.getColor(value.resourceId); 40 | // 消耗的总电量 41 | final int dischargeAmount = USE_FAKE_DATA ? 5000 42 | : stats != null ? stats.getDischargeAmount(mStatsType) : 0; 43 | // 计算最近一次充满电到现在的时间 44 | final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper, 45 | System.currentTimeMillis()); 46 | // 设置Preference的小标题,有关屏幕的耗电量,单位:mAh 47 | updateScreenPreference();//1 48 | // 设置Preference的小标题,有关最近一次充满电到现在的时间 49 | updateLastFullChargePreference(lastFullChargeTime);//2 50 | 51 | final CharSequence timeSequence = Utils.formatElapsedTime(context, lastFullChargeTime, 52 | false); 53 | 54 | // 获取设置的显示是App耗电列表还是Device耗电列表的title,并设置 55 | // 分别为: Device usage since full charge / App usage since full charge 56 | final int resId = mShowAllApps ? R.string.power_usage_list_summary_device 57 | : R.string.power_usage_list_summary; 58 | mAppListGroup.setTitle(TextUtils.expandTemplate(getText(resId), timeSequence)); 59 | 60 | // 当屏幕耗电单位耗电大于10毫安,或者使用伪造数据就进行统计计算(也就是刷新数据),没有看错,是伪造耗电数据.....不知道google是出于什么考虑 61 | if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) { 62 | // 是获取一个BatterySipper类型的数组,getCoalescedUsageList的参数依据是不是用伪造的数据进行判断 63 | final List usageList = getCoalescedUsageList( 64 | USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());//3 65 | // 依据mShowAllApps标示是否要显示所有的app,如果不是,则删除其中要隐藏的app 66 | double hiddenPowerMah = mShowAllApps ? 0 : 67 | mBatteryUtils.removeHiddenBatterySippers(usageList); 68 | // 将usagelist进行排序,如果有隐藏的app,这里已经排除. 69 | // 这里排序的目的是,手机并不是显示所有的耗电项目,符合一定标准的才会显示 70 | // 这里的排序使用的是 Collections.sort ,排序结果为降序 71 | mBatteryUtils.sortUsageList(usageList); 72 | 73 | // 获取所有耗电项目的数目 74 | final int numSippers = usageList.size(); 75 | for (int i = 0; i < numSippers; i++) { 76 | // 每个BatterySipper实例代表一个应用uid的消耗的电量信息 77 | final BatterySipper sipper = usageList.get(i); 78 | // 获取电池总电量,如果是伪造,则使用4000mAh的电池容量 79 | double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower(); 80 | // 计算百分比排出了隐藏的app消耗的电量 81 | // 计算百分比公式 (sipper.totalPowerMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount 82 | final double percentOfTotal = mBatteryUtils.calculateBatteryPercent( 83 | sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount); 84 | 85 | // 并不是计算出来的百分比都会使用,只是会显示满足条件的,下面的代码主要进行筛选 86 | // 不足 1% 的忽略,出现第一个小于 1% 的应该结束计算才对呀,后面的一定不满足条件了.可是google没有这么写,一直在这浪费资源 87 | if (((int) (percentOfTotal + .5)) < 1) { 88 | continue; 89 | } 90 | if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {// 指统计多了的 91 | // Don't show over-counted unless it is at least 2/3 the size of 92 | // the largest real entry, and its percent of total is more significant 93 | if (sipper.totalPowerMah < ((mStatsHelper.getMaxRealPower() * 2) / 3)) { 94 | continue; 95 | } 96 | if (percentOfTotal < 10) {// 小于 5% 不统计 97 | continue; 98 | } 99 | if ("user".equals(Build.TYPE)) {// user版本不统计 100 | continue; 101 | } 102 | } 103 | if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {// DrainType是UNACCOUNTED指的是未统计的耗电 104 | // Don't show over-counted unless it is at least 1/2 the size of 105 | // the largest real entry, and its percent of total is more significant 106 | if (sipper.totalPowerMah < (mStatsHelper.getMaxRealPower() / 2)) { 107 | continue; 108 | } 109 | if (percentOfTotal < 5) {// 小于 5% 不统计 110 | continue; 111 | } 112 | if ("user".equals(Build.TYPE)) {// user版本不统计 113 | continue; 114 | } 115 | } 116 | 117 | // 到此,此次循环拿出的BatterySipper统计的百分比就是一个合格的,接下来就是进行封装和显示 118 | 119 | // UserHandle是设备上用户的表示 120 | final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid())); 121 | // 将BatterySipper类型包裹城一个BatteryEntry类型,多了包名和icon信息 122 | final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper); 123 | final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(), 124 | userHandle); 125 | final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(), 126 | userHandle); 127 | 128 | // Preference 是进行显示相关 129 | // 此处的 key 可能是uid,DrainType,package name,-1 130 | final String key = extractKeyFromSipper(sipper); 131 | PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key); 132 | if (pref == null) { 133 | pref = new PowerGaugePreference(getPrefContext(), badgedIcon, 134 | contentDescription, entry); 135 | pref.setKey(key); 136 | } 137 | 138 | final double percentOfMax = (sipper.totalPowerMah * 100) 139 | / mStatsHelper.getMaxPower(); 140 | sipper.percent = percentOfTotal; 141 | pref.setTitle(entry.getLabel()); 142 | pref.setOrder(i + 1); 143 | // 会调用notifyChanged()通知UI更新数据 144 | pref.setPercent(percentOfTotal); 145 | if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) { 146 | sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs( 147 | BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType); 148 | } 149 | // 设置的是preference,和setSummary功能一样,就是设置的小标题,大于 1min 才会被显示 150 | setUsageSummary(pref, sipper); 151 | if ((sipper.drainType != DrainType.APP 152 | || sipper.uidObj.getUid() == Process.ROOT_UID) 153 | && sipper.drainType != DrainType.USER) { 154 | pref.setTint(colorControl); 155 | } 156 | addedSome = true; 157 | // 添加Preference 158 | mAppListGroup.addPreference(pref); 159 | // 使用伪造数据MAX_ITEMS_TO_LIST等于30,真实数据等于10 160 | if (mAppListGroup.getPreferenceCount() - getCachedCount() 161 | > (MAX_ITEMS_TO_LIST + 1)) {// 如果是真实的数据,这里最多显示是12个 162 | break; 163 | } 164 | } 165 | } 166 | if (!addedSome) {// 没有添加任何Preference,就要该代码块 167 | addNotAvailableMessage(); 168 | } 169 | removeCachedPrefs(mAppListGroup); 170 | 171 | BatteryEntry.startRequestQueue(); 172 | } 173 | ``` 174 | 可以下面的log分析: 175 | ``` 176 | Log.d(TAG,"sipper.drainType = " + sipper.drainType); 177 | 178 | int uid = -2; 179 | if(sipper.uidObj != null){ 180 | uid = sipper.uidObj.getUid(); 181 | } 182 | Log.d(TAG,"uid = " + uid ); 183 | 184 | Log.d("qli3","numSippers = " + numSippers ); 185 | if (sipper.mPackages != null) { 186 | for(int j = 0; j < sipper.mPackages.length; j++){ 187 | Log.d(TAG,"sipper.mPackages = " + sipper.mPackages[j]); 188 | } 189 | } 190 | ``` 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /temp/README.md: -------------------------------------------------------------------------------- 1 | #document 2 | -------------------------------------------------------------------------------- /temp/Solution.java: -------------------------------------------------------------------------------- 1 | package com.leecode; 2 | class Solution { 3 | public static ListNode addTwoNumbers(ListNode l1, ListNode l2) { 4 | ListNode listNodeHead = null; // 头 5 | ListNode listNodeTail = null; // 尾 6 | 7 | int flag = 0; 8 | int temp = -1; 9 | 10 | int flag1_2 = 12; 11 | 12 | while(true){ 13 | if( flag1_2 == 12) { 14 | temp = l1.val + l2.val + flag; 15 | } else if(flag1_2 == 1){ 16 | temp =l1.val + flag; 17 | } else if(flag1_2 == 2){ 18 | temp = l2.val + flag; 19 | } 20 | 21 | flag = temp >= 10 ? 1 : 0; 22 | temp = temp%10; 23 | System.out.println("numtemp = " + temp ); 24 | 25 | if(listNodeHead == null){ 26 | listNodeHead = new ListNode(temp); 27 | listNodeTail = listNodeHead; 28 | } else { 29 | listNodeTail.next = new ListNode(temp); 30 | listNodeTail = listNodeTail.next; 31 | } 32 | 33 | if(l1.next == null && l2.next == null) { 34 | if(flag == 1) 35 | listNodeTail.next = new ListNode(flag); 36 | break; 37 | } 38 | 39 | if(l1.next != null){ 40 | l1 = l1.next; 41 | }else { 42 | flag1_2 = 2; 43 | } 44 | if (l2.next != null) { 45 | l2 = l2.next; 46 | }else { 47 | flag1_2 = 1; 48 | } 49 | } 50 | myPrint(listNodeHead); 51 | return listNodeHead; 52 | } 53 | 54 | public static ListNode addTwoNumbers2(ListNode l1, ListNode l2) { 55 | ListNode listNodeHead; 56 | int flag = 0; 57 | int l1_len = 1; 58 | int l2_len = 1; 59 | int tempNum = 0; 60 | 61 | ListNode temp = null; 62 | temp = l1; 63 | while(temp.next != null) { 64 | l1_len++; 65 | temp = temp.next; 66 | } 67 | 68 | temp = l2; 69 | while(temp.next != null) { 70 | l2_len++; 71 | temp = temp.next; 72 | } 73 | 74 | if(l1_len >= l2_len){ 75 | listNodeHead = l1; 76 | while(true){ 77 | tempNum = l1.val + l2.val + flag; 78 | l1.val =tempNum%10; 79 | System.out.println("listNodeHead = l1 numtemp = " + l1.val ); 80 | flag = tempNum > 9 ? 1 : 0; 81 | //l1 = l1.next; 82 | if(l2.next == null){ 83 | if( flag == 1) { 84 | if(l1.next ==null) { 85 | l1.next = new ListNode(1); 86 | } else { 87 | l1.next.val = l1.next.val + 1; 88 | } 89 | } 90 | break; 91 | } 92 | l2 = l2.next; 93 | } 94 | } else { 95 | listNodeHead = l2; 96 | while(true){ 97 | tempNum = l1.val + l2.val + flag; 98 | l2.val =tempNum%10; 99 | System.out.println("listNodeHead = l2 numtemp = " + l2.val ); 100 | flag = tempNum > 9 ? 1 : 0; 101 | //l1 = l1.next; 102 | if(l1.next == null){ 103 | if( flag == 1) { 104 | if(l2.next ==null) { 105 | l2.next = new ListNode(1); 106 | } else { 107 | l2.next.val = l2.next.val + 1; 108 | } 109 | } 110 | break; 111 | } 112 | l2 = l2.next; 113 | } 114 | } 115 | myPrint(listNodeHead); 116 | return listNodeHead; 117 | } 118 | 119 | 120 | public static void main(String[] args) { 121 | ListNode l1 = new ListNode(2); 122 | ListNode nodea1 = new ListNode(4); 123 | l1.next = nodea1; 124 | ListNode nodea2 = new ListNode(3); 125 | nodea1.next = nodea2; 126 | 127 | ListNode l2 = new ListNode(5); 128 | ListNode nodeb1 = new ListNode(6); 129 | l2.next = nodeb1; 130 | ListNode nodeb2 = new ListNode(4); 131 | nodeb1.next = nodeb2; 132 | 133 | //addTwoNumbers(l1, l2); 134 | addTwoNumbers2(l1, l2); 135 | 136 | } 137 | 138 | static void myPrint(ListNode l) { 139 | ListNode temp = l; 140 | do { 141 | System.out.print(temp.val + ", "); 142 | if (temp.next == null) 143 | break; 144 | temp =temp.next; 145 | } while(true); 146 | 147 | System.out.println(); 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /temp/Treble_VNDK.md: -------------------------------------------------------------------------------- 1 | # Treble VNDK 学习笔记 2 | 3 | 4 | -------------------------------------------------------------------------------- /temp/UsageStatsService.md: -------------------------------------------------------------------------------- 1 | #UsageStatsService 入门 2 | *** 3 | 最近分析一个ANR问题,原因是卡在了UsageStatsService的一把锁(mLock)上,项目是N平台,经过与O平台的对比,发现在O平台上已经增加了一把新锁,来解决UsageStatsService读取数据慢的问题, 4 | 同时发现google为了让UsageStatsService尽可能的不要耗时,把里面的log打印代码全部取消了.说明这块的代码确实是可能会有较长的耗时,会导致ANR(我遇到的问题),也有可能会使得屏幕 5 | 响应慢. 6 | google patch: 7 | 8 | 9 | google 给该patch的说明是:Reduce screen on delay during UsageStats rollover. 10 | *** 11 | 基于上面问题的解决过程,所以有了这篇学习笔记. 12 | 13 | ##1. UsageStatsService简介 14 | UsageStatsService是Android私有service,主要作用是收集用户使用每一个APP的频率、使用时长.google说是为了给用户更好的使用体验(确实有该作用),但是由于google自家的app 15 | 不是开源的,所以google有没有去搜集用户的隐私数据,不得而知. 16 | UsageStatsServicede 使用: 17 | 1. 18 | 2. 19 | 20 | 21 | 22 | 23 | 从源码的角度分析该服务,先从该服务的启动开始, 24 | 25 | 26 | -------------------------------------------------------------------------------- /temp/WindowManagerService启动.md: -------------------------------------------------------------------------------- 1 | # WindowManagerService启动流程(Android O) 2 | 3 | **平台: Android 8.1** 4 | 5 | ## 1 WMS启动 6 | `frameworks/base/services/java/com/android/server/SystemServer.java` 7 | ``` 8 | private void startOtherServices() { 9 | WindowManagerService wm = null; 10 | // WMS needs sensor service ready 11 | ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE); 12 | mSensorServiceStart = null; 13 | wm = WindowManagerService.main(context, inputManager, 14 | mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, 15 | !mFirstBoot, mOnlyCore, new PhoneWindowManager());//1.1 16 | ServiceManager.addService(Context.WINDOW_SERVICE, wm); 17 | ServiceManager.addService(Context.INPUT_SERVICE, inputManager); 18 | traceEnd(); 19 | 20 | 21 | wm.displayReady();//1.2 22 | 23 | wm.systemReady();//1.3 24 | } 25 | ``` 26 | 27 | ### 1.1 WindowManagerService.main 28 | 29 | ``` 30 | public static WindowManagerService main(final Context context, final InputManagerService im, 31 | final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore, 32 | WindowManagerPolicy policy) { 33 | DisplayThread.getHandler().runWithScissors(() -> 34 | sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, 35 | onlyCore, policy), 0); 36 | return sInstance; 37 | } 38 | ``` 39 | 40 | 进入WindowManagerService的构造方法: 41 | 42 | ``` 43 | private WindowManagerService(Context context, InputManagerService inputManager, 44 | boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore, 45 | WindowManagerPolicy policy) { 46 | installLock(this, INDEX_WINDOW); 47 | mRoot = new RootWindowContainer(this); 48 | mContext = context; 49 | mHaveInputMethods = haveInputMethods; 50 | mAllowBootMessages = showBootMsgs; 51 | mOnlyCore = onlyCore; 52 | 53 | mInputManager = inputManager; // Must be before createDisplayContentLocked. 54 | mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); 55 | mDisplaySettings = new DisplaySettings(); 56 | mDisplaySettings.readSettingsLocked(); 57 | 58 | mWindowPlacerLocked = new WindowSurfacePlacer(this); 59 | mPolicy = policy; 60 | mTaskSnapshotController = new TaskSnapshotController(this); 61 | 62 | LocalServices.addService(WindowManagerPolicy.class, mPolicy); 63 | 64 | if(mInputManager != null) { 65 | final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM); 66 | mPointerEventDispatcher = inputChannel != null 67 | ? new PointerEventDispatcher(inputChannel) : null; 68 | } else { 69 | mPointerEventDispatcher = null; 70 | } 71 | 72 | mFxSession = new SurfaceSession(); 73 | mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); 74 | mDisplays = mDisplayManager.getDisplays(); 75 | for (Display display : mDisplays) { 76 | createDisplayContentLocked(display);//创建DisplayContent 77 | } 78 | mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy); 79 | 80 | mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 81 | mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); 82 | 83 | if (mPowerManagerInternal != null) { 84 | mPowerManagerInternal.registerLowPowerModeObserver( 85 | new PowerManagerInternal.LowPowerModeListener() { 86 | @Override 87 | public int getServiceType() { 88 | return ServiceType.ANIMATION; 89 | } 90 | 91 | @Override 92 | public void onLowPowerModeChanged(PowerSaveState result) { 93 | synchronized (mWindowMap) { 94 | final boolean enabled = result.batterySaverEnabled; 95 | if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) { 96 | mAnimationsDisabled = enabled; 97 | dispatchNewAnimatorScaleLocked(null); 98 | } 99 | } 100 | } 101 | }); 102 | mAnimationsDisabled = mPowerManagerInternal 103 | .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled; 104 | } 105 | mScreenFrozenLock = mPowerManager.newWakeLock( 106 | PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); 107 | mScreenFrozenLock.setReferenceCounted(false); 108 | 109 | mAppTransition = new AppTransition(context, this); 110 | mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier); 111 | 112 | final AnimationHandler animationHandler = new AnimationHandler(); 113 | animationHandler.setProvider(new SfVsyncFrameCallbackProvider()); 114 | mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition, 115 | AnimationThread.getHandler(), animationHandler); 116 | mActivityManager = ActivityManager.getService(); 117 | mAmInternal = LocalServices.getService(ActivityManagerInternal.class); 118 | mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); 119 | AppOpsManager.OnOpChangedInternalListener opListener = 120 | new AppOpsManager.OnOpChangedInternalListener() { 121 | @Override public void onOpChanged(int op, String packageName) { 122 | updateAppOpsState(); 123 | } 124 | }; 125 | mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener); 126 | mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener); 127 | 128 | // Get persisted window scale setting 129 | mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(), 130 | Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting); 131 | mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(), 132 | Settings.Global.TRANSITION_ANIMATION_SCALE, 133 | context.getResources().getFloat( 134 | R.dimen.config_appTransitionAnimationDurationScaleDefault)); 135 | 136 | setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(), 137 | Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting)); 138 | 139 | IntentFilter filter = new IntentFilter(); 140 | // Track changes to DevicePolicyManager state so we can enable/disable keyguard. 141 | filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 142 | // Listen to user removal broadcasts so that we can remove the user-specific data. 143 | filter.addAction(Intent.ACTION_USER_REMOVED); 144 | mContext.registerReceiver(mBroadcastReceiver, filter); 145 | 146 | mSettingsObserver = new SettingsObserver(); 147 | 148 | mHoldingScreenWakeLock = mPowerManager.newWakeLock( 149 | PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM); 150 | mHoldingScreenWakeLock.setReferenceCounted(false); 151 | 152 | mAnimator = new WindowAnimator(this); 153 | mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( 154 | com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); 155 | 156 | 157 | LocalServices.addService(WindowManagerInternal.class, new LocalService()); 158 | initPolicy(); // 初始化策略 159 | 160 | // Add ourself to the Watchdog monitors. 161 | Watchdog.getInstance().addMonitor(this); 162 | 163 | openSurfaceTransaction(); 164 | try { 165 | createWatermarkInTransaction(); 166 | } finally { 167 | closeSurfaceTransaction(); 168 | } 169 | 170 | showEmulatorDisplayOverlayIfNeeded(); 171 | } 172 | ``` 173 | 174 | ### 1.2 wm.displayReady() 175 | 176 | ``` 177 | public void displayReady() { 178 | for (Display display : mDisplays) { 179 | displayReady(display.getDisplayId()); 180 | } 181 | 182 | synchronized(mWindowMap) { 183 | final DisplayContent displayContent = getDefaultDisplayContentLocked(); 184 | if (mMaxUiWidth > 0) { 185 | displayContent.setMaxUiWidth(mMaxUiWidth); 186 | } 187 | readForcedDisplayPropertiesLocked(displayContent); 188 | mDisplayReady = true; 189 | } 190 | 191 | try { 192 | mActivityManager.updateConfiguration(null); 193 | } catch (RemoteException e) { 194 | } 195 | 196 | synchronized(mWindowMap) { 197 | mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( 198 | PackageManager.FEATURE_TOUCHSCREEN); 199 | configureDisplayPolicyLocked(getDefaultDisplayContentLocked()); 200 | } 201 | 202 | try { 203 | mActivityManager.updateConfiguration(null); 204 | } catch (RemoteException e) { 205 | } 206 | 207 | updateCircularDisplayMaskIfNeeded(); 208 | } 209 | ``` 210 | 211 | ### 1.2 wm.systemReady() 212 | 213 | ``` 214 | public void systemReady() { 215 | mPolicy.systemReady(); 216 | mTaskSnapshotController.systemReady(); 217 | mHasWideColorGamutSupport = queryWideColorGamutSupport(); 218 | } 219 | ``` 220 | -------------------------------------------------------------------------------- /temp/android 抓取log.md: -------------------------------------------------------------------------------- 1 | # Android 抓取log 2 | 3 | **平台: Android 8.0 ** 4 | 5 | 在android中有各类log,日志文件,在此做一个总结.以及简单的解释. 6 | 7 | # 1 logcat 8 | 9 | ## 1.1 日志类型简介 10 | 11 | 有四种类型的日志: 12 | 13 | - main :应用程序级别的log 14 | - system : 系统级别的日志 15 | - radio : 与无线点设备相关的日志 16 | - events : 记录一些事件的日志,用于诊断系统问题的. 17 | 18 | main,system,radio类型的日志是形式是: 19 | ``` 20 | prioprity |tag |msg 21 | ``` 22 | eg: 23 | ``` 24 | 05-09 12:32:26.454 752 3893 V ActivityManager: Sending arguments to: ServiceRecord{d2cb13e u0 com.jrdcom.mmitest/.common.ParaShow} android.content.Intent$FilterComparison@34956390 args=Intent { cmp=com.jrdcom.mmitest/.common.ParaShow } 25 | ``` 26 | prioprity : V D I W E F 27 | events的log的结构如下: 28 | ``` 29 | tag |msg 30 | ``` 31 | eg: 32 | ``` 33 | 05-09 07:46:33.523 752 2906 I am_create_activity: [0,221906435,9,com.android.settings/.Settings$SystemDashboardActivity,NULL,NULL,NULL,32768] 34 | ``` 35 | 在`frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags`对`am_create_activity`进行了结构说明: 36 | ``` 37 | # A new activity is being created in an existing task: 38 | 30005 am_create_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5) 39 | ``` 40 | 还有一部分在`frameworks/base/services/core/java/com/android/server/EventLogTags.logtags`.这个文件在设备的`/system/etc/event-log-tags` 41 | 对`(User|1|5)`对应例子里`[]`里面的第一个参数0,这个0名称叫user,数据类型是1,数据单位是5. 42 | 43 | 数据类型: 44 | 45 | - 1: int 46 | - 2: long 47 | - 3: string 48 | - 4: list 49 | 50 | 数据单位: 51 | 52 | - 1: Number of objects(对象个数) 53 | - 2: Number of bytes(字节数) 54 | - 3: Number of milliseconds(毫秒) 55 | - 4: Number of allocations(分配个数) 56 | - 5: Id 57 | - 6: Percent(百分比) 58 | 59 | ## 1.2 获取四种log的方式 60 | ``` 61 | adb logcat -v time > logcat.txt //默认是-b main -b system 62 | adb logcat -v time -b main 63 | adb logcat -v time -b radio 64 | adb logcat -v time -b system 65 | adb logcat -v time -b events 66 | ``` 67 | 68 | # 2 bugreport 69 | 70 | ## 2.1 简介与获取 71 | bugreport 会输出系统的信息,一般会比较大.获取: 72 | ``` 73 | adb bugreport 74 | ``` 75 | 76 | ## 2.2 chkbugreport分析工具 77 | 获取的bugreport太大,不利于分析,sony公司做了一个分析工具,可以使用该工具分析.该工具是一个jar包,可以直接获取jar包, 78 | 也可以使用源码进行编译获取.源码地址:https://github.com/sonyxperiadev/ChkBugReport 79 | 下载源码: 80 | ``` 81 | git clone https://github.com/sonyxperiadev/ChkBugReport.git 82 | ``` 83 | 执行`ChkBugReport/core/createjar.sh`脚本,会产生一个jar文件`chkbugreport-0.5-216.jar`,将该文件放在`home/bin/`目录下,方便其他地方使用.将获取的bugreport.zip,解压. 84 | 使用 85 | ``` 86 | chkbugreport.jar bugreport-android-O00623-2018-05-09-14-18-58.txt 87 | ``` 88 | 会在目录下产生一个对应的out文件夹,双击打开里面的`index.html`.就可以更加清晰的分析bugreport内容. 89 | 90 | ## 2.3 battery-historian工具 91 | 92 | Google也推出了自己的用于分析bugreport,主要是用于分析耗电软件,github:https://github.com/google/battery-historian.环境配置的方法按照说明即可,在 93 | 网上也有非常详细的配置教程,注意的一点就是要确保网络可以链接到Google. 94 | 该工具主要用于分析耗电,可以可视化.而且持续更新,由于sony在手机行业的溃败,chkbugreport工具已经没有再更新,不过不影响他的正常使用. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /temp/android_input系统(一).md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/android_input系统(一).md -------------------------------------------------------------------------------- /temp/app-debug-2.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/app-debug-2.apk -------------------------------------------------------------------------------- /temp/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/app-debug.apk -------------------------------------------------------------------------------- /temp/broadcast/broadcast.md: -------------------------------------------------------------------------------- 1 | # android broadcast 2 | 3 | ## 0 广播的分类 4 | android 的广播分发送和接受两个部分. 5 | 6 | 广播发送分类: 7 | - 普通广播 8 | - 有序广播 9 | - 粘性广播 10 | 11 | 广播接收分类: 12 | - 静态注册 13 | - 动态注册 14 | 15 | ## 1 广播注册 16 | 注册广播一般使用`registerReceiver` 方法, 由于 17 | ``` 18 | registerReceiver(myBroadcastReceiver,intentFilter); 19 | ``` 20 | 21 | 22 | ## 2 广播发送 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /temp/broadcast/broadcast.md~: -------------------------------------------------------------------------------- 1 | # android broadcast 2 | 3 | ## 0 广播的分类 4 | android 的广播分发送和接受两个部分. 5 | 6 | 广播发送分类: 7 | - 普通广播 8 | - 有序广播 9 | - 粘性广播 10 | 11 | 广播接收分类: 12 | - 静态注册 13 | - 动态注册 14 | 15 | ## 1 广播注册 16 | 17 | 18 | 19 | 20 | 21 | ## 2 广播发送 22 | -------------------------------------------------------------------------------- /temp/class/_images/ASCII.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/ASCII.jpg -------------------------------------------------------------------------------- /temp/class/_images/access_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/access_flag.png -------------------------------------------------------------------------------- /temp/class/_images/access_flag_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/access_flag_2.png -------------------------------------------------------------------------------- /temp/class/_images/class_detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/class_detail.jpg -------------------------------------------------------------------------------- /temp/class/_images/class_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/class_file.png -------------------------------------------------------------------------------- /temp/class/_images/cp_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/cp_struct.png -------------------------------------------------------------------------------- /temp/class/_images/methon_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/class/_images/methon_info.png -------------------------------------------------------------------------------- /temp/decode_vdex/GUI_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/decode_vdex/GUI_download.png -------------------------------------------------------------------------------- /temp/decode_vdex/dex2jar_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/decode_vdex/dex2jar_download.png -------------------------------------------------------------------------------- /temp/decode_vdex/dex_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/decode_vdex/dex_version.png -------------------------------------------------------------------------------- /temp/decode_vdex/gui_open_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/decode_vdex/gui_open_file.png -------------------------------------------------------------------------------- /temp/decode_vdex/vdex.md: -------------------------------------------------------------------------------- 1 | # Android P 反编译vdex 2 | 3 | 本文记一次解析YouTube的vdex的过程.由于在Android P中有一种新的dex文件格式cdex, 4 | 5 | ## 0 准备文件 6 | 从机器中拷贝出文件: 7 | 8 | ``` 9 | adb pull system/app/YouTube ./ 10 | ``` 11 | 12 | 目录结构如下: 13 | 14 | ``` 15 | YouTube | 16 | |--YouTube.apk 17 | |--oat/arm 18 | |--YouTube.odex 19 | |--YouTube.vdex 20 | ``` 21 | 本例子中,要解析的文件就是`YouTube.vdex`. 22 | 23 | 整个流程如下图 24 | 25 | ![vdex](./vdex.png) 26 | 27 | **在以下解析过程中,如果提示权限问题,请自行使用`chmod`添加可执行权限,下文中不在赘述!!!** 28 | 29 | ## 1 解析vdex到cdex文件 30 | 31 | 需要用到一个开源工具[vdexExtractor](https://github.com/anestisb/vdexExtractor), 32 | 33 | ``` 34 | git clone https://github.com/anestisb/vdexExtractor.git 35 | ``` 36 | 执行`vdexExtractor/make.sh`, 就会在产生`vdexExtractor/bin/vdexExtractor`. 37 | 使用bin文件`vdexExtractor`解析vdex文件: 38 | 39 | ``` 40 | ./vdexExtractor -i ./YouTube.vdex -o ./ --deps -f 41 | ``` 42 | > 此处将vdex文件和vdexExtractor放到同一目录下 43 | 44 | 解析完成后会得到4个cdex文件:`YouTube_classes.cdex`,`YouTube_classes2.cdex`,`YouTube_classes3.cdex`,`YouTube_classes4.cdex`. 45 | 46 | > vdexExtractor 的详细使用,可以使用`./vdexExtractor -h` 查看. 47 | 48 | ## 2 解析cdex到dex文件 49 | 解析cdex到需要用到[compact_dex_converter](https://github.com/anestisb/vdexExtractor/issues/23),在网页中下载`compact_dex_converter_linux.zip` 50 | 可以在ubuntu下使用. 51 | 52 | 使用`compact_dex_converter`,解析 53 | ``` 54 | ./compact_dex_converter YouTube_classes.cdex 55 | ``` 56 | > 四个cdex文件我们以YouTube_classes.cdex为例 57 | 58 | 运行完会产生一个`YouTube_classes.cdex.new`文件,该文件就是我们的dex文件。我们可以用vim打开改文件,文件的开头就是dex文件的格式。 59 | 60 | 使用`xxd`命令将`YouTube_classes.cdex.new`转换成十六进制的文件 61 | 62 | ``` 63 | xxd YouTube_classes.cdex.new > YouTube_classes.xxd 64 | ``` 65 | 使用vim 打开十六进制文件: 66 | 67 | ``` 68 | 0000000: 6465 780a 3033 3900 269f f4eb 7c38 1377 dex.039.&...|8.w 69 | 0000010: 2e77 ca50 98c9 a450 8edb 19c1 1701 32b8 .w.P...P......2. 70 | 0000020: f000 7a00 7000 0000 7856 3412 0000 0000 ..z.p...xV4..... 71 | 0000030: 0000 0000 1400 7a00 1496 0000 7000 0000 ......z.....p... 72 | 0000040: 1c49 0000 c058 0200 6533 0000 307d 0300 .I...X..e3..0}.. 73 | 0000050: d3c4 0000 ece5 0500 f6ff 0000 840c 0c00 ................ 74 | 0000060: 8c30 0000 340c 1400 38e3 5f00 b81d 1a00 .0..4...8._..... 75 | 0000070: 4451 6f00 4651 6f00 4a51 6f00 5e51 6f00 DQo.FQo.JQo.^Qo. 76 | 0000080: 7251 6f00 8751 6f00 9a51 6f00 ae51 6f00 rQo..Qo..Qo..Qo. 77 | 0000090: c251 6f00 d851 6f00 ee51 6f00 0452 6f00 .Qo..Qo..Qo..Ro. 78 | 00000a0: 1a52 6f00 3052 6f00 4752 6f00 5d52 6f00 .Ro.0Ro.GRo.]Ro. 79 | 00000b0: 7152 6f00 8652 6f00 9e52 6f00 b652 6f00 qRo..Ro..Ro..Ro. 80 | ``` 81 | > 这里只是显示了开头的部分 82 | 83 | 熟悉dex文件结构的你可能一眼就看出这个是dex文件了. 84 | 85 | ## 3 解析dex到jar 86 | 解析dex文件到jar文件需要用到[dex2jar](https://github.com/pxb1988/dex2jar/releases). 87 | 找到`Latest release`,进行下载. 88 | 89 | ![dex2jar_download.png](./dex2jar_download.png) 90 | 91 | 查看工具的内容: 92 | 93 | ``` 94 | d2j-baksmali.bat d2j-dex2jar.sh d2j-dex-recompute-checksum.bat 95 | d2j_invoke.sh d2j-jar2jasmin.bat d2j-jasmin2jar.sh d2j-std-apk.bat 96 | d2j-baksmali.sh d2j-dex2smali.bat d2j-dex-recompute-checksum.sh 97 | d2j-jar2dex.bat d2j-jar2jasmin.sh d2j-smali.bat d2j-std-apk.sh 98 | d2j-dex2jar.bat d2j-dex2smali.sh d2j_invoke.bat 99 | d2j-jar2dex.sh d2j-jasmin2jar.bat d2j-smali.sh lib 100 | ``` 101 | 102 | 可以看到工具里面既有sh文件,也有bat文件,该工具适用于Linux & Windows.这里使用shell版本. 103 | 104 | ``` 105 | ./dex2jar-2.0/d2j-dex2jar.sh YouTube_classes.cdex.new 106 | ``` 107 | 108 | 可能你会遇到这样的报错: 109 | 110 | ``` 111 | dex2jar YouTube_classes.cdex.new -> ./YouTube_classes.cdex-dex2jar.jar 112 | com.googlecode.d2j.DexException: not support version. 113 | at com.googlecode.d2j.reader.DexFileReader.(DexFileReader.java:151) 114 | at com.googlecode.d2j.reader.DexFileReader.(DexFileReader.java:211) 115 | at com.googlecode.dex2jar.tools.Dex2jarCmd.doCommandLine(Dex2jarCmd.java:104) 116 | at com.googlecode.dex2jar.tools.BaseCmd.doMain(BaseCmd.java:288) 117 | at com.googlecode.dex2jar.tools.Dex2jarCmd.main(Dex2jarCmd.java:32) 118 | ``` 119 | 120 | 这里如果遇到版本报错, 是由于dex文件版本导致的,可以用vim打开`YouTube_classes.cdex.new`,将第二行的开头的`039`改成`036`. 121 | 122 | ![dex_version](./dex_version.png) 123 | 124 | 再执行`./dex2jar-2.0/d2j-dex2jar.sh YouTube_classes.cdex.new`即可.会产生文件`YouTube_classes.cdex-dex2jar.jar`. 125 | 这就是我们需要的jar文件. 126 | 127 | ## 4 解析jar文件 128 | 查看jar文件使用[JD-GUI](!http://jd.benow.ca/) 129 | 130 | ![GUI_download](./GUI_download.png) 131 | 132 | > 现在所需版本即可. 133 | 134 | 启动`jd-gui`,打开刚刚解析好的jar文件即可. 135 | 136 | ![gui_open_file](./gui_open_file.png) 137 | 138 | ## 参看文献 139 | - [JD-GUI官网](http://java-decompiler.github.io/) 140 | - [vdexExtractor官方仓库](https://github.com/anestisb/vdexExtractor) 141 | - [compact_dex_converter社区贡献](https://github.com/anestisb/vdexExtractor/issues/23) 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /temp/decode_vdex/vdex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/decode_vdex/vdex.png -------------------------------------------------------------------------------- /temp/dex/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/HelloWorld.class -------------------------------------------------------------------------------- /temp/dex/HelloWorld.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/HelloWorld.dex -------------------------------------------------------------------------------- /temp/dex/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | System.out.println("Hello, World!"); 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /temp/dex/data_type.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/data_type.jpg -------------------------------------------------------------------------------- /temp/dex/dex&class.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/dex&class.jpg -------------------------------------------------------------------------------- /temp/dex/dex.md: -------------------------------------------------------------------------------- 1 | # dex 文件解析 2 | 3 | dex文件是android特有的文件,对应java里面的class,更加适合ARM架构.本文的重点是解析一个dex文件. 4 | 5 | ## 0 工具介绍 6 | 分析dex的过程会用到两个bin文件,在Sdk里面 7 | 8 | ``` 9 | Android/Sdk/build-tools/28.0.3/dx 10 | Android/Sdk/build-tools/28.0.3/dexdump 11 | ``` 12 | 28.0.3是版本号。 13 | 14 | `dx`用于生成dex文件,`dexdump`用于解析`dex`文件,类似与解析`class`文件的`javap` 15 | 16 | `dx`,`dexdump `的源码位于 17 | 18 | ``` 19 | aosp/dalvik/dx 20 | aosp/dalvik/dexdump 21 | ``` 22 | 23 | dex 文件能将多个class封装到一个dex文件里,合并他们的constant pool & data: 24 | 25 | ![dex&class.jpg](dex&class.jpg) 26 | 27 | > 图片源自网络 28 | 29 | ## 1 实例介绍 30 | 31 | java文件:HelloWorld.java 32 | 33 | ``` 34 | public class HelloWorld { 35 | public static void main(String[] args) { 36 | System.out.println("Hello, World!"); 37 | } 38 | } 39 | ``` 40 | 通过运行`javac HelloWorld.java` ,产生 `HelloWorld.class`,接着运行`dx --dex --output=HelloWorld.dex HelloWorld.class` 41 | 产生dex文件`HelloWorld.dex`. 42 | 43 | 使用`xxd`命令可以以十六进制的形式查看dex文件, `xxd HelloWorld.dex` : 44 | 45 | ``` 46 | 0000000: 6465 780a 3033 3500 1561 511c 0915 68b5 dex.035..aQ...h. 47 | 0000010: ded9 ceb5 e673 40d2 14d2 3c20 185e abad .....s@...< .^.. 48 | 0000020: ec02 0000 7000 0000 7856 3412 0000 0000 ....p...xV4..... 49 | 0000030: 0000 0000 4c02 0000 0e00 0000 7000 0000 ....L.......p... 50 | 0000040: 0700 0000 a800 0000 0300 0000 c400 0000 ................ 51 | 0000050: 0100 0000 e800 0000 0400 0000 f000 0000 ................ 52 | 0000060: 0100 0000 1001 0000 bc01 0000 3001 0000 ............0... 53 | 0000070: 7601 0000 7e01 0000 8d01 0000 9e01 0000 v...~........... 54 | 0000080: ac01 0000 c301 0000 d701 0000 eb01 0000 ................ 55 | 0000090: ff01 0000 0202 0000 0602 0000 1b02 0000 ................ 56 | 00000a0: 2102 0000 2602 0000 0300 0000 0400 0000 !...&........... 57 | 00000b0: 0500 0000 0600 0000 0700 0000 0800 0000 ................ 58 | 00000c0: 0a00 0000 0800 0000 0500 0000 0000 0000 ................ 59 | 00000d0: 0900 0000 0500 0000 6801 0000 0900 0000 ........h....... 60 | 00000e0: 0500 0000 7001 0000 0400 0100 0c00 0000 ....p........... 61 | 00000f0: 0000 0000 0000 0000 0000 0200 0b00 0000 ................ 62 | 0000100: 0100 0100 0d00 0000 0200 0000 0000 0000 ................ 63 | 0000110: 0000 0000 0100 0000 0200 0000 0000 0000 ................ 64 | 0000120: 0200 0000 0000 0000 3b02 0000 0000 0000 ........;....... 65 | 0000130: 0100 0100 0100 0000 2f02 0000 0400 0000 ......../....... 66 | 0000140: 7010 0300 0000 0e00 0300 0100 0200 0000 p............... 67 | 0000150: 3402 0000 0800 0000 6200 0000 1a01 0100 4.......b....... 68 | 0000160: 6e20 0200 1000 0e00 0100 0000 0300 0000 n .............. 69 | 0000170: 0100 0000 0600 063c 696e 6974 3e00 0d48 .........H 70 | 0000180: 656c 6c6f 2c20 576f 726c 6421 000f 4865 ello, World!..He 71 | 0000190: 6c6c 6f57 6f72 6c64 2e6a 6176 6100 0c4c lloWorld.java..L 72 | 00001a0: 4865 6c6c 6f57 6f72 6c64 3b00 154c 6a61 HelloWorld;..Lja 73 | 00001b0: 7661 2f69 6f2f 5072 696e 7453 7472 6561 va/io/PrintStrea 74 | 00001c0: 6d3b 0012 4c6a 6176 612f 6c61 6e67 2f4f m;..Ljava/lang/O 75 | 00001d0: 626a 6563 743b 0012 4c6a 6176 612f 6c61 bject;..Ljava/la 76 | 00001e0: 6e67 2f53 7472 696e 673b 0012 4c6a 6176 ng/String;..Ljav 77 | 00001f0: 612f 6c61 6e67 2f53 7973 7465 6d3b 0001 a/lang/System;.. 78 | 0000200: 5600 0256 4c00 135b 4c6a 6176 612f 6c61 V..VL..[Ljava/la 79 | 0000210: 6e67 2f53 7472 696e 673b 0004 6d61 696e ng/String;..main 80 | 0000220: 0003 6f75 7400 0770 7269 6e74 6c6e 0001 ..out..println.. 81 | 0000230: 0007 0e00 0301 0007 0e78 0000 0002 0000 .........x...... 82 | 0000240: 8180 04b0 0201 09c8 0200 0000 0d00 0000 ................ 83 | 0000250: 0000 0000 0100 0000 0000 0000 0100 0000 ................ 84 | 0000260: 0e00 0000 7000 0000 0200 0000 0700 0000 ....p........... 85 | 0000270: a800 0000 0300 0000 0300 0000 c400 0000 ................ 86 | 0000280: 0400 0000 0100 0000 e800 0000 0500 0000 ................ 87 | 0000290: 0400 0000 f000 0000 0600 0000 0100 0000 ................ 88 | 00002a0: 1001 0000 0120 0000 0200 0000 3001 0000 ..... ......0... 89 | 00002b0: 0110 0000 0200 0000 6801 0000 0220 0000 ........h.... .. 90 | 00002c0: 0e00 0000 7601 0000 0320 0000 0200 0000 ....v.... ...... 91 | 00002d0: 2f02 0000 0020 0000 0100 0000 3b02 0000 /.... ......;... 92 | 00002e0: 0010 0000 0100 0000 4c02 0000 ........L... 93 | ``` 94 | 95 | 利用dexdump解析dex文件`dexdump -f HelloWorld.dex`: 96 | 97 | ``` 98 | Processing 'HelloWorld.dex'... 99 | Opened 'HelloWorld.dex', DEX version '035' 100 | DEX file header: 101 | magic : 'dex\n035\0' 102 | checksum : 1c516115 103 | signature : 0915...abad 104 | file_size : 748 105 | header_size : 112 106 | link_size : 0 107 | link_off : 0 (0x000000) 108 | string_ids_size : 14 109 | string_ids_off : 112 (0x000070) 110 | type_ids_size : 7 111 | type_ids_off : 168 (0x0000a8) 112 | proto_ids_size : 3 113 | proto_ids_off : 196 (0x0000c4) 114 | field_ids_size : 1 115 | field_ids_off : 232 (0x0000e8) 116 | method_ids_size : 4 117 | method_ids_off : 240 (0x0000f0) 118 | class_defs_size : 1 119 | class_defs_off : 272 (0x000110) 120 | data_size : 444 121 | data_off : 304 (0x000130) 122 | 123 | Class #0 - 124 | Class descriptor : 'LHelloWorld;' 125 | Access flags : 0x0001 (PUBLIC) 126 | Superclass : 'Ljava/lang/Object;' 127 | Interfaces - 128 | Static fields - 129 | Instance fields - 130 | Direct methods - 131 | #0 : (in LHelloWorld;) 132 | name : '' 133 | type : '()V' 134 | access : 0x10001 (PUBLIC CONSTRUCTOR) 135 | code - 136 | registers : 1 137 | ins : 1 138 | outs : 1 139 | insns size : 4 16-bit code units 140 | catches : (none) 141 | positions : 142 | 0x0000 line=1 143 | locals : 144 | 0x0000 - 0x0004 reg=0 this LHelloWorld; 145 | #1 : (in LHelloWorld;) 146 | name : 'main' 147 | type : '([Ljava/lang/String;)V' 148 | access : 0x0009 (PUBLIC STATIC) 149 | code - 150 | registers : 3 151 | ins : 1 152 | outs : 2 153 | insns size : 8 16-bit code units 154 | catches : (none) 155 | positions : 156 | 0x0000 line=3 157 | 0x0007 line=4 158 | locals : 159 | Virtual methods - 160 | source_file_idx : 2 (HelloWorld.java) 161 | ``` 162 | 163 | 接下来的内容就是分析将dex内容对应到dexdump解析出来的内容. 164 | 165 | ## 2 实例分析 166 | 167 | dex文件的结构定义在: `dalvik/libdex/DexFile.h` 168 | 169 | ``` 170 | /* 171 | * Structure representing a DEX file. 172 | * 173 | * Code should regard DexFile as opaque, using the API calls provided here 174 | * to access specific structures. 175 | */ 176 | struct DexFile { 177 | /* directly-mapped "opt" header */ 178 | const DexOptHeader* pOptHeader; 179 | 180 | /* pointers to directly-mapped structs and arrays in base DEX */ 181 | const DexHeader* pHeader; 182 | const DexStringId* pStringIds; 183 | const DexTypeId* pTypeIds; 184 | const DexFieldId* pFieldIds; 185 | const DexMethodId* pMethodIds; 186 | const DexProtoId* pProtoIds; 187 | const DexClassDef* pClassDefs; 188 | const DexLink* pLinkData; 189 | 190 | /* 191 | * These are mapped out of the "auxillary" section, and may not be 192 | * included in the file. 193 | */ 194 | const DexClassLookup* pClassLookup; 195 | const void* pRegisterMapPool; // RegisterMapClassPool 196 | 197 | /* points to start of DEX file data */ 198 | const u1* baseAddr; 199 | 200 | /* track memory overhead for auxillary structures */ 201 | int overhead; 202 | 203 | /* additional app-specific data structures associated with the DEX */ 204 | //void* auxData; 205 | }; 206 | ``` 207 | 208 | ` DexHeader` 的定义也在`DexFile.h`中: 209 | 210 | ``` 211 | /* 212 | * Direct-mapped "header_item" struct. 213 | */ 214 | struct DexHeader { 215 | u1 magic[8]; /* includes version number */ 216 | u4 checksum; /* adler32 checksum */ 217 | u1 signature[kSHA1DigestLen]; /* SHA-1 hash */ 218 | u4 fileSize; /* length of entire file */ 219 | u4 headerSize; /* offset to start of next section */ 220 | u4 endianTag; 221 | u4 linkSize; 222 | u4 linkOff; 223 | u4 mapOff; 224 | u4 stringIdsSize; 225 | u4 stringIdsOff; 226 | u4 typeIdsSize; 227 | u4 typeIdsOff; 228 | u4 protoIdsSize; 229 | u4 protoIdsOff; 230 | u4 fieldIdsSize; 231 | u4 fieldIdsOff; 232 | u4 methodIdsSize; 233 | u4 methodIdsOff; 234 | u4 classDefsSize; 235 | u4 classDefsOff; 236 | u4 dataSize; 237 | u4 dataOff; 238 | }; 239 | ``` 240 | 241 | DexHeader的介绍[官方文章](https://source.android.com/devices/tech/dalvik/dex-format#encoding): 242 | 243 | ![数据类型](dex_header.png) 244 | 245 | >图源自网络 246 | 247 | class 文件的结构 248 | 249 | ``` 250 | /* 251 | * Direct-mapped "class_def_item". 252 | */ 253 | struct DexClassDef { 254 | u4 classIdx; /* index into typeIds for this class */ 255 | u4 accessFlags; 256 | u4 superclassIdx; /* index into typeIds for superclass */ 257 | u4 interfacesOff; /* file offset to DexTypeList */ 258 | u4 sourceFileIdx; /* index into stringIds for source file name */ 259 | u4 annotationsOff; /* file offset to annotations_directory_item */ 260 | u4 classDataOff; /* file offset to class_data_item */ 261 | u4 staticValuesOff; /* file offset to DexEncodedArray */ 262 | }; 263 | ``` 264 | wew 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /temp/dex/dex_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/dex_header.png -------------------------------------------------------------------------------- /temp/dex/dex_product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/dex_product.png -------------------------------------------------------------------------------- /temp/dex/dexdump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/dexdump -------------------------------------------------------------------------------- /temp/dex/dexdump_ubuntu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/dex/dexdump_ubuntu -------------------------------------------------------------------------------- /temp/dex/dx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2007 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Set up prog to be the path of this script, including following symlinks, 18 | # and set up progdir to be the fully-qualified pathname of its directory. 19 | prog="$0" 20 | while [ -h "${prog}" ]; do 21 | newProg=`/bin/ls -ld "${prog}"` 22 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 23 | if expr "x${newProg}" : 'x/' >/dev/null; then 24 | prog="${newProg}" 25 | else 26 | progdir=`dirname "${prog}"` 27 | prog="${progdir}/${newProg}" 28 | fi 29 | done 30 | oldwd=`pwd` 31 | progdir=`dirname "${prog}"` 32 | cd "${progdir}" 33 | progdir=`pwd` 34 | prog="${progdir}"/`basename "${prog}"` 35 | cd "${oldwd}" 36 | 37 | jarfile=dx.jar 38 | libdir="$progdir" 39 | 40 | if [ ! -r "$libdir/$jarfile" ]; then 41 | # set dx.jar location for the SDK case 42 | libdir="$libdir/lib" 43 | fi 44 | 45 | 46 | if [ ! -r "$libdir/$jarfile" ]; then 47 | # set dx.jar location for the Android tree case 48 | libdir=`dirname "$progdir"`/framework 49 | fi 50 | 51 | if [ ! -r "$libdir/$jarfile" ]; then 52 | echo `basename "$prog"`": can't find $jarfile" 53 | exit 1 54 | fi 55 | 56 | # By default, give dx a max heap size of 1 gig. This can be overridden 57 | # by using a "-J" option (see below). 58 | defaultMx="-Xmx1024M" 59 | 60 | # The following will extract any initial parameters of the form 61 | # "-J" from the command line and pass them to the Java 62 | # invocation (instead of to dx). This makes it possible for you to add 63 | # a command-line parameter such as "-JXmx256M" in your scripts, for 64 | # example. "java" (with no args) and "java -X" give a summary of 65 | # available options. 66 | 67 | javaOpts="" 68 | 69 | while expr "x$1" : 'x-J' >/dev/null; do 70 | opt=`expr "x$1" : 'x-J\(.*\)'` 71 | javaOpts="${javaOpts} -${opt}" 72 | if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then 73 | defaultMx="no" 74 | fi 75 | shift 76 | done 77 | 78 | if [ "${defaultMx}" != "no" ]; then 79 | javaOpts="${javaOpts} ${defaultMx}" 80 | fi 81 | 82 | if [ "$OSTYPE" = "cygwin" ]; then 83 | # For Cygwin, convert the jarfile path into native Windows style. 84 | jarpath=`cygpath -w "$libdir/$jarfile"` 85 | else 86 | jarpath="$libdir/$jarfile" 87 | fi 88 | 89 | exec java $javaOpts -jar "$jarpath" "$@" 90 | -------------------------------------------------------------------------------- /temp/linux input subsystem.md: -------------------------------------------------------------------------------- 1 | #linux input subsystem 2 | ##1 3 | 分为三层: 4 | 5 | - 输入子系统事件处理层(EventHandler) 6 | - 输入子系统核心层(InputCore) 7 | - 输入子系统设备驱动层 8 | 9 | 每层的作用简介: 10 | 11 | - *EventHandler*: 12 | 用户编程的接口(设备节点),并处理驱动层提交的数据处理。 13 | - *InputCore*: 14 | 为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据), 15 | 然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。 16 | - *驱动层*: 17 | 主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。 18 | 19 | 20 | -------------------------------------------------------------------------------- /temp/powerHal.drawio: -------------------------------------------------------------------------------- 1 | 3Vtbc5s4FP41zOw+xIO4GT/aid3d2Wa2M+l0233JYJAxG0BUyLHdX78SCDCSHIhtnKaejI0OQhLf0bl9EM28TXYfsJet71EAY83Qg51m3mmGAQzD0NifHuxLydiclIIQRwHv1Ageoh+QC3Uu3UQBzFsdCUIxibK20EdpCn3SknkYo2272wrF7VkzL4SS4MH3Yln6TxSQdSl1bb2R/wGjcF3NDHR+JvGqzlyQr70AbQ9E5lwzbzFCpDxKdrcwZuBVuJTXLY6crReGYUr6XPAYfPls/33/xSZGRL7+lfy7u9/cWG45zLMXb/gd89WSfQUBRps0gGwUXTNn23VE4EPm+ezsliqdytYkiWkL0MMVSgnXInBo24ujMKUNn64SYirg80FM4O7onYAaH7qxIEogwXvahV9g6nZ5Cd9TDkd42yjItrhsfaAcp9KNxzdFWA/d4EYPOHSvgBFcGsUojm9RjHBxrRnY0A0sKs8JRk/w4IxrLE3HuQyslclVliqjargKVI3BUDUkVFOPRM+QyrS5rc2m2sTS5mP2PXW1uavNFtpkxiS06c4kFVBoSBvnNp4pSqEAPhdJu5gBHVEvMeUnkigI2DRKxbZVL1pI0eaLBBdQo9OpRuAo1TiQFsfdtgHTYMp8NUM49vI88ttqoneO9185fkXjG2uM7Kp5tzs8ebfnrVK9ld82azOBgeT0BXRplPFwCMlLrlOthUMnpIC5kmEYl3u5FbAU2PMZPqGILrBWsg3sUdsJGo5VSaphcrTBPuRXHkYGcbCx1T1YiYc0GNWbtz/olrEO+QsLN1ULdwxhm5XDNpuuBvj0faiKdE5MuAW2NqTzfYOqEzd5YatT2gGMs11zskg6KkfcCM3FwtWZS2xETsh/i9mWp0xVDvGA/Ce6J/lAFIWlODiVlXdTiTtdYH/XhiFdn7cshmLWxVVNx7Vnmn3HxtoQlHPXJni68WVilC0EqdptHZjdWGF25lDezVS4NxqbXEebTPiBa2tzR5vq2qwIUu5Ucw0WpOj3rOgznWuTeSGZa7MitNE+k0XR2dRmd+8gkF1IvWNBvZaliF7GNfVrTST9PtNwhfAogUHkEfg0Wns42NJbHCXkKUNbCqulg5F+k0P8HPlltiLnJtwZ4J/PVIdISkxTsFtZr5Z5zaykisMvpSUh3e/Z0ZvnFSdHWauX+gpn5rZBcRUZtyqHMK3Bdns3KOfUMavVyvB9VR0TOEvH7l3HvKBRGex920N0ggsG23ISuNRhfGIO495LvZBa9E/s52tf0PYV9iAqq6KBwvnr13QShqyyynEH0TObkaPZpGoccCl1KwJD8Bhl/iOG/rOfBAcpWzFY7zAgxl2RLPCgu1IameO7cLk6mZQ5RYVmJ1MDVJzCYA7OcCSNZhCvPtLEeup/PwF8CcrXa2Mw8O12eAF9k6nh0JeT5RyS23iTU+wWGP5qCrCEpEcRg4CKqBxMAaZMqUmQM3KkcvcIkzUKUerF80YqhIemz0eEMq6l/yAhe64mlmqqotghL6OuJV7kaV5M60rqo4cj6KR5yj0rq7g3gXOexfTg57vYs2604Y7OqNWMGm1949ey44ZcY42KWztPQ2/FmwGhwJwIltaXMhPHAaLJHqHLLkVm2T2eOAxdvpiCe7MmsnuzVGQMGCxhA6YCljLNWgORK+tN+7kqLq4XuXOMlavF8qqySgDDYnrHS1hKni7zrOwltCmUi3yfsywSMrohL9sEJvTAzzblt17+rIoY22NImZ1YZBj57IemS0mIGfmIUE4efYLjcvDqkPVg3Mcjn00aqr777BVZ7/GS8WiRSYuaKKUo3tlN63MRom6MY8UroqayigtnuqYlEkw7aS+xsiKoKIIRjn7Qnl61/jOSjCNm2DazkX7wAdc0OlPOqd9tUgF6pg9Wz+yhdEhvlT3YF+dzup7uD0T4dAQiMRgDWwpE9dPtNms8WCCSkT9gAHKashUMwLm1Plh6ABoqfHXdmU8XfIS2RujnZA6gQxFGWxGmguYFk2sWPLZc8BzU+5sIw1cqQRFwLlVhdmVbbSpFWc2risnBHo3Y8qORn8/vO5f1+5Vn6Xb8R14PuI7jBzJxWbifxyQPfz2vI7zxoqRZVO5/OK/Tp2r/xSyj8rTdlmGfaRnqitwSXgqseeXXlvbiQDVpfaXS3nkP6fSFN49zbpp8hKURGHBDfLY3tCplOiK8J0+faAbCqmwm0P+sBLTYN6d0vg+QPJRvDvz2+3tJTsQEXJc9sPLd0cFyE2f8FjZzufcUh7S1vrVr6dHfKoVx5Aqqsp2b+qHq+0zlxcdCQGEtqufcJ1gLbTb/YVC6teb/NMz5/w== -------------------------------------------------------------------------------- /temp/settings.md: -------------------------------------------------------------------------------- 1 | # settings界面转屏优化 2 | 3 | author: qli3@tcl.com date:2019/06/19 4 | 5 | ## 0.概述 6 | 在UX测试中,有测试转屏响应速度的问题. 7 | 8 | ## 1.问题描述 9 | 打开settings,旋转屏幕从横到竖,从竖到横.录下旋转的动画,通过数帧计算与对比机器的差距. 10 | 11 | ## 2.解决方案 12 | 13 | ### 2.1 减少转屏动画 14 | 15 | 旋转角度是 90 度的时走的代码逻辑在`ScreenRotationAnimation.java`: 16 | 17 | ``` 18 | case Surface.ROTATION_90: 19 | mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 20 | com.android.internal.R.anim.screen_rotate_plus_90_exit); 21 | mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 22 | com.android.internal.R.anim.screen_rotate_plus_90_enter); 23 | if (USE_CUSTOM_BLACK_FRAME) { 24 | mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext, 25 | com.android.internal.R.anim.screen_rotate_plus_90_frame); 26 | } 27 | break; 28 | ``` 29 | 30 | 修改: `frameworks/base/core/res/res/anim/screen_rotate_plus_90_enter.xml` 31 | 把`config_mediumAnimTime` 修改为 `config_shortAnimTime` 32 | 33 | 在`frameworks/base/core/res/res/values/config.xml`定义了`config_mediumAnimTime`&`config_shortAnimTime` 34 | 35 | ``` 36 | 37 | 200 38 | 39 | 40 | 400 41 | ``` 42 | 43 | 这样可以缩短动画的时间. 44 | 45 | ### 2.2 优化APP转屏流程 46 | 47 | 通过event log对比, 查看转屏流程. 48 | 49 | 在android O中: 50 | 51 | ``` 52 | 06-18 15:12:56.437 916 1018 I am_relaunch_resume_activity: [0,79137715,65,com.android.settings/.Settings] 53 | 06-18 15:12:56.467 1331 1331 I am_on_paused_called: [0,com.android.settings.Settings,handleRelaunchActivity] 54 | 06-18 15:12:56.480 1331 1331 I am_on_stop_called: [0,com.android.settings.Settings,destroy] 55 | 06-18 15:12:56.616 1331 1331 I am_on_resume_called: [0,com.android.settings.Settings,handleRelaunchActivity] 56 | ``` 57 | 58 | 在android P中 : 59 | 60 | ``` 61 | 06-18 15:14:47.822 884 908 I am_relaunch_resume_activity: [0,220143003,1398,com.android.settings/.Settings] 62 | 06-18 15:14:47.838 16171 16171 I am_on_paused_called: [0,com.android.settings.Settings,performPause] 63 | 06-18 15:14:47.840 16171 16171 I am_on_stop_called: [0,com.android.settings.Settings,handleRelaunchActivity] 64 | 06-18 15:14:47.842 16171 16171 I am_on_destroy_called: [0,com.android.settings.Settings,performDestroy] 65 | 06-18 15:14:47.998 16171 16171 I am_on_create_called: [0,com.android.settings.Settings,performCreate] 66 | 06-18 15:14:48.011 16171 16171 I am_on_start_called: [0,com.android.settings.Settings,handleStartActivity] 67 | 06-18 15:14:48.014 16171 16171 I am_on_resume_called: [0,com.android.settings.Settings,RESUME_ACTIVITY] 68 | ``` 69 | 70 | 对比可以看到在P和O之间的转屏流程不一样, 71 | 72 | 由于转屏是在APP内部转屏的,所以,通过在`AndroidManifest`对应的Activity设置 `android:configChanges="orientation|keyboardHidden|screenSize"` 可以优化转屏时间. 73 | 原理是在转屏的时候,设置不重新构建activity. 74 | 75 | ### 2.3 减少设备动画时间 76 | 77 | 修改setting里面,开发者模式里面的Transition animation scale,默认值修改为.5x 这样也可以减少旋转的时间. 78 | 79 | ### 2.4 优化转屏算法 80 | 81 | 在转屏算法上,有一定的优化空间,可参看: 5630465 82 | 83 | ## 参考文章 84 | - [Android转屏流程与优化(Google转屏算法)](https://blog.csdn.net/yun_hen/article/details/78799172) 85 | - [Android性能优化之Rotation Performance](https://chendongqi.me/2017/02/17/rotate_performance/) 86 | -------------------------------------------------------------------------------- /temp/systrace/res/Chrome_open_systrace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/Chrome_open_systrace.png -------------------------------------------------------------------------------- /temp/systrace/res/Chrome_systrace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/Chrome_systrace.png -------------------------------------------------------------------------------- /temp/systrace/res/DDMS_systrace_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/DDMS_systrace_01.png -------------------------------------------------------------------------------- /temp/systrace/res/DDMS_systrace_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/DDMS_systrace_02.png -------------------------------------------------------------------------------- /temp/systrace/res/DDMS_systrace_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/DDMS_systrace_03.png -------------------------------------------------------------------------------- /temp/systrace/res/Setting_systrace_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/Setting_systrace_01.png -------------------------------------------------------------------------------- /temp/systrace/res/Setting_systrace_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/systrace/res/Setting_systrace_02.png -------------------------------------------------------------------------------- /temp/systrace/systrace 的几种获取方式.md: -------------------------------------------------------------------------------- 1 | # systrace的几种获取方式 2 | 3 | ## 0 systrace 简介 4 | systrace是android本身提供的一套分析耗时的工具,可以帮助定位问题.本文只是介绍获取systrace的4种方式. 5 | 并不涉及具体的怎么使用systrace去分析问题. 6 | 7 | ## 1 获取的方式 8 | 9 | ### 1.1 使用DDMS获取 10 | 11 | 打开DDMS,下面是在Ubuntu中打开DDMS的方式,因为新版的Android Studio不再提供进入DDMS的入口. 12 | ``` 13 | Ubuntu~$ ./Android/Sdk/tools/monitor 14 | ``` 15 | 1. DDMS获取systrace第一步 16 | ![DDMS获取systrace第一步](/res/DDMS_systrace_01.png) 17 | 18 | 2. DDMS获取systrace第二步 19 | ![DDMS获取systrace第二步](/res/DDMS_systrace_02.png) 20 | **说明:** 21 | - Destination File: 保存的路径,文件一定要是html类型 22 | - Trace duration(seconds): 抓取时间, 默认是5s 23 | - Trace Buffer Size(kb): buffer大小,抓取的时间加长,该值要相应的加大,一般设置为2048的倍数 24 | - Enable Application Traces from: 如果在app中自己添加了Trace,抓取时,需要选择进程 25 | - Commonly Used Tags:默认都会抓取的Trace Tag 26 | - Advanced Options: 高级选项,依据自己需求选择勾选 27 | 28 | 点击ok之后,就会开始抓取,就可以操作android设备.此时会出现以下界面: 29 | ![DDMS获取systrace第三步](/res/DDMS_systrace_03.png) 30 | 31 | 当界面消失时,就说明抓取完毕, 在指定的路径Destination File,就会有一个html文件,该文件就是systrace文件. 32 | 33 | ### 1.2 使用systrace.py脚本获取 34 | 35 | systrace.py位于`Android/Sdk/platform-tools/systrace`, 使用 `./systrace.py -h` 可以看到参数介绍. 36 | 查看 trace tag 37 | ``` 38 | Ubuntu:~/Android/Sdk/platform-tools/systrace$ ./systrace.py -list 39 | Usage: systrace.py [options] [category1 [category2 ...]] 40 | 41 | systrace.py: error: no such option: -i 42 | Ubuntu:~/Android/Sdk/platform-tools/systrace$ ./systrace.py --list 43 | gfx - Graphics 44 | input - Input 45 | view - View System 46 | webview - WebView 47 | wm - Window Manager 48 | am - Activity Manager 49 | sm - Sync Manager 50 | audio - Audio 51 | video - Video 52 | camera - Camera 53 | hal - Hardware Modules 54 | res - Resource Loading 55 | dalvik - Dalvik VM 56 | rs - RenderScript 57 | bionic - Bionic C Library 58 | power - Power Management 59 | pm - Package Manager 60 | ss - System Server 61 | database - Database 62 | network - Network 63 | adb - ADB 64 | vibrator - Vibrator 65 | aidl - AIDL calls 66 | pdx - PDX services 67 | sched - CPU Scheduling 68 | irq - IRQ Events 69 | i2c - I2C Events 70 | freq - CPU Frequency 71 | idle - CPU Idle 72 | disk - Disk I/O 73 | mmc - eMMC commands 74 | load - CPU Load 75 | sync - Synchronization 76 | workq - Kernel Workqueues 77 | memreclaim - Kernel Memory Reclaim 78 | regulators - Voltage and Current Regulators 79 | binder_driver - Binder Kernel driver 80 | binder_lock - Binder global lock trace 81 | pagecache - Page cache 82 | 83 | ``` 84 | 可以看到和使用DDMS是一样的. 这里给一个Demo使用: 85 | ``` 86 | ./systrace.py gfx input view webview -o ./trace.html 87 | ``` 88 | 这样就会在该目录下产生systrace文件. 89 | 90 | ### 1.3 使用atrace命令获取 91 | 92 | adb shell进入安卓: 93 | ``` 94 | phoneName:/ # atrace --help 95 | usage: atrace [options] [categories...] 96 | options include: 97 | -a appname enable app-level tracing for a comma separated list of cmdlines; * is a wildcard matching any process 98 | -b N use a trace buffer size of N KB 99 | -c trace into a circular buffer 100 | -f filename use the categories written in a file as space-separated 101 | values in a line 102 | -k fname,... trace the listed kernel functions 103 | -n ignore signals 104 | -s N sleep for N seconds before tracing [default 0] 105 | -t N trace for N seconds [default 5] 106 | -z compress the trace dump 107 | --async_start start circular trace and return immediately 108 | --async_dump dump the current contents of circular trace buffer 109 | --async_stop stop tracing and dump the current contents of circular 110 | trace buffer 111 | --stream stream trace to stdout as it enters the trace buffer 112 | Note: this can take significant CPU time, and is best 113 | used for measuring things that are not affected by 114 | CPU performance, like pagecache usage. 115 | --list_categories 116 | list the available tracing categories 117 | -o filename write the trace to the specified file instead 118 | of stdout. 119 | 120 | ``` 121 | 执行下面命令,开始抓取 122 | 123 | ``` 124 | atrace --async_start -c -b 16384 -a '*' res pdx idle dalvik freq am network binder_driver input hal view database disk sched binder_lock wm bionic gfx audio 125 | ``` 126 | 操作android设备,执行下面命令,结束 127 | ``` 128 | atrace --async_stop -z -c -o /data/local/traces/trace-test.ctrace 129 | ``` 130 | 会在产生一个`/data/local/traces/trace-test.ctrace`文件, 将文件copy出来,接下来使用systrace.py 将其转换成html文件 131 | ``` 132 | ./systrace.py --from-file=trace-test.ctrace -o trace-test.html 133 | ``` 134 | 这样就会产生systrace文件 135 | 136 | ### 1.4 开发者选项里获取 137 | 138 | 开发者选项里打开`System Tracing`. 显示如下界面: 139 | ![开发者选项获取systrace](/res/Setting_systrace_01.png) 140 | 141 | 里面有一些配置,都很简单,按字面意思理解就行. 打开Record trace就开始抓取. 142 | 操作设备, 抓取完毕,需要点击通知栏的System Tracing结束.如下图: 143 | ![开发者选项获取systrace](/res/Setting_systrace_02.png) 144 | 145 | 会在/data/local/traces/产生一个带时间戳的ctrace文件, 拷贝出来, 用systrace.py 将其转换成html文件 146 | ``` 147 | ./systrace.py --from-file=traceXXXXX.ctrace -o trace.html 148 | ``` 149 | 这样就会产生systrace文件 150 | 151 | ## 2 打开方式 152 | 153 | 需要使用**Chrome**打开. 154 | - 如果安装了最新版本的Chrome,可以直接双击打开. 155 | - 如果是老版本的Chrome,需要在地址栏输入`chrome://tracing/` 156 | ![Chrome打开systrace](/res/Chrome_systrace.png) 157 | 选择load,加载之前抓取的html, 成功打开后界面: 158 | ![Chrome打开systrace](/res/Chrome_open_systrace.png) 159 | -------------------------------------------------------------------------------- /temp/tct-block.md: -------------------------------------------------------------------------------- 1 | # tct-block实现原理 2 | 3 | ## 1 主要功能模块介绍 4 | 5 | 主要模块的功能模块: 6 | 'vendor/jrdcom/proprietary/tct-blockinfo/': 7 | 8 | ``` 9 | .: 10 | Android.mk README.md src 11 | 12 | ./src: 13 | com 14 | 15 | ./src/com: 16 | tct 17 | 18 | ./src/com/tct: 19 | blockinfo 20 | 21 | ./src/com/tct/blockinfo: 22 | AbstractSampler.java BlockInfo.java CpuSampler.java HandlerThreadFactory.java LogWriter.java LooperMonitor.java StackSampler.java 23 | ``` 24 | 25 | 该模块主要参考[AndroidPerformanceMonitor](https://github.com/markzhai/AndroidPerformanceMonitor),感谢大牛的贡献. 26 | 27 | 最核心的代码在'LooperMonitor.java'中,该模块的核心是基于message处理时间,如果超过一定的阈值,就可以认定为卡顿, 28 | 这样就可以进行我们的操作,比如:搜集系统的log信息,至于搜集哪些信息,我们可以自己定制.而且不需要root权限, 29 | android本身就有接口函数(Looper.myLooper().setMessageLogging),如果app想要使用该功能,就需要将检测的模块集成到代码中, 30 | tct-block功能就是将检测模块直接集成到系统ActivityThread,所有的应用进程就可以被检测了.只要应用卡顿,就搜集卡顿时刻的信息. 31 | 具体的原理可以百度"BlockCanary",有很多介绍. 32 | 33 | 重要代码: 34 | 35 | ``` 36 | @Override 37 | public void println(String x) { 38 | if (Debug.isDebuggerConnected()) { 39 | return ; 40 | } 41 | 42 | if (!mPrintingStarted) { 43 | mStartTimestamp = System.currentTimeMillis(); 44 | mStartThreadTimestamp = SystemClock.currentThreadTimeMillis(); 45 | mPrintingStarted = true; 46 | startDump(); 47 | } else { 48 | final long endTime = System.currentTimeMillis(); 49 | mPrintingStarted = false; 50 | if (isBlock(endTime)) { 51 | long realTimeStart = mStartTimestamp; 52 | long startThreadTimestamp = mStartThreadTimestamp; 53 | long endThreadTimestamp = SystemClock.currentThreadTimeMillis(); 54 | HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() { 55 | @Override 56 | public void run() { 57 | notifyBlockEvent(realTimeStart, endTime, startThreadTimestamp, endThreadTimestamp); 58 | } 59 | }); 60 | } 61 | stopDump(); 62 | } 63 | } 64 | 65 | ``` 66 | 该模块最终被打包成jar文件: 67 | ``` 68 | PRODUCT_PACKAGES += tct-blockinfo 69 | PRODUCT_BOOT_JARS += tct-blockinfo 70 | ``` 71 | 在ActivityThread中使用反射进行调用. 72 | 73 | # 2 ActivityThread调用: 74 | 应用启动进程时会在ActivityThread的里走到handleBindApplication方法,在handleBindApplication方法里面添加: 75 | 76 | ``` 77 | Looper.myLooper().setMessageLogging((android.util.Printer)Class 78 | .forName("com.tct.blockinfo.LooperMonitor") 79 | .getDeclaredConstructor(Context.class).newInstance(mInitialApplication)); 80 | 81 | private Printer mLogging; 82 | public void setMessageLogging(@Nullable Printer printer) { 83 | mLogging = printer; 84 | } 85 | 86 | public static void loop() { 87 | // This must be in a local variable, in case a UI event sets the logger 88 | final Printer logging = me.mLogging; 89 | if (logging != null) { 90 | logging.println(">>>>> Dispatching to " + msg.target + " " + 91 | msg.callback + ": " + msg.what); 92 | } 93 | 94 | ...... 95 | msg.target.dispatchMessage(msg); 96 | ...... 97 | 98 | if (logging != null) { 99 | logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 100 | } 101 | ...... 102 | ``` 103 | 104 | 在`dispatchMessage`前后分别调用`println`方法,该方法就是`LooperMonitor.java`里面的`println`方法,该方法里面会记录两次调用的时间差, 105 | 当差值大于3000ms就搜集相关的log信息. 106 | 107 | # 3 信息的保存 108 | 109 | 主要是调用`LogWriter.java`的save方法保存,需要注意的是修改文件的权限,不然在user版不能使用`adb pull `出文件. 110 | 111 | ``` 112 | Runtime.getRuntime().exec("chmod 777 " + path); 113 | ``` 114 | 115 | 搜集的log信息就会被保存到`/data/blockinfo`目录下面. 116 | 这里有一个问题就是,需要`/data/blockinfo`目录能被所有进程能够写. 117 | 118 | # 4 /data/blockinfo文件夹创建 119 | 120 | ``` 121 | # add blockinfo dir for blockinfo feature by qli3@tcl.com. task: 6517476 122 | mkdir /data/blockinfo 0777 system system 123 | ``` 124 | 125 | 在init.rc中创建文件夹,接下来需要解决SELinux权限. 126 | `file_contexts`添加: 127 | 128 | ``` 129 | /data/blockinfo(/.*)? u:object_r:media_rw_data_file:s0 130 | ``` 131 | 132 | 这里必须使用`media_rw_data_file`,其他的file类型是被neverallow的,只有这一个特列. 133 | 在system_app.te, priv_app.te添加 134 | 135 | ``` 136 | allow system_app proc_stat:file {read getattr open}; 137 | allow system_app media_rw_data_file:dir { search read open}; 138 | allow system_app media_rw_data_file:file { read open getattr}; 139 | ``` 140 | 141 | 其他还有一些权限,可以调试的时候看却什么就加上. 142 | 143 | # 其他 144 | - 该功能使用perso开关控制`ro.tct.blockinfo` 145 | - 在AMS添加getCurrentCPUState,并在AP侧添加接口 146 | -------------------------------------------------------------------------------- /temp/tctlogs.md: -------------------------------------------------------------------------------- 1 | # tctlog功能文档总结 2 | 3 | Date: 2018/08/17 Author: qli3@tcl.com 4 | 5 | tctlog主要是针对系统性能,提供系统接口,抓取系统性能相关的log信息. 并统一汇总到指定文件夹.该功能也可以扩展到系统性能之外的log信息的抓取. 6 | 目前只是提供一个基础版本. 7 | 8 | # 1 收集的主要log信息 9 | 10 | - bugreport 11 | - blockinfo 12 | - system_server maps 信息 13 | - vmstat, cpu ,uptime 等信息 14 | - mtklog 信息 15 | - dropbox 信息 16 | - anr 信息 17 | 18 | # 2 log信息搜集 19 | 20 | ## 2.1 bugreport信息搜集 21 | 通过AMS的接口,调用: 22 | 23 | ``` 24 | requestBugReport(ActivityManager.BUGREPORT_OPTION_FULL); 25 | ``` 26 | 27 | 该接口通过修改属性值: 28 | 29 | ``` 30 | SystemProperties.set("ctl.start", "bugreport"); 31 | ``` 32 | 33 | 启动`bugreport`bin程序,开始抓取bugreport文件.这个服务定义在init.rc文件中: 34 | 35 | ``` 36 | # bugreport is triggered by holding down volume down, volume up and power 37 | service bugreport /system/bin/dumpstate -d -p -B -z \ 38 | -o /data/user_de/0/com.android.shell/files/bugreports/bugreport 39 | class late_start 40 | disabled 41 | oneshot 42 | keycodes 114 115 116 43 | 44 | ``` 45 | 46 | 但是这样抓取的bugreport的文件并没有在指定的文件夹下. 47 | 最开始的想法是看bugreport的实现,发现可以指定bugreport抓取的位置.但是在AMS里面使用了带参数的`/system/bin/dumpstate`bin程序, 48 | 由于有太多的SELinux权限问题需要解决,有一些权限问题得不到很好的解决方案.所以找了其他方式.抓取的`bugreport`是在`com.android.shell`下, 49 | 能否考虑抓取`bugreport`完成后,从该apk的data目录下`copy`到指定的目录.最后发现该方案可行. 50 | 51 | `com.android.shell`在`frameworks/basepackages/Shell`下,查看代码,在接收到`INTENT_BUGREPORT_FINISHED`消息,即bgreport完成的消息,会调用 52 | `onBugreportFinished`方法,在`onBugreportFinished`方法中添加copy操作就可以: 53 | ``` 54 | File f_input = new File("/bugreports"); 55 | File f_output = new File("/data/tctlogs/bugreports_" + timeStamp + ".zip"); 56 | zipDir(f_input,f_output); 57 | ``` 58 | 59 | 这样可以将`bugreport`文件,抓取到指定的文件下面了. 60 | 61 | # 3 blockinfo信息的搜集 62 | 63 | ## 3.1 主要工作 64 | 65 | - 在blockinfo原有的功能中添加搜集binder信息,用于分析binder导致的问题 66 | - 将`/data/blockinfo`打包到`/data/tctlogs` 67 | 68 | ## 3.2 搜集binder信息 69 | 70 | 分两步: 71 | 72 | - 在framework添加接口`get_binderinfo`,搜集binder信息的接口; 73 | - 在blockinfo的功能中,卡顿打印log时,调用`get_binderinfo`. 74 | 75 | ``` 76 | String input_path = "/sys/kernel/debug/binder/"; 77 | String output_path = "/data/blockinfo/"; 78 | String [] fileList = {"failed_transaction_log", 79 | "state", 80 | "stats", 81 | "transaction_log", 82 | "transactions"}; 83 | ``` 84 | 85 | binder信息就是读取fileList中的指定的节点数据.然后将其保存在`/data/blockinfo`中. 86 | 87 | 在blockinfo功能的`LogWriter.java`中的`save`方法中调用该接口: 88 | ``` 89 | ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 90 | mActivityManager.get_binderinfo(FILE_NAME_FORMATTER.format(time)); 91 | ``` 92 | 每次当应用出现卡顿的时候,就搜集binder信息. 93 | 94 | ## 3.3 system_server maps信息搜集 95 | 96 | ``` 97 | /** 98 | * for get system_server maps. 99 | */ 100 | public void get_maps(String output_path){ 101 | String input_path = "/proc/self/maps"; 102 | 103 | try { 104 | Files.copy(Paths.get(input_path), Paths.get(output_path), StandardCopyOption.REPLACE_EXISTING); 105 | String shellComm = "chmod 755 " + output_path; 106 | Runtime.getRuntime().exec(shellComm); 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | ``` 112 | 113 | 为了搜集的log能够在shell权限下能够被copy,需要用`chmod`修改权限. 114 | 115 | ## 3.4 其他log信息的搜集 116 | 其他的log信息是以下几种情况: 117 | 118 | - copy 119 | - compress 120 | - shell commmand 121 | 122 | 这集中情况,都比较简单,这里不再赘述.至于用到的java语言的copy compress 以及调用shell command都是通用的方式没有什么特别之处. 123 | 124 | ## 3.5 权限 125 | 126 | - sdcard 读写权限 127 | - SELinux 权限 128 | -------------------------------------------------------------------------------- /temp/watchdog.md: -------------------------------------------------------------------------------- 1 | #Watchdog机制分析 2 | 3 | Android在软件层面有一个watchdog机制,检测一些重要的服务,当这些服务出现问题时,通知Android进行重启,定期检测重要的服务是否出现deadblock. 4 | watchdog主要就是检测mMonitors里面(重要服务,核心线程)的服务是不是处于block状态. 5 | 6 | - 监视reboot广播; 7 | - 监视mMonitors关键系统服务是否死锁。 8 | 9 | ##1 Watchdog初始化: 10 | ###1.1 startOtherServices() 11 | `frameworks/base/services/java/com/android/server/SystemServer.java` 12 | 在`startOtherServices()`中进行初始化: 13 | ``` 14 | traceBeginAndSlog("InitWatchdog"); 15 | final Watchdog watchdog = Watchdog.getInstance();//1 获取对象 16 | watchdog.init(context, mActivityManagerService);//2 初始化 17 | Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 18 | 19 | Watchdog.getInstance().start();//3 启动watchdog 20 | ``` 21 | ####1.1.1 getInstance() 22 | ``` 23 | public static Watchdog getInstance() { 24 | if (sWatchdog == null) { 25 | sWatchdog = new Watchdog(); 26 | } 27 | 28 | return sWatchdog; 29 | } 30 | ``` 31 | 单例模式 32 | #####1.1.1.1 new Watchdog() 33 | ``` 34 | private Watchdog() { 35 | super("watchdog"); 36 | // Initialize handler checkers for each common thread we want to check. Note 37 | // that we are not currently checking the background thread, since it can 38 | // potentially hold longer running operations with no guarantees about the timeliness 39 | // of operations there. 40 | 41 | // 逐一添加HandlerChecker 42 | // The shared foreground thread is the main checker. It is where we 43 | // will also dispatch monitor checks and do other work. 44 | mMonitorChecker = new HandlerChecker(FgThread.getHandler(), 45 | "foreground thread", DEFAULT_TIMEOUT); 46 | mHandlerCheckers.add(mMonitorChecker); 47 | // Add checker for main thread. We only do a quick check since there 48 | // can be UI running on the thread. 49 | mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()), 50 | "main thread", DEFAULT_TIMEOUT)); 51 | // Add checker for shared UI thread. 52 | mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), 53 | "ui thread", DEFAULT_TIMEOUT)); 54 | // And also check IO thread. 55 | mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), 56 | "i/o thread", DEFAULT_TIMEOUT)); 57 | // And the display thread. 58 | mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(), 59 | "display thread", DEFAULT_TIMEOUT)); 60 | // Initialize monitor for Binder threads. 61 | 62 | // 添加需要检测的服务,会形成一个检测集合,这个检测集合里面都是系统层面的核心系统服务 63 | addMonitor(new BinderThreadMonitor()); 64 | if (SystemProperties.get("ro.have_aee_feature").equals("1")) { 65 | exceptionHWT = new ExceptionLog(); 66 | } 67 | 68 | } 69 | ``` 70 | Watchdog继承了Thread类,Watchdog是一个单例线程.在该方法中添加了很多的HandlerChecker,主要可以分为两类: 71 | 72 | - **Monitor Checker :** 用于检查是Monitor对象可能发生的死锁, AMS, PKMS, WMS等核心的系统服务都是Monitor对象 73 | - **Looper Checker :** 用于检查线程的消息队列是否长时间处于工作状态。Watchdog自身的消息队列,Ui, Io, Display这些全局的消息队列都是被检查的对象。 74 | 此外,一些重要的线程的消息队列,也会加入到Looper Checker中,譬如AMS, PKMS,这些是在对应的对象初始化时加入的 75 | 76 | 两类HandlerChecker的侧重点不同,Monitor Checker预警我们不能长时间持有核心系统服务的对象锁,否则会阻塞很多函数的运行; 77 | Looper Checker预警我们不能长时间的霸占消息队列,否则其他消息将得不到处理。这两类都会导致系统卡住(System Not Responding)。 78 | 79 | #####1.1.1.1.1 HandlerChecker 80 | ``` 81 | public final class HandlerChecker implements Runnable { 82 | private final Handler mHandler; //Handler对象 83 | private final String mName; //线程描述名 84 | private final long mWaitMax; //最长等待时间 85 | //记录着监控的服务 86 | private final ArrayList mMonitors = new ArrayList(); 87 | private boolean mCompleted; //开始检查时先设置成false 88 | private Monitor mCurrentMonitor; 89 | private long mStartTime; //开始准备检查的时间点 90 | 91 | HandlerChecker(Handler handler, String name, long waitMaxMillis) { 92 | mHandler = handler; 93 | mName = name; 94 | mWaitMax = waitMaxMillis; 95 | mCompleted = true; 96 | } 97 | } 98 | ``` 99 | #####1.1.1.1.2 addMonitor 100 | ``` 101 | public void addMonitor(Monitor monitor) { 102 | synchronized (this) { 103 | if (isAlive()) { 104 | throw new RuntimeException("Monitors can't be added once the Watchdog is running"); 105 | } 106 | mMonitorChecker.addMonitor(monitor); 107 | } 108 | } 109 | ``` 110 | 由于mMonitorChecker是一个HandlerChecker类, 111 | ``` 112 | public void addMonitor(Monitor monitor) { 113 | mMonitors.add(monitor); 114 | } 115 | ``` 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /temp/培训.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/temp/培训.png -------------------------------------------------------------------------------- /temp/未命名绘图.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbk6I4FP41VO0+tMUdfNRWZ/YyW7PVW7XbTxZCVLbRMIB2O79+E0i4JLGJCurMjl3VSggBvpPv5JwvAcV43Lx9SLx4/QkGIFJ0NXhTjImi65pqDNEXLjkUJbbuFgWrJAxIpargKfwK6JGkdBcGIG1UzCCMsjBuFvpwuwV+1ijzkgS+NqstYdQ8a+ytAFfw5HsRX/p3GGTrotS11Kr8IwhXa3pmTSV7Nh6tTArStRfA11qRMVWMxwTCrPi1eXsEEQaP4lIcNzuyt7ywBGwzmQMOz8mf6cT84zf72fEPk/ni4avxYBFr7L1oR+6YXG12oBAkcLcNAG5FVYzx6zrMwFPs+XjvKzI6KltnmwhtaejnEm4zYkXNQdteFK62aMNHVwkSXCGMokcYwSRv3Fguge37qDzNEvgCansCZ7hQ8Rn5GyX3vgdJBt5qReTGPwC4AVlyQFXemr3p0Nx8rSyqu6RsXbOmTQs90otWZcsV0OgHwfoE3A29a9ybsAYecJdCWG3fBYtlN7AaVgNW3eJhNQSw6qbVF6z29wCr7gyawBqmoMNSEBvIDvvqsFrXwNb8hC3yE110T7UJo82jaJki2qt9oSjBerANRnjcwlhEXpqGfhM4BEdy+IeAnG884w3UY8jm5K2+c3IgW0WPpWOY8R7CmZesQPbObThFPRA0xk7eDnWgBTjTsgREXhbumyOuCHtyhs8wRFdcmtnSLIYvum0OGBeTwl3iA3JkfZRkG3PM9sYKgLjGkN28Q61ajCuk71y4IbpwW2e6WdFs1elKgC/wk4J+aEcZ4WWjQ9pfdpDueEhzxo5QBTd+y7sQ3Z2HYNQbVoXGbOaqmE5Vkb0i3/n5FuedrGjkCfovqKOSphASC7Z5VFbcES1m2IY8SdbkF+eIsL8JUUA4Ijs2YRDgw8cJQFfoLfKmMMOIuVG71lixJritXQZT4uSaPs/txsOV7ov2H8FAIfJwel8OzuQ71tRSXFsZDskP11KmtjJSlbGrTF3FHSkuquPg/+O8zmiqDKd5yVQZq/lRI2U4yysbyngiYcPm6LyFW8AM5aRI3tai0aw53rGBbwfWdZiw1RRYV9MF5jX6Mq/FmXePxiuYDDYgCL0MvAzWXhK8opsebLKXGL4iVE1VG6gPKUj2IUIPXS4y5HimDMfYxiNk1MoXJHfH0zw2ybfJtQjSrtPjEoNhrYC0wri5L7NKRM0r1Ntj+XsvE3KCu1LPeYWezG1i4goiXlEMUYbGnYPitINySSqxXC71I4mvvbAtW+hC3rVee/+j6YQstlpf2PKqA3IXn7G7+ORtvRXi8x07+dITND2F1YfF6EggcPzqNT3E8GjAGIR7fEKCZRWjEbi5mC0fFIJ5GPvzBPh7fxPUYrW8MekhgB1yT0/WJbLObgxotGaimkiA6s25UWbXDBqDZPk7CqhH/pczsG/T+c5STrrB3mqOLJpsFNUf+LyYkoLsMdqlCLpZAr4z/NkcRTD8aKIkpT/8ZWSYYAWop4dJtoYruPWiaVXKjAxVnd8hjImR/gVZdiBWwjGmaACrKzLtOcR7FikkDgnSt+o7tIO2CjzSys1l9hLJFSfKZu1gg7cwy3W1QkpDW8/kWPy7UtXwBhXVTjPQnehlGpNWspqxrFTGtqOxhD0ik3UlYmkCseHaaYvBODdzyDs30xHYUustVNP4JJ1GWGuN1cdk5T4NS3C8iCcj6RxT4spi/qpiWgBW+eltb4Nj8e0ijYtazDaCcpYeUhxAAqwypMV2Bjbohx/viv9q8bXMB1iJJnlRYhYn0MdfKFTarBIsOEKYZnM/S6KicfoT18CSx5ycjWuqvPv4hID3eKp4NLlE2Uy4RShOrGrrr3yAetCPJa0Q8WIZ5b50jXIjsG0Vu9iUKoN58guT8Cuq6dHrl3SXJbHbQ4wmzQZq7aNdlXQSEsqdhhSaZPTgyAYP1n0FD53rOG0zl10IPadrijozGIvWOIi14t44was8teQ/RRFbnvxfmuZrC08DughfVbWnoxlpoWkR9JFL/y82RIlvPd0ZXjPd0fl08/5807s0aPVN9I7anZN7V86JXjdLkfkmXX1/zLAlhADtqswQyZrXZ0a1pKNaxfGs1BdxXLyko51BliSDdNlk9tIslZ0kYycjes4udT6N8jbpGMf7n1EWANL0MQFeBmRo2qQl0AILOCJaDm3H8GyG2KbCzzv0QU+GnZaAnCKJure5Ztrw/5CLtiwXZRdiXWk045OgbX7aT2Ry7yPlatd84WPwkyfqTl+dwQTcuj7g179q9lUpo3Ho3iSwO3eN4gWUkc1OjTsLAPns9BfKFix9GSN0O0/F2pmffr49d8yOxhpm2aMoRxKppv0xhx/to3CRx+JrL8IiwCCFEvDzs2v5R6AKWPhPBP+FyxNq5yg+3VisXG5QrvWXnEXtb6nhTZxdd/FBd8oc9Wnt8YJ+V87PED18dOK8hHNkaTBtKI297UUNbRqxSynbF+1euti4x4Sd0wZqXl092tfOz+Y10RolUTbf3+JU8yYOQThtXM4UC6eNjzmR0sHcwImYpqQTMTsXAC4bBvhZ4NpKpl2YSGXo75BFmG+fHmZ1QDm3GTVZgjWymiV4Kqw/xtkctj8YJ884WcnNvK803+RjZcy4eYQoN/fOWj14pkLWsyLmNPlmivRqUZbSm15Nyf2Db2fxTVZWM+9rBts8Jqt9vmjF7p1yjn322ZLVBvpjnXMT1nX2nG+HDJJV2az7WkBqHlHZEH/OE9m+CSrZ3GsERI8dXHUpgslPuEbhAj8qiAMIPwrR3X4rQhubL/QkvDFBiC1Y1yN0h73pbpZxU3eo3Y87pF7uBrpbfujJ7xtglya5zBuDWupTzbfXVxNQULvN5+97YQzzhJLQT4seCOgv5Dn+vF/5ioVfvb2HV3kPTfwU99RWXFcZO3JPd4te2HCiy/92n+pvGtsWGVs0a3yGQ0eb1fvFCoJWb2kzpv8B -------------------------------------------------------------------------------- /temp/转屏算法.md: -------------------------------------------------------------------------------- 1 | # 转屏算法 2 | 3 | `frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java` 4 | 5 | `onSensorChanged` 就是转屏算法所在的方法. 6 | 7 | 按顺序列出在 `onSensorChanged` 中用到的相关常量 & 类属性. 8 | 9 | ```java 10 | // Indices into SensorEvent.values for the accelerometer sensor. 11 | private static final int ACCELEROMETER_DATA_X = 0; 12 | private static final int ACCELEROMETER_DATA_Y = 1; 13 | private static final int ACCELEROMETER_DATA_Z = 2; 14 | 15 | // Timestamp and value of the last accelerometer sample. 16 | private long mLastFilteredTimestampNanos; 17 | private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; 18 | 19 | // The maximum sample inter-arrival time in milliseconds. 20 | // If the acceleration samples are further apart than this amount in time, we reset the 21 | // state of the low-pass filter and orientation properties. This helps to handle 22 | // boundary conditions when the device is turned on, wakes from suspend or there is 23 | // a significant gap in samples. 24 | private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS; 25 | 26 | // Number of nanoseconds per millisecond. 27 | protected static final long NANOS_PER_MS = 1000000; 28 | 29 | // Number of milliseconds per nano second. 30 | protected static final float MILLIS_PER_NANO = 0.000001f; 31 | ``` 32 | 33 | ``` 34 | @Override 35 | public void onSensorChanged(SensorEvent event) { 36 | int proposedRotation; 37 | int oldProposedRotation; 38 | 39 | synchronized (mLock) { 40 | // The vector given in the SensorEvent points straight up (towards the sky) under 41 | // ideal conditions (the phone is not accelerating). I'll call this up vector 42 | // elsewhere. 43 | // 加速度分解成x,y,z三个方向 44 | float x = event.values[ACCELEROMETER_DATA_X]; 45 | float y = event.values[ACCELEROMETER_DATA_Y]; 46 | float z = event.values[ACCELEROMETER_DATA_Z]; 47 | 48 | if (LOG) { 49 | Slog.v(TAG, "Raw acceleration vector: " 50 | + "x=" + x + ", y=" + y + ", z=" + z 51 | + ", magnitude=" + Math.sqrt(x * x + y * y + z * z)); 52 | } 53 | 54 | // Apply a low-pass filter to the acceleration up vector in cartesian space. 55 | // Reset the orientation listener state if the samples are too far apart in time 56 | // or when we see values of (0, 0, 0) which indicates that we polled the 57 | // accelerometer too soon after turning it on and we don't have any data yet. 58 | final long now = event.timestamp;// gsensor上报的时间,纳秒为单位 59 | final long then = mLastFilteredTimestampNanos;// gsensor event 上一次的上报时间 60 | final float timeDeltaMS = (now - then) * 0.000001f; // 以毫秒为单位,记录两次上报的时间差 61 | final boolean skipSample; // 用于标记是否要跳过此次上报时间的flag 62 | // now < then 明显是一组错误的数据,当然应该要放弃 63 | // 当两次上报时间大于1s,那么也需要放弃数据 64 | // (0,0,0) 是轮询加速度计过快,没有意义的数据 65 | if (now < then 66 | || now > then + MAX_FILTER_DELTA_TIME_NANOS // MAX_FILTER_DELTA_TIME_NANOS 是10^9纳秒,也就是一秒 67 | || (x == 0 && y == 0 && z == 0)) { 68 | if (LOG) { 69 | Slog.v(TAG, "Resetting orientation listener."); 70 | } 71 | resetLocked(true /* clearCurrentRotation */); // 清理之前的数据 72 | skipSample = true; // 设置跳过这此上报时间 73 | } else { //此处的算法是低通滤波器算法 74 | //https://blog.csdn.net/u013608300/article/details/78814693 75 | final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); 76 | x = alpha * (x - mLastFilteredX) + mLastFilteredX; 77 | y = alpha * (y - mLastFilteredY) + mLastFilteredY; 78 | z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; 79 | if (LOG) { 80 | Slog.v(TAG, "Filtered acceleration vector: " 81 | + "x=" + x + ", y=" + y + ", z=" + z 82 | + ", magnitude=" + Math.sqrt(x * x + y * y + z * z)); 83 | } 84 | skipSample = false; 85 | } 86 | // 更新lastfilter 87 | mLastFilteredTimestampNanos = now; 88 | mLastFilteredX = x; 89 | mLastFilteredY = y; 90 | mLastFilteredZ = z; 91 | 92 | // 初始化数据 93 | boolean isAccelerating = false; 94 | boolean isFlat = false; 95 | boolean isSwinging = false; 96 | if (!skipSample) { // 没有被舍弃的上报事件,处理逻辑, 重点!!! 97 | // Calculate the magnitude of the acceleration vector. 98 | final float magnitude = (float) Math.sqrt(x * x + y * y + z * z); // 计算矢量大小 99 | if (magnitude < NEAR_ZERO_MAGNITUDE) { 100 | if (LOG) { 101 | Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero."); 102 | } 103 | clearPredictedRotationLocked(); 104 | } else { 105 | // Determine whether the device appears to be undergoing external 106 | // acceleration. 107 | if (isAcceleratingLocked(magnitude)) { // 在(9.8-4 和 9.8+4 之间) 108 | isAccelerating = true; 109 | mAccelerationTimestampNanos = now; 110 | } 111 | 112 | // Calculate the tilt angle. 113 | // This is the angle between the up vector and the x-y plane (the plane of 114 | // the screen) in a range of [-90, 90] degrees. 115 | // -90 degrees: screen horizontal and facing the ground (overhead) 116 | // 0 degrees: screen vertical 117 | // 90 degrees: screen horizontal and facing the sky (on table) 118 | final int tiltAngle = (int) Math.round( 119 | Math.asin(z / magnitude) * RADIANS_TO_DEGREES); 120 | addTiltHistoryEntryLocked(now, tiltAngle); 121 | 122 | // Determine whether the device appears to be flat or swinging. 123 | if (isFlatLocked(now)) { 124 | isFlat = true; 125 | mFlatTimestampNanos = now; 126 | } 127 | if (isSwingingLocked(now, tiltAngle)) { 128 | isSwinging = true; 129 | mSwingTimestampNanos = now; 130 | } 131 | 132 | // If the tilt angle is too close to horizontal then we cannot determine 133 | // the orientation angle of the screen. 134 | if (tiltAngle <= TILT_OVERHEAD_ENTER) { 135 | mOverhead = true; 136 | } else if (tiltAngle >= TILT_OVERHEAD_EXIT) { 137 | mOverhead = false; 138 | } 139 | if (mOverhead) { 140 | if (LOG) { 141 | Slog.v(TAG, "Ignoring sensor data, device is overhead: " 142 | + "tiltAngle=" + tiltAngle); 143 | } 144 | clearPredictedRotationLocked(); 145 | } else if (Math.abs(tiltAngle) > MAX_TILT) { 146 | if (LOG) { 147 | Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " 148 | + "tiltAngle=" + tiltAngle); 149 | } 150 | clearPredictedRotationLocked(); 151 | } else { 152 | // Calculate the orientation angle. 153 | // This is the angle between the x-y projection of the up vector onto 154 | // the +y-axis, increasing clockwise in a range of [0, 360] degrees. 155 | int orientationAngle = (int) Math.round( 156 | -Math.atan2(-x, y) * RADIANS_TO_DEGREES); 157 | if (orientationAngle < 0) { 158 | // atan2 returns [-180, 180]; normalize to [0, 360] 159 | orientationAngle += 360; 160 | } 161 | 162 | // Find the nearest rotation. 163 | int nearestRotation = (orientationAngle + 45) / 90; 164 | if (nearestRotation == 4) { 165 | nearestRotation = 0; 166 | } 167 | // Determine the predicted orientation. 168 | if (isTiltAngleAcceptableLocked(nearestRotation, tiltAngle) 169 | && isOrientationAngleAcceptableLocked(nearestRotation, 170 | orientationAngle)) { 171 | updatePredictedRotationLocked(now, nearestRotation); 172 | if (LOG) { 173 | Slog.v(TAG, "Predicted: " 174 | + "tiltAngle=" + tiltAngle 175 | + ", orientationAngle=" + orientationAngle 176 | + ", predictedRotation=" + mPredictedRotation 177 | + ", predictedRotationAgeMS=" 178 | + ((now - mPredictedRotationTimestampNanos) 179 | * 0.000001f)); 180 | } 181 | } else { 182 | if (LOG) { 183 | Slog.v(TAG, "Ignoring sensor data, no predicted rotation: " 184 | + "tiltAngle=" + tiltAngle 185 | + ", orientationAngle=" + orientationAngle); 186 | } 187 | clearPredictedRotationLocked(); 188 | } 189 | } 190 | } 191 | } 192 | // 设置mFlat/mSwinging/mAccelerating 193 | // 如果是被舍弃的上报事件,那么isFlat/isSwinging/isAccelerating依然是初始值 false 194 | mFlat = isFlat; 195 | mSwinging = isSwinging; 196 | mAccelerating = isAccelerating; 197 | 198 | // Determine new proposed rotation. 199 | oldProposedRotation = mProposedRotation; 200 | if (mPredictedRotation < 0 || isPredictedRotationAcceptableLocked(now)) { 201 | mProposedRotation = mPredictedRotation; 202 | } 203 | proposedRotation = mProposedRotation; 204 | // Write final statistics about where we are in the orientation detection process. 205 | if (LOG) { 206 | Slog.v(TAG, "Result: currentRotation=" + mCurrentRotation 207 | + ", proposedRotation=" + proposedRotation 208 | + ", predictedRotation=" + mPredictedRotation 209 | + ", timeDeltaMS=" + timeDeltaMS 210 | + ", isAccelerating=" + isAccelerating 211 | + ", isFlat=" + isFlat 212 | + ", isSwinging=" + isSwinging 213 | + ", isOverhead=" + mOverhead 214 | + ", isTouched=" + mTouched 215 | + ", timeUntilSettledMS=" + remainingMS(now, 216 | mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) 217 | + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now, 218 | mAccelerationTimestampNanos + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) 219 | + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now, 220 | mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) 221 | + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now, 222 | mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) 223 | + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now, 224 | mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS)); 225 | } 226 | } 227 | 228 | // Tell the listener. 229 | if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { 230 | if (LOG || mWindowManagerDebugger.WMS_DEBUG_USER) { 231 | Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation 232 | + ", oldProposedRotation=" + oldProposedRotation); 233 | } 234 | onProposedRotationChanged(proposedRotation); 235 | } 236 | } 237 | 238 | ``` 239 | 240 | - 当`skipSample` 被设置成true时,并没有做什么处理; 241 | 242 | 243 | -------------------------------------------------------------------------------- /tools/反编译odex工具箱及文档.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lqktz/document/571d8635bc501cb66c12fbdbd32fd472609d0476/tools/反编译odex工具箱及文档.zip --------------------------------------------------------------------------------