diff --git a/.gitignore b/.gitignore index b6ddbd3..342f18d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,11 @@ .cxx .classpath .project -.settings \ No newline at end of file +.settings +systemhook +infer-out +md5_repeat_result.txt +name_repeat_result.txt +*.hprof +.python-version +.kotlin \ No newline at end of file diff --git a/README.md b/README.md index 18ae5d9..22e6078 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,413 @@ -### Android ams hook, 启动未在AndroidManifest.xml中注册的Activity - -适配了Android4-9,要彻底搞清楚代码,需要提前掌握的知识点如下: -1. [反射的使用](https://blog.csdn.net/gdutxiaoxu/article/details/68947735) -2. [泛型](https://blog.csdn.net/s10461/article/details/53941091) -3. [动态代理](https://blog.csdn.net/u011784767/article/details/78281384) -4. [AIDL通信](https://blog.csdn.net/luoyanglizi/article/details/51980630) -5. [Activity启动流程以及其中涉及到的两次跨进程通信](https://blog.csdn.net/jiangwei0910410003/article/details/52549333) -6. [Handler消息处理机制](https://blog.csdn.net/guolin_blog/article/details/9991569) -7. [Activity启动拦截](https://blog.csdn.net/jiangwei0910410003/article/details/52550147) - - -### 问题思考 -1. 如何确保我们启动的未注册的Activity,有正常的Activity的生命周期? - -[源码探索系列29---插件化基础之启动插件的Activity](http://sanjay-f.github.io/2016/04/01/%E6%BA%90%E7%A0%81%E6%8E%A2%E7%B4%A2%E7%B3%BB%E5%88%9729---%E6%8F%92%E4%BB%B6%E5%8C%96%E5%9F%BA%E7%A1%80%E4%B9%8B%E5%90%AF%E5%8A%A8%E6%8F%92%E4%BB%B6%E7%9A%84Activity/) - -2. 从整体宏观的角度看,我们到底做了什么? - -3. 对PackageManager的hook,为什么要hook两个地方 - -[hook技术(三)对AMS&PMS进行Hook](https://blog.csdn.net/wangwei708846696/article/details/79525467) - -```java -@Override -public PackageManager getPackageManager() { - if (mPackageManager != null) { - return mPackageManager; - } - IPackageManager pm = ActivityThread.getPackageManager(); - if (pm != null) { - return (mPackageManager = new ApplicationPackageManager(this, pm)); - } - return null; -} -``` - -由于系统的执行肯定在我们代码之前,所以系统先生成了一个pm,这个是原生的pm然后保存在ApplicationPackageManager中, -使得以后使用ContextImp.getPackageManager()都返回这个IPackageManager 对象。 -就算我们后来替换了ActivityThread.getPackageManager(),但是也不影响mPackageManager 里面之前包装好的。 -所以我们还需要改变mPackageManager 里面的原来的pm对象。 - -4. Hook AMS和Hook Instrumentation两种方式的区别? - - -### 参考文章列表 -1. [Android:学习AIDL,这一篇文章就够了(上)](https://blog.csdn.net/luoyanglizi/article/details/51980630) -2. [Android:学习AIDL,这一篇文章就够了(下)](https://blog.csdn.net/luoyanglizi/article/details/52029091) -3. [大白话说Java反射:入门、使用、原理](https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html) -4. [Android 插件化原理解析——Hook机制之AMS&PMS](http://weishu.me/2016/03/07/understand-plugin-framework-ams-pms-hook/) -5. [Android系统篇之----Hook系统的AMS服务实现应用启动的拦截功能](https://blog.csdn.net/jiangwei0910410003/article/details/52550147) -6. [Android插件化的兼容性(中):Android P的适配](https://www.cnblogs.com/Jax/p/9521305.html) -7. [Android Hook Activity 的几种姿势](https://blog.csdn.net/gdutxiaoxu/article/details/81459910) +### Android 启动未在AndroidManifest.xml中注册的Activity + +1. 支持2种方式.第一种,hook Instrument实现;第二种,hook Handler实现. + +2. 兼容android4.1 ~ android15 + +3. kotlin 实现 + +4. 增加了代码混淆配置 + +5. 使用最新gradle,apg版本 + +6. 采用gradle kotlin dsl + +### 开发调试 + +1. 编译debug插件并拷贝到宿主中, 编译debug宿主, 安装到手机并启动, 请执行 `./build_debug_apk.sh` 脚本 + +2. 编译release插件并拷贝到宿主中, 编译release宿主, 安装到手机并启动, 请执行 `./build_release_apk.sh` 脚本 + +### 代码的兼容性测试说明:使用腾讯WeTest真机测试平台,少部分是Testin云测和阿里云测试 +### 测试代码为 develop_kotlin分支, 代码兼容 android 4.1 到 android 15 +### WeTest设备兼容测试情况如下: +### 测试设备数量386, 通过386(100%), 全部通过测试. + +| 设备品牌 | 设备型号 | 设备别名 | 系统版本 | 通过 | +|:----------:|:--------------------------:|:-------------------:|:-----:|:--:| +| GOOGLE | Pixel8pro | Pixel8pro模拟器 | 15 | ✅ | +| GOOGLE | Redfin 64-bit only | Pixel 5 | 14 | ✅ | +| vivo | V2049A | iQOO 7 | 13 | ✅ | +| samsung | SM-G9860 | 三星Galaxy S20+ 5G | 13 | ✅ | +| Google | Pixel 4 XL | PIXEL 4 XL | 13 | ✅ | +| vivo | V2196A | VIVO iQOO Neo6 | 13 | ✅ | +| XIAOMI | 2201123C | Xiaomi 12 | 13 | ✅ | +| vivo | V2136A | Vivo iQOO 8 | 13 | ✅ | +| Google | Pixel 5 | PIXEL 5 | 12 | ✅ | +| XIAOMI | M2011K2C | 小米11 5G | 12 | ✅ | +| vivo | V2054A | iQOO | 11 | ✅ | +| Xiaomi | M2011K2C | 小米11 5G | 11 | ✅ | +| OnePlus | KB2000 | OnePlus 8T 5G | 11 | ✅ | +| OPPO | PEGT00 | Reno 5 | 11 | ✅ | +| samsung | SM-G9860 | 三星Galaxy S20+ 5G | 11 | ✅ | +| Xiaomi | Redmi K30 Pro Zoom Edition | K30 Pro | 11 | ✅ | +| SAMSUNG | SM-N9760 | SM-N9760 | 11 | ✅ | +| ONEPLUS | HD1910 | OnePlus 7T | 11 | ✅ | +| SAMSUNG | SM-G9880 | Galaxy S20 Ultra 5G | 11 | ✅ | +| ONEPLUS | IN2010 | 一加8 | 11 | ✅ | +| OnePlus | IN2020 | 8 Pro | 11 | ✅ | +| OPPO | PDEM30 | Find X2 Pro | 11 | ✅ | +| OPPO | PDPM00 | oppo Reno4 5G | 11 | ✅ | +| GOOGLE | Pixel 4 | PIXEL 4 | 11 | ✅ | +| vivo | V1986A | VIVO iQOO Z1 | 11 | ✅ | +| OPPO | PDAM10 | OPPO A52 | 11 | ✅ | +| GOOGLE | Pixel 5 | PIXEL 5 | 11 | ✅ | +| Xiaomi | M2012K11AC | 红米 K40 | 11 | ✅ | +| vivo | V1824BA | VIVO iQOO | 11 | ✅ | +| vivo | V1916A | VIVO iQOO Pro 5G | 11 | ✅ | +| OPPO | PDPM00 | oppo Reno4 5G | 11 | ✅ | +| OPPO | PDCM00 | OPPO Reno3 | 11 | ✅ | +| SAMSUNG | SM-G9730 | Galaxy S10 | 11 | ✅ | +| ONEPLUS | LE2110 | 一加9 | 11 | ✅ | +| REALME | RMX3041 | realme V13 | 11 | ✅ | +| XIAOMI | M2102K1AC | 小米11 Pro | 11 | ✅ | +| XIAOMI | M2102J2SC | 小米10S | 11 | ✅ | +| REALME | RMX3121 | realmeV11 | 11 | ✅ | +| Xiaomi | M2012K11AC | 红米 K40 | 11 | ✅ | +| realme | RMX2202 | realme GT | 11 | ✅ | +| VIVO | V1936A | VIVO iQOO Neo 855版 | 11 | ✅ | +| HUAWEI | TAS-AL00 | Mate 30 | 10 | ✅ | +| Xiaomi | M2006C3LC | 红米9a | 10 | ✅ | +| Xiaomi | M2007J17C | Redmi Note 9 Pro 5G | 10 | ✅ | +| XIAOMI | M2010J19SC | Redmi Note 9 4G | 10 | ✅ | +| vivo | V1955A | VIVO iQOO 3 | 10 | ✅ | +| ASUS | ASUS_I003DD | ROG Phone 3 | 10 | ✅ | +| realme | RMX2121 | realme X7 Pro | 10 | ✅ | +| ZTE | ZTE A2121 | Axon 20 5G | 10 | ✅ | +| REALME | RMX2201 | Realme V3 5G | 10 | ✅ | +| HUAWEI | OXP-AN00 | Play4 Pro | 10 | ✅ | +| Xiaomi | Redmi Note 8 Pro | Redmi Note 8 Pro | 10 | ✅ | +| OnePlus | HD1900 | 一加7T | 10 | ✅ | +| HUAWEI | TNN-AN00 | 麦芒9 | 10 | ✅ | +| HUAWEI | BRQ-AN00 | Nova 8 Pro | 10 | ✅ | +| HUAWEI | EBG-AN10 | Honor 30 Pro+ | 10 | ✅ | +| HUAWEI | MOA-AL00 | 荣耀畅玩9A | 10 | ✅ | +| HUAWEI | OCE-AN10 | Mate 40 | 10 | ✅ | +| HUAWEI | ANG-AN00 | nova 8 | 10 | ✅ | +| HUAWEI | JSC-AN00 | nova 8 SE | 10 | ✅ | +| HUAWEI | NOH-AN00 | HUAWEI Mate 40 Pro | 10 | ✅ | +| HUAWEI | WKG-AN00 | 畅享20 5G | 10 | ✅ | +| HUAWEI | LIO-AN00m | Mate 30E Pro 5G | 10 | ✅ | +| HUAWEI | DVC-AN20 | 华为畅享20 Pro | 10 | ✅ | +| HUAWEI | AKA-AL10 | 荣耀Play4T | 10 | ✅ | +| OPPO | PDEM30 | Find X2 Pro | 10 | ✅ | +| HUAWEI | BMH-AN10 | 荣耀30 | 10 | ✅ | +| HUAWEI | LIO-AN00 | 华为 Mate 30 Pro | 10 | ✅ | +| HUAWEI | OXF-AN00 | 荣耀V30 | 10 | ✅ | +| HUAWEI | EBG-AN00 | 荣耀30 Pro | 10 | ✅ | +| GOOGLE | Pixel 3a | Pixel 3A | 10 | ✅ | +| HUAWEI | CDY-AN90 | 荣耀30s | 10 | ✅ | +| HUAWEI | TNY-AL00 | 荣耀Magic2 | 10 | ✅ | +| SAMSUNG | SM-F7000 | Galaxy Z Flip | 10 | ✅ | +| VIVO | V1950A | VIVO NEX 3S | 10 | ✅ | +| OPPO | PBCM30 | OPPO K1 | 10 | ✅ | +| HUAWEI | EBG-TN00 | 30 Pro | 10 | ✅ | +| BLACKSHARK | SHARK MBU-A0 | 游戏手机3 Pro | 10 | ✅ | +| BLACKSHARK | SHARK MBU-A0 | 游戏手机3 Pro | 10 | ✅ | +| HUAWEI | ANA-AN00 | 华为P40 | 10 | ✅ | +| HUAWEI | JEF-AN00 | 华为 nova7 5G | 10 | ✅ | +| HUAWEI | CDY-AN00 | 华为nova 7 SE | 10 | ✅ | +| HUAWEI | ANA-AN00 | 华为P40 | 10 | ✅ | +| HUAWEI | PCT-AL10 | 荣耀V20 | 10 | ✅ | +| SAMSUNG | SM-G9810 | 三星Galaxy S20 5G | 10 | ✅ | +| OPPO | PERM00 | K7x | 10 | ✅ | +| Xiaomi | M2007J22C | Redmi Note 9 5G | 10 | ✅ | +| HUAWEI | SEA-AL10 | 华为nova 5 Pro | 10 | ✅ | +| Xiaomi | M2003J15SC | Redmi 10X 4G | 10 | ✅ | +| SAMSUNG | SM-G7810 | Galaxy S20 FE 5G | 10 | ✅ | +| OPPO | PDEM10 | OPPO FIND X2 | 10 | ✅ | +| HUAWEI | TEL-AN00a | 荣耀X10 | 10 | ✅ | +| XIAOMI | M2004J7AC | Redmi 10X | 10 | ✅ | +| samsung | SM-G9650 | 三星 Galaxy S9+ | 10 | ✅ | +| samsung | SM-G9860 | 三星Galaxy S20+ 5G | 10 | ✅ | +| HONOR | YOK-AN10 | 荣耀V40 | 10 | ✅ | +| HUAWEI | TNNH-AN00 | Play4 | 10 | ✅ | +| OPPO | PDEM10 | OPPO FIND X2 | 10 | ✅ | +| vivo | V1838A | VIVO X27 | 10 | ✅ | +| XIAOMI | MI 8 Explorer Edition | 8 透明探索版 | 10 | ✅ | +| Xiaomi | MI 8 UD | MI 8 UD | 10 | ✅ | +| SAMSUNG | SM-A9200 | Galaxy A9s | 10 | ✅ | +| HUAWEI | OXP-AN00 | Play4 Pro | 10 | ✅ | +| GOOGLE | Pixel 2 XL | 2 XL | 10 | ✅ | +| OPPO | PDNM00 | OPPO Reno4 Pro | 10 | ✅ | +| HUAWEI | OXF-AN10 | 荣耀V30 PRO | 10 | ✅ | +| HUAWEI | TNNH-AN00 | Play4 | 10 | ✅ | +| OPPO | PDHM00 | OPPO Ace2 | 10 | ✅ | +| HUAWEI | TEL-AN00a | 荣耀X10 | 10 | ✅ | +| HUAWEI | JER-AN10 | 华为nova 7 Pro | 10 | ✅ | +| Xiaomi | Redmi K30 Pro | 红米 K30 Pro | 10 | ✅ | +| OPPO | PBEM00 | OPPO R17 | 10 | ✅ | +| vivo | V2011A | X50 Pro+ | 10 | ✅ | +| vivo | V2023A | iQOO U1 | 10 | ✅ | +| Xiaomi | Mi 10 | 小米10 | 10 | ✅ | +| XIAOMI | MI 8 Explorer Edition | 8 透明探索版 | 10 | ✅ | +| HUAWEI | VOG-AL00 | 华为P30 Pro | 10 | ✅ | +| HUAWEI | HRY-AL00Ta | 荣耀20i | 10 | ✅ | +| Xiaomi | M2004J19C | Redmi 9 | 10 | ✅ | +| HUAWEI | LYA-AL00 | 华为 Mate 20 Pro | 10 | ✅ | +| HUAWEI | PCT-AL10 | 荣耀V20 | 10 | ✅ | +| vivo | V1821A | VIVO NEX 双屏版 | 10 | ✅ | +| HUAWEI | TNY-AL00 | 荣耀Magic2 | 10 | ✅ | +| HUAWEI | HMA-AL00 | 华为 Mate 20 | 10 | ✅ | +| vivo | V1831A | VIVO S1 | 10 | ✅ | +| HUAWEI | OXF-AN00 | 荣耀V30 | 10 | ✅ | +| vivo | V1986A | VIVO iQOO Z1 | 10 | ✅ | +| samsung | SM-A6060 | Galaxy A60 | 10 | ✅ | +| VIVO | V1950A | VIVO NEX 3S | 10 | ✅ | +| HUAWEI | FRL-AN00a | 畅享20 Plus | 10 | ✅ | +| HUAWEI | WKG-AN00 | 畅享20 5G | 10 | ✅ | +| OPPO | PCLM50 | OPPO Reno3元气版 | 10 | ✅ | +| OPPO | PDAM10 | OPPO A52 | 10 | ✅ | +| GOOGLE | Pixel 4 XL | PIXEL 4 XL | 10 | ✅ | +| MEIZU | meizu 17 Pro | 17 Pro | 10 | ✅ | +| vivo | V2005A | VIVO X50 Pro | 10 | ✅ | +| vivo | V2001A | VIVO X50 | 10 | ✅ | +| vivo | V1831A | VIVO S1 | 10 | ✅ | +| vivo | V2012A | VIVO iQOO Z1x | 10 | ✅ | +| vivo | V1962A | VIVO S6 | 10 | ✅ | +| OPPO | PDPM00 | oppo Reno4 5G | 10 | ✅ | +| vivo | V2012A | VIVO iQOO Z1x | 10 | ✅ | +| vivo | V1938CT | VIVO X30 | 10 | ✅ | +| OPPO | PCNM00 | OPPO K5 | 10 | ✅ | +| OPPO | PDPM00 | oppo Reno4 5G | 10 | ✅ | +| HUAWEI | OXF-AN10 | 荣耀V30 PRO | 10 | ✅ | +| OPPO | PBEM00 | OPPO R17 | 10 | ✅ | +| VIVO | V1923A | NEX 3 | 10 | ✅ | +| vivo | V1965A | VIVO Y50 | 10 | ✅ | +| Xiaomi | Mi 10 | 小米10 | 10 | ✅ | +| Xiaomi | Mi 10 | 小米10 | 10 | ✅ | +| Xiaomi | Redmi K30 5G | Redmi K30 5G | 10 | ✅ | +| HUAWEI | HMA-TL00 | Mate20 | 10 | ✅ | +| OnePlus | GM1910 | 一加7 Pro | 10 | ✅ | +| HUAWEI | STK-AL00 | 华为畅享10 Plus | 10 | ✅ | +| Xiaomi | Redmi K20 Pro | 红米 K20 Pro | 10 | ✅ | +| VIVO | V1981A | VIVO iQOO Neo3 | 10 | ✅ | +| HUAWEI | HMA-AL00 | 华为 Mate 20 | 10 | ✅ | +| HUAWEI | SEA-AL10 | 华为nova 5 Pro | 10 | ✅ | +| HUAWEI | VOG-AL00 | 华为P30 Pro | 10 | ✅ | +| SAMSUNG | SM-G9650 | 三星 Galaxy S9+ | 10 | ✅ | +| HUAWEI | TAS-AN00 | 华为 Mate 30 5G | 10 | ✅ | +| HUAWEI | VOG-AL00 | 华为P30 Pro | 10 | ✅ | +| HUAWEI | YAL-AL00 | 荣耀20 | 10 | ✅ | +| HUAWEI | ELS-AN00 | 华为 P40 Pro 5G | 10 | ✅ | +| HUAWEI | ELS-AN00 | 华为 P40 Pro 5G | 10 | ✅ | +| ONEPLUS | IN2010 | 一加8 | 10 | ✅ | +| HUAWEI | ELE-AL00 | 华为 P30 | 10 | ✅ | +| HUAWEI | YAL-AL00 | 荣耀20 | 10 | ✅ | +| OPPO | PCKM00 | OPPO Reno2 | 10 | ✅ | +| OPPO | PCNM00 | OPPO K5 | 10 | ✅ | +| HUAWEI | STK-AL00 | 华为畅享10 Plus | 10 | ✅ | +| Xiaomi | Redmi K30 5G | Redmi K30 5G | 10 | ✅ | +| HUAWEI | JEF-AN20 | 华为nova 7 | 10 | ✅ | +| HUAWEI | EML-AL00 | 华为 P20 | 10 | ✅ | +| OPPO | PAAM00 | R15 梦镜版 | 10 | ✅ | +| HUAWEI | HLK-AL10 | 荣耀 9X Pro | 10 | ✅ | +| HUAWEI | YAL-AL50 | 荣耀20S | 10 | ✅ | +| ZTE | ZTE 8010 | 中兴Blade V2020 Smart | 10 | ✅ | +| HUAWEI | EVR-AN00 | HUAWEI Mate 20 X 5G | 10 | ✅ | +| HUAWEI | OCE-AN50 | Mate 40E | 10 | ✅ | +| HUAWEI | NEO-AL00 | 华为Mate RS 保时捷版 | 10 | ✅ | +| HUAWEI | ALP-TL00 | 华为Mate10 | 10 | ✅ | +| HUAWEI | LRA-AL00 | 荣耀20 青春版 | 10 | ✅ | +| HUAWEI | SPN-AL00 | 华为nova 5Z | 10 | ✅ | +| vivo | V1838A | VIVO X27 | 10 | ✅ | +| realme | RMX1931 | X2 Pro | 10 | ✅ | +| ONEPLUS | ONEPLUS A5010 | 一加5T | 10 | ✅ | +| HUAWEI | EBG-AN10 | Honor 30 Pro+ | 10 | ✅ | +| HUAWEI | POT-AL00a | 华为 畅享9S | 10 | ✅ | +| HUAWEI | DVC-AN20 | 华为畅享20 Pro | 10 | ✅ | +| HUAWEI | AQM-AL00 | 华为畅享10S | 10 | ✅ | +| OPPO | PDKM00 | OPPO A92s | 10 | ✅ | +| OPPO | PBEM00 | OPPO R17 | 9 | ✅ | +| OPPO | PADM00 | OPPO A3 | 9 | ✅ | +| OPPO | PCAM00 | OPPO Reno | 9 | ✅ | +| OPPO | PACT00 | R15 | 9 | ✅ | +| GOOGLE | Pixel 3a | Pixel 3A | 9 | ✅ | +| OPPO | PBCM10 | OPPO R15x | 9 | ✅ | +| HUAWEI | DUK-TL30 | V9 移动全网通 | 9 | ✅ | +| HUAWEI | HRY-AL00Ta | 荣耀20i | 9 | ✅ | +| Xiaomi | MI 8 SE | 小米8 SE | 9 | ✅ | +| ONEPLUS | ONEPLUS A6010 | 一加6T | 9 | ✅ | +| HUAWEI | INE-AL00 | 华为 nova3i | 9 | ✅ | +| SAMSUNG | SM-G9650 | 三星 Galaxy S9+ | 9 | ✅ | +| OPPO | PBBT00 | A7x | 9 | ✅ | +| OPPO | PACM00 | OPPO R15 | 9 | ✅ | +| HUAWEI | BLA-AL00 | Mate 10 Pro | 9 | ✅ | +| VIVO | vivo X21UD A | VIVO X21 屏幕指纹版 | 9 | ✅ | +| HUAWEI | EML-AL00 | 华为 P20 | 9 | ✅ | +| OPPO | PACT00 | R15 | 9 | ✅ | +| OPPO | OPPO R11s Plus | OPPO R11s Plus | 9 | ✅ | +| OPPO | PCGM00 | OPPO K3 | 9 | ✅ | +| BLACKSHARK | SKR-A0 | 黑鲨1代 | 9 | ✅ | +| OPPO | PAFM00 | OPPO Find X | 9 | ✅ | +| HUAWEI | MHA-AL00 | 华为 Mate 9 | 9 | ✅ | +| HUAWEI | VTR-AL00 | P10 | 9 | ✅ | +| Xiaomi | Redmi Note 5 | 红米Note 5 | 9 | ✅ | +| HUAWEI | COL-AL10 | 荣耀10 | 9 | ✅ | +| XIAOMI | MI 9 Transparent Edition | 小米9 透明尊享版 | 9 | ✅ | +| Xiaomi | Redmi K20 Pro | 红米 K20 Pro | 9 | ✅ | +| XIAOMI | MIX 2S | 小米 MIX2S | 9 | ✅ | +| HUAWEI | LON-AL00 | 华为 Mate 9 pro | 9 | ✅ | +| HUAWEI | JKM-AL00a | 为畅享9 Plus | 9 | ✅ | +| VIVO | vivo X21A | VIVO X21A | 9 | ✅ | +| vivo | V1901A | VIVO Y3 | 9 | ✅ | +| HUAWEI | HRY-AL00a | 华为 荣耀10 青春版 | 9 | ✅ | +| vivo | V1838A | VIVO X27 | 9 | ✅ | +| HUAWEI | BKL-AL20 | 荣耀V10 | 9 | ✅ | +| HUAWEI | PAR-AL00 | 华为 nova 3 | 9 | ✅ | +| HUAWEI | VOG-AL00 | 华为P30 Pro | 9 | ✅ | +| HUAWEI | GLK-AL00 | Nova 5i | 9 | ✅ | +| Xiaomi | Redmi Note 7 Pro | 红米Note 7 Pro | 9 | ✅ | +| HUAWEI | HMA-AL00 | 华为 Mate 20 | 9 | ✅ | +| HUAWEI | COR-AL10 | 荣耀Play | 9 | ✅ | +| XIAOMI | Redmi 7 | 红米 7 | 9 | ✅ | +| HUAWEI | BKL-AL00 | 荣耀 V10 | 9 | ✅ | +| Xiaomi | Redmi Note 7 | 红米Note 7 | 9 | ✅ | +| HUAWEI | YAL-AL00 | 荣耀20 | 9 | ✅ | +| HUAWEI | JSN-AL00a | 荣耀8X | 9 | ✅ | +| vivo | V1821A | VIVO NEX 双屏版 | 9 | ✅ | +| vivo | V1813BT | VIVO Z3 | 9 | ✅ | +| VIVO | V1816A | VIVO X23 炫彩版 | 9 | ✅ | +| HUAWEI | LLD-AL10 | 荣耀9青春版 | 9 | ✅ | +| OPPO | PADM00 | OPPO A3 | 9 | ✅ | +| HUAWEI | JKM-AL00b | 华为畅享9 Plus | 9 | ✅ | +| Xiaomi | Redmi Note 8 | 红米 note 8 | 9 | ✅ | +| XIAOMI | Redmi 8 | 红米 8 | 9 | ✅ | +| HUAWEI | BND-AL10 | 荣耀畅玩 7X | 9 | ✅ | +| vivo | vivo NEX A | VIVO NEX A | 9 | ✅ | +| VIVO | V1923A | NEX 3 | 9 | ✅ | +| vivo | V1934A | VIVO Y5s | 9 | ✅ | +| vivo | V1945A | VIVO Y9s | 9 | ✅ | +| OPPO | PCAM10 | OPPO A9 | 9 | ✅ | +| vivo | V1916A | VIVO iQOO Pro 5G | 9 | ✅ | +| OPPO | PCHM30 | OPPO A11x | 9 | ✅ | +| OPPO | PCAM10 | OPPO A9 | 9 | ✅ | +| vivo | V1816A | VIVO X23 炫彩版 | 9 | ✅ | +| HUAWEI | VKY-AL00 | P10 Plus | 9 | ✅ | +| OPPO | OPPO R11s | OPPO R11s | 9 | ✅ | +| HISENSE | HNR550T | Hisense F50 | 9 | ✅ | +| vivo | V1831A | VIVO S1 | 9 | ✅ | +| HUAWEI | ART-AL00x | 华为 畅享10 | 9 | ✅ | +| OPPO | PDBM00 | OPPO A8 | 9 | ✅ | +| vivo | V1930A | Y3 | 9 | ✅ | +| HUAWEI | GLK-AL00 | Nova 5i | 9 | ✅ | +| OPPO | PCEM00 | OPPO A9x | 9 | ✅ | +| SAMSUNG | SM-A705F | Galaxy A70 | 9 | ✅ | +| OPPO | PBCM10 | OPPO R15x | 9 | ✅ | +| HUAWEI | ASK-AL00x | 荣耀Play3 | 9 | ✅ | +| OPPO | PCPM00 | OPPO A91 | 9 | ✅ | +| vivo | V1901A | VIVO Y3 | 9 | ✅ | +| OPPO | PADM00 | OPPO A3 | 8.1.0 | ✅ | +| OPPO | PACT00 | R15 | 8.1.0 | ✅ | +| OPPO | PBAM00 | OPPO A5 | 8.1.0 | ✅ | +| OnePlus | ONEPLUS A6000 | 一加手机6 | 8.1.0 | ✅ | +| Xiaomi | Redmi 5 Plus | 红米5 Plus | 8.1.0 | ✅ | +| Xiaomi | MI 5X | 5X | 8.1.0 | ✅ | +| OPPO | OPPO R11s | OPPO R11s | 8.1.0 | ✅ | +| OPPO | PAAM00 | R15 梦镜版 | 8.1.0 | ✅ | +| VIVO | vivo X20A | VIVO X20 A | 8.1.0 | ✅ | +| BLACKSHARK | SKR-A0 | 黑鲨1代 | 8.1.0 | ✅ | +| OPPO | PBAM00 | OPPO A5 | 8.1.0 | ✅ | +| OPPO | OPPO R11s Plus | OPPO R11s Plus | 8.1.0 | ✅ | +| OPPO | OPPO R11 Plusk | R11 Plusk | 8.1.0 | ✅ | +| Xiaomi | MI 6X | 小米6X | 8.1.0 | ✅ | +| HUAWEI | COL-L29 | 10 国际版 | 8.1.0 | ✅ | +| HUAWEI | CLT-L29 | P20 Pro | 8.1.0 | ✅ | +| Xiaomi | MI 8 Lite | 小米8 Lite | 8.1.0 | ✅ | +| OPPO | OPPO R11 Pluskt | R11 Plus kt | 8.1.0 | ✅ | +| XIAOMI | MI 6X | 小米6X | 8.1.0 | ✅ | +| vivo | vivo X20A | VIVO X20 A | 8.1.0 | ✅ | +| vivo | V1816A | VIVO X23 炫彩版 | 8.1.0 | ✅ | +| HUAWEI | BLA-AL00 | Mate 10 Pro | 8.1.0 | ✅ | +| Xiaomi | MI 8 | 小米8 | 8.1.0 | ✅ | +| Xiaomi | MI 8 SE | 小米8 SE | 8.1.0 | ✅ | +| XIAOMI | MI 8 Explorer Edition | 8 透明探索版 | 8.1.0 | ✅ | +| HUAWEI | JSN-AL00a | 荣耀8X | 8.1.0 | ✅ | +| HUAWEI | DUB-AL00 | 华为畅享9 | 8.1.0 | ✅ | +| vivo | V1818CA | VIVO Y93s | 8.1.0 | ✅ | +| XIAOMI | Redmi 6 | Redmi 6 | 8.1.0 | ✅ | +| vivo | vivo X9s L | VIVO X9s L | 8.1.0 | ✅ | +| vivo | V1818A | VIVO Y93 | 8.1.0 | ✅ | +| VIVO | V1813A | VIVO Y97 | 8.1.0 | ✅ | +| vivo | V1813A | VIVO Y97 | 8.1.0 | ✅ | +| Xiaomi | MI 8 | 小米8 | 8.1.0 | ✅ | +| OPPO | PBCM30 | OPPO K1 | 8.1.0 | ✅ | +| HUAWEI | DUB-AL00 | 华为畅享9 | 8.1.0 | ✅ | +| OPPO | OPPO R11t | OPPO R11t | 8.1.0 | ✅ | +| Xiaomi | MIX 2S | 小米 MIX2S | 8.0.0 | ✅ | +| HUAWEI | HWI-AL00 | 华为 nova 2s | 8.0.0 | ✅ | +| SAMSUNG | SM-G8850 | 三星GalaxyA9 | 8.0.0 | ✅ | +| blackshark | SKR-A0 | 黑鲨1代 | 8.0.0 | ✅ | +| HUAWEI | BAC-AL00 | nova 2 Plus | 8.0.0 | ✅ | +| HUAWEI | FRD-AL10 | 荣耀8 高配版 | 8.0.0 | ✅ | +| HUAWEI | HUAWEI NXT-AL10 | 华为 Mate 8 | 8.0.0 | ✅ | +| HUAWEI | EVA-AL10 | 华为P9 | 8.0.0 | ✅ | +| Xiaomi | MI 6 | 小米6 | 8.0.0 | ✅ | +| HUAWEI | LLD-AL20 | 荣耀9i | 8.0.0 | ✅ | +| HUAWEI | BND-AL10 | 荣耀畅玩 7X | 8.0.0 | ✅ | +| HUAWEI | PRA-AL00 | 荣耀8青春版 | 8.0.0 | ✅ | +| Xiaomi | MI 5s Plus | 小米5s Plus | 8.0.0 | ✅ | +| XIAOMI | MI 5 | 小米5 | 8.0.0 | ✅ | +| XIAOMI | Mi Note 2 | Note 2 | 8.0.0 | ✅ | +| HUAWEI | BND-AL00 | 畅玩7X | 8.0.0 | ✅ | +| Xiaomi | MI 5X | 5X | 7.1.2 | ✅ | +| XIAOMI | Redmi Note 5A | 红米Note 5A | 7.1.2 | ✅ | +| Xiaomi | Redmi 5 Plus | 红米5 Plus | 7.1.2 | ✅ | +| XIAOMI | Redmi 5A | 红米5A | 7.1.2 | ✅ | +| MEIZU | M6 Note | M6 Note | 7.1.2 | ✅ | +| vivo | vivo X9Plus | VIVO X9Plus | 7.1.2 | ✅ | +| vivo | vivo X9L | VIVO X9L | 7.1.2 | ✅ | +| vivo | vivo X9 | VIVO X9 | 7.1.2 | ✅ | +| SAMSUNG | SM-N9508 | Galaxy Note8 | 7.1.1 | ✅ | +| XIAOMI | MIX 2 | MIX 2 | 7.1.1 | ✅ | +| Xiaomi | Mi Note 3 | 小米Note 3 | 7.1.1 | ✅ | +| Xiaomi | MI 6 | 小米6 | 7.1.1 | ✅ | +| OPPO | OPPO A73 | OPPO A73 | 7.1.1 | ✅ | +| OPPO | OPPO A83 | OPPO A83 | 7.1.1 | ✅ | +| Xiaomi | MI MAX 2 | Max2 | 7.1.1 | ✅ | +| OPPO | OPPO A77 | A77 | 7.1.1 | ✅ | +| SMARTISAN | OS105 | 锤子科技坚果Pro 2 | 7.1.1 | ✅ | +| OPPO | OPPO A83 | OPPO A83 | 7.1.1 | ✅ | +| Meizu | 15 | 15 | 7.1.1 | ✅ | +| VIVO | vivo Xplay6 | VIVO Xplay 6 | 7.1.1 | ✅ | +| VIVO | vivo X9s | VIVO X9s | 7.1.1 | ✅ | +| OPPO | OPPO A83t | A83T | 7.1.1 | ✅ | +| SAMSUNG | SM-G9280 | 三星GALAXY S6 Edge+ | 7 | ✅ | +| HUAWEI | BLN-AL10 | 畅玩6X | 7 | ✅ | +| HUAWEI | SLA-AL00 | 畅享7 | 7 | ✅ | +| samsung | SM-G9550 | Galaxy S8+ | 7 | ✅ | +| HUAWEI | HUAWEI CAZ-AL10 | nova 高配版 | 7 | ✅ | +| HUAWEI | BLN-AL40 | 荣耀畅玩6x | 7 | ✅ | +| Xiaomi | MI 5s | 5s | 7 | ✅ | +| XIAOMI | MI 5 | 小米5 | 7 | ✅ | +| samsung | SM-G9350 | Galaxy S7 edge | 7 | ✅ | +| HUAWEI | JMM-AL10 | V9 play | 7 | ✅ | +| HUAWEI | BLN-AL30 | 畅玩6X | 7 | ✅ | +| HUAWEI | FRD-AL00 | 荣耀8 | 7 | ✅ | +| HUAWEI | STF-AL10 | 荣耀9 尊享版 | 7 | ✅ | +| HUAWEI | TRT-AL00A | 畅享7 Plus | 7 | ✅ | +| HUAWEI | PRA-AL00 | 荣耀8青春版 | 7 | ✅ | +| HUAWEI | FRD-AL10 | 荣耀8 高配版 | 7 | ✅ | +| SAMSUNG | SM-G9350 | Galaxy S7 edge | 6.0.1 | ✅ | +| vivo | vivo Y66 | VIVO Y66 | 6.0.1 | ✅ | +| MEIZU | M2 E | 魅族魅蓝E2 | 6.0.1 | ✅ | +| OPPO | OPPO R9sk | OPPO R9sk | 6.0.1 | ✅ | +| OPPO | OPPO R9s Plus | OPPO R9s Plus | 6.0.1 | ✅ | +| XIAOMI | Redmi 4X | 红米Note 4X | 6.0.1 | ✅ | +| Xiaomi | Redmi Note 4X | 红米 Note 4X | 6.0.1 | ✅ | +| vivo | vivo Y66 | VIVO Y66 | 6.0.1 | ✅ | +| HUAWEI | NTS-AL00 | Magic | 6 | ✅ | +| XIAOMI | Redmi Note 4X | 红米 Note 4X | 6 | ✅ | +| HUAWEI | HUAWEI MLA-AL10 | 麦芒5 高配版 | 6 | ✅ | +| MEIZU | M5 Note | 魅族M5 Note | 6 | ✅ | +| HUAWEI | BLN-AL10 | 畅玩6X | 6 | ✅ | +| MEIZU | PRO 6s | Pro 6s | 6 | ✅ | +| Meitu | MP1602 | T8 | 6 | ✅ | +| HUAWEI | HUAWEI CAZ-AL10 | nova 高配版 | 6 | ✅ | +| vivo | vivo X7 | VIVO X7 | 5.1.1 | ✅ | +| VIVO | vivo Xplay5A | VIVO Xplay 5 | 5.1.1 | ✅ | +| vivo | vivo X7 | VIVO X7 | 5.1.1 | ✅ | +| OPPO | OPPO A51(Testin云测) | OPPO A51 | 5.1.1 | ✅ | +| OPPO | OPPO A30(Testin云测) | OPPO A30 | 5.1.1 | ✅ | +| OPPO | OPPO R9M(阿里云测试) | R9M | 5.1 | ✅ | +| OPPO | OPPO A59s(Testin云测) | A59s | 5.1 | ✅ | \ No newline at end of file diff --git a/android_java/.gitignore b/android_java/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/android_java/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/android_java/build.gradle b/android_java/build.gradle deleted file mode 100644 index 0f4101f..0000000 --- a/android_java/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'application' -apply plugin: 'java-library' - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -dependencies { - implementation 'org.junit.jupiter:junit-jupiter:5.7.1' -} \ No newline at end of file diff --git a/android_java/src/main/java/com/malin/android/Activity.java b/android_java/src/main/java/com/malin/android/Activity.java deleted file mode 100644 index ea60d84..0000000 --- a/android_java/src/main/java/com/malin/android/Activity.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.malin.android; - -public class Activity extends ContextWrapper { - @Override - protected void attachBaseContext(Context newBase) { - super.attachBaseContext(newBase); - } - - public final void attach(Context context) { - attachBaseContext(context); - } -} diff --git a/android_java/src/main/java/com/malin/android/ActivityClientRecord.java b/android_java/src/main/java/com/malin/android/ActivityClientRecord.java deleted file mode 100644 index c391da9..0000000 --- a/android_java/src/main/java/com/malin/android/ActivityClientRecord.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.malin.android; - -public class ActivityClientRecord { -} diff --git a/android_java/src/main/java/com/malin/android/ActivityThread.java b/android_java/src/main/java/com/malin/android/ActivityThread.java deleted file mode 100644 index 1439690..0000000 --- a/android_java/src/main/java/com/malin/android/ActivityThread.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.malin.android; - -public class ActivityThread { - - LoadedApk loadedApk; - Instrumentation mInstrumentation; - Application mInitialApplication; - - public Activity performLaunchActivity() { - loadedApk = new LoadedApk(this); - mInitialApplication = loadedApk.makeApplication(); - - ContextImpl appContext = new ContextImpl(this); - ClassLoader classLoader = appContext.getClassLoader(); - - Activity activity = null; - String activityName = "com.malin.android.Activity"; - try { - activity = mInstrumentation.newActivity(classLoader, activityName, null); - } catch (Exception e) { - e.printStackTrace(); - } - return activity; - } - - public Application getApplication() { - return mInitialApplication; - } - - public static void main(String[] args) { - ActivityThread thread = new ActivityThread(); - thread.attach(); - thread.performLaunchActivity(); - } - - public void attach() { - mInstrumentation = new Instrumentation(); - } -} diff --git a/android_java/src/main/java/com/malin/android/Application.java b/android_java/src/main/java/com/malin/android/Application.java deleted file mode 100644 index d242cbe..0000000 --- a/android_java/src/main/java/com/malin/android/Application.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.malin.android; - -public class Application extends ContextWrapper { - public void attach(Context context) { - attachBaseContext(context); - } -} diff --git a/android_java/src/main/java/com/malin/android/Context.java b/android_java/src/main/java/com/malin/android/Context.java deleted file mode 100644 index 00e15b8..0000000 --- a/android_java/src/main/java/com/malin/android/Context.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.malin.android; - -public abstract class Context { - public abstract void startActivity(Intent intent); - - public abstract Context getApplicationContext(); -} diff --git a/android_java/src/main/java/com/malin/android/ContextImpl.java b/android_java/src/main/java/com/malin/android/ContextImpl.java deleted file mode 100644 index b831909..0000000 --- a/android_java/src/main/java/com/malin/android/ContextImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.malin.android; - -public class ContextImpl extends Context { - final ActivityThread mMainThread; - - - public ContextImpl(ActivityThread activityThread) { - mMainThread = activityThread; - } - - public ClassLoader getClassLoader() { - return ClassLoader.getSystemClassLoader(); - } - - - public static ContextImpl createAppContext(ActivityThread activityThread) { - return new ContextImpl(activityThread); - } - - @Override - public void startActivity(Intent intent) { - System.out.println("ContextImpl#startActivity(Intent intent)"); - } - - @Override - public Context getApplicationContext() { - return mMainThread.getApplication(); - } -} diff --git a/android_java/src/main/java/com/malin/android/ContextWrapper.java b/android_java/src/main/java/com/malin/android/ContextWrapper.java deleted file mode 100644 index 69c48c5..0000000 --- a/android_java/src/main/java/com/malin/android/ContextWrapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.malin.android; - -public class ContextWrapper extends Context { - - Context mBase; - - protected void attachBaseContext(Context base) { - mBase = base; - } - - @Override - public void startActivity(Intent intent) { - mBase.startActivity(intent); - } - - @Override - public Context getApplicationContext() { - return mBase.getApplicationContext(); - } -} diff --git a/android_java/src/main/java/com/malin/android/Instrumentation.java b/android_java/src/main/java/com/malin/android/Instrumentation.java deleted file mode 100644 index 4298070..0000000 --- a/android_java/src/main/java/com/malin/android/Instrumentation.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.malin.android; - - -public class Instrumentation { - - public Application newApplication(ClassLoader cl, String className, Context context) - throws IllegalAccessException, ClassNotFoundException, InstantiationException { - Application app = instantiateApplication(cl, className); - app.attach(context); - return app; - } - - public Activity newActivity(ClassLoader cl, String className, Intent intent) - throws InstantiationException, IllegalAccessException, - ClassNotFoundException { - return instantiateActivity(cl, className, intent); - } - - public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - return (Activity) cl.loadClass(className).newInstance(); - } - - public Application instantiateApplication(ClassLoader cl, String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - return (Application) cl.loadClass(className).newInstance(); - } -} diff --git a/android_java/src/main/java/com/malin/android/Intent.java b/android_java/src/main/java/com/malin/android/Intent.java deleted file mode 100644 index 58c7c42..0000000 --- a/android_java/src/main/java/com/malin/android/Intent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.malin.android; - -public class Intent { -} diff --git a/android_java/src/main/java/com/malin/android/LoadedApk.java b/android_java/src/main/java/com/malin/android/LoadedApk.java deleted file mode 100644 index 704982a..0000000 --- a/android_java/src/main/java/com/malin/android/LoadedApk.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.malin.android; - -public class LoadedApk { - Application app; - ActivityThread mActivityThread; - - public LoadedApk(ActivityThread activityThread) { - this.mActivityThread = activityThread; - } - - public Application makeApplication() { - ContextImpl appContext = ContextImpl.createAppContext(mActivityThread); - try { - String appClass = "com.malin.android.Application"; - app = mActivityThread.mInstrumentation.newApplication(ClassLoader.getSystemClassLoader(), appClass, appContext); - } catch (Exception e) { - e.printStackTrace(); - } - return app; - } -} diff --git a/android_java/src/main/java/com/malin/android/Main.java b/android_java/src/main/java/com/malin/android/Main.java deleted file mode 100644 index eb83dbe..0000000 --- a/android_java/src/main/java/com/malin/android/Main.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.malin.android; - -import org.junit.jupiter.api.Test; - -public class Main { - - @Test - public void main() { - ActivityThread thread = new ActivityThread(); - thread.attach(); - - Activity activity = thread.performLaunchActivity(); - activity.attach(thread.getApplication()); - - //activity.getApplicationContext().startActivity(new Intent()); - activity.startActivity(new Intent()); - } -} diff --git a/android_java/src/main/java/com/malin/android/Singleton.java b/android_java/src/main/java/com/malin/android/Singleton.java deleted file mode 100644 index 9d057a6..0000000 --- a/android_java/src/main/java/com/malin/android/Singleton.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.malin.android; - -/** - * Singleton helper class for lazily initialization. - */ -public abstract class Singleton { - - public Singleton() { - } - - private T mInstance; - - protected abstract T create(); - - public final T get() { - synchronized (this) { - if (mInstance == null) { - mInstance = create(); - } - return mInstance; - } - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/Activity.java b/android_java/src/main/java/com/malin/android/simple/Activity.java deleted file mode 100644 index 9450ce2..0000000 --- a/android_java/src/main/java/com/malin/android/simple/Activity.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.malin.android.simple; - -public class Activity { -} diff --git a/android_java/src/main/java/com/malin/android/simple/ActivityThread.java b/android_java/src/main/java/com/malin/android/simple/ActivityThread.java deleted file mode 100644 index 36182a9..0000000 --- a/android_java/src/main/java/com/malin/android/simple/ActivityThread.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.malin.android.simple; - -public class ActivityThread { - - LoadedApk loadedApk = new LoadedApk(); - Instrumentation mInstrumentation; - - private Activity performLaunchActivity() { - loadedApk.makeApplication(); - return new Activity(); - } - - public static void main(String[] args) { - ActivityThread thread = new ActivityThread(); - thread.attach(); - - Activity activity = thread.performLaunchActivity(); - System.out.println(activity); - } - - private void attach() { - mInstrumentation = new Instrumentation(); - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/Application.java b/android_java/src/main/java/com/malin/android/simple/Application.java deleted file mode 100644 index be3cfb2..0000000 --- a/android_java/src/main/java/com/malin/android/simple/Application.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.malin.android.simple; - -public class Application extends ContextWrapper { - public void attach(Context context) { - attachBaseContext(context); - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/Context.java b/android_java/src/main/java/com/malin/android/simple/Context.java deleted file mode 100644 index 266c765..0000000 --- a/android_java/src/main/java/com/malin/android/simple/Context.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.malin.android.simple; - -public abstract class Context { - public abstract void startActivity(Intent intent); -} diff --git a/android_java/src/main/java/com/malin/android/simple/ContextImpl.java b/android_java/src/main/java/com/malin/android/simple/ContextImpl.java deleted file mode 100644 index d3c8626..0000000 --- a/android_java/src/main/java/com/malin/android/simple/ContextImpl.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.malin.android.simple; - -public class ContextImpl extends Context { - - public static ContextImpl createAppContext() { - return new ContextImpl(); - } - - @Override - public void startActivity(Intent intent) { - System.out.println("ContextImpl#startActivity(Intent intent)"); - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/ContextWrapper.java b/android_java/src/main/java/com/malin/android/simple/ContextWrapper.java deleted file mode 100644 index 5044b73..0000000 --- a/android_java/src/main/java/com/malin/android/simple/ContextWrapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.malin.android.simple; - -public class ContextWrapper extends Context { - - Context mBase; - - protected void attachBaseContext(Context base) { - mBase = base; - } - - @Override - public void startActivity(Intent intent) { - mBase.startActivity(intent); - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/Instrumentation.java b/android_java/src/main/java/com/malin/android/simple/Instrumentation.java deleted file mode 100644 index 76ae635..0000000 --- a/android_java/src/main/java/com/malin/android/simple/Instrumentation.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.malin.android.simple; - - -public class Instrumentation { - - public Application newApplication(ClassLoader cl, String className, Context context) - throws IllegalAccessException, ClassNotFoundException, InstantiationException { - Application app = instantiateApplication(cl, className); - app.attach(context); - return app; - } - - public Application instantiateApplication(ClassLoader cl, String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - return (Application) cl.loadClass(className).newInstance(); - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/Intent.java b/android_java/src/main/java/com/malin/android/simple/Intent.java deleted file mode 100644 index e1c546d..0000000 --- a/android_java/src/main/java/com/malin/android/simple/Intent.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.malin.android.simple; - -public class Intent { -} diff --git a/android_java/src/main/java/com/malin/android/simple/LoadedApk.java b/android_java/src/main/java/com/malin/android/simple/LoadedApk.java deleted file mode 100644 index e71799d..0000000 --- a/android_java/src/main/java/com/malin/android/simple/LoadedApk.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.malin.android.simple; - -public class LoadedApk { - Application app; - ActivityThread mActivityThread = new ActivityThread(); - - public Application makeApplication() { - ContextImpl appContext = ContextImpl.createAppContext(); - try { - String appClass = "com.malin.android.simple.Application"; - app = mActivityThread.mInstrumentation.newApplication(ClassLoader.getSystemClassLoader(), appClass, appContext); - } catch (Exception e) { - e.printStackTrace(); - } - return app; - } -} diff --git a/android_java/src/main/java/com/malin/android/simple/MainActivity.java b/android_java/src/main/java/com/malin/android/simple/MainActivity.java deleted file mode 100644 index 4d4359d..0000000 --- a/android_java/src/main/java/com/malin/android/simple/MainActivity.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.malin.android.simple; - - -public class MainActivity { - public static void main(String[] args) { - Intent intent = new Intent(); - getApplicationContext().startActivity(intent); - } - - private static Context getApplicationContext() { - ContextWrapper wrapper = new ContextWrapper(); - ContextImpl contextImpl = new ContextImpl(); - wrapper.attachBaseContext(contextImpl); - return wrapper; - } -} diff --git a/app/.gitignore b/app/.gitignore index 5684d87..cdfefb5 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -2,4 +2,5 @@ .classpath .project .settings -proguard-merge-config.txt \ No newline at end of file +proguard-merge-config.txt +mapping.txt \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 3cb5012..0000000 --- a/app/build.gradle +++ /dev/null @@ -1,72 +0,0 @@ -apply plugin: 'com.android.application' - -android { - - compileSdkVersion rootProject.ext.android.compileSdkVersion - buildToolsVersion rootProject.ext.android.buildToolsVersion - ndkVersion rootProject.ext.android.ndkVersion - - defaultConfig { - resConfigs "zh", "en" - applicationId rootProject.ext.android.applicationId - minSdkVersion rootProject.ext.android.minSdkVersion - targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.android.versionCode - versionName rootProject.ext.android.versionName - ndk { - abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86' - } - } - - signingConfigs { - release { - v1SigningEnabled true - v2SigningEnabled true - keyAlias "plugin" - keyPassword "plugin" - storeFile file("../plugin.jks") - storePassword "plugin" - } - debug { - v1SigningEnabled true - v2SigningEnabled true - } - } - - packagingOptions { - exclude 'META-INF/*' - exclude 'META-INF/CERT.SF' - exclude 'META-INF/CERT.RSA' - exclude 'META-INF/MANIFEST.MF' - } - - buildTypes { - debug { - debuggable true - minifyEnabled false - zipAlignEnabled false - shrinkResources false - crunchPngs false - } - release { - debuggable false - shrinkResources true - zipAlignEnabled true - minifyEnabled true - signingConfig signingConfigs.release - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') - proguardFile 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation rootProject.ext.dependencies["androidx.appcompat"] - implementation project(path: ':pluingImpl') -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..b3e745f --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,102 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("com.android.application") + id("project-report") + kotlin("android") +} + +android { + compileSdk = AppConfig.COMPILE_SDK_VERSION + buildToolsVersion = AppConfig.BUILD_TOOLS_VERSION + ndkVersion = AppConfig.NDK_VERSION + namespace = AppConfig.APPLICATION_ID + defaultConfig { + resourceConfigurations.addAll(arrayOf("zh", "en")) + applicationId = AppConfig.APPLICATION_ID + minSdk = AppConfig.MIN_SDK_VERSION + targetSdk = AppConfig.TARGET_SDK_VERSION + versionCode = AppConfig.VERSION_CODE + versionName = AppConfig.VERSION_NAME + ndk { + abiFilters.addAll(AppConfig.ABI) + } + } + + signingConfigs { + getByName("debug") { + enableV1Signing = true + enableV2Signing = true + enableV3Signing = true + enableV4Signing = true + } + create("release") { + enableV1Signing = true + enableV2Signing = true + keyAlias = "plugin" + keyPassword = "plugin" + storeFile = file("../plugin.jks") + storePassword = "plugin" + } + } + + packaging { + resources.excludes += "META-INF/*" + resources.excludes += "META-INF/CERT.SF" + resources.excludes += "META-INF/CERT.RSA" + resources.excludes += "META-INF/MANIFEST.MF" + } + + buildTypes { + getByName("debug") { + isDebuggable = true + isCrunchPngs = false + isMinifyEnabled = false + isShrinkResources = false + signingConfig = signingConfigs.getByName("debug") + } + getByName("release") { + isMinifyEnabled = true + isShrinkResources = true + signingConfig = signingConfigs.getByName("release") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + android.applicationVariants.all { + val buildType = this.buildType.name + outputs.all { + if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) { + if (buildType == "debug") { + this.outputFileName = + "app-${buildType}.apk" + } else if (buildType == "release") { + this.outputFileName = + "app-${buildType}.apk" + } + } + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) + implementation(DependenciesConfig.STD_LIB) + implementation(DependenciesConfig.APP_COMPAT) + implementation(DependenciesConfig.KTX_CORE) + implementation(DependenciesConfig.HIDDEN_API_PASS) + implementation(DependenciesConfig.MATERIAL) + implementation(project(":pluingImpl")) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a2883f4..e84bef8 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -22,15 +22,24 @@ #https://github.com/ysrc/AndroidObfuseDictionary #https://github.com/WrBug/FrenziedProguard # ---------------------------------------------------------------------------- -# 混淆的压缩比例,0-7 +# 混淆的压缩比例,0-7 表示对代码进行迭代优化的次数,optimization可以对代码进行各种优化,每次优化后还可以继续优化,故称之迭代优化 -optimizationpasses 7 +# 为了让插件和原来的宿主兼容,需要使用ProGuard的applymapping功能的进行增量混淆, +# 先编译插件release包,然后把插件release编译生成的mapping.txt文件复制到宿主的代码目录下,在宿主的proguard-rules.pro文件中增加配置 -applymapping mapping.txt +-applymapping mapping.txt + +# https://blog.csdn.net/wmadao11/article/details/102613078 -allowaccessmodification + +# 不做预校验,预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度 -dontpreverify # 指定不去忽略非公共的库的类的成员 -dontskipnonpubliclibraryclassmembers + # 指定混淆是采用的算法 -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* + # 指定外部模糊字典 dic.txt 改为混淆文件名,下同 -obfuscationdictionary dic.txt # 指定class模糊字典 @@ -40,15 +49,20 @@ #-------------------------------common------------------------------- +# 混淆后类型都为小写 -dontusemixedcaseclassnames + +# 不跳过非公共的库的类 -dontskipnonpubliclibraryclasses + +# 混淆时记录日志 -verbose -#保留Annotation不混淆,避免混淆泛型;抛出异常时保留代码行号 +# 保留Annotation不混淆,避免混淆泛型;抛出异常时保留代码行号;避免混淆注解、内部类、泛型、匿名类 -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,LocalVariableTable,*JavascriptInterface* -###keepattributes### -#hide the original source file name. +# 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码 +# hide the original source file name. -renamesourcefileattribute SourceFile #==============proguard6.2.2/gradle/src/proguard/gradle/proguard-android-common.pro start====== @@ -165,86 +179,10 @@ #-------------------------------common------------------------------- -#-------------------------------androidx------------------------------- -#https://stackoverflow.com/a/52592334/3326683 -#noinspection ShrinkerUnresolvedReference --keep class com.google.android.material.** {*;} --dontwarn com.google.android.material.** --dontnote com.google.android.material.** -#noinspection ShrinkerUnresolvedReference --keep class androidx.** {*;} --keep interface androidx.** {*;} --dontwarn androidx.** --dontnote androidx.** --keep public class * extends androidx.** -#-------------------------------androidx------------------------------- - -#-------------------------------androidx Understand the @Keep support annotation.------------------------------- -#noinspection ShrinkerUnresolvedReference --keep,allowobfuscation @interface androidx.annotation.Keep - --keep @androidx.annotation.Keep class * {*;} - --keepclasseswithmembers class * { - @androidx.annotation.Keep ; -} - --keepclasseswithmembers class * { - @androidx.annotation.Keep ; -} - --keepclasseswithmembers class * { - @androidx.annotation.Keep (...); -} -#-------------------------------androidx Understand the @Keep support annotation.------------------------------- - - -#proguard6.2.2/examples/android/proguard-project.txt -# If you wish, you can let the optimization step remove Android logging calls. -#-assumenosideeffects class android.util.Log { -# public static boolean isLoggable(java.lang.String, int); -# public static int v(...); -# public static int i(...); -# public static int w(...); -# public static int d(...); -# public static int e(...); -#} +##-------------------------------androidx------------------------------- +-keep class androidx.appcompat.view.ContextThemeWrapper {*;} +##-------------------------------androidx------------------------------- -#proguard6.2.2/examples/android/proguard-project.txt start -# In that case, it's especially useful to also clean up any corresponding -# string concatenation calls. --assumenoexternalsideeffects class java.lang.StringBuilder { - public java.lang.StringBuilder(); - public java.lang.StringBuilder(int); - public java.lang.StringBuilder(java.lang.String); - public java.lang.StringBuilder append(java.lang.Object); - public java.lang.StringBuilder append(java.lang.String); - public java.lang.StringBuilder append(java.lang.StringBuffer); - public java.lang.StringBuilder append(char[]); - public java.lang.StringBuilder append(char[], int, int); - public java.lang.StringBuilder append(boolean); - public java.lang.StringBuilder append(char); - public java.lang.StringBuilder append(int); - public java.lang.StringBuilder append(long); - public java.lang.StringBuilder append(float); - public java.lang.StringBuilder append(double); - public java.lang.String toString(); -} - --assumenoexternalreturnvalues class java.lang.StringBuilder { - public java.lang.StringBuilder append(java.lang.Object); - public java.lang.StringBuilder append(java.lang.String); - public java.lang.StringBuilder append(java.lang.StringBuffer); - public java.lang.StringBuilder append(char[]); - public java.lang.StringBuilder append(char[], int, int); - public java.lang.StringBuilder append(boolean); - public java.lang.StringBuilder append(char); - public java.lang.StringBuilder append(int); - public java.lang.StringBuilder append(long); - public java.lang.StringBuilder append(float); - public java.lang.StringBuilder append(double); -} -#proguard6.2.2/examples/android/proguard-project.txt end #https://proandroiddev.com/improving-proguard-name-obfuscation-83b27b34c52a -repackageclasses 'o' @@ -266,3 +204,6 @@ # final android.os.Handler$Callback mCallback; #} #-------------------------------reflect system api.------------------------------- +-dontwarn kotlin.** +-keep class kotlinx.** {*;} +-dontwarn kotlinx.** \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 06c3576..b5cf46c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,20 +1,18 @@ + xmlns:tools="http://schemas.android.com/tools"> + android:theme="@style/AppCompatThemeHost" + tools:ignore="AllowBackup,DataExtractionRules,GoogleAppIndexingWarning"> @@ -22,7 +20,8 @@ - + - - \ No newline at end of file + diff --git a/app/src/main/assets/pluginapk-debug.apk b/app/src/main/assets/pluginapk-debug.apk index a462478..681554f 100644 Binary files a/app/src/main/assets/pluginapk-debug.apk and b/app/src/main/assets/pluginapk-debug.apk differ diff --git a/app/src/main/java/com/malin/hook/BaseDexClassLoaderReporter.java b/app/src/main/java/com/malin/hook/BaseDexClassLoaderReporter.java deleted file mode 100644 index 81b30d6..0000000 --- a/app/src/main/java/com/malin/hook/BaseDexClassLoaderReporter.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.malin.hook; - -import android.util.Log; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import dalvik.system.BaseDexClassLoader; - -/** - * author: androidmalin - * date: 2020.5.3 17:00 - */ -public class BaseDexClassLoaderReporter { - - @SuppressWarnings("JavaReflectionMemberAccess") - public static void setReporterHook() { - - try { - //0.Reporter class - Class reporterClazz = Class.forName("dalvik.system.BaseDexClassLoader$Reporter"); - - //1.setReporter - //public static void setReporter(Reporter newReporter) {} - Method setReporterMethod = BaseDexClassLoader.class.getDeclaredMethod("setReporter", reporterClazz); - setReporterMethod.setAccessible(true); - - //2.make a proxy Reporter - Object reportProxy = Proxy.newProxyInstance( - Thread.currentThread().getContextClassLoader(), - new Class[]{reporterClazz}, - new ReportInvocationHandler() - ); - - //3.invoke - setReporterMethod.invoke(null, reportProxy); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - - - } - - private static class ReportInvocationHandler implements InvocationHandler { - - ReportInvocationHandler() { - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String TAG = "ReportInvocationHandler"; - Log.e(TAG, "method:" + method.getName()); - Log.e(TAG, "args1:" + args[0]); - Log.e(TAG, "args2:" + args[1]); - return null; - } - } - - //args1: - //[ - // com.malin.hook.CustomClassLoader - // [DexPathList[[zip file "/data/user/0/com.malin.hook/files/pluginBroadcastReceiver-debug-1.0.apk"], - // nativeLibraryDirectories=[/data/user/0/com.malin.hook/files/plugin/com.malin.receiver.plugin/lib, /system/lib64, /vendor/lib64]] - // ], - - // dalvik.system.PathClassLoader - // [ - // DexPathList[ - // [ - // zip file "/data/user/0/com.malin.hook/files/pluginActivity-debug-1.0.apk", - // zip file "/data/user/0/com.malin.hook/files/pluginService-debug-1.0.apk", - // zip file "/data/app/com.malin.hook-M1sKKjgi0kgdrmfkM6B4iA==/base.apk" - // ], - // nativeLibraryDirectories=[/data/app/com.malin.hook-M1sKKjgi0kgdrmfkM6B4iA==/lib/arm64, /system/lib64, /vendor/lib64] - // ] - // ]] - - //args2: - //[ - // /data/user/0/com.malin.hook/files/pluginBroadcastReceiver-debug-1.0.apk, - // /data/user/0/com.malin.hook/files/pluginActivity-debug-1.0.apk: - // /data/user/0/com.malin.hook/files/pluginService-debug-1.0.apk: - // /data/app/com.malin.hook-M1sKKjgi0kgdrmfkM6B4iA==/base.apk - // ] -} diff --git a/app/src/main/java/com/malin/hook/HostRegisterActivity.kt b/app/src/main/java/com/malin/hook/HostRegisterActivity.kt new file mode 100644 index 0000000..aedc8a3 --- /dev/null +++ b/app/src/main/java/com/malin/hook/HostRegisterActivity.kt @@ -0,0 +1,75 @@ +package com.malin.hook + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.Gravity +import android.view.View +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat + +@SuppressLint("SetTextI18n") +class HostRegisterActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(TAG, "$TAG:onCreate") + setContentView(getRootLayout()) + supportActionBar?.hide() + lightStatus() + } + + private fun getRootLayout(): View { + val relativeLayout = RelativeLayout(this) + relativeLayout.gravity = Gravity.CENTER + + val textView = TextView(this) + textView.text = "宿主中自带的已注册的HostRegisterActivity,启动成功!" + textView.setTextColor(Color.parseColor("#000000")) + relativeLayout.addView(textView) + return relativeLayout + } + + private fun lightStatus() { + val window = window ?: return + val decorView = window.decorView + val controller = WindowCompat.getInsetsController(window, decorView) + controller.isAppearanceLightStatusBars = true + } + + override fun onStart() { + super.onStart() + Log.d(TAG, "$TAG:onStart") + } + + override fun onResume() { + super.onResume() + Log.d(TAG, "$TAG:onResume") + } + + override fun onRestart() { + super.onRestart() + Log.d(TAG, "$TAG:onRestart") + } + + override fun onPause() { + super.onPause() + Log.d(TAG, "$TAG:onPause") + } + + override fun onStop() { + super.onStop() + Log.d(TAG, "$TAG:onStop") + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "$TAG:onDestroy") + } + + companion object { + private const val TAG = "HostRegisterActivity" + } +} diff --git a/app/src/main/java/com/malin/hook/MApplication.java b/app/src/main/java/com/malin/hook/MApplication.java deleted file mode 100644 index 808ba57..0000000 --- a/app/src/main/java/com/malin/hook/MApplication.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.malin.hook; - -import android.app.Application; -import android.content.Context; - -import com.malin.plugin.impl.PluginImpl; - - -public class MApplication extends Application { - - @Override - protected void attachBaseContext(Context context) { - super.attachBaseContext(context); - PluginImpl.getInstance().init(context, true, true); - } -} diff --git a/app/src/main/java/com/malin/hook/MApplication.kt b/app/src/main/java/com/malin/hook/MApplication.kt new file mode 100644 index 0000000..cf069a9 --- /dev/null +++ b/app/src/main/java/com/malin/hook/MApplication.kt @@ -0,0 +1,41 @@ +package com.malin.hook + +import android.app.Application +import android.content.Context +import android.os.Build +import android.util.Log +import com.malin.plugin.impl.PluginImpl +import org.lsposed.hiddenapibypass.HiddenApiBypass + +class MApplication : Application() { + + /** + * 处理反射系统API的限制 + * 原理参考: + * https://github.com/LSPosed/AndroidHiddenApiBypass + */ + override fun attachBaseContext(context: Context) { + super.attachBaseContext(context) + if (Build.VERSION.SDK_INT >= 28) { + try { + HiddenApiBypass.addHiddenApiExemptions("") + } catch (th: Throwable) { + Log.e("MApplication", "error", th) + } + } + } + + /** + * 开启插件hook,加载插件apk和进行相关的hook + */ + override fun onCreate() { + super.onCreate() + instance = this + PluginImpl.init(context = baseContext, instrumentation = false, firstMode = false) + } + + companion object { + lateinit var instance: Application + private set + } +} diff --git a/app/src/main/java/com/malin/hook/MainActivity.java b/app/src/main/java/com/malin/hook/MainActivity.java deleted file mode 100644 index e2ab7ed..0000000 --- a/app/src/main/java/com/malin/hook/MainActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.malin.hook; - -import android.content.ComponentName; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; - -import androidx.appcompat.app.AppCompatActivity; - - -public class MainActivity extends AppCompatActivity implements View.OnClickListener { - - private ImageView mImageView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - initView(); - initListener(); - } - - - private void initView() { - mImageView = findViewById(R.id.iv_image_plugin); - } - - private void initListener() { - mImageView.setOnClickListener(this); - findViewById(R.id.btn_start).setOnClickListener(this); - findViewById(R.id.btn_start_appcompat).setOnClickListener(this); - findViewById(R.id.btn_start_plugin_apk_activity).setOnClickListener(this); - findViewById(R.id.btn_start_plugin_apk_appcompat_activity).setOnClickListener(this); - findViewById(R.id.btn_load_img).setOnClickListener(this); - findViewById(R.id.btn_start_inner).setOnClickListener(this); - } - - - @Override - public void onClick(View v) { - int id = v.getId(); - if (id == R.id.btn_start) { - startActivity(1, false); - } else if (id == R.id.btn_start_appcompat) { - startActivity(2, false); - } else if (id == R.id.btn_start_plugin_apk_activity) { - startActivity(3, false); - } else if (id == R.id.btn_start_plugin_apk_appcompat_activity) { - startActivity(4, false); - } else if (id == R.id.btn_load_img) { - Drawable drawable = PluginResourceUtil.getPluginDrawableByName(this, "pluginapk-debug.apk", "com.malin.plugin", "plugin_img"); - mImageView.setImageDrawable(drawable); - } else if (id == R.id.btn_start_inner) { - startActivity(5, false); - } - } - - - private void startActivity(int type, boolean context) { - if (type < 1 || type > 5) { - return; - } - Intent intent = null; - switch (type) { - case 1: { - intent = new Intent(this, TargetActivity.class); - break; - } - case 2: { - intent = new Intent(this, TargetAppCompatActivity.class); - break; - } - case 3: { - intent = new Intent(); - intent.setComponent(new ComponentName("com.malin.plugin", "com.malin.plugin.PluginActivity")); - break; - } - case 4: { - intent = new Intent(); - intent.setComponent(new ComponentName("com.malin.plugin", "com.malin.plugin.PluginAppCompatActivity")); - break; - } - case 5: { - intent = new Intent(this, SecondActivity.class); - break; - } - } - if (context) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getApplicationContext().startActivity(intent); - } else { - startActivity(intent); - } - } - -} diff --git a/app/src/main/java/com/malin/hook/MainActivity.kt b/app/src/main/java/com/malin/hook/MainActivity.kt new file mode 100644 index 0000000..41abf7b --- /dev/null +++ b/app/src/main/java/com/malin/hook/MainActivity.kt @@ -0,0 +1,220 @@ +package com.malin.hook + +import android.app.Activity +import android.content.ComponentName +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.widget.ImageView +import androidx.annotation.IdRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat + +class MainActivity : AppCompatActivity(), View.OnClickListener { + + private val mIvPluginRes: ImageView by bindView(R.id.iv_plugin_img) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + initView() + lightStatus() + initListener() + initLoadPluginResourceImg() + } + + private fun initView() { + supportActionBar?.hide() + } + + private fun lightStatus() { + val localWindow = window ?: return + val localDecorView = localWindow.decorView + val controller = WindowCompat.getInsetsController(localWindow, localDecorView) + controller.isAppearanceLightStatusBars = true + } + + private fun Activity.bindView(@IdRes res: Int): Lazy { + return lazy { findViewById(res) } + } + + private fun initListener() { + findViewById(R.id.btn_start_host_register_act).setOnClickListener(this) + findViewById(R.id.btn_start_host_unregister_act).setOnClickListener(this) + findViewById(R.id.btn_start_host_unregister_appcompat_act).setOnClickListener(this) + findViewById(R.id.btn_start_plugin_apk_activity).setOnClickListener(this) + findViewById(R.id.btn_start_plugin_apk_appcompat_activity).setOnClickListener(this) + } + + /** + * 宿主中使用插件APK中的资源 + */ + private fun initLoadPluginResourceImg() { + findViewById(R.id.btn_load_plugin_img).setOnClickListener { + val drawableImg = PluginResourceUtil.getPluginDrawableByName( + context = applicationContext, + pluginApkFileName = PLUGIN_APK_FILE_NAME, + pluginPackageName = PLUGIN_PACKAGE_NAME, + resourceName = PLUGIN_IMG_NAME, + loadResourceType = 2 + ) + mIvPluginRes.setImageDrawable(drawableImg) + } + } + + override fun onClick(v: View) { + when (v.id) { + + // 启动宿主中注册的 HostRegisterActivity + R.id.btn_start_host_register_act -> { + startActivity( + startActType = LaunchTargetActType.HOST_EXIST_ACTIVITY, + isApplicationContext = true + ) + } + + // 启动宿主中未注册的 TargetActivity + R.id.btn_start_host_unregister_act -> { + startActivity( + startActType = LaunchTargetActType.HOST_UNREGISTER_ACTIVITY, + isApplicationContext = false + ) + } + + // 启动宿主中未注册的 TargetAppCompatActivity + R.id.btn_start_host_unregister_appcompat_act -> { + startActivity( + startActType = LaunchTargetActType.HOST_UNREGISTER_APPCOMPAT_ACTIVITY, + isApplicationContext = false + ) + } + + // 启动插件APK中的 PluginActivity + R.id.btn_start_plugin_apk_activity -> { + startActivity( + startActType = LaunchTargetActType.PLUGIN_ACTIVITY, + isApplicationContext = false + ) + } + + // 启动插件APK中的 PluginAppCompatActivity + R.id.btn_start_plugin_apk_appcompat_activity -> { + startActivity( + startActType = LaunchTargetActType.PLUGIN_APPCOMPAT_ACTIVITY, + isApplicationContext = false + ) + } + } + } + + + private fun startActivity(startActType: LaunchTargetActType, isApplicationContext: Boolean) { + + val intent: Intent = when (startActType) { + + // 启动 宿主中注册的 [HostRegisterActivity] + LaunchTargetActType.HOST_EXIST_ACTIVITY -> { + Intent(this, HostRegisterActivity::class.java) + } + + // 启动 宿主中未注册的 [TargetActivity] + LaunchTargetActType.HOST_UNREGISTER_ACTIVITY -> { + Intent(this, TargetActivity::class.java) + } + + // 启动 宿主中未注册的 [TargetAppCompatActivity] + LaunchTargetActType.HOST_UNREGISTER_APPCOMPAT_ACTIVITY -> { + Intent(this, TargetAppCompatActivity::class.java) + } + + // 启动 插件APK中的 PluginActivity + LaunchTargetActType.PLUGIN_ACTIVITY -> { + Intent().apply { + component = ComponentName(PLUGIN_PACKAGE_NAME, PLUGIN_ACTIVITY_NAME) + } + } + + // 启动 插件APK中的 PluginAppCompatActivity + LaunchTargetActType.PLUGIN_APPCOMPAT_ACTIVITY -> { + Intent().apply { + component = ComponentName(PLUGIN_PACKAGE_NAME, PLUGIN_APPCOMPAT_ACTIVITY_NAME) + } + } + } + + when { + // 启动的上下文是 ApplicationContext + isApplicationContext -> { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + applicationContext.startActivity(intent) + } + + // 启动的上下文不是 ApplicationContext + else -> { + this@MainActivity.startActivity(intent) + } + } + } + + private companion object { + /** + * 插件apk的文件名称 + * 位置: app/src/main/assets/pluginapk-debug.apk + */ + private const val PLUGIN_APK_FILE_NAME = "pluginapk-debug.apk" + + /** + * 插件apk的包名 + * 插件apk的包名和宿主包名一致,原理参考如下博客 + * https://juejin.cn/post/6844903875284058119 + */ + private const val PLUGIN_PACKAGE_NAME = "com.malin.hook" + + /** + * 插件apk中插件PluginActivity的类名 + * 位置: pluginapk/src/main/java/com/malin/plugin/PluginActivity.kt + */ + private const val PLUGIN_ACTIVITY_NAME = "com.malin.plugin.PluginActivity" + + /** + * 插件apk中插件PluginAppCompatActivity的类名 + * 位置: pluginapk/src/main/java/com/malin/plugin/PluginAppCompatActivity.kt + */ + private const val PLUGIN_APPCOMPAT_ACTIVITY_NAME = + "com.malin.plugin.PluginAppCompatActivity" + + /** + * 插件apk的一种图片 + * 位置: pluginapk/src/main/res/drawable/plugin_img.png + */ + private const val PLUGIN_IMG_NAME = "plugin_img" + } + + + private enum class LaunchTargetActType { + /** + * 宿主中注册的 [HostRegisterActivity] + */ + HOST_EXIST_ACTIVITY, + + /** + * 宿主中未注册的 [TargetActivity] + */ + HOST_UNREGISTER_ACTIVITY, + + /** + * 宿主中未注册的 [TargetAppCompatActivity] + */ + HOST_UNREGISTER_APPCOMPAT_ACTIVITY, + + /** + * 插件APK中的 PluginActivity + */ + PLUGIN_ACTIVITY, + + /** + * 插件APK中的 PluginAppCompatActivity + */ + PLUGIN_APPCOMPAT_ACTIVITY, + } +} diff --git a/app/src/main/java/com/malin/hook/PluginResourceUtil.java b/app/src/main/java/com/malin/hook/PluginResourceUtil.java deleted file mode 100644 index 8ab340d..0000000 --- a/app/src/main/java/com/malin/hook/PluginResourceUtil.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.malin.hook; - -import android.content.Context; -import android.content.res.AssetManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.DisplayMetrics; - -import androidx.core.content.res.ResourcesCompat; - -import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import dalvik.system.DexClassLoader; - -public class PluginResourceUtil { - - ///data/user/0/com.malin.hook/files/pluginapk-debug.apk - public static Drawable getPluginDrawableByName(Context context, String pluginApkName, String pluginPackageName, String sourceName) { - String plugin_apk_path = context.getFileStreamPath(pluginApkName).getAbsolutePath(); - Resources resources = getPluginResources(context, plugin_apk_path); - int resId = 0; - int mode = 1; - switch (mode) { - case 1: { - resId = getResId(pluginPackageName, sourceName); - break; - } - case 2: { - resId = getResId2(resources, pluginPackageName, sourceName); - break; - } - - case 3: { - resId = getResId3(context, plugin_apk_path, pluginPackageName, sourceName); - break; - } - - case 4: { - resId = getResId4(context, pluginPackageName, sourceName); - break; - } - } - if (resources != null) { - return ResourcesCompat.getDrawable(resources, resId, context.getTheme()); - } - return null; - } - - - @SuppressWarnings({"JavaReflectionMemberAccess", "PrivateApi", "deprecation"}) - public static Resources getPluginResources(Context context, String pluginPath) { - try { - //1.调用assetManager.addAssetPath(pluginPath); - AssetManager assetManager = AssetManager.class.newInstance(); - Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class); - addAssetPathMethod.setAccessible(true); - addAssetPathMethod.invoke(assetManager, pluginPath); - - Resources superRes = context.getResources(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - //Resources#public void setImpl(ResourcesImpl impl) {} - Class displayAdjustmentsClazz = Class.forName("android.view.DisplayAdjustments"); - Constructor displayAdjustmentsConstructor = displayAdjustmentsClazz.getDeclaredConstructor(); - displayAdjustmentsConstructor.setAccessible(true); - Object displayAdjustmentsObj = displayAdjustmentsConstructor.newInstance(); - - //new ResourcesImpl(AssetManager assets,DisplayMetrics metrics, - // Configuration config, DisplayAdjustments displayAdjustments) {} - Class resourcesImplClazz = Class.forName("android.content.res.ResourcesImpl"); - Constructor resourcesImplConstructor = resourcesImplClazz.getDeclaredConstructor(AssetManager.class, DisplayMetrics.class, Configuration.class, displayAdjustmentsClazz); - resourcesImplConstructor.setAccessible(true); - Object resourcesImplObj = resourcesImplConstructor.newInstance(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration(), displayAdjustmentsObj); - - //private Resources() {} - Class resourcesClazz = Class.forName("android.content.res.Resources"); - Constructor resourcesConstructor = resourcesClazz.getDeclaredConstructor(); - resourcesConstructor.setAccessible(true); - Object resourcesObj = resourcesConstructor.newInstance(); - - //Resources - //public void setImpl(ResourcesImpl impl) {} - Method setImplMethod = resourcesClazz.getDeclaredMethod("setImpl", resourcesImplClazz); - setImplMethod.setAccessible(true); - - //resources.setImpl(ResourcesImpl impl){} - setImplMethod.invoke(resourcesObj, resourcesImplObj); - return (Resources) resourcesObj; - } else { - return new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); - } - } catch (Throwable e) { - e.printStackTrace(); - } - return null; - } - - public static int getResId(String pluginPackageName, String resName) { - try { - Class r$DrawableClazz = Class.forName(pluginPackageName + ".R$drawable"); - Field resField = r$DrawableClazz.getDeclaredField(resName); - return resField.getInt(null); - } catch (Throwable e) { - e.printStackTrace(); - } - return 0; - } - - public static int getResId2(Resources resources, String pluginPackageName, String resName) { - return resources.getIdentifier(resName, "drawable", pluginPackageName); - } - - public static int getResId3(Context context, String pluginPath, String pluginPackageName, String resName) { - try { - File optimizedDirectoryFile = context.getDir("dex", Context.MODE_PRIVATE); - DexClassLoader dexClassLoader = new DexClassLoader(pluginPath, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader()); - Class r$DrawableClazz = dexClassLoader.loadClass(pluginPackageName + ".R$drawable"); - Field resField = r$DrawableClazz.getDeclaredField(resName); - return resField.getInt(null); - } catch (Throwable e) { - e.printStackTrace(); - } - return 0; - } - - public static int getResId4(Context context, String pluginPackageName, String resName) { - int drawableResId = 0; - try { - drawableResId = context - .getClassLoader() - .loadClass(pluginPackageName + ".R$drawable") - .getDeclaredField(resName) - .getInt(null); - } catch (Throwable e) { - e.printStackTrace(); - } - return drawableResId; - } -} diff --git a/app/src/main/java/com/malin/hook/PluginResourceUtil.kt b/app/src/main/java/com/malin/hook/PluginResourceUtil.kt new file mode 100644 index 0000000..99f43c0 --- /dev/null +++ b/app/src/main/java/com/malin/hook/PluginResourceUtil.kt @@ -0,0 +1,116 @@ +package com.malin.hook + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.Resources +import android.graphics.drawable.Drawable +import androidx.core.content.res.ResourcesCompat +import dalvik.system.DexClassLoader + +object PluginResourceUtil { + + /** + * 获取插件apk中的图片. + * 有几种不同的方式. + */ + fun getPluginDrawableByName( + context: Context, + pluginApkFileName: String, + pluginPackageName: String, + resourceName: String, + loadResourceType: Int, + ): Drawable? { + val pluginApkPath = context.getFileStreamPath(pluginApkFileName).absolutePath + val resources = ResourceUtil.createResources(context, pluginApkPath) ?: return null + var resId = 0 + when (loadResourceType) { + 1 -> { + resId = getResId(pluginPackageName = pluginPackageName, resName = resourceName) + } + + 2 -> { + resId = getResId2( + resources = resources, + pluginPackageName = pluginPackageName, + resName = resourceName + ) + } + + 3 -> { + resId = getResId3( + context = context, + pluginPath = pluginApkPath, + pluginPackageName = pluginPackageName, + resName = resourceName + ) + } + + 4 -> { + resId = + getResId4( + context = context, + pluginPackageName = pluginPackageName, + resName = resourceName + ) + } + } + return ResourcesCompat.getDrawable(resources, resId, context.theme) + } + + private fun getResId(pluginPackageName: String, resName: String): Int { + try { + val rDrawableClazz = Class.forName("$pluginPackageName.R\$drawable") + val resField = rDrawableClazz.getDeclaredField(resName) + return resField.getInt(null) + } catch (e: Throwable) { + e.printStackTrace() + } + return 0 + } + + @SuppressLint("DiscouragedApi") + private fun getResId2( + resources: Resources, + pluginPackageName: String, + resName: String, + ): Int { + return resources.getIdentifier(resName, "drawable", pluginPackageName) + } + + private fun getResId3( + context: Context, + pluginPath: String, + pluginPackageName: String, + resName: String, + ): Int { + try { + val optimizedDirectoryFile = context.getDir("dex", Context.MODE_PRIVATE) + val dexClassLoader = DexClassLoader( + pluginPath, + optimizedDirectoryFile.path, + null, + ClassLoader.getSystemClassLoader() + ) + val rDrawableClazz = dexClassLoader.loadClass("$pluginPackageName.R\$drawable") + val resField = rDrawableClazz.getDeclaredField(resName) + return resField.getInt(null) + } catch (e: Throwable) { + e.printStackTrace() + } + return 0 + } + + private fun getResId4(context: Context, pluginPackageName: String, resName: String): Int { + var drawableResId = 0 + try { + drawableResId = context + .classLoader + .loadClass("$pluginPackageName.R\$drawable") + .getDeclaredField(resName) + .getInt(null) + } catch (e: Throwable) { + e.printStackTrace() + } + return drawableResId + } +} diff --git a/app/src/main/java/com/malin/hook/ResourceUtil.kt b/app/src/main/java/com/malin/hook/ResourceUtil.kt new file mode 100644 index 0000000..1a1830f --- /dev/null +++ b/app/src/main/java/com/malin/hook/ResourceUtil.kt @@ -0,0 +1,191 @@ +package com.malin.hook + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.AssetManager +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Build +import android.util.DisplayMetrics + +/** + * 插件可以访问宿主的资源. + * 插件调用宿主资源则需要将宿主的APK和插件的APK一起添加到同一个AssetManager里. + * + * 相关的源码分析,原理参考如下链接. + * + * [VirtualAPK资源的插件化处理](https://www.zybuluo.com/dodola/note/814116) + * + * [VirtualAPK资源的插件化处理(备用)](https://www.notion.so/VirtualAPK-1fce1a910c424937acde9528d2acd537) + * + * [AssetManager.cpp#155](https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r2.0.1/libs/androidfw/AssetManager.cpp#155) + * + * [android_util_AssetManager.cpp](https://android.googlesource.com/platform/frameworks/base.git/+/android-4.4.2_r2.0.1/core/jni/android_util_AssetManager.cpp) + * + * [AssetManager.java#751](https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r2.0.1/core/java/android/content/res/AssetManager.java#751) + * + * [VirtualApk解决插件资源ID与宿主冲突的问题](https://github.com/SusionSuc/AdvancedAndroid/tree/master/plugin/VirtualApk/) + */ +object ResourceUtil { + fun createResources(hostContext: Context, apk: String?): Resources? { + val version: Int = Build.VERSION.SDK_INT + val resource: Resources? = when { + version < Build.VERSION_CODES.LOLLIPOP -> { // 15 <= api < 21 + getPluginResourceForAndroidL(hostContext, apk) + } + + version < Build.VERSION_CODES.N -> { // 21 <= api < 24 + getPluginResourceForAndroidM(hostContext, apk) + } + + else -> { // 24 <= api < 32 + getPluginResourceForAndroidS(hostContext, apk) + } + } + return resource + } + + // Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP + private fun getPluginResourceForAndroidL(hostContext: Context, apk: String?): Resources { + val hostResources = hostContext.resources + val assetManager = hostResources!!.assets + val assetManagerClazz: Class<*> = assetManager.javaClass + // 我们需要将应用原来加载的地址取出来,详情见① + // 由于我们将host的AssetManager已经destroy后,需要还原原来的地址,否则就会发生找不到资源的情况, + // 此时需要提前将host加载的资源路径全部取出来,理论上,这个过程系统是做了一部分的,当我们调用init方法的时候 + val cookieNames: MutableList = ArrayList() + + // private native final int getStringBlockCount(); + val stringBlockCount = assetManagerClazz.getDeclaredMethod("getStringBlockCount") + .also { it.isAccessible = true }.invoke(assetManager) as Int + + // public native final String getCookieName(int cookie); + val getCookieNameMethod = + AssetManager::class.java.getDeclaredMethod("getCookieName", Integer.TYPE) + .also { it.isAccessible = true } + + for (i in 0 until stringBlockCount) { + cookieNames.add(getCookieNameMethod.invoke(assetManager, i + 1) as String) + } + + // private native final void destroy(); + assetManagerClazz.getDeclaredMethod("destroy").also { it.isAccessible = true } + .invoke(assetManager) + + // private native final void init(); + assetManagerClazz.getDeclaredMethod("init").also { it.isAccessible = true } + .invoke(assetManager) + + // ②③引用:它记录了之前加载过的所有资源包中的String Pool,很多时候访问字符串是从此处来的,如果不重新构造就会导致崩溃 + // private StringBlock mStringBlocks[] = null; + assetManagerClazz.getDeclaredField("mStringBlocks") + .also { it.isAccessible = true }[assetManager] = null //② + + // public final int addAssetPath(String path) {...} + @SuppressLint("DiscouragedPrivateApi") + val addAssetPathMethod = + assetManagerClazz.getDeclaredMethod("addAssetPath", String::class.java) + .also { it.isAccessible = true } + + // 将原来的assets添加进去,有了此步骤就不用刻意添加sourceDir了 + for (path in cookieNames) { + addAssetPathMethod.invoke(assetManager, path) + } + + // 插入插件的资源地址 + addAssetPathMethod.invoke(assetManager, apk) + + // final void ensureStringBlocks() {} //③ + assetManagerClazz.getDeclaredMethod("ensureStringBlocks") + .also { it.isAccessible = true }.invoke(assetManager) + + // 4:过程中很重要的一步,因为后面在资源查找的时候是需要通过一个ResTable_config来获取当前手机的一些配置从而获取到准确的资源, + // 如果不进行初始化则会出现找不到资源的崩溃 + @Suppress("DEPRECATION") + hostResources.updateConfiguration( + hostResources.configuration, + hostResources.displayMetrics + ) //此行代码非常重要4. + + return hostResources + } + + @SuppressLint("DiscouragedPrivateApi") + private fun getPluginResourceForAndroidM( + hostContext: Context, + pluginPath: String?, + ): Resources? { + try { + val assetManager = AssetManager::class.java.getDeclaredConstructor().newInstance() + assetManager.javaClass.getDeclaredMethod("addAssetPath", String::class.java) + .also { it.isAccessible = true } + .invoke(assetManager, pluginPath) + val superRes = hostContext.resources + + @Suppress("DEPRECATION") + return Resources(assetManager, superRes.displayMetrics, superRes.configuration) + } catch (e: Throwable) { + e.printStackTrace() + } + return null + } + + @SuppressLint("DiscouragedPrivateApi") + private fun getPluginResourceForAndroidS(context: Context, pluginPath: String?): Resources? { + try { + //1.调用assetManager.addAssetPath(pluginPath); + val assetManager = AssetManager::class.java.getDeclaredConstructor().newInstance() + assetManager.javaClass.getDeclaredMethod("addAssetPath", String::class.java) + .also { it.isAccessible = true } + .invoke(assetManager, pluginPath) + val superRes = context.resources + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + //Resources#public void setImpl(ResourcesImpl impl) {} + @SuppressLint("PrivateApi") + val displayAdjustmentsClazz = Class.forName("android.view.DisplayAdjustments") + + val displayAdjustmentsObj = displayAdjustmentsClazz.getDeclaredConstructor() + .also { it.isAccessible = true }.newInstance() + + // new ResourcesImpl(AssetManager assets, DisplayMetrics metrics, Configuration config, DisplayAdjustments displayAdjustments) {} + @SuppressLint("PrivateApi") + val resourcesImplClazz = Class.forName("android.content.res.ResourcesImpl") + val resourcesImplConstructor = resourcesImplClazz.getDeclaredConstructor( + AssetManager::class.java, + DisplayMetrics::class.java, + Configuration::class.java, + displayAdjustmentsClazz + ).also { it.isAccessible = true } + + val resourcesImplObj = resourcesImplConstructor.newInstance( + assetManager, + superRes.displayMetrics, + superRes.configuration, + displayAdjustmentsObj + ) + + //private Resources() {} + val resourcesClazz = Class.forName("android.content.res.Resources") + val resourcesObj = + resourcesClazz.getDeclaredConstructor().also { it.isAccessible = true } + .newInstance() + + // Resources.java + // public void setImpl(ResourcesImpl impl) {} + // resources.setImpl(ResourcesImpl impl){} + resourcesClazz.getDeclaredMethod("setImpl", resourcesImplClazz) + .also { it.isAccessible = true } + .invoke(resourcesObj, resourcesImplObj) + + resourcesObj as Resources + } else { + @Suppress("DEPRECATION") + Resources(assetManager, superRes.displayMetrics, superRes.configuration) + } + } catch (e: Throwable) { + e.printStackTrace() + } + return null + } +} diff --git a/app/src/main/java/com/malin/hook/SecondActivity.java b/app/src/main/java/com/malin/hook/SecondActivity.java deleted file mode 100644 index 509b6f4..0000000 --- a/app/src/main/java/com/malin/hook/SecondActivity.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.malin.hook; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.util.Log; -import android.view.Gravity; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - -@SuppressLint("SetTextI18n") -public class SecondActivity extends AppCompatActivity { - private static final String TAG = "SecondActivity"; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.d(TAG, TAG + ":onCreate"); - - RelativeLayout relativeLayout = new RelativeLayout(this); - relativeLayout.setGravity(Gravity.CENTER); - - TextView textView = new TextView(this); - textView.setText("宿主中自带的已注册的SecondActivity,启动成功!"); - - relativeLayout.addView(textView); - setContentView(relativeLayout); - } - - @Override - protected void onStart() { - super.onStart(); - Log.d(TAG, TAG + ":onStart"); - } - - - @Override - protected void onResume() { - super.onResume(); - Log.d(TAG, TAG + ":onResume"); - } - - @Override - protected void onRestart() { - super.onRestart(); - Log.d(TAG, TAG + ":onRestart"); - } - - @Override - protected void onPause() { - super.onPause(); - Log.d(TAG, TAG + ":onPause"); - } - - @Override - protected void onStop() { - super.onStop(); - Log.d(TAG, TAG + ":onStop"); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Log.d(TAG, TAG + ":onDestroy"); - } -} diff --git a/app/src/main/java/com/malin/hook/TargetActivity.java b/app/src/main/java/com/malin/hook/TargetActivity.java deleted file mode 100644 index 62c0ab2..0000000 --- a/app/src/main/java/com/malin/hook/TargetActivity.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.malin.hook; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; -import android.view.Gravity; -import android.widget.RelativeLayout; -import android.widget.TextView; - -/** - * 普通类型的未注册的Activity - */ -@SuppressLint({"SetTextI18n", "Registered"}) -public class TargetActivity extends Activity { - - private static final String TAG = "TargetActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.d(TAG, TAG + ":onCreate"); - - RelativeLayout relativeLayout = new RelativeLayout(this); - relativeLayout.setGravity(Gravity.CENTER); - - TextView textView = new TextView(this); - textView.setText("宿主中未注册的TargetActivity,启动成功!"); - - relativeLayout.addView(textView); - setContentView(relativeLayout); - } - - @Override - protected void onStart() { - super.onStart(); - Log.d(TAG, TAG + ":onStart"); - } - - - @Override - protected void onResume() { - super.onResume(); - Log.d(TAG, TAG + ":onResume"); - } - - @Override - protected void onRestart() { - super.onRestart(); - Log.d(TAG, TAG + ":onRestart"); - } - - @Override - protected void onPause() { - super.onPause(); - Log.d(TAG, TAG + ":onPause"); - } - - @Override - protected void onStop() { - super.onStop(); - Log.d(TAG, TAG + ":onStop"); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Log.d(TAG, TAG + ":onDestroy"); - } -} diff --git a/app/src/main/java/com/malin/hook/TargetActivity.kt b/app/src/main/java/com/malin/hook/TargetActivity.kt new file mode 100644 index 0000000..ef1079b --- /dev/null +++ b/app/src/main/java/com/malin/hook/TargetActivity.kt @@ -0,0 +1,78 @@ +package com.malin.hook + +import android.annotation.SuppressLint +import android.app.Activity +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.Gravity +import android.view.View +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.core.view.WindowCompat + +/** + * 宿主中未注册的Activity + */ +@SuppressLint("SetTextI18n", "Registered") +class TargetActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(TAG, "$TAG:onCreate") + setContentView(getRootLayout()) + lightStatus() + } + + private fun getRootLayout(): View { + val relativeLayout = RelativeLayout(this) + relativeLayout.gravity = Gravity.CENTER + + val textView = TextView(this) + textView.text = "宿主中未注册的TargetActivity,启动成功!" + textView.setTextColor(Color.parseColor("#000000")) + relativeLayout.addView(textView) + return relativeLayout + } + + private fun lightStatus() { + val window = window ?: return + val decorView = window.decorView + val controller = WindowCompat.getInsetsController(window, decorView) + controller.isAppearanceLightStatusBars = true + } + + override fun onStart() { + super.onStart() + Log.d(TAG, "$TAG:onStart") + } + + override fun onResume() { + super.onResume() + Log.d(TAG, "$TAG:onResume") + } + + override fun onRestart() { + super.onRestart() + Log.d(TAG, "$TAG:onRestart") + } + + override fun onPause() { + super.onPause() + Log.d(TAG, "$TAG:onPause") + } + + override fun onStop() { + super.onStop() + Log.d(TAG, "$TAG:onStop") + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "$TAG:onDestroy") + } + + companion object { + private const val TAG = "TargetActivity" + } +} diff --git a/app/src/main/java/com/malin/hook/TargetAppCompatActivity.java b/app/src/main/java/com/malin/hook/TargetAppCompatActivity.java deleted file mode 100644 index c6b1abe..0000000 --- a/app/src/main/java/com/malin/hook/TargetAppCompatActivity.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.malin.hook; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.util.Log; -import android.view.Gravity; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import androidx.appcompat.app.AppCompatActivity; - -/** - * AppCompatActivity类型的未注册的Activity - */ -@SuppressLint({"SetTextI18n", "Registered"}) -public class TargetAppCompatActivity extends AppCompatActivity { - - private static final String TAG = "TargetAppCompatActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.d(TAG, TAG + ":onCreate"); - - RelativeLayout relativeLayout = new RelativeLayout(this); - relativeLayout.setGravity(Gravity.CENTER); - - TextView textView = new TextView(this); - textView.setText("宿主中未注册的TargetAppCompatActivity,启动成功!"); - - relativeLayout.addView(textView); - setContentView(relativeLayout); - } - - - @Override - protected void onStart() { - super.onStart(); - Log.d(TAG, TAG + ":onStart"); - } - - - @Override - protected void onResume() { - super.onResume(); - Log.d(TAG, TAG + ":onResume"); - } - - @Override - protected void onRestart() { - super.onRestart(); - Log.d(TAG, TAG + ":onRestart"); - } - - @Override - protected void onPause() { - super.onPause(); - Log.d(TAG, TAG + ":onPause"); - } - - @Override - protected void onStop() { - super.onStop(); - Log.d(TAG, TAG + ":onStop"); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Log.d(TAG, TAG + ":onDestroy"); - } - -} diff --git a/app/src/main/java/com/malin/hook/TargetAppCompatActivity.kt b/app/src/main/java/com/malin/hook/TargetAppCompatActivity.kt new file mode 100644 index 0000000..2ca85d4 --- /dev/null +++ b/app/src/main/java/com/malin/hook/TargetAppCompatActivity.kt @@ -0,0 +1,78 @@ +package com.malin.hook + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.Gravity +import android.view.View +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat + +/** + * 宿主中未注册的AppCompatActivity类型的Activity + */ +@SuppressLint("SetTextI18n", "Registered") +class TargetAppCompatActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d(TAG, "$TAG:onCreate") + setContentView(getRootLayout()) + supportActionBar?.hide() + lightStatus() + } + + private fun getRootLayout(): View { + val relativeLayout = RelativeLayout(this) + relativeLayout.gravity = Gravity.CENTER + val textView = TextView(this) + textView.text = "宿主中未注册的TargetAppCompatActivity,启动成功!" + textView.setTextColor(Color.parseColor("#000000")) + relativeLayout.addView(textView) + return relativeLayout + } + + private fun lightStatus() { + val window = window ?: return + val decorView = window.decorView + val controller = WindowCompat.getInsetsController(window, decorView) + controller.isAppearanceLightStatusBars = true + } + + override fun onStart() { + super.onStart() + Log.d(TAG, "$TAG:onStart") + } + + override fun onResume() { + super.onResume() + Log.d(TAG, "$TAG:onResume") + } + + override fun onRestart() { + super.onRestart() + Log.d(TAG, "$TAG:onRestart") + } + + override fun onPause() { + super.onPause() + Log.d(TAG, "$TAG:onPause") + } + + override fun onStop() { + super.onStop() + Log.d(TAG, "$TAG:onStop") + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "$TAG:onDestroy") + } + + companion object { + private const val TAG = "TargetAppCompatActivity" + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e990e9b..952bcd4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,43 +8,48 @@ tools:context=".MainActivity"> + tools:ignore="ContentDescription" + tools:src="@tools:sample/avatars" />