├── .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 | 
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 | 
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 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
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 extends Application> realClass = (Class extends Application>) 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 extends Application> 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, WeakReference> 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, WeakReference> 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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/chapter_14/HookInstrumentation/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
12 |
13 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/layout/activity_stub.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/layout/activity_target.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PluginActivity
3 |
4 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/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_15/PluginActivity/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_15/PluginActivity/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginActivity/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/chapter_15/PluginActivity/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 21 15:02:27 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_15/PluginActivity/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_15/PluginActivity/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_15/PluginActivity/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.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/PluginService/.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/PluginService/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/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.pluginservice"
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/PluginService/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/PluginService/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/HookLibrary/FieldUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice.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/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/HookLibrary/HookHelper.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice.HookLibrary;
2 |
3 |
4 | import android.app.Instrumentation;
5 | import android.content.Context;
6 | import android.os.Build;
7 | import android.os.Handler;
8 |
9 | import com.example.liuwangshu.pluginservice.IActivityManagerProxy;
10 |
11 | import java.lang.reflect.Field;
12 | import java.lang.reflect.Proxy;
13 |
14 | /**
15 | * Created by Administrator on 2018/3/23 0023.
16 | */
17 |
18 | public class HookHelper {
19 |
20 | public static void hookAMS() throws Exception {
21 | Object defaultSingleton=null;
22 | if (Build.VERSION.SDK_INT >= 26) {
23 | Class> activityManagerClass = Class.forName("android.app.ActivityManager");
24 | defaultSingleton= FieldUtil.getField(activityManagerClass,null,"IActivityManagerSingleton");
25 | } else {
26 | Class> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
27 | defaultSingleton= FieldUtil.getField(activityManagerNativeClass,null,"gDefault");
28 | }
29 | Class> singletonClass = Class.forName("android.util.Singleton");
30 | Field mInstanceField= FieldUtil.getField(singletonClass,"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 | }
38 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/IActivityManagerProxy.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Intent;
5 | import android.util.Log;
6 |
7 | import com.example.liuwangshu.pluginservice.HookLibrary.HookHelper;
8 |
9 | import java.lang.reflect.InvocationHandler;
10 | import java.lang.reflect.Method;
11 |
12 | public class IActivityManagerProxy implements InvocationHandler {
13 | private Object mActivityManager;
14 | private static final String TAG = "IActivityManagerProxy";
15 |
16 | public IActivityManagerProxy(Object activityManager) {
17 | this.mActivityManager = activityManager;
18 | }
19 |
20 | @Override
21 | public Object invoke(Object o, Method method, Object[] args) throws Throwable {
22 | if ("startService".equals(method.getName())) {
23 | Intent intent = null;
24 | int index = 0;
25 | for (int i = 0; i < args.length; i++) {
26 | if (args[i] instanceof Intent) {
27 | index = i;
28 | break;
29 | }
30 | }
31 | intent = (Intent) args[index];
32 | Intent proxyIntent = new Intent();
33 | String packageName = "com.example.liuwangshu.pluginservice";
34 | proxyIntent.setClassName(packageName, packageName + ".ProxyService");
35 | proxyIntent.putExtra(ProxyService.TARGET_SERVICE, intent.getComponent().getClassName());
36 | args[index] = proxyIntent;
37 | Log.d(TAG, "Hook成功");
38 | }
39 | return method.invoke(mActivityManager, args);
40 | }
41 | }
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.widget.Button;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 | private Button bt_hook;
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_main);
16 | bt_hook = (Button) this.findViewById(R.id.bt_hook);
17 | bt_hook.setOnClickListener(new View.OnClickListener() {
18 | @Override
19 | public void onClick(View view) {
20 | Intent intent = new Intent(MainActivity.this, TargetService.class);
21 | startService(intent);
22 | }
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.example.liuwangshu.pluginservice.HookLibrary.HookHelper;
7 |
8 | /**
9 | * Created by Administrator on 2018/4/19 0019.
10 | */
11 |
12 | public class MyApplication extends Application {
13 |
14 | @Override
15 | protected void attachBaseContext(Context base) {
16 | super.attachBaseContext(base);
17 | try {
18 | HookHelper.hookAMS();
19 | } catch (Exception e) {
20 | e.printStackTrace();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/ProxyService.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice;
2 |
3 | import android.app.Application;
4 | import android.app.Service;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.IBinder;
9 | import android.support.annotation.Nullable;
10 |
11 |
12 | import com.example.liuwangshu.pluginservice.HookLibrary.FieldUtil;
13 | import java.lang.reflect.Field;
14 | import java.lang.reflect.Method;
15 |
16 |
17 | /**
18 | * Created by Administrator on 2018/4/16 0016.
19 | */
20 |
21 | public class ProxyService extends Service {
22 | public static final String TARGET_SERVICE = "target_service";
23 | @Override
24 | public void onCreate() {
25 | super.onCreate();
26 | }
27 | @Nullable
28 | @Override
29 | public IBinder onBind(Intent intent) {
30 | return null;
31 | }
32 | @Override
33 | public int onStartCommand(Intent intent, int flags, int startId) {
34 | if (null == intent || !intent.hasExtra(TARGET_SERVICE)) {
35 | return START_STICKY;
36 | }
37 | String serviceName = intent.getStringExtra(TARGET_SERVICE);
38 | if (null == serviceName) {
39 | return START_STICKY;
40 | }
41 | Service targetService=null;
42 | try {
43 | Class activityThreadClazz = Class.forName("android.app.ActivityThread");
44 | Method getActivityThreadMethod = activityThreadClazz.getDeclaredMethod("getApplicationThread");
45 | getActivityThreadMethod.setAccessible(true);
46 | Object activityThread = FieldUtil.getField(activityThreadClazz, null, "sCurrentActivityThread");
47 | Object applicationThread = getActivityThreadMethod.invoke(activityThread);
48 | Class iInterfaceClazz = Class.forName("android.os.IInterface");
49 | Method asBinderMethod = iInterfaceClazz.getDeclaredMethod("asBinder");
50 | asBinderMethod.setAccessible(true);
51 | Object token = asBinderMethod.invoke(applicationThread);
52 | Class serviceClazz = Class.forName("android.app.Service");
53 | Method attachMethod = serviceClazz.getDeclaredMethod("attach",
54 | Context.class, activityThreadClazz, String.class, IBinder.class, Application.class, Object.class);
55 | attachMethod.setAccessible(true);
56 | Object defaultSingleton=null;
57 | if (Build.VERSION.SDK_INT >= 26) {
58 | Class> activityManageClazz = Class.forName("android.app.ActivityManager");
59 | //获取activityManager中的IActivityManagerSingleton字段
60 | defaultSingleton = FieldUtil.getField(activityManageClazz, null, "IActivityManagerSingleton");
61 | } else {
62 | Class> activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative");
63 | //获取ActivityManagerNative中的gDefault字段
64 | defaultSingleton = FieldUtil.getField(activityManagerNativeClazz, null, "gDefault");
65 | }
66 | Class> singletonClazz = Class.forName("android.util.Singleton");
67 | Field mInstanceField = FieldUtil.getField(singletonClazz, "mInstance");
68 | //获取iActivityManager
69 | Object iActivityManager = mInstanceField.get(defaultSingleton);
70 | targetService = (Service) Class.forName(serviceName).newInstance();
71 | attachMethod.invoke(targetService, this, activityThread, intent.getComponent().getClassName(), token,
72 | getApplication(), iActivityManager);
73 | targetService.onCreate();
74 | } catch (Exception e) {
75 | e.printStackTrace();
76 | return START_STICKY;
77 | }
78 | targetService.onStartCommand(intent, flags, startId);
79 | return START_STICKY;
80 | }
81 | }
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/java/com/example/liuwangshu/pluginservice/TargetService.java:
--------------------------------------------------------------------------------
1 | package com.example.liuwangshu.pluginservice;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.IBinder;
6 | import android.support.annotation.Nullable;
7 | import android.util.Log;
8 |
9 | /**
10 | * Created by Administrator on 2018/4/19 0019.
11 | */
12 |
13 | public class TargetService extends Service {
14 | private static final String TAG = "TargetService";
15 | @Nullable
16 | @Override
17 | public IBinder onBind(Intent intent) {
18 | return null;
19 | }
20 |
21 | @Override
22 | public void onCreate() {
23 | super.onCreate();
24 | Log.d(TAG, "onCreate");
25 | }
26 |
27 | @Override
28 | public int onStartCommand(Intent intent, int flags, int startId) {
29 | Log.d(TAG, "onStartCommand");
30 | return super.onStartCommand(intent, flags, startId);
31 | }
32 | }
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PluginService
3 |
4 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/chapter_15/PluginService/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_15/PluginService/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_15/PluginService/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/henrymorgen/android-advanced-decode/8446efc5e8c0ce4952016f81802e18576562d23e/chapter_15/PluginService/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/chapter_15/PluginService/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 16 18:10:31 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_15/PluginService/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_15/PluginService/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_15/PluginService/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------