├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── chapter_12 └── MoonClassLoader │ ├── .gitignore │ ├── .idea │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── liuwangshu │ │ │ └── moonclassloader │ │ │ └── MainActivity.java │ │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── customclassloader │ ├── .gitignore │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── example │ │ ├── ClassLoaderTest.java │ │ └── DiskClassLoader.java │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── lib │ ├── Jobs.class │ └── Jobs.java │ └── settings.gradle ├── chapter_13 └── InstantRunSourceCode │ └── com │ └── android │ └── tools │ ├── fd │ ├── common │ │ ├── Log.java │ │ └── ProtocolConstants.java │ └── runtime │ │ ├── AbstractPatchesLoaderImpl.java │ │ ├── AndroidInstantRuntime.java │ │ ├── AppInfo.java │ │ ├── ApplicationPatch.java │ │ ├── BasicType.java │ │ ├── BootstrapApplication.java │ │ ├── FileManager.java │ │ ├── IncrementalChange.java │ │ ├── IncrementalClassLoader.java │ │ ├── InstantReloadException.java │ │ ├── MonkeyPatcher.java │ │ ├── PatchesLoader.java │ │ ├── PatchesLoaderDumper.java │ │ ├── Paths.java │ │ ├── Restarter.java │ │ └── Server.java │ └── ir │ └── api │ └── DisableInstantRun.java ├── chapter_14 └── HookInstrumentation │ ├── .gitignore │ ├── .idea │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── liuwangshu │ │ │ └── hookinstrumentation │ │ │ ├── HookHelper.java │ │ │ ├── InstrumentationProxy.java │ │ │ └── MainActivity.java │ │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle └── chapter_15 ├── PluginActivity ├── .gitignore ├── .idea │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── liuwangshu │ │ │ └── pluginactivity │ │ │ ├── HookLibrary │ │ │ ├── FieldUtil.java │ │ │ ├── HCallback.java │ │ │ ├── HookHelper.java │ │ │ ├── IActivityManagerProxy.java │ │ │ └── InstrumentationProxy.java │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ ├── StubActivity.java │ │ │ └── TargetActivity.java │ │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_stub.xml │ │ └── activity_target.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── PluginService ├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── liuwangshu │ │ └── pluginservice │ │ ├── HookLibrary │ │ ├── FieldUtil.java │ │ └── HookHelper.java │ │ ├── IActivityManagerProxy.java │ │ ├── MainActivity.java │ │ ├── MyApplication.java │ │ ├── ProxyService.java │ │ └── TargetService.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 刘望舒, http://liuwangshu.cn 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## **Android进阶三部曲第二部《Android进阶解密》源码** 2 | 3 | ![](https://upload-images.jianshu.io/upload_images/1417629-9c4d6f64ebf88d26.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 4 | 5 | ### **源码目录介绍** 6 | 7 | 8 | 目录 | 简介 9 | ------------|-------------------------- 10 | chapter_12 | 第12章 理解ClassLoader 11 | chapter_13 | 第13章 热修复原理 12 | chapter_14 | 第14章 Hook技术 13 | chapter_15 | 第15章 插件化技术 14 | 15 | ### **本书内容** 16 | 本书共分为17章,各章内容如下:
17 | 第1章介绍Android系统架构、系统源码目录和如何阅读源码,带领大家走进Android系统源码的世界。
18 | 第2章介绍Android系统启动过程,为下面的章节做好铺垫。
19 | 第3章介绍应用程序进程启动过程。
20 | 第4章介绍四大组件的工作过程,包括根Activity的启动过程,Service的启动和绑定过程,广播的注册、发送和接收过程,Content Provider的启动过程。
21 | 第5章从源码角度分析上下文Context。
22 | 第6章介绍ActivityManagerService,包括AMS家族、AMS的启动过程、AMS重要的数据结构和Activity栈管理等内容。
23 | 第7章介绍WindowManager,包括WindowManager的关联类、Window的属性和Window的操作等内容。
24 | 第8章介绍WindowManagerService,包括WMS的创建过程、WMS的重要成员和Window的添加过程等内容。
25 | 第9章结合MediaRecorder框架来介绍JNI的原理。
26 | 第10章介绍Android开发所需要了解的Java虚拟机知识。
27 | 第11章介绍Dalvik和ART虚拟机。
28 | 第12章介绍ClassLoader,它是理解热修复原理和插件化原理必备的知识点。
29 | 第13章介绍热修复原理,包括热修复框架的对比、资源修复、代码修复和动态链接库的修复。
30 | 第14章介绍Hook技术,为讲解插件化原理做铺垫。
31 | 第15章介绍插件化原理,包括插件化的产生、四大组件的插件化、资源的插件化和so的插件化。
32 | 第16章介绍绘制优化,包括绘制性能分析和布局优化。
33 | 第17章介绍内存优化,从避免内存泄漏开始讲起,然后介绍常用的内存分析工具:Memory Monitor、Allocation Tracker和Heap Dump,最后介绍分析内存泄漏的利器:MAT和LeakCanary。
34 | ### **后续内容** 35 | 由于本书篇幅有限,我还有很多想要讲的技术知识无法在本书中展现,这些技术知识我会继续在自己的博客和微信公众号上分享出来。如果感兴趣,你可以继续关注我的博客和微信公众号
36 | 独立博客:http://liuwangshu.cn
37 | CSDN博客:https://liuwangshu.blog.csdn.net/
38 | QQ群:499174415
39 | 40 | 分享Android、Java和大前端相关技术。
41 | 42 | ![](https://upload-images.jianshu.io/upload_images/1417629-be3e83818814b47c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 43 | 44 | ### **License** 45 | 46 | © 2018 [刘望舒][itachi85]. This code is distributed under the MIT license. 47 | 48 | 49 | [itachi85]:http://liuwangshu.cn 50 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.1" 6 | defaultConfig { 7 | applicationId "com.example.liuwangshu.moonclassloader" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator.EIT-20160520RHS\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/java/com/example/liuwangshu/moonclassloader/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.moonclassloader; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | ClassLoader loader = MainActivity.class.getClassLoader(); 14 | while (loader != null) { 15 | Log.d("liuwangshu",loader.toString()); 16 | loader = loader.getParent(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MoonClassLoader 3 | 4 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/customclassloader/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/customclassloader/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | sourceCompatibility = "1.7" 8 | targetCompatibility = "1.7" 9 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/customclassloader/src/main/java/com/example/ClassLoaderTest.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | /** 4 | * Created by Administrator on 2018/1/18 0018. 5 | */ 6 | 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.lang.reflect.Method; 9 | public class ClassLoaderTest { 10 | public static void main(String[] args) { 11 | DiskClassLoader diskClassLoader = new DiskClassLoader("D:\\lib"); 12 | try { 13 | Class c = diskClassLoader.loadClass("com.example.Jobs"); 14 | if (c != null) { 15 | try { 16 | Object obj = c.newInstance(); 17 | System.out.println(obj.getClass().getClassLoader()); 18 | Method method = c.getDeclaredMethod("say", null); 19 | method.invoke(obj, null); 20 | } catch (InstantiationException | IllegalAccessException 21 | | NoSuchMethodException 22 | | SecurityException | 23 | IllegalArgumentException | 24 | InvocationTargetException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | } catch (ClassNotFoundException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/customclassloader/src/main/java/com/example/DiskClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | /** 4 | * Created by Administrator on 2018/1/18 0018. 5 | */ 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | public class DiskClassLoader extends ClassLoader { 13 | private String path; 14 | public DiskClassLoader(String path) { 15 | this.path = path; 16 | } 17 | @Override 18 | protected Class findClass(String name) throws ClassNotFoundException { 19 | Class clazz = null; 20 | byte[] classData = loadClassData(name); 21 | if (classData == null) { 22 | throw new ClassNotFoundException(); 23 | } else { 24 | clazz= defineClass(name, classData, 0, classData.length); 25 | } 26 | return clazz; 27 | } 28 | private byte[] loadClassData(String name) { 29 | String fileName = getFileName(name); 30 | File file = new File(path,fileName); 31 | InputStream in=null; 32 | ByteArrayOutputStream out=null; 33 | try { 34 | in = new FileInputStream(file); 35 | out = new ByteArrayOutputStream(); 36 | byte[] buffer = new byte[1024]; 37 | int length=0; 38 | while ((length = in.read(buffer)) != -1) { 39 | out.write(buffer, 0, length); 40 | } 41 | return out.toByteArray(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | }finally { 45 | try { 46 | if(in!=null) { 47 | in.close(); 48 | } 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | try{ 53 | if(out!=null) { 54 | out.close(); 55 | } 56 | }catch (IOException e){ 57 | e.printStackTrace(); 58 | } 59 | } 60 | return null; 61 | } 62 | private String getFileName(String name) { 63 | int index = name.lastIndexOf('.'); 64 | if(index == -1){//如果没有找到'.'则直接在末尾添加.class 65 | return name+".class"; 66 | }else{ 67 | return name.substring(index+1)+".class"; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 07 14:26:06 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/lib/Jobs.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_12/MoonClassLoader/lib/Jobs.class -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/lib/Jobs.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | /** 4 | * Created by Administrator on 2017/9/24 0024. 5 | */ 6 | 7 | 8 | public class Jobs { 9 | public void say() { 10 | System.out.println("One more thing"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter_12/MoonClassLoader/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':customclassloader' 2 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/common/Log.java: -------------------------------------------------------------------------------- 1 | /* */ package com.android.tools.fd.common; 2 | /* */ 3 | /* */ import java.util.logging.Level; 4 | /* */ 5 | /* */ 6 | /* */ 7 | /* */ 8 | /* */ 9 | /* */ 10 | /* */ 11 | /* */ 12 | /* */ 13 | /* */ 14 | /* */ 15 | /* */ 16 | /* */ 17 | /* */ 18 | /* */ 19 | /* */ 20 | /* */ 21 | /* */ 22 | /* */ 23 | /* */ 24 | /* */ 25 | /* */ public class Log 26 | /* */ { 27 | /* 27 */ public static Logging logging = null; 28 | /* */ 29 | /* */ public static abstract interface Logging 30 | /* */ { 31 | /* */ public abstract void log(Level paramLevel, String paramString); 32 | /* */ 33 | /* */ public abstract boolean isLoggable(Level paramLevel); 34 | /* */ 35 | /* */ public abstract void log(Level paramLevel, String paramString, Throwable paramThrowable); 36 | /* */ } 37 | /* */ } 38 | 39 | 40 | /* Location: D:\decodeapk\new_aliptrip\instant-run.jar!\com\android\tools\fd\common\Log.class 41 | * Java compiler version: 6 (50.0) 42 | * JD-Core Version: 0.7.1 43 | */ -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/common/ProtocolConstants.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.common; 2 | 3 | public abstract interface ProtocolConstants 4 | { 5 | public static final long PROTOCOL_IDENTIFIER = 890269988L; 6 | public static final int PROTOCOL_VERSION = 4; 7 | public static final int MESSAGE_PATCHES = 1; 8 | public static final int MESSAGE_PING = 2; 9 | public static final int MESSAGE_PATH_EXISTS = 3; 10 | public static final int MESSAGE_PATH_CHECKSUM = 4; 11 | public static final int MESSAGE_RESTART_ACTIVITY = 5; 12 | public static final int MESSAGE_SHOW_TOAST = 6; 13 | public static final int MESSAGE_EOF = 7; 14 | public static final int UPDATE_MODE_NONE = 0; 15 | public static final int UPDATE_MODE_HOT_SWAP = 1; 16 | public static final int UPDATE_MODE_WARM_SWAP = 2; 17 | public static final int UPDATE_MODE_COLD_SWAP = 3; 18 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/AbstractPatchesLoaderImpl.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import com.android.tools.fd.common.Log; 4 | import com.android.tools.fd.common.Log.Logging; 5 | import java.lang.reflect.Field; 6 | import java.util.logging.Level; 7 | 8 | public abstract class AbstractPatchesLoaderImpl implements PatchesLoader { 9 | public abstract String[] getPatchedClasses(); 10 | 11 | public boolean load() { 12 | try { 13 | for (String className : getPatchedClasses()) { 14 | ClassLoader cl = getClass().getClassLoader(); 15 | Class aClass = cl.loadClass(className + "$override"); 16 | Object o = aClass.newInstance(); 17 | Class originalClass = cl.loadClass(className); 18 | Field changeField = originalClass.getDeclaredField("$change"); 19 | 20 | changeField.setAccessible(true); 21 | 22 | Object previous = changeField.get(null); 23 | if (previous != null) { 24 | Field isObsolete = previous.getClass().getDeclaredField( 25 | "$obsolete"); 26 | if (isObsolete != null) { 27 | isObsolete.set(null, Boolean.valueOf(true)); 28 | } 29 | } 30 | changeField.set(null, o); 31 | if ((Log.logging != null) 32 | && (Log.logging.isLoggable(Level.FINE))) { 33 | Log.logging.log(Level.FINE, String.format("patched %s", 34 | new Object[] { className })); 35 | } 36 | } 37 | } catch (Exception e) { 38 | if (Log.logging != null) { 39 | Log.logging.log(Level.SEVERE, String.format( 40 | "Exception while patching %s", 41 | new Object[] { "foo.bar" }), e); 42 | } 43 | return false; 44 | } 45 | return true; 46 | } 47 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/AndroidInstantRuntime.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.lang.reflect.Method; 7 | import java.util.NoSuchElementException; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import com.android.tools.fd.common.Log; 12 | 13 | public class AndroidInstantRuntime { 14 | public static void setLogger(Logger logger) { 15 | Log.logging = new Log.Logging() { 16 | 17 | @Override 18 | public void log(Level paramLevel, String paramString) { 19 | // TODO Auto-generated method stub 20 | 21 | } 22 | 23 | @Override 24 | public boolean isLoggable(Level paramLevel) { 25 | // TODO Auto-generated method stub 26 | return false; 27 | } 28 | 29 | @Override 30 | public void log(Level paramLevel, String paramString, 31 | Throwable paramThrowable) { 32 | // TODO Auto-generated method stub 33 | 34 | } 35 | 36 | }; 37 | } 38 | 39 | public static Object getStaticPrivateField(Class targetClass, 40 | String fieldName) { 41 | return getPrivateField(null, targetClass, fieldName); 42 | } 43 | 44 | public static void setStaticPrivateField(Object value, Class targetClass, 45 | String fieldName) { 46 | setPrivateField(null, value, targetClass, fieldName); 47 | } 48 | 49 | public static void setPrivateField(Object targetObject, Object value, 50 | Class targetClass, String fieldName) { 51 | try { 52 | Field declaredField = getField(targetClass, fieldName); 53 | declaredField.set(targetObject, value); 54 | } catch (IllegalAccessException e) { 55 | if (Log.logging != null) { 56 | Log.logging.log(Level.SEVERE, String.format( 57 | "Exception during setPrivateField %s", 58 | new Object[] { fieldName }), e); 59 | } 60 | throw new RuntimeException(e); 61 | } 62 | } 63 | 64 | public static Object getPrivateField(Object targetObject, 65 | Class targetClass, String fieldName) { 66 | try { 67 | Field declaredField = getField(targetClass, fieldName); 68 | return declaredField.get(targetObject); 69 | } catch (IllegalAccessException e) { 70 | if (Log.logging != null) { 71 | Log.logging.log(Level.SEVERE, String.format( 72 | "Exception during%1$s getField %2$s", new Object[] { 73 | targetObject == null ? " static" : "", 74 | fieldName }), e); 75 | } 76 | throw new RuntimeException(e); 77 | } 78 | } 79 | 80 | private static Field getField(Class target, String name) { 81 | Field declareField = getFieldByName(target, name); 82 | if (declareField == null) { 83 | throw new RuntimeException(new NoSuchElementException(name)); 84 | } 85 | declareField.setAccessible(true); 86 | return declareField; 87 | } 88 | 89 | public static Object invokeProtectedMethod(Object receiver, 90 | Object[] params, Class[] parameterTypes, String methodName) 91 | throws Throwable { 92 | if ((Log.logging != null) && (Log.logging.isLoggable(Level.FINE))) { 93 | Log.logging.log( 94 | Level.FINE, 95 | String.format("protectedMethod:%s on %s", new Object[] { 96 | methodName, receiver })); 97 | } 98 | try { 99 | Method toDispatchTo = getMethodByName(receiver.getClass(), 100 | methodName, parameterTypes); 101 | if (toDispatchTo == null) { 102 | throw new RuntimeException( 103 | new NoSuchMethodException(methodName)); 104 | } 105 | toDispatchTo.setAccessible(true); 106 | return toDispatchTo.invoke(receiver, params); 107 | } catch (InvocationTargetException e) { 108 | throw e.getCause(); 109 | } catch (IllegalAccessException e) { 110 | Log.logging.log(Level.SEVERE, 111 | String.format("Exception while invoking %s", 112 | new Object[] { methodName }), e); 113 | throw new RuntimeException(e); 114 | } 115 | } 116 | 117 | public static Object invokeProtectedStaticMethod(Object[] params, 118 | Class[] parameterTypes, String methodName, Class receiverClass) 119 | throws Throwable { 120 | if ((Log.logging != null) && (Log.logging.isLoggable(Level.FINE))) { 121 | Log.logging.log(Level.FINE, String.format( 122 | "protectedStaticMethod:%s on %s", new Object[] { 123 | methodName, receiverClass.getName() })); 124 | } 125 | try { 126 | Method toDispatchTo = getMethodByName(receiverClass, methodName, 127 | parameterTypes); 128 | if (toDispatchTo == null) { 129 | throw new RuntimeException(new NoSuchMethodException(methodName 130 | + " in class " + receiverClass.getName())); 131 | } 132 | toDispatchTo.setAccessible(true); 133 | return toDispatchTo.invoke(null, params); 134 | } catch (InvocationTargetException e) { 135 | throw e.getCause(); 136 | } catch (IllegalAccessException e) { 137 | Log.logging.log(Level.SEVERE, 138 | String.format("Exception while invoking %s", 139 | new Object[] { methodName }), e); 140 | throw new RuntimeException(e); 141 | } 142 | } 143 | 144 | public static T newForClass(Object[] params, Class[] paramTypes, 145 | Class targetClass) throws Throwable { 146 | Constructor declaredConstructor; 147 | try { 148 | declaredConstructor = targetClass 149 | .getDeclaredConstructor(paramTypes); 150 | } catch (NoSuchMethodException e) { 151 | Log.logging.log(Level.SEVERE, 152 | "Exception while resolving constructor", e); 153 | throw new RuntimeException(e); 154 | } 155 | declaredConstructor.setAccessible(true); 156 | try { 157 | return (T) targetClass 158 | .cast(declaredConstructor.newInstance(params)); 159 | } catch (InvocationTargetException e) { 160 | throw e.getCause(); 161 | } catch (InstantiationException e) { 162 | Log.logging.log(Level.SEVERE, String.format( 163 | "Exception while instantiating %s", 164 | new Object[] { targetClass }), e); 165 | throw new RuntimeException(e); 166 | } catch (IllegalAccessException e) { 167 | Log.logging.log(Level.SEVERE, String.format( 168 | "Exception while instantiating %s", 169 | new Object[] { targetClass }), e); 170 | throw new RuntimeException(e); 171 | } 172 | } 173 | 174 | private static Field getFieldByName(Class aClass, String name) { 175 | if ((Log.logging != null) && (Log.logging.isLoggable(Level.FINE))) { 176 | Log.logging.log( 177 | Level.FINE, 178 | String.format("getFieldByName:%s in %s", new Object[] { 179 | name, aClass.getName() })); 180 | } 181 | Class currentClass = aClass; 182 | while (currentClass != null) { 183 | try { 184 | return currentClass.getDeclaredField(name); 185 | } catch (NoSuchFieldException e) { 186 | } 187 | currentClass = currentClass.getSuperclass(); 188 | } 189 | return null; 190 | } 191 | 192 | private static Method getMethodByName(Class aClass, String name, 193 | Class[] paramTypes) { 194 | if (aClass == null) { 195 | return null; 196 | } 197 | Class currentClass = aClass; 198 | while (currentClass != null) { 199 | try { 200 | return currentClass.getDeclaredMethod(name, paramTypes); 201 | } catch (NoSuchMethodException e) { 202 | currentClass = currentClass.getSuperclass(); 203 | if (currentClass == null) { 204 | continue; 205 | } 206 | } 207 | if ((Log.logging != null) && (Log.logging.isLoggable(Level.FINE))) { 208 | Log.logging.log(Level.FINE, String.format( 209 | "getMethodByName:Looking in %s now", 210 | new Object[] { currentClass.getName() })); 211 | } 212 | } 213 | return null; 214 | } 215 | 216 | public static void trace(String s) { 217 | if (Log.logging != null) { 218 | Log.logging.log(Level.FINE, s); 219 | } 220 | } 221 | 222 | public static void trace(String s1, String s2) { 223 | if (Log.logging != null) { 224 | Log.logging.log(Level.FINE, 225 | String.format("%s %s", new Object[] { s1, s2 })); 226 | } 227 | } 228 | 229 | public static void trace(String s1, String s2, String s3) { 230 | if (Log.logging != null) { 231 | Log.logging.log(Level.FINE, 232 | String.format("%s %s %s", new Object[] { s1, s2, s3 })); 233 | } 234 | } 235 | 236 | public static void trace(String s1, String s2, String s3, String s4) { 237 | if (Log.logging != null) { 238 | Log.logging.log(Level.FINE, String.format("%s %s %s %s", 239 | new Object[] { s1, s2, s3, s4 })); 240 | } 241 | } 242 | 243 | protected static abstract interface Logging { 244 | public abstract void log(Level paramLevel, String paramString); 245 | 246 | public abstract boolean isLoggable(Level paramLevel); 247 | 248 | public abstract void log(Level paramLevel, String paramString, 249 | Throwable paramThrowable); 250 | } 251 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/AppInfo.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | /** 4 | * @Author Zheng Haibo (mochuan) 5 | * @Company Alibaba Group 6 | * @PersonalWebsite http://www.mobctrl.net 7 | * @version $Id: AppInfo.java, v 0.1 2016年6月28日 上午11:23:50 mochuan.zhb Exp $ 8 | * @Description 9 | */ 10 | public class AppInfo { 11 | 12 | public static String applicationClass = null; 13 | public static String applicationId = "trip.taobao.com.testandroid"; 14 | public static long token = -1066838687043834658L; 15 | public static boolean usingApkSplits = false; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/ApplicationPatch.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import java.io.DataInputStream; 4 | import java.io.IOException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.logging.Level; 8 | 9 | import com.android.tools.fd.common.Log; 10 | 11 | public class ApplicationPatch { 12 | public final String path; 13 | public final byte[] data; 14 | 15 | public ApplicationPatch(String path, byte[] data) { 16 | this.path = path; 17 | this.data = data; 18 | } 19 | 20 | public String toString() { 21 | return "ApplicationPatch{path='" + this.path + '\'' + ", data.length='" 22 | + this.data.length + '\'' + '}'; 23 | } 24 | 25 | public static List read(DataInputStream input) 26 | throws IOException { 27 | int changeCount = input.readInt(); 28 | if ((Log.logging != null) && (Log.logging.isLoggable(Level.FINE))) { 29 | Log.logging 30 | .log(Level.FINE, "Receiving " + changeCount + " changes"); 31 | } 32 | List changes = new ArrayList(changeCount); 33 | for (int i = 0; i < changeCount; i++) { 34 | String path = input.readUTF(); 35 | int size = input.readInt(); 36 | byte[] bytes = new byte[size]; 37 | input.readFully(bytes); 38 | changes.add(new ApplicationPatch(path, bytes)); 39 | } 40 | return changes; 41 | } 42 | 43 | public String getPath() { 44 | return this.path; 45 | } 46 | 47 | public byte[] getBytes() { 48 | return this.data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/BasicType.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public enum BasicType { 4 | I(Integer.TYPE), J(Long.TYPE), C(Character.TYPE), Z(Boolean.TYPE), F( 5 | Float.TYPE), D(Double.TYPE), V(Void.TYPE); 6 | 7 | private final Class primitiveJavaType; 8 | 9 | private BasicType(Class primitiveType) { 10 | this.primitiveJavaType = primitiveType; 11 | } 12 | 13 | public Class getJavaType() { 14 | return this.primitiveJavaType; 15 | } 16 | 17 | public static BasicType parse(String name) { 18 | for (BasicType basicType : BasicType.values()) { 19 | if (basicType.getJavaType().getName().equals(name)) { 20 | return basicType; 21 | } 22 | } 23 | return null; 24 | } 25 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/BootstrapApplication.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Method; 6 | import java.util.List; 7 | import java.util.logging.Level; 8 | 9 | import android.app.ActivityManager; 10 | import android.app.Application; 11 | import android.content.Context; 12 | import android.content.ContextWrapper; 13 | import android.os.Process; 14 | import android.util.Log; 15 | 16 | import com.android.tools.fd.common.Log.Logging; 17 | 18 | public class BootstrapApplication extends Application { 19 | public static final String LOG_TAG = "InstantRun"; 20 | private String externalResourcePath; 21 | private Application realApplication; 22 | 23 | static { 24 | com.android.tools.fd.common.Log.logging = new Logging() { 25 | 26 | @Override 27 | public void log(Level paramLevel, String paramString) { 28 | // TODO Auto-generated method stub 29 | 30 | } 31 | 32 | @Override 33 | public boolean isLoggable(Level paramLevel) { 34 | // TODO Auto-generated method stub 35 | return false; 36 | } 37 | 38 | @Override 39 | public void log(Level paramLevel, String paramString, 40 | Throwable paramThrowable) { 41 | // TODO Auto-generated method stub 42 | 43 | } 44 | 45 | }; 46 | } 47 | 48 | public BootstrapApplication() { 49 | if (Log.isLoggable("InstantRun", 2)) { 50 | Log.v("InstantRun", 51 | String.format( 52 | "BootstrapApplication created. Android package is %s, real application class is %s.", 53 | new Object[] { AppInfo.applicationId, 54 | AppInfo.applicationClass })); 55 | } 56 | } 57 | 58 | private void createResources(long apkModified) { 59 | FileManager.checkInbox(); 60 | 61 | File file = FileManager.getExternalResourceFile(); 62 | this.externalResourcePath = (file != null ? file.getPath() : null); 63 | if (Log.isLoggable("InstantRun", 2)) { 64 | Log.v("InstantRun", "Resource override is " 65 | + this.externalResourcePath); 66 | } 67 | if (file != null) { 68 | try { 69 | long resourceModified = file.lastModified(); 70 | if (Log.isLoggable("InstantRun", 2)) { 71 | Log.v("InstantRun", "Resource patch last modified: " 72 | + resourceModified); 73 | Log.v("InstantRun", "APK last modified: " + apkModified 74 | + " " 75 | + (apkModified > resourceModified ? ">" : "<") 76 | + " resource patch"); 77 | } 78 | if ((apkModified == 0L) || (resourceModified <= apkModified)) { 79 | if (Log.isLoggable("InstantRun", 2)) { 80 | Log.v("InstantRun", 81 | "Ignoring resource file, older than APK"); 82 | } 83 | this.externalResourcePath = null; 84 | } 85 | } catch (Throwable t) { 86 | Log.e("InstantRun", "Failed to check patch timestamps", t); 87 | } 88 | } 89 | } 90 | 91 | private static void setupClassLoaders(Context context, String codeCacheDir, 92 | long apkModified) { 93 | List dexList = FileManager.getDexList(context, apkModified); 94 | 95 | Class server = Server.class; 96 | Class patcher = MonkeyPatcher.class; 97 | if (!dexList.isEmpty()) { 98 | if (Log.isLoggable("InstantRun", 2)) { 99 | Log.v("InstantRun", "Bootstrapping class loader with dex list " 100 | + join('\n', dexList)); 101 | } 102 | ClassLoader classLoader = BootstrapApplication.class 103 | .getClassLoader(); 104 | String nativeLibraryPath; 105 | try { 106 | nativeLibraryPath = (String) classLoader.getClass() 107 | .getMethod("getLdLibraryPath", new Class[0]) 108 | .invoke(classLoader, new Object[0]); 109 | if (Log.isLoggable("InstantRun", 2)) { 110 | Log.v("InstantRun", "Native library path: " 111 | + nativeLibraryPath); 112 | } 113 | } catch (Throwable t) { 114 | Log.e("InstantRun", "Failed to determine native library path " 115 | + t.getMessage()); 116 | nativeLibraryPath = FileManager.getNativeLibraryFolder() 117 | .getPath(); 118 | } 119 | IncrementalClassLoader.inject(classLoader, nativeLibraryPath, 120 | codeCacheDir, dexList); 121 | } 122 | } 123 | 124 | public static String join(char on, List list) { 125 | StringBuilder stringBuilder = new StringBuilder(); 126 | for (String item : list) { 127 | stringBuilder.append(item).append(on); 128 | } 129 | stringBuilder.deleteCharAt(stringBuilder.length() - 1); 130 | return stringBuilder.toString(); 131 | } 132 | 133 | private void createRealApplication() { 134 | if (AppInfo.applicationClass != null) { 135 | if (Log.isLoggable("InstantRun", 2)) { 136 | Log.v("InstantRun", 137 | "About to create real application of class name = " 138 | + AppInfo.applicationClass); 139 | } 140 | try { 141 | Class realClass = (Class) Class 142 | .forName(AppInfo.applicationClass); 143 | if (Log.isLoggable("InstantRun", 2)) { 144 | Log.v("InstantRun", 145 | "Created delegate app class successfully : " 146 | + realClass + " with class loader " 147 | + realClass.getClassLoader()); 148 | } 149 | Constructor constructor = realClass 150 | .getConstructor(new Class[0]); 151 | this.realApplication = ((Application) constructor 152 | .newInstance(new Object[0])); 153 | if (Log.isLoggable("InstantRun", 2)) { 154 | Log.v("InstantRun", 155 | "Created real app instance successfully :" 156 | + this.realApplication); 157 | } 158 | } catch (Exception e) { 159 | throw new IllegalStateException(e); 160 | } 161 | } else { 162 | this.realApplication = new Application(); 163 | } 164 | } 165 | 166 | protected void attachBaseContext(Context context) { 167 | if (!AppInfo.usingApkSplits) { 168 | String apkFile = context.getApplicationInfo().sourceDir; 169 | long apkModified = apkFile != null ? new File(apkFile) 170 | .lastModified() : 0L; 171 | createResources(apkModified); 172 | setupClassLoaders(context, context.getCacheDir().getPath(), 173 | apkModified); 174 | } 175 | createRealApplication(); 176 | 177 | super.attachBaseContext(context); 178 | if (this.realApplication != null) { 179 | try { 180 | Method attachBaseContext = ContextWrapper.class 181 | .getDeclaredMethod("attachBaseContext", 182 | new Class[] { Context.class }); 183 | 184 | attachBaseContext.setAccessible(true); 185 | attachBaseContext.invoke(this.realApplication, 186 | new Object[] { context }); 187 | } catch (Exception e) { 188 | throw new IllegalStateException(e); 189 | } 190 | } 191 | } 192 | 193 | public void onCreate() { 194 | if (!AppInfo.usingApkSplits) { 195 | MonkeyPatcher.monkeyPatchApplication(this, this, 196 | this.realApplication, this.externalResourcePath); 197 | 198 | MonkeyPatcher.monkeyPatchExistingResources(this, 199 | this.externalResourcePath, null); 200 | } else { 201 | MonkeyPatcher.monkeyPatchApplication(this, this, 202 | this.realApplication, null); 203 | } 204 | super.onCreate(); 205 | if (AppInfo.applicationId != null) { 206 | try { 207 | boolean foundPackage = false; 208 | int pid = Process.myPid(); 209 | ActivityManager manager = (ActivityManager) getSystemService("activity"); 210 | 211 | List processes = manager 212 | .getRunningAppProcesses(); 213 | boolean startServer = false; 214 | if ((processes != null) && (processes.size() > 1)) { 215 | for (ActivityManager.RunningAppProcessInfo processInfo : processes) { 216 | if (AppInfo.applicationId 217 | .equals(processInfo.processName)) { 218 | foundPackage = true; 219 | if (processInfo.pid == pid) { 220 | startServer = true; 221 | break; 222 | } 223 | } 224 | } 225 | if ((!startServer) && (!foundPackage)) { 226 | startServer = true; 227 | if (Log.isLoggable("InstantRun", 2)) { 228 | Log.v("InstantRun", 229 | "Multiprocess but didn't find process with package: starting server anyway"); 230 | } 231 | } 232 | } else { 233 | startServer = true; 234 | } 235 | if (startServer) { 236 | Server.create(AppInfo.applicationId, this); 237 | } 238 | } catch (Throwable t) { 239 | if (Log.isLoggable("InstantRun", 2)) { 240 | Log.v("InstantRun", "Failed during multi process check", t); 241 | } 242 | Server.create(AppInfo.applicationId, this); 243 | } 244 | } 245 | if (this.realApplication != null) { 246 | this.realApplication.onCreate(); 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/IncrementalChange.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public abstract interface IncrementalChange { 4 | public abstract Object access$dispatch(String paramString, 5 | Object... paramVarArgs); 6 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/IncrementalClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import android.util.Log; 4 | import dalvik.system.BaseDexClassLoader; 5 | import java.io.File; 6 | import java.lang.reflect.Field; 7 | import java.util.List; 8 | 9 | public class IncrementalClassLoader extends ClassLoader { 10 | public static final boolean DEBUG_CLASS_LOADING = false; 11 | private final DelegateClassLoader delegateClassLoader; 12 | 13 | public IncrementalClassLoader(ClassLoader original, 14 | String nativeLibraryPath, String codeCacheDir, List dexes) { 15 | super(original.getParent()); 16 | 17 | this.delegateClassLoader = createDelegateClassLoader(nativeLibraryPath, 18 | codeCacheDir, dexes, original); 19 | } 20 | 21 | public Class findClass(String className) throws ClassNotFoundException { 22 | try { 23 | return this.delegateClassLoader.findClass(className); 24 | } catch (ClassNotFoundException e) { 25 | throw e; 26 | } 27 | } 28 | 29 | private static class DelegateClassLoader extends BaseDexClassLoader { 30 | 31 | private DelegateClassLoader(String dexPath, File optimizedDirectory, 32 | String libraryPath, ClassLoader parent) { 33 | super(dexPath, optimizedDirectory, libraryPath, parent); 34 | } 35 | 36 | public Class findClass(String name) throws ClassNotFoundException { 37 | try { 38 | return super.findClass(name); 39 | } catch (ClassNotFoundException e) { 40 | throw e; 41 | } 42 | } 43 | } 44 | 45 | private static DelegateClassLoader createDelegateClassLoader( 46 | String nativeLibraryPath, String codeCacheDir, List dexes, 47 | ClassLoader original) { 48 | String pathBuilder = createDexPath(dexes); 49 | return new DelegateClassLoader(pathBuilder, new File(codeCacheDir), 50 | nativeLibraryPath, original); 51 | } 52 | 53 | private static String createDexPath(List dexes) { 54 | StringBuilder pathBuilder = new StringBuilder(); 55 | boolean first = true; 56 | for (String dex : dexes) { 57 | if (first) { 58 | first = false; 59 | } else { 60 | pathBuilder.append(File.pathSeparator); 61 | } 62 | pathBuilder.append(dex); 63 | } 64 | if (Log.isLoggable("InstantRun", 2)) { 65 | Log.v("InstantRun", "Incremental dex path is " 66 | + BootstrapApplication.join('\n', dexes)); 67 | } 68 | return pathBuilder.toString(); 69 | } 70 | 71 | private static void setParent(ClassLoader classLoader, ClassLoader newParent) { 72 | try { 73 | Field parent = ClassLoader.class.getDeclaredField("parent"); 74 | parent.setAccessible(true); 75 | parent.set(classLoader, newParent); 76 | } catch (IllegalArgumentException e) { 77 | throw new RuntimeException(e); 78 | } catch (IllegalAccessException e) { 79 | throw new RuntimeException(e); 80 | } catch (NoSuchFieldException e) { 81 | throw new RuntimeException(e); 82 | } 83 | } 84 | 85 | public static ClassLoader inject(ClassLoader classLoader, 86 | String nativeLibraryPath, String codeCacheDir, List dexes) { 87 | IncrementalClassLoader incrementalClassLoader = new IncrementalClassLoader( 88 | classLoader, nativeLibraryPath, codeCacheDir, dexes); 89 | 90 | setParent(classLoader, incrementalClassLoader); 91 | 92 | return incrementalClassLoader; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/InstantReloadException.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public class InstantReloadException extends Exception { 4 | public InstantReloadException(String s) { 5 | super(s); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/MonkeyPatcher.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import java.lang.ref.WeakReference; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.util.Collection; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import android.annotation.SuppressLint; 12 | import android.app.Activity; 13 | import android.app.Application; 14 | import android.content.Context; 15 | import android.content.res.AssetManager; 16 | import android.content.res.Resources; 17 | import android.os.Build; 18 | import android.util.ArrayMap; 19 | import android.util.Log; 20 | import android.util.LongSparseArray; 21 | import android.util.SparseArray; 22 | import android.view.ContextThemeWrapper; 23 | 24 | @SuppressLint("NewApi") 25 | public class MonkeyPatcher { 26 | 27 | public static void monkeyPatchApplication(Context context, 28 | Application bootstrap, Application realApplication, 29 | String externalResourceFile) { 30 | try { 31 | Class activityThread = Class 32 | .forName("android.app.ActivityThread"); 33 | Object currentActivityThread = getActivityThread(context, 34 | activityThread); 35 | 36 | Field mInitialApplication = activityThread 37 | .getDeclaredField("mInitialApplication"); 38 | mInitialApplication.setAccessible(true); 39 | Application initialApplication = (Application) mInitialApplication 40 | .get(currentActivityThread); 41 | if ((realApplication != null) && (initialApplication == bootstrap)) { 42 | mInitialApplication.set(currentActivityThread, realApplication); 43 | } 44 | if (realApplication != null) { 45 | Field mAllApplications = activityThread 46 | .getDeclaredField("mAllApplications"); 47 | mAllApplications.setAccessible(true); 48 | List allApplications = (List) mAllApplications 49 | .get(currentActivityThread); 50 | for (int i = 0; i < allApplications.size(); i++) { 51 | if (allApplications.get(i) == bootstrap) { 52 | allApplications.set(i, realApplication); 53 | } 54 | } 55 | } 56 | Class loadedApkClass; 57 | try { 58 | loadedApkClass = Class.forName("android.app.LoadedApk"); 59 | } catch (ClassNotFoundException e) { 60 | loadedApkClass = Class 61 | .forName("android.app.ActivityThread$PackageInfo"); 62 | } 63 | Field mApplication = loadedApkClass 64 | .getDeclaredField("mApplication"); 65 | mApplication.setAccessible(true); 66 | Field mResDir = loadedApkClass.getDeclaredField("mResDir"); 67 | mResDir.setAccessible(true); 68 | 69 | Field mLoadedApk = null; 70 | try { 71 | mLoadedApk = Application.class.getDeclaredField("mLoadedApk"); 72 | } catch (NoSuchFieldException e) { 73 | } 74 | for (String fieldName : new String[] { "mPackages", 75 | "mResourcePackages" }) { 76 | Field field = activityThread.getDeclaredField(fieldName); 77 | field.setAccessible(true); 78 | Object value = field.get(currentActivityThread); 79 | for (Map.Entry> entry : ((Map>) value) 80 | .entrySet()) { 81 | Object loadedApk = ((WeakReference) entry.getValue()).get(); 82 | if (loadedApk != null) { 83 | if (mApplication.get(loadedApk) == bootstrap) { 84 | if (realApplication != null) { 85 | mApplication.set(loadedApk, realApplication); 86 | } 87 | if (externalResourceFile != null) { 88 | mResDir.set(loadedApk, externalResourceFile); 89 | } 90 | if ((realApplication != null) 91 | && (mLoadedApk != null)) { 92 | mLoadedApk.set(realApplication, loadedApk); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } catch (Throwable e) { 99 | throw new IllegalStateException(e); 100 | } 101 | } 102 | 103 | public static Object getActivityThread(Context context, 104 | Class activityThread) { 105 | try { 106 | if (activityThread == null) { 107 | activityThread = Class.forName("android.app.ActivityThread"); 108 | } 109 | Method m = activityThread.getMethod("currentActivityThread", 110 | new Class[0]); 111 | m.setAccessible(true); 112 | Object currentActivityThread = m.invoke(activityThread, 113 | new Object[0]); 114 | Object apk = null; 115 | Field mActivityThreadField = null; 116 | if ((currentActivityThread == null) && (context != null)) { 117 | Field mLoadedApk = context.getClass().getField("mLoadedApk"); 118 | mLoadedApk.setAccessible(true); 119 | apk = mLoadedApk.get(context); 120 | mActivityThreadField = apk.getClass().getDeclaredField( 121 | "mActivityThread"); 122 | mActivityThreadField.setAccessible(true); 123 | } 124 | return mActivityThreadField.get(apk); 125 | } catch (Throwable ignore) { 126 | } 127 | return null; 128 | } 129 | 130 | public static void monkeyPatchExistingResources(Context context, 131 | String externalResourceFile, Collection activities) { 132 | if (externalResourceFile == null) { 133 | return; 134 | } 135 | try { 136 | AssetManager newAssetManager = (AssetManager) AssetManager.class 137 | .getConstructor(new Class[0]).newInstance(new Object[0]); 138 | Method mAddAssetPath = AssetManager.class.getDeclaredMethod( 139 | "addAssetPath", new Class[] { String.class }); 140 | mAddAssetPath.setAccessible(true); 141 | if (((Integer) mAddAssetPath.invoke(newAssetManager, 142 | new Object[] { externalResourceFile })).intValue() == 0) { 143 | throw new IllegalStateException( 144 | "Could not create new AssetManager"); 145 | } 146 | Method mEnsureStringBlocks = AssetManager.class.getDeclaredMethod( 147 | "ensureStringBlocks", new Class[0]); 148 | mEnsureStringBlocks.setAccessible(true); 149 | mEnsureStringBlocks.invoke(newAssetManager, new Object[0]); 150 | if (activities != null) { 151 | for (Activity activity : activities) { 152 | Resources resources = activity.getResources(); 153 | try { 154 | Field mAssets = Resources.class 155 | .getDeclaredField("mAssets"); 156 | mAssets.setAccessible(true); 157 | mAssets.set(resources, newAssetManager); 158 | } catch (Throwable ignore) { 159 | Field mResourcesImpl = Resources.class 160 | .getDeclaredField("mResourcesImpl"); 161 | mResourcesImpl.setAccessible(true); 162 | Object resourceImpl = mResourcesImpl.get(resources); 163 | Field implAssets = resourceImpl.getClass() 164 | .getDeclaredField("mAssets"); 165 | implAssets.setAccessible(true); 166 | implAssets.set(resourceImpl, newAssetManager); 167 | } 168 | Resources.Theme theme = activity.getTheme(); 169 | try { 170 | try { 171 | Field ma = Resources.Theme.class 172 | .getDeclaredField("mAssets"); 173 | ma.setAccessible(true); 174 | ma.set(theme, newAssetManager); 175 | } catch (NoSuchFieldException ignore) { 176 | Field themeField = Resources.Theme.class 177 | .getDeclaredField("mThemeImpl"); 178 | themeField.setAccessible(true); 179 | Object impl = themeField.get(theme); 180 | Field ma = impl.getClass().getDeclaredField( 181 | "mAssets"); 182 | ma.setAccessible(true); 183 | ma.set(impl, newAssetManager); 184 | } 185 | Field mt = ContextThemeWrapper.class 186 | .getDeclaredField("mTheme"); 187 | mt.setAccessible(true); 188 | mt.set(activity, null); 189 | Method mtm = ContextThemeWrapper.class 190 | .getDeclaredMethod("initializeTheme", 191 | new Class[0]); 192 | mtm.setAccessible(true); 193 | mtm.invoke(activity, new Object[0]); 194 | 195 | Method mCreateTheme = AssetManager.class 196 | .getDeclaredMethod("createTheme", new Class[0]); 197 | mCreateTheme.setAccessible(true); 198 | Object internalTheme = mCreateTheme.invoke( 199 | newAssetManager, new Object[0]); 200 | Field mTheme = Resources.Theme.class 201 | .getDeclaredField("mTheme"); 202 | mTheme.setAccessible(true); 203 | mTheme.set(theme, internalTheme); 204 | } catch (Throwable e) { 205 | Log.e("InstantRun", 206 | "Failed to update existing theme for activity " 207 | + activity, e); 208 | } 209 | pruneResourceCaches(resources); 210 | } 211 | } 212 | Collection> references; 213 | if (Build.VERSION.SDK_INT >= 19) { 214 | Class resourcesManagerClass = Class 215 | .forName("android.app.ResourcesManager"); 216 | Method mGetInstance = resourcesManagerClass.getDeclaredMethod( 217 | "getInstance", new Class[0]); 218 | mGetInstance.setAccessible(true); 219 | Object resourcesManager = mGetInstance.invoke(null, 220 | new Object[0]); 221 | try { 222 | Field fMActiveResources = resourcesManagerClass 223 | .getDeclaredField("mActiveResources"); 224 | fMActiveResources.setAccessible(true); 225 | 226 | ArrayMap> arrayMap = (ArrayMap) fMActiveResources 227 | .get(resourcesManager); 228 | references = arrayMap.values(); 229 | } catch (NoSuchFieldException ignore) { 230 | Field mResourceReferences = resourcesManagerClass 231 | .getDeclaredField("mResourceReferences"); 232 | mResourceReferences.setAccessible(true); 233 | 234 | references = (Collection) mResourceReferences 235 | .get(resourcesManager); 236 | } 237 | } else { 238 | Class activityThread = Class 239 | .forName("android.app.ActivityThread"); 240 | Field fMActiveResources = activityThread 241 | .getDeclaredField("mActiveResources"); 242 | fMActiveResources.setAccessible(true); 243 | Object thread = getActivityThread(context, activityThread); 244 | 245 | HashMap> map = (HashMap) fMActiveResources 246 | .get(thread); 247 | 248 | references = map.values(); 249 | } 250 | for (WeakReference wr : references) { 251 | Resources resources = (Resources) wr.get(); 252 | if (resources != null) { 253 | try { 254 | Field mAssets = Resources.class 255 | .getDeclaredField("mAssets"); 256 | mAssets.setAccessible(true); 257 | mAssets.set(resources, newAssetManager); 258 | } catch (Throwable ignore) { 259 | Field mResourcesImpl = Resources.class 260 | .getDeclaredField("mResourcesImpl"); 261 | mResourcesImpl.setAccessible(true); 262 | Object resourceImpl = mResourcesImpl.get(resources); 263 | Field implAssets = resourceImpl.getClass() 264 | .getDeclaredField("mAssets"); 265 | implAssets.setAccessible(true); 266 | implAssets.set(resourceImpl, newAssetManager); 267 | } 268 | resources.updateConfiguration(resources.getConfiguration(), 269 | resources.getDisplayMetrics()); 270 | } 271 | } 272 | } catch (Throwable e) { 273 | throw new IllegalStateException(e); 274 | } 275 | } 276 | 277 | private static void pruneResourceCaches(Object resources) { 278 | if (Build.VERSION.SDK_INT >= 21) { 279 | try { 280 | Field typedArrayPoolField = Resources.class 281 | .getDeclaredField("mTypedArrayPool"); 282 | 283 | typedArrayPoolField.setAccessible(true); 284 | Object pool = typedArrayPoolField.get(resources); 285 | Class poolClass = pool.getClass(); 286 | Method acquireMethod = poolClass.getDeclaredMethod("acquire", 287 | new Class[0]); 288 | acquireMethod.setAccessible(true); 289 | for (;;) { 290 | Object typedArray = acquireMethod.invoke(pool, 291 | new Object[0]); 292 | if (typedArray == null) { 293 | break; 294 | } 295 | } 296 | } catch (Throwable ignore) { 297 | } 298 | } 299 | if (Build.VERSION.SDK_INT >= 23) { 300 | try { 301 | Field mResourcesImpl = Resources.class 302 | .getDeclaredField("mResourcesImpl"); 303 | mResourcesImpl.setAccessible(true); 304 | 305 | resources = mResourcesImpl.get(resources); 306 | } catch (Throwable ignore) { 307 | } 308 | } 309 | Object lock = null; 310 | if (Build.VERSION.SDK_INT >= 18) { 311 | try { 312 | Field field = resources.getClass().getDeclaredField( 313 | "mAccessLock"); 314 | field.setAccessible(true); 315 | lock = field.get(resources); 316 | } catch (Throwable ignore) { 317 | } 318 | } else { 319 | try { 320 | Field field = Resources.class.getDeclaredField("mTmpValue"); 321 | field.setAccessible(true); 322 | lock = field.get(resources); 323 | } catch (Throwable ignore) { 324 | } 325 | } 326 | if (lock == null) { 327 | lock = MonkeyPatcher.class; 328 | } 329 | synchronized (lock) { 330 | pruneResourceCache(resources, "mDrawableCache"); 331 | pruneResourceCache(resources, "mColorDrawableCache"); 332 | pruneResourceCache(resources, "mColorStateListCache"); 333 | if (Build.VERSION.SDK_INT >= 23) { 334 | pruneResourceCache(resources, "mAnimatorCache"); 335 | pruneResourceCache(resources, "mStateListAnimatorCache"); 336 | } 337 | } 338 | } 339 | 340 | private static boolean pruneResourceCache(Object resources, String fieldName) { 341 | try { 342 | Class resourcesClass = resources.getClass(); 343 | Field cacheField; 344 | try { 345 | cacheField = resourcesClass.getDeclaredField(fieldName); 346 | } catch (NoSuchFieldException ignore) { 347 | cacheField = Resources.class.getDeclaredField(fieldName); 348 | } 349 | cacheField.setAccessible(true); 350 | Object cache = cacheField.get(resources); 351 | 352 | Class type = cacheField.getType(); 353 | if (Build.VERSION.SDK_INT < 16) { 354 | if ((cache instanceof SparseArray)) { 355 | ((SparseArray) cache).clear(); 356 | return true; 357 | } 358 | if ((Build.VERSION.SDK_INT >= 14) 359 | && ((cache instanceof LongSparseArray))) { 360 | ((LongSparseArray) cache).clear(); 361 | return true; 362 | } 363 | } else if (Build.VERSION.SDK_INT < 23) { 364 | if ("mColorStateListCache".equals(fieldName)) { 365 | if ((cache instanceof LongSparseArray)) { 366 | ((LongSparseArray) cache).clear(); 367 | } 368 | } else { 369 | if (type.isAssignableFrom(ArrayMap.class)) { 370 | Method clearArrayMap = Resources.class 371 | .getDeclaredMethod("clearDrawableCachesLocked", 372 | new Class[] { ArrayMap.class, 373 | Integer.TYPE }); 374 | 375 | clearArrayMap.setAccessible(true); 376 | clearArrayMap.invoke(resources, new Object[] { cache, 377 | Integer.valueOf(-1) }); 378 | return true; 379 | } 380 | if (type.isAssignableFrom(LongSparseArray.class)) { 381 | Method clearSparseMap = Resources.class 382 | .getDeclaredMethod("clearDrawableCachesLocked", 383 | new Class[] { LongSparseArray.class, 384 | Integer.TYPE }); 385 | 386 | clearSparseMap.setAccessible(true); 387 | clearSparseMap.invoke(resources, new Object[] { cache, 388 | Integer.valueOf(-1) }); 389 | return true; 390 | } 391 | } 392 | } else { 393 | while (type != null) { 394 | try { 395 | Method configChangeMethod = type.getDeclaredMethod( 396 | "onConfigurationChange", 397 | new Class[] { Integer.TYPE }); 398 | 399 | configChangeMethod.setAccessible(true); 400 | configChangeMethod.invoke(cache, 401 | new Object[] { Integer.valueOf(-1) }); 402 | return true; 403 | } catch (Throwable ignore) { 404 | } 405 | type = type.getSuperclass(); 406 | } 407 | } 408 | } catch (Throwable ignore) { 409 | } 410 | return false; 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/PatchesLoader.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public abstract interface PatchesLoader { 4 | public abstract boolean load(); 5 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/PatchesLoaderDumper.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public class PatchesLoaderDumper { 4 | public static void main(String[] args) { 5 | try { 6 | Class aClass = Class 7 | .forName("com.android.tools.fd.runtime.AppPatchesLoaderImpl"); 8 | PatchesLoader patchesLoader = (PatchesLoader) aClass.newInstance(); 9 | patchesLoader.load(); 10 | } catch (ClassNotFoundException e) { 11 | e.printStackTrace(); 12 | } catch (InstantiationException e) { 13 | e.printStackTrace(); 14 | } catch (IllegalAccessException e) { 15 | e.printStackTrace(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/Paths.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | public final class Paths { 4 | public static final String DEX_DIRECTORY_NAME = "dex"; 5 | public static final String DEVICE_TEMP_DIR = "/data/local/tmp"; 6 | public static final String BUILD_ID_TXT = "build-id.txt"; 7 | public static final String RESOURCE_FILE_NAME = "resources.ap_"; 8 | public static final String RELOAD_DEX_FILE_NAME = "classes.dex.3"; 9 | public static final String DEX_SLICE_PREFIX = "slice-"; 10 | 11 | public static String getMainApkDataDirectory(String applicationId) { 12 | return "/data/data/" + applicationId; 13 | } 14 | 15 | public static String getDataDirectory(String applicationId) { 16 | return "/data/data/" + applicationId + "/files/instant-run"; 17 | } 18 | 19 | public static String getDexFileDirectory(String applicationId) { 20 | return getDataDirectory(applicationId) + "/" + "dex"; 21 | } 22 | 23 | public static String getInboxDirectory(String applicationId) { 24 | return getDataDirectory(applicationId) + "/inbox"; 25 | } 26 | 27 | public static String getDeviceIdFolder(String pkg) { 28 | return "/data/local/tmp/" + pkg + "-" + "build-id.txt"; 29 | } 30 | } -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/Restarter.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.app.AlarmManager; 6 | import android.app.PendingIntent; 7 | import android.content.Context; 8 | import android.content.ContextWrapper; 9 | import android.content.Intent; 10 | import android.os.Build; 11 | import android.os.Build.VERSION; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.util.ArrayMap; 15 | import android.util.Log; 16 | import android.widget.Toast; 17 | import java.lang.reflect.Field; 18 | import java.util.ArrayList; 19 | import java.util.Collection; 20 | import java.util.HashMap; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | public class Restarter { 26 | public static void restartActivityOnUiThread(final Activity activity) { 27 | activity.runOnUiThread(new Runnable() { 28 | public void run() { 29 | if (Log.isLoggable("InstantRun", 2)) { 30 | Log.v("InstantRun", "Resources updated: notify activities"); 31 | } 32 | Restarter.updateActivity(activity); 33 | } 34 | }); 35 | } 36 | 37 | private static void restartActivity(Activity activity) { 38 | if (Log.isLoggable("InstantRun", 2)) { 39 | Log.v("InstantRun", "About to restart " 40 | + activity.getClass().getSimpleName()); 41 | } 42 | while (activity.getParent() != null) { 43 | if (Log.isLoggable("InstantRun", 2)) { 44 | Log.v("InstantRun", activity.getClass().getSimpleName() 45 | + " is not a top level activity; restarting " 46 | + activity.getParent().getClass().getSimpleName() 47 | + " instead"); 48 | } 49 | activity = activity.getParent(); 50 | } 51 | activity.recreate(); 52 | } 53 | 54 | public static void restartApp(Context appContext, 55 | Collection knownActivities, boolean toast) { 56 | if (!knownActivities.isEmpty()) { 57 | Activity foreground = getForegroundActivity(appContext); 58 | if (foreground != null) { 59 | if (toast) { 60 | showToast(foreground, 61 | "Restarting app to apply incompatible changes"); 62 | } 63 | if (Log.isLoggable("InstantRun", 2)) { 64 | Log.v("InstantRun", "RESTARTING APP"); 65 | } 66 | Context context = foreground; 67 | Intent intent = new Intent(context, foreground.getClass()); 68 | int intentId = 0; 69 | PendingIntent pendingIntent = PendingIntent.getActivity( 70 | context, intentId, intent, 268435456); 71 | 72 | AlarmManager mgr = (AlarmManager) context 73 | .getSystemService("alarm"); 74 | mgr.set(1, System.currentTimeMillis() + 100L, pendingIntent); 75 | if (Log.isLoggable("InstantRun", 2)) { 76 | Log.v("InstantRun", "Scheduling activity " + foreground 77 | + " to start after exiting process"); 78 | } 79 | } else { 80 | showToast((Activity) knownActivities.iterator().next(), 81 | "Unable to restart app"); 82 | if (Log.isLoggable("InstantRun", 2)) { 83 | Log.v("InstantRun", 84 | "Couldn't find any foreground activities to restart for resource refresh"); 85 | } 86 | } 87 | System.exit(0); 88 | } 89 | } 90 | 91 | static void showToast(final Activity activity, final String text) { 92 | if (Log.isLoggable("InstantRun", 2)) { 93 | Log.v("InstantRun", "About to show toast for activity " + activity 94 | + ": " + text); 95 | } 96 | activity.runOnUiThread(new Runnable() { 97 | public void run() { 98 | try { 99 | Context context = activity.getApplicationContext(); 100 | if ((context instanceof ContextWrapper)) { 101 | Context base = ((ContextWrapper) context) 102 | .getBaseContext(); 103 | if (base == null) { 104 | if (Log.isLoggable("InstantRun", 5)) { 105 | Log.w("InstantRun", 106 | "Couldn't show toast: no base context"); 107 | } 108 | return; 109 | } 110 | } 111 | int duration = 0; 112 | if ((text.length() >= 60) || (text.indexOf('\n') != -1)) { 113 | duration = 1; 114 | } 115 | Toast.makeText(activity, text, duration).show(); 116 | } catch (Throwable e) { 117 | if (Log.isLoggable("InstantRun", 5)) { 118 | Log.w("InstantRun", "Couldn't show toast", e); 119 | } 120 | } 121 | } 122 | }); 123 | } 124 | 125 | public static Activity getForegroundActivity(Context context) { 126 | List list = getActivities(context, true); 127 | return list.isEmpty() ? null : (Activity) list.get(0); 128 | } 129 | 130 | @SuppressLint("NewApi") 131 | public static List getActivities(Context context, 132 | boolean foregroundOnly) { 133 | List list = new ArrayList(); 134 | try { 135 | Class activityThreadClass = Class 136 | .forName("android.app.ActivityThread"); 137 | Object activityThread = MonkeyPatcher.getActivityThread(context, 138 | activityThreadClass); 139 | Field activitiesField = activityThreadClass 140 | .getDeclaredField("mActivities"); 141 | activitiesField.setAccessible(true); 142 | 143 | Object collection = activitiesField.get(activityThread); 144 | Collection c; 145 | if ((collection instanceof HashMap)) { 146 | Map activities = (HashMap) collection; 147 | c = activities.values(); 148 | } else { 149 | if ((Build.VERSION.SDK_INT >= 19) 150 | && ((collection instanceof ArrayMap))) { 151 | ArrayMap activities = (ArrayMap) collection; 152 | ArrayList aList = new ArrayList(); 153 | for(Map.Entry entry:((ArrayMap)activities).entrySet()){ 154 | aList.add(entry.getValue()); 155 | } 156 | c = aList; 157 | } else { 158 | return list; 159 | } 160 | } 161 | for (Object activityRecord : c) { 162 | Class activityRecordClass = activityRecord.getClass(); 163 | if (foregroundOnly) { 164 | Field pausedField = activityRecordClass 165 | .getDeclaredField("paused"); 166 | pausedField.setAccessible(true); 167 | if (pausedField.getBoolean(activityRecord)) { 168 | } 169 | } else { 170 | Field activityField = activityRecordClass 171 | .getDeclaredField("activity"); 172 | activityField.setAccessible(true); 173 | Activity activity = (Activity) activityField 174 | .get(activityRecord); 175 | if (activity != null) { 176 | list.add(activity); 177 | } 178 | } 179 | } 180 | } catch (Throwable ignore) { 181 | } 182 | return list; 183 | } 184 | 185 | private static void updateActivity(Activity activity) { 186 | restartActivity(activity); 187 | } 188 | 189 | public static void showToastWhenPossible(Context context, String message) { 190 | Activity activity = getForegroundActivity(context); 191 | if (activity != null) { 192 | showToast(activity, message); 193 | } else { 194 | showToastWhenPossible(context, message, 10); 195 | } 196 | } 197 | 198 | private static void showToastWhenPossible(final Context context, 199 | final String message, final int remainingAttempts) { 200 | Looper mainLooper = Looper.getMainLooper(); 201 | Handler handler = new Handler(mainLooper); 202 | handler.postDelayed(new Runnable() { 203 | public void run() { 204 | Activity activity = Restarter 205 | .getForegroundActivity(context); 206 | if (activity != null) { 207 | Restarter.showToast(activity, message); 208 | } else if (remainingAttempts > 0) { 209 | Restarter.showToastWhenPossible(context, message, 210 | remainingAttempts - 1); 211 | } 212 | } 213 | }, 1000L); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/fd/runtime/Server.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.fd.runtime; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.net.LocalServerSocket; 6 | import android.net.LocalSocket; 7 | import android.util.Log; 8 | import dalvik.system.DexClassLoader; 9 | import java.io.DataInputStream; 10 | import java.io.DataOutputStream; 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.lang.reflect.Method; 14 | import java.math.BigInteger; 15 | import java.util.List; 16 | 17 | public class Server { 18 | private static final boolean RESTART_LOCALLY = false; 19 | private static final boolean POST_ALIVE_STATUS = false; 20 | private LocalServerSocket mServerSocket; 21 | private final Application mApplication; 22 | private static int sWrongTokenCount; 23 | 24 | public static void create(String packageName, Application application) { 25 | new Server(packageName, application); 26 | } 27 | 28 | private Server(String packageName, Application application) { 29 | this.mApplication = application; 30 | try { 31 | this.mServerSocket = new LocalServerSocket(packageName); 32 | if (Log.isLoggable("InstantRun", 2)) { 33 | Log.v("InstantRun", 34 | "Starting server socket listening for package " 35 | + packageName + " on " 36 | + this.mServerSocket.getLocalSocketAddress()); 37 | } 38 | } catch (IOException e) { 39 | Log.e("InstantRun", "IO Error creating local socket at " 40 | + packageName, e); 41 | return; 42 | } 43 | startServer(); 44 | if (Log.isLoggable("InstantRun", 2)) { 45 | Log.v("InstantRun", "Started server for package " + packageName); 46 | } 47 | } 48 | 49 | private void startServer() { 50 | try { 51 | Thread socketServerThread = new Thread(new SocketServerThread()); 52 | socketServerThread.start(); 53 | } catch (Throwable e) { 54 | if (Log.isLoggable("InstantRun", 6)) { 55 | Log.e("InstantRun", "Fatal error starting Instant Run server", 56 | e); 57 | } 58 | } 59 | } 60 | 61 | private class SocketServerThread extends Thread { 62 | private SocketServerThread() { 63 | } 64 | 65 | public void run() { 66 | try { 67 | for (;;) { 68 | LocalServerSocket serverSocket = Server.this.mServerSocket; 69 | if (serverSocket == null) { 70 | break; 71 | } 72 | LocalSocket socket = serverSocket.accept(); 73 | if (Log.isLoggable("InstantRun", 2)) { 74 | Log.v("InstantRun", 75 | "Received connection from IDE: spawning connection thread"); 76 | } 77 | Server.SocketServerReplyThread socketServerReplyThread = new Server.SocketServerReplyThread( 78 | socket); 79 | 80 | socketServerReplyThread.run(); 81 | if (Server.sWrongTokenCount > 50) { 82 | if (Log.isLoggable("InstantRun", 2)) { 83 | Log.v("InstantRun", 84 | "Stopping server: too many wrong token connections"); 85 | } 86 | Server.this.mServerSocket.close(); 87 | break; 88 | } 89 | } 90 | } catch (Throwable e) { 91 | if (Log.isLoggable("InstantRun", 2)) { 92 | Log.v("InstantRun", 93 | "Fatal error accepting connection on local socket", 94 | e); 95 | } 96 | } 97 | } 98 | } 99 | 100 | private class SocketServerReplyThread extends Thread { 101 | private final LocalSocket mSocket; 102 | 103 | SocketServerReplyThread(LocalSocket socket) { 104 | this.mSocket = socket; 105 | } 106 | 107 | public void run() { 108 | try { 109 | DataInputStream input = new DataInputStream( 110 | this.mSocket.getInputStream()); 111 | DataOutputStream output = new DataOutputStream( 112 | this.mSocket.getOutputStream()); 113 | try { 114 | handle(input, output); 115 | } finally { 116 | try { 117 | input.close(); 118 | } catch (IOException ignore) { 119 | } 120 | try { 121 | output.close(); 122 | } catch (IOException ignore) { 123 | } 124 | } 125 | return; 126 | } catch (IOException e) { 127 | if (Log.isLoggable("InstantRun", 2)) { 128 | Log.v("InstantRun", "Fatal error receiving messages", e); 129 | } 130 | } 131 | } 132 | 133 | private void handle(DataInputStream input, DataOutputStream output) 134 | throws IOException { 135 | long magic = input.readLong(); 136 | if (magic != 890269988L) { 137 | Log.w("InstantRun", 138 | "Unrecognized header format " + Long.toHexString(magic)); 139 | 140 | return; 141 | } 142 | int version = input.readInt(); 143 | 144 | output.writeInt(4); 145 | if (version != 4) { 146 | Log.w("InstantRun", 147 | "Mismatched protocol versions; app is using version 4 and tool is using version " 148 | + version); 149 | } else { 150 | int message; 151 | for (;;) { 152 | message = input.readInt(); 153 | switch (message) { 154 | case 7: 155 | if (Log.isLoggable("InstantRun", 2)) { 156 | Log.v("InstantRun", "Received EOF from the IDE"); 157 | } 158 | return; 159 | case 2: 160 | boolean active = Restarter 161 | .getForegroundActivity(Server.this.mApplication) != null; 162 | output.writeBoolean(active); 163 | if (Log.isLoggable("InstantRun", 2)) { 164 | Log.v("InstantRun", 165 | "Received Ping message from the IDE; returned active = " 166 | + active); 167 | } 168 | break; 169 | case 3: 170 | String path = input.readUTF(); 171 | long size = FileManager.getFileSize(path); 172 | output.writeLong(size); 173 | if (Log.isLoggable("InstantRun", 2)) { 174 | Log.v("InstantRun", "Received path-exists(" + path 175 | + ") from the " + "IDE; returned size=" 176 | + size); 177 | } 178 | break; 179 | case 4: 180 | long begin = System.currentTimeMillis(); 181 | path = input.readUTF(); 182 | byte[] checksum = FileManager.getCheckSum(path); 183 | if (checksum != null) { 184 | output.writeInt(checksum.length); 185 | output.write(checksum); 186 | if (Log.isLoggable("InstantRun", 2)) { 187 | long end = System.currentTimeMillis(); 188 | String hash = new BigInteger(1, checksum) 189 | .toString(16); 190 | Log.v("InstantRun", "Received checksum(" + path 191 | + ") from the " + "IDE: took " 192 | + (end - begin) + "ms to compute " 193 | + hash); 194 | } 195 | } else { 196 | output.writeInt(0); 197 | if (Log.isLoggable("InstantRun", 2)) { 198 | Log.v("InstantRun", "Received checksum(" + path 199 | + ") from the " 200 | + "IDE: returning "); 201 | } 202 | } 203 | break; 204 | case 5: 205 | if (!authenticate(input)) { 206 | return; 207 | } 208 | Activity activity = Restarter 209 | .getForegroundActivity(Server.this.mApplication); 210 | if (activity != null) { 211 | if (Log.isLoggable("InstantRun", 2)) { 212 | Log.v("InstantRun", 213 | "Restarting activity per user request"); 214 | } 215 | Restarter.restartActivityOnUiThread(activity); 216 | } 217 | break; 218 | case 1: 219 | if (!authenticate(input)) { 220 | return; 221 | } 222 | List changes = ApplicationPatch 223 | .read(input); 224 | if (changes != null) { 225 | boolean hasResources = Server.hasResources(changes); 226 | int updateMode = input.readInt(); 227 | updateMode = Server.this.handlePatches(changes, 228 | hasResources, updateMode); 229 | 230 | boolean showToast = input.readBoolean(); 231 | 232 | output.writeBoolean(true); 233 | 234 | Server.this.restart(updateMode, hasResources, 235 | showToast); 236 | } 237 | break; 238 | case 6: 239 | String text = input.readUTF(); 240 | Activity foreground = Restarter 241 | .getForegroundActivity(Server.this.mApplication); 242 | if (foreground != null) { 243 | Restarter.showToast(foreground, text); 244 | } else if (Log.isLoggable("InstantRun", 2)) { 245 | Log.v("InstantRun", 246 | "Couldn't show toast (no activity) : " 247 | + text); 248 | } 249 | break; 250 | } 251 | } 252 | 253 | } 254 | } 255 | 256 | private boolean authenticate(DataInputStream input) throws IOException { 257 | long token = input.readLong(); 258 | if (token != AppInfo.token) { 259 | Log.w("InstantRun", 260 | "Mismatched identity token from client; received " 261 | + token + " and expected " + AppInfo.token); 262 | return true; 263 | } 264 | return true; 265 | } 266 | } 267 | 268 | private static boolean isResourcePath(String path) { 269 | return (path.equals("resources.ap_")) || (path.startsWith("res/")); 270 | } 271 | 272 | private static boolean hasResources(List changes) { 273 | for (ApplicationPatch change : changes) { 274 | String path = change.getPath(); 275 | if (isResourcePath(path)) { 276 | return true; 277 | } 278 | } 279 | return false; 280 | } 281 | 282 | private int handlePatches(List changes, 283 | boolean hasResources, int updateMode) { 284 | if (hasResources) { 285 | FileManager.startUpdate(); 286 | } 287 | for (ApplicationPatch change : changes) { 288 | String path = change.getPath(); 289 | if (path.endsWith(".dex")) { 290 | handleColdSwapPatch(change); 291 | 292 | boolean canHotSwap = false; 293 | for (ApplicationPatch c : changes) { 294 | if (c.getPath().equals("classes.dex.3")) { 295 | canHotSwap = true; 296 | break; 297 | } 298 | } 299 | if (!canHotSwap) { 300 | updateMode = 3; 301 | } 302 | } else if (path.equals("classes.dex.3")) { 303 | updateMode = handleHotSwapPatch(updateMode, change); 304 | } else if (isResourcePath(path)) { 305 | updateMode = handleResourcePatch(updateMode, change, path); 306 | } 307 | } 308 | if (hasResources) { 309 | FileManager.finishUpdate(true); 310 | } 311 | return updateMode; 312 | } 313 | 314 | private static int handleResourcePatch(int updateMode, 315 | ApplicationPatch patch, String path) { 316 | if (Log.isLoggable("InstantRun", 2)) { 317 | Log.v("InstantRun", "Received resource changes (" + path + ")"); 318 | } 319 | FileManager.writeAaptResources(path, patch.getBytes()); 320 | 321 | updateMode = Math.max(updateMode, 2); 322 | return updateMode; 323 | } 324 | 325 | private int handleHotSwapPatch(int updateMode, ApplicationPatch patch) { 326 | if (Log.isLoggable("InstantRun", 2)) { 327 | Log.v("InstantRun", "Received incremental code patch"); 328 | } 329 | try { 330 | String dexFile = FileManager.writeTempDexFile(patch.getBytes()); 331 | if (dexFile == null) { 332 | Log.e("InstantRun", "No file to write the code to"); 333 | return updateMode; 334 | } 335 | if (Log.isLoggable("InstantRun", 2)) { 336 | Log.v("InstantRun", "Reading live code from " + dexFile); 337 | } 338 | String nativeLibraryPath = FileManager.getNativeLibraryFolder() 339 | .getPath(); 340 | DexClassLoader dexClassLoader = new DexClassLoader(dexFile, 341 | this.mApplication.getCacheDir().getPath(), 342 | nativeLibraryPath, getClass().getClassLoader()); 343 | 344 | Class aClass = Class.forName( 345 | "com.android.tools.fd.runtime.AppPatchesLoaderImpl", true, 346 | dexClassLoader); 347 | try { 348 | if (Log.isLoggable("InstantRun", 2)) { 349 | Log.v("InstantRun", "Got the patcher class " + aClass); 350 | } 351 | PatchesLoader loader = (PatchesLoader) aClass.newInstance(); 352 | if (Log.isLoggable("InstantRun", 2)) { 353 | Log.v("InstantRun", "Got the patcher instance " + loader); 354 | } 355 | String[] getPatchedClasses = (String[]) aClass 356 | .getDeclaredMethod("getPatchedClasses", new Class[0]) 357 | .invoke(loader, new Object[0]); 358 | if (Log.isLoggable("InstantRun", 2)) { 359 | Log.v("InstantRun", "Got the list of classes "); 360 | for (String getPatchedClass : getPatchedClasses) { 361 | Log.v("InstantRun", "class " + getPatchedClass); 362 | } 363 | } 364 | if (!loader.load()) { 365 | updateMode = 3; 366 | } 367 | } catch (Exception e) { 368 | Log.e("InstantRun", "Couldn't apply code changes", e); 369 | e.printStackTrace(); 370 | updateMode = 3; 371 | } 372 | } catch (Throwable e) { 373 | Log.e("InstantRun", "Couldn't apply code changes", e); 374 | updateMode = 3; 375 | } 376 | return updateMode; 377 | } 378 | 379 | private static void handleColdSwapPatch(ApplicationPatch patch) { 380 | if (patch.path.startsWith("slice-")) { 381 | File file = FileManager.writeDexShard(patch.getBytes(), patch.path); 382 | if (Log.isLoggable("InstantRun", 2)) { 383 | Log.v("InstantRun", "Received dex shard " + file); 384 | } 385 | } 386 | } 387 | 388 | private void restart(int updateMode, boolean incrementalResources, 389 | boolean toast) { 390 | if (Log.isLoggable("InstantRun", 2)) { 391 | Log.v("InstantRun", "Finished loading changes; update mode =" 392 | + updateMode); 393 | } 394 | if ((updateMode == 0) || (updateMode == 1)) { 395 | if (Log.isLoggable("InstantRun", 2)) { 396 | Log.v("InstantRun", "Applying incremental code without restart"); 397 | } 398 | if (toast) { 399 | Activity foreground = Restarter 400 | .getForegroundActivity(this.mApplication); 401 | if (foreground != null) { 402 | Restarter.showToast(foreground, 403 | "Applied code changes without activity restart"); 404 | } else if (Log.isLoggable("InstantRun", 2)) { 405 | Log.v("InstantRun", 406 | "Couldn't show toast: no activity found"); 407 | } 408 | } 409 | return; 410 | } 411 | List activities = Restarter.getActivities(this.mApplication, 412 | false); 413 | if ((incrementalResources) && (updateMode == 2)) { 414 | File file = FileManager.getExternalResourceFile(); 415 | if (Log.isLoggable("InstantRun", 2)) { 416 | Log.v("InstantRun", "About to update resource file=" + file 417 | + ", activities=" + activities); 418 | } 419 | if (file != null) { 420 | String resources = file.getPath(); 421 | MonkeyPatcher.monkeyPatchApplication(this.mApplication, null, 422 | null, resources); 423 | MonkeyPatcher.monkeyPatchExistingResources(this.mApplication, 424 | resources, activities); 425 | } else { 426 | Log.e("InstantRun", "No resource file found to apply"); 427 | updateMode = 3; 428 | } 429 | } 430 | Activity activity = Restarter.getForegroundActivity(this.mApplication); 431 | if (updateMode == 2) { 432 | if (activity != null) { 433 | if (Log.isLoggable("InstantRun", 2)) { 434 | Log.v("InstantRun", "Restarting activity only!"); 435 | } 436 | boolean handledRestart = false; 437 | try { 438 | Method method = activity.getClass().getMethod( 439 | "onHandleCodeChange", new Class[] { Long.TYPE }); 440 | Object result = method.invoke(activity, 441 | new Object[] { Long.valueOf(0L) }); 442 | if (Log.isLoggable("InstantRun", 2)) { 443 | Log.v("InstantRun", "Activity " + activity 444 | + " provided manual restart method; return " 445 | + result); 446 | } 447 | if (Boolean.TRUE.equals(result)) { 448 | handledRestart = true; 449 | if (toast) { 450 | Restarter.showToast(activity, "Applied changes"); 451 | } 452 | } 453 | } catch (Throwable ignore) { 454 | } 455 | if (!handledRestart) { 456 | if (toast) { 457 | Restarter.showToast(activity, 458 | "Applied changes, restarted activity"); 459 | } 460 | Restarter.restartActivityOnUiThread(activity); 461 | } 462 | return; 463 | } 464 | if (Log.isLoggable("InstantRun", 2)) { 465 | Log.v("InstantRun", 466 | "No activity found, falling through to do a full app restart"); 467 | } 468 | updateMode = 3; 469 | } 470 | if (updateMode != 3) { 471 | if (Log.isLoggable("InstantRun", 6)) { 472 | Log.e("InstantRun", "Unexpected update mode: " + updateMode); 473 | } 474 | return; 475 | } 476 | if (Log.isLoggable("InstantRun", 2)) { 477 | Log.v("InstantRun", 478 | "Waiting for app to be killed and restarted by the IDE..."); 479 | } 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /chapter_13/InstantRunSourceCode/com/android/tools/ir/api/DisableInstantRun.java: -------------------------------------------------------------------------------- 1 | package com.android.tools.ir.api; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Documented 10 | @Retention(RetentionPolicy.CLASS) 11 | @Target({java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD}) 12 | public @interface DisableInstantRun {} 13 | 14 | 15 | /* Location: D:\decodeapk\new_aliptrip\instant-run.jar!\com\android\tools\ir\api\DisableInstantRun.class 16 | * Java compiler version: 6 (50.0) 17 | * JD-Core Version: 0.7.1 18 | */ -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.1" 6 | defaultConfig { 7 | applicationId "com.example.liuwangshu.hookinstrumentation" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator.EIT-20160520RHS\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/java/com/example/liuwangshu/hookinstrumentation/HookHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.hookinstrumentation; 2 | 3 | import android.app.Activity; 4 | import android.app.Instrumentation; 5 | 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * Created by Administrator on 2018/3/13 0013. 11 | */ 12 | 13 | public class HookHelper { 14 | public static void replaceActivityInstrumentation(Activity activity){ 15 | try { 16 | Field field = Activity.class.getDeclaredField("mInstrumentation"); 17 | field.setAccessible(true); 18 | Instrumentation instrumentation = (Instrumentation)field.get(activity); 19 | Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation); 20 | field.set(activity,instrumentationProxy); 21 | } catch (Exception e){ 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | public static void replaceContextInstrumentation(){ 27 | try { 28 | Class activityThreadClazz = Class.forName("android.app.ActivityThread"); 29 | Field activityThreadField=activityThreadClazz.getDeclaredField("sCurrentActivityThread"); 30 | activityThreadField.setAccessible(true); 31 | Object currentActivityThread= activityThreadField.get(null); 32 | Field mInstrumentationField = activityThreadClazz.getDeclaredField("mInstrumentation"); 33 | mInstrumentationField.setAccessible(true); 34 | Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); 35 | Instrumentation mInstrumentationProxy = new InstrumentationProxy(mInstrumentation); 36 | mInstrumentationField.set(currentActivityThread, mInstrumentationProxy); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/java/com/example/liuwangshu/hookinstrumentation/InstrumentationProxy.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.hookinstrumentation; 2 | 3 | import android.app.Activity; 4 | import android.app.Instrumentation; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.IBinder; 9 | import android.util.Log; 10 | 11 | import java.lang.reflect.Method; 12 | 13 | 14 | public class InstrumentationProxy extends Instrumentation { 15 | private static final String TAG = "InstrumentationProxy"; 16 | Instrumentation mInstrumentation; 17 | 18 | public InstrumentationProxy(Instrumentation instrumentation) { 19 | mInstrumentation = instrumentation; 20 | } 21 | public ActivityResult execStartActivity( 22 | Context who, IBinder contextThread, IBinder token, Activity target, 23 | Intent intent, int requestCode, Bundle options) { 24 | Log.d(TAG, "Hook成功" + "---who:" + who); 25 | try { 26 | Method execStartActivity = Instrumentation.class.getDeclaredMethod( 27 | "execStartActivity", 28 | Context.class, IBinder.class, IBinder.class, Activity.class, 29 | Intent.class, int.class, Bundle.class); 30 | return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, 31 | contextThread, token, target, intent, requestCode, options); 32 | } catch (Exception e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/java/com/example/liuwangshu/hookinstrumentation/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.hookinstrumentation; 2 | 3 | import android.app.Activity; 4 | import android.app.Instrumentation; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.os.Bundle; 10 | 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.Method; 13 | 14 | public class MainActivity extends Activity { 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | HookHelper.replaceContextInstrumentation(); 20 | // HookHelper.replaceActivityInstrumentation(this); 21 | Intent intent = new Intent(Intent.ACTION_VIEW); 22 | intent.setData(Uri.parse("http://liuwangshu.cn")); 23 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 24 | // startActivity(intent); 25 | getApplicationContext().startActivity(intent); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | HookInstrumentation 3 | 4 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_14/HookInstrumentation/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 08 17:36:19 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /chapter_14/HookInstrumentation/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '26.0.0' 6 | defaultConfig { 7 | applicationId "com.example.liuwangshu.pluginactivity" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:26.+' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator.EIT-20160520RHS\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | S 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/HookLibrary/FieldUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity.HookLibrary; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * Created by Administrator on 2018/3/24 0024. 7 | */ 8 | 9 | public class FieldUtil { 10 | public static Object getField(Class clazz, Object target, String name) throws Exception { 11 | Field field = clazz.getDeclaredField(name); 12 | field.setAccessible(true); 13 | return field.get(target); 14 | } 15 | public static Field getField(Class clazz, String name) throws Exception{ 16 | Field field = clazz.getDeclaredField(name); 17 | field.setAccessible(true); 18 | return field; 19 | } 20 | public static void setField(Class clazz, Object target, String name, Object value) throws Exception { 21 | Field field = clazz.getDeclaredField(name); 22 | field.setAccessible(true); 23 | field.set(target, value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/HookLibrary/HCallback.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity.HookLibrary; 2 | 3 | import android.content.Intent; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | 7 | /** 8 | * Created by Administrator on 2018/3/29 0029. 9 | */ 10 | 11 | public class HCallback implements Handler.Callback{ 12 | public static final int LAUNCH_ACTIVITY = 100; 13 | Handler mHandler; 14 | 15 | public HCallback(Handler handler) { 16 | mHandler = handler; 17 | } 18 | @Override 19 | public boolean handleMessage(Message msg) { 20 | if (msg.what == LAUNCH_ACTIVITY) { 21 | Object r = msg.obj; 22 | try { 23 | Intent intent = (Intent) FieldUtil.getField(r.getClass(), r, "intent"); 24 | Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT); 25 | intent.setComponent(target.getComponent()); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | mHandler.handleMessage(msg); 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/HookLibrary/HookHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity.HookLibrary; 2 | 3 | 4 | import android.app.Instrumentation; 5 | import android.content.Context; 6 | import android.os.Build; 7 | import android.os.Handler; 8 | import android.util.Log; 9 | 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Proxy; 12 | 13 | /** 14 | * Created by Administrator on 2018/3/23 0023. 15 | */ 16 | 17 | public class HookHelper { 18 | public static final String TARGET_INTENT = "target_intent"; 19 | public static final String TARGET_INTENT_NAME = "target_intent_name"; 20 | public static void hookAMS() throws Exception { 21 | Object defaultSingleton=null; 22 | if (Build.VERSION.SDK_INT >= 26) { 23 | Class activityManagerClazz = Class.forName("android.app.ActivityManager"); 24 | defaultSingleton= FieldUtil.getField(activityManagerClazz,null,"IActivityManagerSingleton"); 25 | } else { 26 | Class activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); 27 | defaultSingleton= FieldUtil.getField(activityManagerNativeClass,null,"gDefault"); 28 | } 29 | Class singletonClazz = Class.forName("android.util.Singleton"); 30 | Field mInstanceField= FieldUtil.getField(singletonClazz,"mInstance"); 31 | Object iActivityManager = mInstanceField.get(defaultSingleton); 32 | Class iActivityManagerClass = Class.forName("android.app.IActivityManager"); 33 | Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), 34 | new Class[] { iActivityManagerClass }, new IActivityManagerProxy(iActivityManager)); 35 | mInstanceField.set(defaultSingleton, proxy); 36 | } 37 | public static void hookHandler() throws Exception { 38 | Class activityThreadClazz = Class.forName("android.app.ActivityThread"); 39 | Object currentActivityThread= FieldUtil.getField(activityThreadClazz,null,"sCurrentActivityThread"); 40 | Field mHField = FieldUtil.getField(activityThreadClazz,"mH"); 41 | Handler mH = (Handler) mHField.get(currentActivityThread); 42 | FieldUtil.setField(Handler.class,mH,"mCallback",new HCallback(mH)); 43 | } 44 | public static void hookInstrumentation(Context context) throws Exception { 45 | Class contextImplClazz = Class.forName("android.app.ContextImpl"); 46 | Field mMainThreadField =FieldUtil.getField(contextImplClazz,"mMainThread"); 47 | Object activityThread = mMainThreadField.get(context); 48 | Class activityThreadClazz = Class.forName("android.app.ActivityThread"); 49 | Field mInstrumentationField=FieldUtil.getField(activityThreadClazz,"mInstrumentation"); 50 | FieldUtil.setField(activityThreadClazz,activityThread,"mInstrumentation",new InstrumentationProxy((Instrumentation) mInstrumentationField.get(activityThread), 51 | context.getPackageManager())); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/HookLibrary/IActivityManagerProxy.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity.HookLibrary; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | import com.example.liuwangshu.pluginactivity.StubActivity; 8 | 9 | import java.lang.reflect.InvocationHandler; 10 | import java.lang.reflect.Method; 11 | 12 | /** 13 | * Created by Administrator on 2018/3/24 0024. 14 | */ 15 | 16 | public class IActivityManagerProxy implements InvocationHandler { 17 | private Object mActivityManager; 18 | private static final String TAG = "IActivityManagerProxy"; 19 | 20 | public IActivityManagerProxy(Object activityManager) { 21 | this.mActivityManager = activityManager; 22 | } 23 | 24 | @Override 25 | public Object invoke(Object o, Method method, Object[] args) throws Throwable { 26 | if ("startActivity".equals(method.getName())) { 27 | Intent intent = null; 28 | int index = 0; 29 | for (int i = 0; i < args.length; i++) { 30 | if (args[i] instanceof Intent) { 31 | index = i; 32 | break; 33 | } 34 | } 35 | intent = (Intent) args[index]; 36 | Intent subIntent = new Intent(); 37 | String packageName = "com.example.liuwangshu.pluginactivity"; 38 | subIntent.setClassName(packageName,packageName+".StubActivity"); 39 | subIntent.putExtra(HookHelper.TARGET_INTENT, intent); 40 | args[index] = subIntent; 41 | Log.d(TAG, "hook成功"); 42 | } 43 | return method.invoke(mActivityManager, args); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/HookLibrary/InstrumentationProxy.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity.HookLibrary; 2 | 3 | import android.app.Activity; 4 | import android.app.Instrumentation; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | import android.content.pm.ResolveInfo; 10 | import android.os.Bundle; 11 | import android.os.IBinder; 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | 15 | import com.example.liuwangshu.pluginactivity.StubActivity; 16 | 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.Method; 19 | import java.util.List; 20 | 21 | /** 22 | * Created by Administrator on 2018/4/8 0008. 23 | */ 24 | 25 | public class InstrumentationProxy extends Instrumentation { 26 | private Instrumentation mInstrumentation; 27 | private PackageManager mPackageManager; 28 | 29 | public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) { 30 | mInstrumentation = instrumentation; 31 | mPackageManager = packageManager; 32 | } 33 | 34 | public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, 35 | IllegalAccessException, ClassNotFoundException { 36 | String intentName = intent.getStringExtra(HookHelper.TARGET_INTENT_NAME); 37 | if (!TextUtils.isEmpty(intentName)) { 38 | return super.newActivity(cl, intentName, intent); 39 | } 40 | return super.newActivity(cl, className, intent); 41 | } 42 | 43 | public ActivityResult execStartActivity( 44 | Context who, IBinder contextThread, IBinder token, Activity target, 45 | Intent intent, int requestCode, Bundle options) { 46 | List infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL); 47 | if (infos == null || infos.size() == 0) { 48 | intent.putExtra(HookHelper.TARGET_INTENT_NAME, intent.getComponent().getClassName()); 49 | intent.setClassName(who, "com.example.liuwangshu.pluginactivity.StubActivity"); 50 | } 51 | try { 52 | Method execMethod = Instrumentation.class.getDeclaredMethod("execStartActivity", 53 | Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); 54 | return (ActivityResult) execMethod.invoke(mInstrumentation, who, contextThread, token, 55 | target, intent, requestCode, options); 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | 10 | import com.example.liuwangshu.pluginactivity.HookLibrary.HookHelper; 11 | 12 | public class MainActivity extends Activity { 13 | private Button bt_hook; 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | bt_hook = (Button) this.findViewById(R.id.bt_hook); 19 | bt_hook.setOnClickListener(new View.OnClickListener() { 20 | @Override 21 | public void onClick(View view) { 22 | Intent intent = new Intent(MainActivity.this, TargetActivity.class); 23 | startActivity(intent); 24 | } 25 | }); 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.example.liuwangshu.pluginactivity.HookLibrary.HookHelper; 7 | 8 | /** 9 | * Created by Administrator on 2018/4/12 0012. 10 | */ 11 | 12 | public class MyApplication extends Application { 13 | @Override 14 | public void attachBaseContext(Context base) { 15 | super.attachBaseContext(base); 16 | try { 17 | //Hook AMS 18 | HookHelper.hookAMS(); 19 | HookHelper.hookHandler(); 20 | 21 | //Hook Instrumentation 22 | // HookHelper.hookInstrumentation(base); 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/StubActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class StubActivity extends Activity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_stub); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/java/com/example/liuwangshu/pluginactivity/TargetActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.liuwangshu.pluginactivity; 2 | 3 | import android.app.Activity; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.widget.TextView; 8 | 9 | import org.w3c.dom.Text; 10 | 11 | public class TargetActivity extends Activity { 12 | private TextView textView; 13 | private static final String TAG = "TargetActivity"; 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_target); 18 | 19 | } 20 | @Override 21 | protected void onResume() { 22 | super.onResume(); 23 | Log.d(TAG,"onResume"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /chapter_15/PluginActivity/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 |