[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # replace\npatreon: weishu\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: http://paypal.me/virtualxposed\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Android CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v1\n    - name: set up JDK 1.8\n      uses: actions/setup-java@v1\n      with:\n        java-version: 1.8\n    - name: Build with Gradle\n      run: ./gradlew build\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.idea\n"
  },
  {
    "path": "LICENSE",
    "content": "\n   Original work Copyright (c) 2005-2008, The Android Open Source Project\n   Modified work Copyright (c) 2013, rovo89 and Tungstwenty\n   Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team\n   Modified work Copyright (c) 2017, weishu\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "[![Download](https://api.bintray.com/packages/twsxtd/maven/epic/images/download.svg) ](https://bintray.com/twsxtd/maven/epic/_latestVersion)\n[![Join the chat at https://gitter.im/android-hacker/epic](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)   \n\n[中文文档入口](README_cn.md \"中文\")\n\nWhat is it?\n-----------\n\nEpic is the continuation of [Dexposed](https://github.com/alibaba/dexposed) on ART (Supports 5.0 ~ 11).\n\n> Dexposed is a powerful yet non-invasive runtime [AOP (Aspect-oriented Programming)](http://en.wikipedia.org/wiki/Aspect-oriented_programming) framework\nfor Android app development, based on the work of open-source [Xposed](https://github.com/rovo89/Xposed) [framework](https://github.com/rovo89/XposedBridge) project.\n>\n> The AOP of Dexposed is implemented purely non-invasive, without any annotation processor,\nweaver or bytecode rewriter. The integration is as simple as loading a small JNI library\nin just one line of code at the initialization phase of your app.\n>\n> Not only the code of your app, but also the code of Android framework that running in your\napp process can be hooked.\n\nEpic keeps the same API and all capability of Dexposed, you can do anything which is supported by Dexposed.\n\nTypical use-cases\n-----------------\n\n* Classic AOP programming\n* Instrumentation (for testing, performance monitoring and etc.)\n* Security audit (sensitive api check,Smash shell)\n* Just for fun :)\n\n\nIntegration\n-----------\n\nDirectly add epic aar to your project as compile libraries, Gradle dependency like following(jitpack):\n\n```groovy\ndependencies {\n    compile 'com.github.tiann:epic:0.11.2'\n}\n```\n\nEverything is ready.\n\nBasic usage\n-----------\n\nThere are three injection points for a given method: *before*, *after*, *origin*.\n\nExample 1: monitor the creation and destroy of java thread\n\n```java\nclass ThreadMethodHook extends XC_MethodHook{\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n        Thread t = (Thread) param.thisObject;\n        Log.i(TAG, \"thread:\" + t + \", started..\");\n    }\n\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n        Thread t = (Thread) param.thisObject;\n        Log.i(TAG, \"thread:\" + t + \", exit..\");\n    }\n}\n\nDexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n        Thread thread = (Thread) param.thisObject;\n        Class<?> clazz = thread.getClass();\n        if (clazz != Thread.class) {\n            Log.d(TAG, \"found class extend Thread:\" + clazz);\n            DexposedBridge.findAndHookMethod(clazz, \"run\", new ThreadMethodHook());\n        }\n        Log.d(TAG, \"Thread: \" + thread.getName() + \" class:\" + thread.getClass() +  \" is created.\");\n    }\n});\nDexposedBridge.findAndHookMethod(Thread.class, \"run\", new ThreadMethodHook());\n```\n\nExample 2: Intercept the dex loading behavior\n\n```java\nDexposedBridge.findAndHookMethod(DexFile.class, \"loadDex\", String.class, String.class, int.class, new XC_MethodHook() {\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n        String dex = (String) param.args[0];\n        String odex = (String) param.args[1];\n        Log.i(TAG, \"load dex, input:\" + dex + \", output:\" + odex);\n    }\n});\n```\n\nCheckout the `sample` project to find out more.\n\nSupport\n----------\n\nEpic supports ART thumb2 and arm64 architecture from Android 5.0 ~ 11. arm32, x86, x86_64 and mips are not supported now (Thus it cannot work on android emulator).\n\n\nKnown Issues\n-------------\n\n1. Short method (instruction less 8 bytes on thumb2 or less 16bytes in ARM64) are not supported.\n2. Fully inline methods are not supported.\n\nContribute\n----------\n\nWe are open to constructive contributions from the community, especially pull request\nand quality bug report. **Currently, the implementation for ART is not proved in large scale, we value your help to test or improve the implementation.**\n\nYou can clone this project, build and install the sample app, just make some click  in your device, if some bugs/crash occurs, please file an issue or a pull request, I would appreciate it :)\n\nThanks\n-------\n\n1. [Dexposed](https://github.com/alibaba/dexposed)\n2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)\n3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)\n4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)\n\nContact me\n----------\n\ntwsxtd@gmail.com\n\n[Join discussion](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) \n"
  },
  {
    "path": "README_cn.md",
    "content": "## 简介\n\nEpic 是一个在虚拟机层面、以 Java Method 为粒度的 **运行时** AOP Hook 框架。简单来说，Epic 就是 ART 上的 [Dexposed](https://github.com/alibaba/dexposed)（支持 Android 5.0 ~ 11）。它可以拦截本进程内部几乎任意的 Java 方法调用，可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。\n\nEpic 被 [VirtualXposed](https://github.com/android-hacker/VirtualXposed) 以及 [太极](https://www.coolapk.com/apk/me.weishu.exp) 使用，用来实现非 Root 场景下的 Xposed 功能，已经经过了相当广泛的验证。\n\n关于 Epic 的实现原理，可以参考 [本文](http://weishu.me/2017/11/23/dexposed-on-art/)。\n\n## 使用\n\n### 添加依赖\n\n在你项目的 build.gradle 中添加如下依赖（jitpack 仓库):\n\n```groovy\ndependencies {\n    compile 'com.github.tiann:epic:0.11.2'\n}\n```\n\n然后就可以使用了。\n\n\n### 几个例子\n\n1. 监控 Java 线程的创建和销毁：\n\n```java\nclass ThreadMethodHook extends XC_MethodHook{\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n        Thread t = (Thread) param.thisObject;\n        Log.i(TAG, \"thread:\" + t + \", started..\");\n    }\n\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n        Thread t = (Thread) param.thisObject;\n        Log.i(TAG, \"thread:\" + t + \", exit..\");\n    }\n}\n\nDexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n        Thread thread = (Thread) param.thisObject;\n        Class<?> clazz = thread.getClass();\n        if (clazz != Thread.class) {\n            Log.d(TAG, \"found class extend Thread:\" + clazz);\n            DexposedBridge.findAndHookMethod(clazz, \"run\", new ThreadMethodHook());\n        }\n        Log.d(TAG, \"Thread: \" + thread.getName() + \" class:\" + thread.getClass() +  \" is created.\");\n    }\n});\nDexposedBridge.findAndHookMethod(Thread.class, \"run\", new ThreadMethodHook());\n```\n\n以上代码拦截了 `Thread` 类以及 `Thread` 类所有子类的 `run`方法，在 `run` 方法开始执行和退出的时候进行拦截，就可以知道进程内部所有 Java 线程创建和销毁的时机；更进一步，你可以结合 Systrace 等工具，来生成整个过程的执行流程图，比如：\n\n<img src=\"http://7xp3xc.com1.z0.glb.clouddn.com/201601/1511840542774.png\" width=\"480\"/>\n\n2. 监控 dex 文件的加载：\n\n```java\nDexposedBridge.findAndHookMethod(DexFile.class, \"loadDex\", String.class, String.class, int.class, new XC_MethodHook() {\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n        String dex = (String) param.args[0];\n        String odex = (String) param.args[1];\n        Log.i(TAG, \"load dex, input:\" + dex + \", output:\" + odex);\n    }\n});\n```\n\n## 支持情况\n\n目前 Epic 支持 Android 5.0 ~ 11 的 Thumb-2/ARM64 指令集，arm32/x86/x86_64/mips/mips64 不支持。本项目被 [VirtualXposed](https://github.com/android-hacker/VirtualXposed) 和 [太极](http://taichi.cool) 以及大量企业级用户使用，经过了数千万用户的验证，已经被证明非常稳定。目前，手机 QQ 已经在产品中使用 Epic。\n\n\n## 已知问题\n\n1. 受限于 inline hook 本身，短方法 (Thumb-2 下指令小于 8 个字节，ARM64 小于 16 字节) 无法支持。\n2. 被完全内联的方法不支持。\n\n## 致谢\n\n1. [Dexposed](https://github.com/alibaba/dexposed)\n2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)\n3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)\n4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)\n\n\n## 联系我\n\ntwsxtd@gmail.com\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 30\n    defaultConfig {\n        applicationId \"me.weishu.epic.samples\"\n        minSdkVersion 21\n        targetSdkVersion 30\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n        externalNativeBuild {\n            ndkBuild {\n                abiFilters \"armeabi-v7a\", \"x86\"\n            }\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation project(\":library\")\n    implementation 'com.qmuiteam:qmui:1.0.4'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/me/weishu/epic/ExampleInstrumentedTest.java",
    "content": "//package me.weishu.epic;\n//\n//import android.content.Context;\n//import android.support.test.InstrumentationRegistry;\n//import android.support.test.runner.AndroidJUnit4;\n//\n//import org.junit.Test;\n//import org.junit.runner.RunWith;\n//\n//import static junit.framework.Assert.assertEquals;\n//import static org.junit.Assert.*;\n//\n///**\n// * Instrumentation test, which will execute on an Android device.\n// *\n// * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n// */\n//@RunWith(AndroidJUnit4.class)\n//public class ExampleInstrumentedTest {\n//    @Test\n//    public void useAppContext() throws Exception {\n//        // Context of the app under test.\n//        Context appContext = InstrumentationRegistry.getTargetContext();\n//\n//        assertEquals(\"me.weishu.epic\", appContext.getPackageName());\n//    }\n//}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"me.weishu.epic.samples\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:name=\".MainApplication\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/QMUI\">\n        <activity android:name=\".MainActivity\" android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/MainActivity.java",
    "content": "package me.weishu.epic.samples;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseExpandableListAdapter;\nimport android.widget.Button;\nimport android.widget.ExpandableListAdapter;\nimport android.widget.ExpandableListView;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport java.util.List;\n\nimport me.weishu.epic.samples.tests.TestCase;\nimport me.weishu.epic.samples.tests.TestManager;\nimport me.weishu.epic.samples.tests.TestSuite;\n\npublic class MainActivity extends Activity {\n\n    private static final String TAG = \"MainActivity\";\n\n    ExpandableListView listView;\n\n    List<TestSuite> allSuites;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.i(TAG, \"savedInstance:\" + savedInstanceState);\n        setContentView(R.layout.activity_main);\n        setTitle(\"Epic Test\");\n        listView = findViewById(R.id.list);\n        allSuites = TestManager.getInstance().getAllSuites();\n        ExpandableListAdapter adapter = new MyAdapter();\n        listView.setAdapter(adapter);\n\n    }\n\n    private class MyAdapter extends BaseExpandableListAdapter {\n\n        @Override\n        public int getGroupCount() {\n            return allSuites.size();\n        }\n\n        @Override\n        public int getChildrenCount(int groupPosition) {\n            return allSuites.get(groupPosition).getAllCases().size();\n        }\n\n        @Override\n        public TestSuite getGroup(int groupPosition) {\n            return allSuites.get(groupPosition);\n        }\n\n        @Override\n        public TestCase getChild(int groupPosition, int childPosition) {\n            return allSuites.get(groupPosition).getAllCases().get(childPosition);\n        }\n\n        @Override\n        public long getGroupId(int groupPosition) {\n            return 0;\n        }\n\n        @Override\n        public long getChildId(int groupPosition, int childPosition) {\n            return 0;\n        }\n\n        @Override\n        public boolean hasStableIds() {\n            return false;\n        }\n\n        @Override\n        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {\n            View parentView = View.inflate(parent.getContext(), R.layout.parent_layout, null);\n            TextView t = parentView.findViewById(R.id.text);\n            t.setText(getGroup(groupPosition).getName());\n\n            ImageView indicator = parentView.findViewById(R.id.indicator);\n            if (isExpanded) {\n                indicator.setImageResource(R.drawable.arrow_down);\n            } else {\n                indicator.setImageResource(R.drawable.arrow_up);\n            }\n            return parentView;\n        }\n\n        @Override\n        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {\n            final TestCase child = getChild(groupPosition, childPosition);\n\n            final View childView = View.inflate(parent.getContext(), R.layout.child_layout, null);\n            final TextView title = childView.findViewById(R.id.label);\n            title.setText(child.getName());\n\n            Button test = childView.findViewById(R.id.test);\n            test.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    child.test();\n                }\n            });\n\n            Button validate = childView.findViewById(R.id.validate);\n            validate.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    boolean validate = child.validate();\n                    final int color = v.getContext().getResources().getColor(validate ?\n                            android.R.color.holo_green_light : android.R.color.holo_red_light);\n                    childView.setBackgroundColor(color);\n                }\n            });\n\n            return childView;\n        }\n\n        @Override\n        public boolean isChildSelectable(int groupPosition, int childPosition) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/MainApplication.java",
    "content": "package me.weishu.epic.samples;\n\nimport android.app.Application;\nimport android.content.Context;\n\n/**\n * Created by weishu on 17/10/31.\n */\npublic class MainApplication extends Application {\n\n    private static Context sContext;\n\n    public static Context getAppContext() {\n        return sContext;\n    }\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n\n        sContext = base;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/CallingConventationTest.java",
    "content": "package me.weishu.epic.samples.tests;\n\nimport me.weishu.epic.samples.tests.arguments.ArgumentTarget;\n\n/**\n * Created by weishu on 17/11/15.\n */\npublic class CallingConventationTest {\n\n    public static void longParams1() {\n        // r0 = ArtMethod.this\n        // r1 = 4\n        // r2 = 8\n        // r3 = parital 12\n        // sp + 16 = partial 12\n        ArgumentTarget.arg3(4, 8, 12L);\n    }\n\n    public static void longParams2() {\n        // r0 = ArtMethod\n        // r1, r2 = 1024L\n        // r3 = bbcc1122\n        // sp + 16 = 0xffaa\n        ArgumentTarget.arg2(1024L, 0xffaabbcc1122L);\n    }\n\n    public static void longParams3() {\n        ArgumentTarget.arg2(123, 0xffaabbcc1122L);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/LogMethodHook.java",
    "content": "package me.weishu.epic.samples.tests;\n\nimport android.util.Log;\n\nimport java.util.Arrays;\n\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class LogMethodHook extends XC_MethodHook {\n\n    @Override\n    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n        super.beforeHookedMethod(param);\n        Log.i(getClass().getSimpleName(), \"beforeHookedMethod() called with: param = [\" + paramToString(param) + \"]\");\n    }\n\n    @Override\n    protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n        super.afterHookedMethod(param);\n        Log.i(getClass().getSimpleName(), \"afterHookedMethod() called with: param = [\" + paramToString(param) + \"]\");\n    }\n\n    private static String paramToString(MethodHookParam param) {\n        StringBuilder sb = new StringBuilder(param.getClass().getSimpleName()).append(\"{\");\n        sb.append(\"method = \").append(param.method.getName()).append(\", \");\n        sb.append(\"this = \").append(param.thisObject).append(\", \");\n        sb.append(\"args = \").append(Arrays.toString(param.args)).append(\",\");\n        sb.append(\"result = \").append(param.getResult()).append(\"}\");\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/TestCase.java",
    "content": "package me.weishu.epic.samples.tests;\n\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport me.weishu.epic.samples.MainApplication;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic abstract class TestCase {\n\n    private static final String TAG = \"TestCase\";\n\n    protected String name;\n    public TestCase(String name) {\n        this.name = name;\n    }\n\n    public abstract void test();\n\n    public abstract boolean predicate();\n\n    public boolean validate() {\n        boolean validate;\n        try {\n            validate = predicate();\n        } catch (Throwable e) {\n            validate = false;\n            Log.e(TAG, \"error happened:\", e);\n        }\n        if (!validate) {\n            Toast.makeText(MainApplication.getAppContext(), \"测试不通过。\", Toast.LENGTH_SHORT).show();\n        }\n        return validate;\n    }\n\n    public String getName() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/TestManager.java",
    "content": "package me.weishu.epic.samples.tests;\n\nimport android.os.Build;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport me.weishu.epic.samples.tests.arguments.ArgStatic0;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic44;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic444;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4444;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4448;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic448;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4484;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic48;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic484;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4844;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic488;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic4888;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic84;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic844;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8444;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8448;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic848;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8484;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8488;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic88;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic884;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8844;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8848;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic888;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8884;\nimport me.weishu.epic.samples.tests.arguments.ArgStatic8888;\nimport me.weishu.epic.samples.tests.custom.Case1;\nimport me.weishu.epic.samples.tests.custom.Case10_Default_Constructor;\nimport me.weishu.epic.samples.tests.custom.Case11_SuspendAll;\nimport me.weishu.epic.samples.tests.custom.Case12_MultiCallback;\nimport me.weishu.epic.samples.tests.custom.Case13_FastNative;\nimport me.weishu.epic.samples.tests.custom.Case14_GC;\nimport me.weishu.epic.samples.tests.custom.Case17_SameMethod;\nimport me.weishu.epic.samples.tests.custom.Case18_returnConst;\nimport me.weishu.epic.samples.tests.custom.Case2;\nimport me.weishu.epic.samples.tests.custom.Case3;\nimport me.weishu.epic.samples.tests.custom.Case4;\nimport me.weishu.epic.samples.tests.custom.Case5;\nimport me.weishu.epic.samples.tests.custom.Case6;\nimport me.weishu.epic.samples.tests.custom.Case7;\nimport me.weishu.epic.samples.tests.custom.Case8_Activity_onCreate;\nimport me.weishu.epic.samples.tests.custom.Case9_ThreadMonitor;\nimport me.weishu.epic.samples.tests.custom.CaseManager;\nimport me.weishu.epic.samples.tests.invoketype.InvokeConstructor;\nimport me.weishu.epic.samples.tests.returntype.BooleanType;\nimport me.weishu.epic.samples.tests.returntype.ByteType;\nimport me.weishu.epic.samples.tests.returntype.CharType;\nimport me.weishu.epic.samples.tests.returntype.CustomType;\nimport me.weishu.epic.samples.tests.returntype.DoubleType;\nimport me.weishu.epic.samples.tests.returntype.FloatType;\nimport me.weishu.epic.samples.tests.returntype.IntType;\nimport me.weishu.epic.samples.tests.returntype.LongType;\nimport me.weishu.epic.samples.tests.returntype.ShortType;\nimport me.weishu.epic.samples.tests.returntype.StringArrayType;\nimport me.weishu.epic.samples.tests.returntype.StringType;\nimport me.weishu.epic.samples.tests.returntype.VoidType;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class TestManager {\n    private static TestManager sManager = new TestManager();\n\n    private List<TestSuite> suites = new ArrayList<>();\n\n    private TestManager() {\n        initAllSuites();\n    }\n\n    public static TestManager getInstance() {\n        return sManager;\n    }\n\n    public void addSuite(TestSuite suite) {\n        suites.add(suite);\n    }\n\n    public List<TestSuite> getAllSuites() {\n        return suites;\n    }\n\n    private void initAllSuites() {\n        TestSuite returnType = new TestSuite(\"返回值测试\");\n\n        returnType.addCase(new VoidType());\n        returnType.addCase(new ByteType());\n        returnType.addCase(new ShortType());\n        returnType.addCase(new CharType());\n        returnType.addCase(new IntType());\n        returnType.addCase(new FloatType());\n        returnType.addCase(new LongType());\n        returnType.addCase(new DoubleType());\n        returnType.addCase(new BooleanType());\n        returnType.addCase(new StringType());\n        returnType.addCase(new StringArrayType());\n        returnType.addCase(new CustomType());\n\n        addSuite(returnType);\n\n\n        TestSuite arguments = new TestSuite(\"参数测试\");\n\n        arguments.addCase(new ArgStatic0());\n        arguments.addCase(new ArgStatic4());\n        arguments.addCase(new ArgStatic8());\n\n        arguments.addCase(new ArgStatic44());\n        arguments.addCase(new ArgStatic48());\n        arguments.addCase(new ArgStatic84());\n        arguments.addCase(new ArgStatic88());\n\n        arguments.addCase(new ArgStatic444());\n        arguments.addCase(new ArgStatic448());\n        arguments.addCase(new ArgStatic484());\n        arguments.addCase(new ArgStatic844());\n        arguments.addCase(new ArgStatic488());\n        arguments.addCase(new ArgStatic848());\n        arguments.addCase(new ArgStatic884());\n        arguments.addCase(new ArgStatic888());\n\n        arguments.addCase(new ArgStatic4444());\n        arguments.addCase(new ArgStatic4448());\n        arguments.addCase(new ArgStatic4484());\n        arguments.addCase(new ArgStatic4844());\n        arguments.addCase(new ArgStatic8444());\n\n        arguments.addCase(new ArgStatic8844());\n        arguments.addCase(new ArgStatic8484());\n        arguments.addCase(new ArgStatic8448());\n        arguments.addCase(new ArgStatic8884());\n        arguments.addCase(new ArgStatic8848());\n        arguments.addCase(new ArgStatic8488());\n        arguments.addCase(new ArgStatic4888());\n        arguments.addCase(new ArgStatic8888());\n\n        addSuite(arguments);\n\n\n        TestSuite invokeType = new TestSuite(\"调用类型\");\n        invokeType.addCase(new InvokeConstructor());\n\n        addSuite(invokeType);\n\n        TestSuite custom = new TestSuite(\"自定义\");\n        CaseManager.getInstance().getCase(Case1.class);\n        CaseManager.getInstance().getCase(Case2.class);\n        CaseManager.getInstance().getCase(Case3.class);\n        CaseManager.getInstance().getCase(Case4.class);\n        CaseManager.getInstance().getCase(Case4.class);\n        CaseManager.getInstance().getCase(Case5.class);\n        CaseManager.getInstance().getCase(Case6.class);\n        CaseManager.getInstance().getCase(Case7.class);\n        CaseManager.getInstance().getCase(Case8_Activity_onCreate.class);\n        CaseManager.getInstance().getCase(Case9_ThreadMonitor.class);\n        CaseManager.getInstance().getCase(Case10_Default_Constructor.class);\n        CaseManager.getInstance().getCase(Case12_MultiCallback.class);\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            CaseManager.getInstance().getCase(Case11_SuspendAll.class);\n            CaseManager.getInstance().getCase(Case14_GC.class);\n            // CaseManager.getInstance().getCase(Case15_StopJit.class);\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            CaseManager.getInstance().getCase(Case13_FastNative.class);\n        }\n\n        // CaseManager.getInstance().getCase(Case16_SameEntry.class);\n        CaseManager.getInstance().getCase(Case17_SameMethod.class);\n        CaseManager.getInstance().getCase(Case18_returnConst.class);\n\n        final Set<Class<?>> cases = CaseManager.getInstance().getCases();\n        for (final Class<?> aCase : cases) {\n            custom.addCase(new TestCase(aCase.getSimpleName()) {\n                @Override\n                public void test() {\n                    CaseManager.getInstance().getCase(aCase).hook();\n                }\n\n                @Override\n                public boolean predicate() {\n                    return CaseManager.getInstance().getCase(aCase).validate();\n                }\n            });\n        }\n        addSuite(custom);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/TestSuite.java",
    "content": "package me.weishu.epic.samples.tests;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class TestSuite {\n    private String name;\n    private List<TestCase> cases = new ArrayList<>();\n\n    public TestSuite(String name) {\n        this.name = name;\n    }\n\n    public void addCase(TestCase caze) {\n        cases.add(caze);\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public List<TestCase> getAllCases() {\n        return cases;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/AbsArgStaticCase.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Random;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic abstract class AbsArgStaticCase extends TestCase {\n\n    private final String TAG = getClass().getSimpleName();\n\n    final long[] args;\n\n    private Random r = new Random();\n\n    public AbsArgStaticCase() {\n        super(null);\n        args = new long[8];\n        for (int i = 0; i < args.length; i++) {\n            args[i] = 0L;\n        }\n\n        name = getClass().getSimpleName();\n    }\n\n    @Override\n    public void test() {\n        DexposedBridge.hookMethod(getTargetMethod(), new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                int length = param.args.length;\n                if (length > 0) {\n                    for (int i = 0; i < length; i++) {\n                        long tmp = ((Number) param.args[i]).longValue();\n                        args[i] = tmp;\n                    }\n                }\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        long[] arguments = getArguments();\n        Log.i(TAG, \"call arguments: \" + Arrays.toString(toHex(arguments)));\n\n        makeCall(arguments);\n\n        boolean ret = true;\n\n        int length = getArgumentNumber();\n        for (int i = 0; i < length; i++) {\n            if (arguments[i] != args[i]) {\n                ret = false;\n                Log.i(TAG, \"hooked arguments: \" + Arrays.toString(toHex(args)));\n            }\n        }\n        return ret;\n    }\n\n    private long[] getArguments() {\n        final int argumentNumber = getArgumentNumber();\n        long[] arguments = new long[argumentNumber];\n        for (int i = 0; i < arguments.length; i++) {\n            arguments[i] = nextLong();\n        }\n        return arguments;\n    }\n\n    protected Method getTargetMethod() {\n        final int argumentNumber = getArgumentNumber();\n        String methodName = \"arg\" + argumentNumber;\n\n        Method method = XposedHelpers.findMethodExact(ArgumentTarget.class, methodName, getParamsSignature());\n        Log.i(TAG, \"find target Method:\" + method);\n        return method;\n    }\n\n    protected int getArgumentNumber() {\n        return getParamsSignature().length;\n    }\n\n    protected void makeCall(long... args) {\n        Log.i(TAG, getName() + \" make call with arguments:\" + Arrays.toString(args));\n    }\n\n    private long nextLong() {\n        int ret = r.nextInt();\n        return ret;\n    }\n\n    private Class<?>[] getParamsSignature() {\n        String className = getClass().getSimpleName();\n        String signature = className.substring(\"ArgStatic\".length());\n        final int length = signature.length();\n        Class<?>[] clazz = new Class<?>[length];\n        for (int i = 0; i < length; i++) {\n            final char c = signature.charAt(i);\n            if (c == '4') {\n                clazz[i] = int.class;\n            } else if (c == '8') {\n                clazz[i] = long.class;\n            } else {\n                throw new RuntimeException(\"Unknown signature!!\");\n            }\n        }\n        return clazz;\n    }\n\n    private String[] toHex(long[] value) {\n        String[] ret = new String[value.length];\n\n        for (int i = 0; i < value.length; i++) {\n            ret[i] = Long.toHexString(value[i]);\n        }\n        return ret;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic0.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic0 extends TestCase {\n\n    boolean beforeCalled = false;\n    boolean afterCalled = false;\n    public ArgStatic0() {\n        super(\"ArgStatic0\");\n    }\n\n    @Override\n    public void test() {\n        DexposedBridge.findAndHookMethod(ArgumentTarget.class, \"arg0\", new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                beforeCalled = true;\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                afterCalled = true;\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n\n        ArgumentTarget.arg0();\n\n        return beforeCalled && afterCalled;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic4 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg1((int)args[0]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic44.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic44 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg2((int) args[0], (int) args[1]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic444.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\nimport android.util.Log;\n\nimport java.util.Arrays;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic444 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        Log.i(\"mylog\", \"make call args:\" + Arrays.toString(args));\n        ArgumentTarget.arg3((int) args[0], (int) args[1], (int) args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4444.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4444 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], (int)args[1], (int)args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4448.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4448 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int) args[0], (int)args[1], (int)args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic448.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic448 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3((int) args[0], (int) args[1], args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4484.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4484 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], (int) args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4488.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4488 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic48.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic48 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg2((int) args[0], args[1]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic484.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic484 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3((int) args[0], args[1], (int) args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4844.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4844 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int) args[0], args[1], (int)args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4848.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4848 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], args[1], (int)args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic488.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic488 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3((int) args[0], args[1], args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4884.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4884 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], args[1], args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4888.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic4888 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4((int)args[0], args[1], args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic8 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg1(args[0]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic84.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic84 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg2(args[0], (int)args[1]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic844.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic844 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3(args[0], (int) args[1], (int) args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8444.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8444 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8448.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8448 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic848.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class ArgStatic848 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3(args[0], (int) args[1], args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8484.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8484 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], (int)args[1], args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8488.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8488 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], (int)args[1], args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic88.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n/**\n * @author weishu\n * @date 17/11/14.\n */\n\npublic class ArgStatic88 extends AbsArgStaticCase {\n\n    @Override\n    protected void makeCall(long... args) {\n        super.makeCall(args);\n        ArgumentTarget.arg2(args[0], args[1]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic884.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic884 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3(args[0], args[1], (int) args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8844.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8844 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], args[1], (int)args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8848.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8848 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], args[1], (int)args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic888.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic888 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg3(args[0], args[1], args[2]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8884.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8884 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], args[1], args[2], (int)args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8888.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\n\n/**\n * Created by weishu on 17/11/14.\n */\npublic class ArgStatic8888 extends AbsArgStaticCase {\n    @Override\n    protected void makeCall(long... args) {\n        ArgumentTarget.arg4(args[0], args[1], args[2], args[3]);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgumentTarget.java",
    "content": "package me.weishu.epic.samples.tests.arguments;\n\nimport android.util.Log;\n\n/**\n * @author weishu\n * @date 17/11/13.\n */\n\npublic class ArgumentTarget {\n\n    private static final String TAG = \"ArgumentTarget\";\n\n    //region ---------------static---------------\n\n    public static void arg0() {\n        Log.i(TAG, \"arg0() called\");\n    }\n\n    public static void arg1(int v1) {\n        Log.i(TAG, \"arg1() called with: v1 = [\" + v1 + \"]\");\n    }\n\n    public static void arg1(long v1) {\n        Log.i(TAG, \"arg1() called with: v1 = [\" + v1 + \"]\");\n    }\n\n    public static void arg2(int v1, int v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public static void arg2(int v1, long v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public static void arg2(long v1, int v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public static void arg2(long v1, long v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public static void arg3(int v1, int v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(long v1, int v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(int v1, long v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(int v1, int v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(long v1, long v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(long v1, int v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(int v1, long v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg3(long v1, long v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public static void arg4(int v1, int v2, int v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, int v2, int v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, long v2, int v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, int v2, long v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, int v2, int v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, long v2, int v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, int v2, long v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, int v2, int v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, long v2, long v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, long v2, int v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, int v2, long v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(int v1, long v2, long v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, int v2, long v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, long v2, int v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, long v2, long v3, int v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg4(long v1, long v2, long v3, long v4) {\n        Log.i(TAG, \"arg4() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"]\");\n    }\n\n    public static void arg5(int v1, int v2, int v3, int v4, int v5) {\n        Log.i(TAG, \"arg5() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"]\");\n    }\n\n    public static void arg5(long v1, long v2, long v3, long v4, long v5) {\n        Log.i(TAG, \"arg5() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"]\");\n    }\n\n    public static void arg6(int v1, int v2, int v3, int v4, int v5, int v6) {\n        Log.i(TAG, \"arg6() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"], v6 = [\" + v6 + \"]\");\n    }\n\n    public static void arg6(long v1, long v2, long v3, long v4, long v5, long v6) {\n        Log.i(TAG, \"arg6() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"], v6 = [\" + v6 + \"]\");\n    }\n\n    public static void arg7(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {\n        Log.i(TAG, \"arg7() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"], v6 = [\" + v6 + \"], v7 = [\" + v7 + \"]\");\n    }\n\n    public static void arg7(long v1, long v2, long v3, long v4, long v5, long v6, long v7) {\n        Log.i(TAG, \"arg7() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"], v4 = [\" + v4 + \"], v5 = [\" + v5 + \"], v6 = [\" + v6 + \"], v7 = [\" + v7 + \"]\");\n    }\n\n    //endregion\n\n    //region ---------------non static---------------\n\n    public void iarg0() {\n        Log.i(TAG, \"arg0() called\");\n    }\n\n    public void iarg1(int v1) {\n        Log.i(TAG, \"arg1() called with: v1 = [\" + v1 + \"]\");\n    }\n\n    public void iarg1(long v1) {\n        Log.i(TAG, \"arg1() called with: v1 = [\" + v1 + \"]\");\n    }\n\n    public void iarg2(int v1, int v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public void iarg2(int v1, long v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public void iarg2(long v1, long v2) {\n        Log.i(TAG, \"arg2() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"]\");\n    }\n\n    public void iarg3(int v1, int v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(long v1, int v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(int v1, long v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(int v1, int v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(long v1, long v2, int v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(long v1, int v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(int v1, long v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    public void iarg3(long v1, long v2, long v3) {\n        Log.i(TAG, \"arg3() called with: v1 = [\" + v1 + \"], v2 = [\" + v2 + \"], v3 = [\" + v3 + \"]\");\n    }\n\n    //endregion\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic interface Case {\n    void hook();\n\n    boolean validate(Object... args);\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case1.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case1 implements Case {\n\n    static Field sThread$target;\n    static {\n        try {\n            // Thread#target\n            sThread$target = Thread.class.getDeclaredField(\"target\");\n            sThread$target.setAccessible(true);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static final String TAG = \"Case1\";\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(Thread.class, \"run\", new XC_MethodHook() {\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.i(TAG, \"afterHookedMethod: \" + param.thisObject);\n                Thread thread = (Thread) param.thisObject;\n                Runnable target = (Runnable) sThread$target.get(thread);\n                // start|threadName|priority|class|startTime|stacktrace\n                Log.i(TAG, \"runnable target:\" + target);\n            }\n\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.i(TAG, \"beforeHookedMethod: \" + param.thisObject);\n\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object...args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                Log.i(TAG, \"before sleep\");\n                SystemClock.sleep(3000);\n                Log.i(TAG, \"after sleep\");\n            }\n        });\n        t1.start();\n\n        class MyThread extends Thread {\n            @Override\n            public void run() {\n                super.run();\n                Log.i(TAG, \"before sleep\");\n                SystemClock.sleep(3000);\n                Log.i(TAG, \"after sleep\");\n            }\n        }\n\n        Thread t2 = new MyThread();\n        t2.start();\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case10_Default_Constructor.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Constructor;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport de.robv.android.xposed.XposedHelpers;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class Case10_Default_Constructor implements Case {\n\n    private static final String TAG = \"Case10_Default_Constructor\";\n\n    @Override\n    public void hook() {\n        Constructor<?> constructor = XposedHelpers.findConstructorExact(Target.class);\n        DexposedBridge.hookMethod(constructor, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.i(TAG, \"Target constructor called\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        new Target();\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case11_SuspendAll.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.os.Build;\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\n\nimport me.weishu.epic.art.EpicNative;\n\n/**\n * Created by weishu on 17/11/18.\n */\n\npublic class Case11_SuspendAll implements Case {\n\n    private static final String TAG = \"Case11_SuspendAll\";\n\n    @Override\n    public void hook() {\n        Executor executor = Executors.newCachedThreadPool();\n        for (int i = 0; i < 5; i++) {\n            executor.execute(new Runnable() {\n                @Override\n                public void run() {\n                    int j = 0;\n                    while (true) {\n                        Log.i(TAG, \"I am:\" + Thread.currentThread().getName() + \", count:\" + (j++));\n                        SystemClock.sleep(1000);\n                        if (j > 3) {\n                            break;\n                        }\n                    }\n                }\n            });\n        }\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        if (Build.VERSION.SDK_INT < 24) {\n            Log.i(TAG, \"resume/suspend only support Android N+ now.\");\n            return false;\n        }\n        long cookie = EpicNative.suspendAll();\n        for (Thread thread : Thread.getAllStackTraces().keySet()) {\n            Log.i(TAG, thread.getName() + \" status:\" + thread.getState());\n        }\n        EpicNative.resumeAll(cookie);\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case12_MultiCallback.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/21.\n */\npublic class Case12_MultiCallback implements Case {\n    private static final String TAG = \"Case12_MultiCallback\";\n\n    int beforeCount = 0;\n    int afterCount = 0;\n\n    XC_MethodHook callback1 = new XC_MethodHook() {\n        @Override\n        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n            super.beforeHookedMethod(param);\n            Log.i(TAG, \"beforeHookMethod 1\");\n            beforeCount++;\n        }\n\n        @Override\n        protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n            super.afterHookedMethod(param);\n            Log.i(TAG, \"afterHookMethod 1\");\n            afterCount++;\n        }\n    };\n\n    XC_MethodHook callback2 = new XC_MethodHook() {\n        @Override\n        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n            super.beforeHookedMethod(param);\n            Log.i(TAG, \"beforeHookMethod 2 lalala \");\n            beforeCount++;\n        }\n\n        @Override\n        protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n            super.afterHookedMethod(param);\n            Log.i(TAG, \"afterHookMethod 2 lalala\");\n            afterCount++;\n        }\n    };\n\n    XC_MethodHook callback3 = new XC_MethodHook() {\n        @Override\n        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n            super.beforeHookedMethod(param);\n            Log.i(TAG, \"beforeHookMethod 3 zezeze\");\n            beforeCount++;\n        }\n\n        @Override\n        protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n            super.afterHookedMethod(param);\n            Log.i(TAG, \"afterHookMethod 3 zezeze\");\n            afterCount++;\n        }\n    };\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(Target.class, \"test1\", Object.class, int.class, callback1);\n        DexposedBridge.findAndHookMethod(Target.class, \"test1\", Object.class, int.class, callback2);\n        DexposedBridge.findAndHookMethod(Target.class, \"test1\", Object.class, int.class, callback3);\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        new Target().test1(\"123\", 1);\n        boolean ret = beforeCount == 3 && afterCount == 3;\n        // reset.\n        beforeCount = 0;\n        afterCount = 0;\n\n        return ret;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case13_FastNative.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Method;\n\nimport de.robv.android.xposed.XposedHelpers;\n\n/**\n * Created by weishu on 17/12/13.\n */\n\npublic class Case13_FastNative implements Case {\n\n    @Override\n    public void hook() {\n//        DexposedBridge.findAndHookMethod(Target.class, \"longRunMethod\", new LogMethodHook());\n        final Method invoke = XposedHelpers.findMethodExact(Method.class, \"invoke\", Object.class, Object[].class);\n        Log.i(\"mylog\", \"invole: \" + invoke);\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        new Target().longRunMethod();\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case14_GC.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.returntype.ReturnTypeTarget;\n\n/**\n * Created by weishu on 17/12/13.\n */\n\npublic class Case14_GC implements Case {\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnString\", String.class, new LogMethodHook());\n        System.gc();\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        return \"123\".equals(ReturnTypeTarget.returnString(new String(\"123\")));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case15_StopJit.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport me.weishu.epic.art.EpicNative;\n\n/**\n * Created by weishu on 17/12/13.\n */\n\npublic class Case15_StopJit implements Case {\n    long cookie;\n    @Override\n    public void hook() {\n        cookie = EpicNative.stopJit();\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        EpicNative.startJit(cookie);\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case16_SameEntry.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport com.taobao.android.dexposed.utility.Debug;\n\nimport java.lang.reflect.Method;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.method.ArtMethod;\n\n/**\n * Created by weishu on 18/1/8.\n */\n\npublic class Case16_SameEntry implements Case {\n    private static final String TAG = \"Case16_SameEntry\";\n\n    @Override\n    public void hook() {\n        final Method add = XposedHelpers.findMethodExact(Target.class, \"add\", int.class, int.class);\n        final Method plus = XposedHelpers.findMethodExact(Target.class, \"plus\", int.class, int.class);\n\n        ArtMethod artMethod3 = ArtMethod.of(add);\n        ArtMethod artMethod4 = ArtMethod.of(plus);\n\n        Log.i(TAG, \"plus: addr: \" + Debug.addrHex(artMethod3.getAddress()) + \", entry:\"\n                + Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));\n        Log.i(TAG, \"milus: addr: \" + Debug.addrHex(artMethod4.getAddress()) + \", entry:\"\n                + Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));\n\n        DexposedBridge.hookMethod(add, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.d(TAG, \"beforeHookedMethod() called with: param = [\" + param + \"]\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.d(TAG, \"afterHookedMethod() called with: param = [\" + param + \"]\");\n            }\n        });\n\n        DexposedBridge.hookMethod(plus, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.d(TAG, \"beforeHookedMethod() called with: param = [\" + param + \"]\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.d(TAG, \"afterHookedMethod() called with: param = [\" + param + \"]\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        Target target = new Target();\n        int add = target.add(1, 2);\n        Log.i(TAG, \"1 + 2 = \" + add);\n        int plus = target.plus(3, 4);\n        Log.i(TAG, \"3 + 4 = \" + plus);\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case17_SameMethod.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 18/1/11.\n */\n\npublic class Case17_SameMethod implements Case {\n    private static final String TAG = \"Case17_SameMethod\";\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(Target.class, \"add\", int.class, int.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.d(TAG, \"beforeHookedMethod() called with: param = [\" + param + \"]\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.d(TAG, \"afterHookedMethod() called with: param = [\" + param + \"]\");\n            }\n        });\n\n        DexposedBridge.findAndHookMethod(Target.class, \"add\", int.class, int.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.d(TAG, \"beforeHookedMethod2() called with: param = [\" + param + \"]\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.d(TAG, \"afterHookedMethod2() called with: param = [\" + param + \"]\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        new Target().add(1, 2);\n        new Target().add(3, 4);\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case18_returnConst.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport java.util.Arrays;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 18/1/11.\n */\npublic class Case18_returnConst implements Case {\n    private static final String TAG = \"Case18_returnConst\";\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(Target.class, \"returnConst\", int.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                param.setResult(124);\n                Log.d(TAG, \"beforeHookedMethod() called with: param = [\" + Arrays.toString(param.args) + \"]\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.d(TAG, \"afterHookedMethod() called with: param = [\" + Arrays.toString(param.args) + \"]\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n\n        int i = new Target().returnConst(123);\n        Log.i(TAG, \"return : \" + i);\n        return (i == 124);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case2.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case2 implements Case{\n    private static final String TAG = \"Case2\";\n\n    @Override\n    public void hook() {\n//        DexposedBridge.findAndHookMethod(Target.class, \"add\", int.class, int.class, new XC_MethodHook() {\n//            @Override\n//            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n//                Log.i(TAG, \"before add hooked:\" + Arrays.toString(param.args));\n//                param.setResult(4);\n//                super.beforeHookedMethod(param);\n//            }\n//        });\n//\n//        DexposedBridge.findAndHookMethod(Target.class, \"test4\", int.class, new XC_MethodHook() {\n//            @Override\n//            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n//                super.beforeHookedMethod(param);\n//                Log.i(\"mylog\", \"before\", new RuntimeException(\"stack\"));\n//                Log.i(\"mylog\", \"this:\" + param.thisObject);\n//                Log.i(\"mylog\", \"method:\" + param.method);\n//                Log.i(\"mylog\", \"args:\" + Arrays.toString(param.args));\n//\n//            }\n//\n//            @Override\n//            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n//                super.afterHookedMethod(param);\n//                Log.i(\"mylog\", \"after\");\n//            }\n//        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n//        Log.i(TAG, \"1 + 2: \" + Target.add(1, 2));\n//        return Target.add(1, 2) == 4;\n        Target.validate();\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case3.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case3 implements Case {\n    private static final String TAG = \"Case3\";\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(System.class, \"currentTimeMillis\", new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.i(\"mylog\", \"before currentTimeMillis\");\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.i(\"mylog\", \"after currentTimeMillis\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        Log.i(TAG, \"currentTimeMillis: \" + System.currentTimeMillis());\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case4.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case4 implements Case {\n    private static final String TAG = \"Case4\";\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(TextUtils.class, \"equals\", CharSequence.class, CharSequence.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                Log.i(TAG, \"beforeHookedMethod: this:\" + param.thisObject, new RuntimeException(\"stack\"));\n                Log.i(TAG, \"beforeHookedMethod: String1:\" + param.args[0]);\n                Log.i(TAG, \"beforeHookedMethod: String2:\" + param.args[1]);\n\n                param.setResult(false);\n\n                super.beforeHookedMethod(param);\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        String a1 = new String(\"123\");\n        String a2 = new String(\"123\");\n\n        Log.i(TAG, \" '123' equals '123' ? \" + TextUtils.equals(a1, a2));\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case5.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\nimport android.widget.TextView;\n\nimport com.taobao.android.dexposed.utility.Unsafe;\n\nimport java.lang.reflect.Method;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport me.weishu.epic.art.EpicNative;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case5 implements Case {\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(TextView.class, \"setPadding\", int.class, int.class, int.class, int.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                if (param.thisObject != null) {\n                    Log.i(\"mylog\", \"this:\" + Long.toHexString(Unsafe.getObjectAddress(param.thisObject)));\n                }\n                if (param.method != null) {\n                    Log.i(\"mylog\", \"mehod:\" + Long.toHexString(EpicNative.getMethodAddress((Method) param.method)));\n                }\n                if (param.args != null) {\n                    for (Object arg : param.args) {\n                        Log.i(\"mylog\", \"param:\" + arg);\n                        if (arg != null) {\n                            Log.i(\"mylog\", \"<\" + arg.getClass() + \"> : 0x\" +\n                                    Long.toHexString(Unsafe.getObjectAddress(arg)) + \", value: \" + arg);\n                        } else {\n                            Log.i(\"mylog\", \"param: null\");\n                        }\n                    }\n                }\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                Log.i(\"mylog\", \"after\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case6.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Unsafe;\n\nimport java.lang.reflect.Method;\n\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.EpicNative;\nimport me.weishu.epic.art.method.ArtMethod;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class Case6 implements Case {\n\n    private static final String TAG = \"Case6\";\n\n    @Override\n    public void hook() {\n        Object test = new Object();\n        Log.i(TAG, \"test object:\" + test);\n\n        long testAddr = Unsafe.getObjectAddress(test);\n        Log.i(TAG, \"test object address :\" + testAddr);\n        Log.i(TAG, \"test object :\" + EpicNative.getObject(XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\"), testAddr));\n\n        // Log.i(TAG, \"object:\" + EpicNative.getObject())\n        final Method nanoTime = XposedHelpers.findMethodExact(System.class, \"nanoTime\");\n        final Method uptimeMillis = XposedHelpers.findMethodExact(SystemClock.class, \"uptimeMillis\");\n        final Method map = XposedHelpers.findMethodExact(Target.class, \"test1\", Object.class, int.class);\n        final Method malloc = XposedHelpers.findMethodExact(Target.class, \"test3\", Object.class, int.class);\n\n        ArtMethod artMethod1 = ArtMethod.of(nanoTime);\n        ArtMethod artMethod2 = ArtMethod.of(uptimeMillis);\n\n        ArtMethod artMethod3 = ArtMethod.of(map);\n        ArtMethod artMethod4 = ArtMethod.of(malloc);\n\n        Log.i(TAG, \"nanoTime: addr: 0x\" + artMethod1.getAddress() + \", entry:\" + Debug.addrHex(artMethod1.getEntryPointFromQuickCompiledCode()));\n        Log.i(TAG, \"uptimeMills: addr: 0x\" + artMethod2.getAddress() + \", entry:\" + Debug.addrHex(artMethod2.getEntryPointFromQuickCompiledCode()));\n        Log.i(TAG, \"map : addr: 0x\" + artMethod3.getAddress() + \", entry:\" + Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));\n        Log.i(TAG, \"malloc: addr: 0x\" + artMethod4.getAddress() + \", entry:\" + Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case7.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport java.util.Arrays;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/8.\n */\n\npublic class Case7 implements Case {\n\n    private static final String TAG = \"Case7\";\n\n    @Override\n    public void hook() {\n        Log.i(TAG, \"hook test1\");\n        DexposedBridge.findAndHookMethod(Target.class, \"test1\", Object.class, int.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                Log.i(TAG, \"before add hooked:\" + Arrays.toString(param.args));\n                param.setResult(4);\n                super.beforeHookedMethod(param);\n            }\n        });\n\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        Target t = new Target();\n        t.test1(t, 123);\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case8_Activity_onCreate.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport me.weishu.epic.samples.MainActivity;\n\n/**\n * Created by weishu on 17/11/9.\n */\n\npublic class Case8_Activity_onCreate implements Case {\n    private static final String TAG = \"Case8_Activity_onCreate\";\n\n    @Override\n    public void hook() {\n        DexposedBridge.findAndHookMethod(MainActivity.class, \"onCreate\", Bundle.class, new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                Log.i(TAG, \"before hooked\");\n                if (param.args[0] == null) {\n                    param.args[0] = new Bundle();\n                }\n                Bundle b = (Bundle) param.args[0];\n                Log.i(TAG, \"bundle: \" + param.args[0]);\n                b.putString(\"hehe\", \"hacked\");\n            }\n        });\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Case9_ThreadMonitor.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.util.Log;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\n\n/**\n * Created by weishu on 17/11/10.\n */\npublic class Case9_ThreadMonitor implements Case {\n\n    private static final String TAG = \"Case9_ThreadMonitor\";\n\n    @Override\n    public void hook() {\n        try {\n\n            class ThreadMethodHook extends XC_MethodHook {\n                @Override\n                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                    super.beforeHookedMethod(param);\n                    Thread t = (Thread) param.thisObject;\n                    Log.i(TAG, \"thread:\" + t + \", started..\");\n                }\n\n                @Override\n                protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                    super.afterHookedMethod(param);\n                    Thread t = (Thread) param.thisObject;\n                    Log.i(TAG, \"thread:\" + t + \", exit..\");\n                }\n            }\n\n            DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {\n                @Override\n                protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                    super.afterHookedMethod(param);\n                    Thread thread = (Thread) param.thisObject;\n                    Class<?> clazz = thread.getClass();\n                    if (clazz != Thread.class) {\n                        Log.d(TAG, \"found class extend Thread:\" + clazz);\n                        DexposedBridge.findAndHookMethod(clazz, \"run\", new ThreadMethodHook());\n                    }\n                    Log.d(TAG, \"Thread: \" + thread.getName() + \" class:\" + thread.getClass() +  \" is created.\");\n                }\n            });\n            DexposedBridge.findAndHookMethod(Thread.class, \"run\", new ThreadMethodHook());\n\n        } catch (Throwable e) {\n            Log.e(TAG, \"hook failed\", e);\n        }\n    }\n\n    @Override\n    public boolean validate(Object... args) {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                Log.i(TAG, \"I am started..\");\n            }\n        }).start();\n\n        new MyThread().start();\n\n        final ExecutorService executorService = Executors.newCachedThreadPool();\n        for (int i = 0; i < 4; i++) {\n            // final int num = i;\n            executorService.execute(new Runnable() {\n                @Override\n                public void run() {\n                    Log.i(TAG, \" lalala\");\n                }\n            });\n        }\n        return true;\n    }\n\n    static class MyThread extends Thread {\n        @Override\n        public void run() {\n            Log.i(TAG, \"dang dang dang..\");\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/CaseManager.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created by weishu on 17/11/6.\n */\n\npublic class CaseManager {\n\n    private static volatile CaseManager INSTANCE = new CaseManager();\n\n    private Map<Class<?>, Case> caseMap = new ConcurrentHashMap<>();\n\n    public static synchronized CaseManager getInstance() {\n        return INSTANCE;\n    }\n\n    public synchronized Case getCase(Class<?> clazz) {\n        Case caze = caseMap.get(clazz);\n        if (caze != null) {\n            return caze;\n        } else {\n            try {\n                caze = (Case) clazz.newInstance();\n                caseMap.put(clazz, caze);\n            } catch (Throwable e) {\n                throw new RuntimeException(\"can not get case !!\", e);\n            }\n        }\n        return caze;\n    }\n\n    public synchronized Set<Class<?>> getCases() {\n        return caseMap.keySet();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/custom/Target.java",
    "content": "package me.weishu.epic.samples.tests.custom;\n\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport java.lang.reflect.Method;\n\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.method.ArtMethod;\n\n/**\n * Created by weishu on 17/11/3.\n */\n\npublic class Target {\n\n    public Target() {\n    }\n\n    public int test1(Object a, int b) {\n        Log.i(\"mylog\", \"test1, arg1: \" + a + \" , arg2:\" + b);\n        new Runnable() {\n\n            @Override\n            public void run() {\n                final Method enclosingMethod = getClass().getEnclosingMethod();\n                long entry = ArtMethod.of(enclosingMethod).getEntryPointFromQuickCompiledCode();\n                if (entry != ArtMethod.getQuickToInterpreterBridge()) {\n                    Log.w(\"mylog\", \"method compiled....\");\n                }\n                Log.i(\"mylog\", enclosingMethod + \"entry: point: 0x\" + Long.toHexString(entry));\n            }\n        }.run();\n\n        return a.hashCode() + b;\n    }\n\n    public int returnConst(int a) {\n        return a;\n    }\n\n    public int add(int a, int b) {\n        return a + b;\n    }\n\n    public int plus(int a, int b) {\n        return a + b;\n    }\n\n    public int test3(Object a, int b) {\n        Log.i(\"mylog\", \"test1, arg1: \" + a + \" , arg2:\" + b);\n        return a.hashCode() + b;\n    }\n\n    public static int test4(int a) {\n        return Integer.valueOf(a).hashCode();\n    }\n\n\n    public static float add(int a, float b) {\n        return a + b;\n    }\n\n    public static int test2(Object a, int b) {\n        Log.i(\"mylog\", \"test1, arg1: \" + a + \" , arg2:\" + b);\n\n        return a.hashCode() + b;\n    }\n\n    public long longRunMethod() {\n        SystemClock.sleep(4000);\n        return SystemClock.elapsedRealtime();\n    }\n\n    public static void validate() {\n        final Method validate = XposedHelpers.findMethodExact(Target.class, \"validate\");\n\n        long entry = ArtMethod.of(validate).getEntryPointFromQuickCompiledCode();\n        if (entry != ArtMethod.getQuickToInterpreterBridge()) {\n            Log.w(\"mylog\", \"method compiled....\");\n            new Target().test1(\"123\", 1);\n        }\n        Log.i(\"mylog\", validate + \"entry: point: 0x\" + Long.toHexString(entry));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeConstructor.java",
    "content": "package me.weishu.epic.samples.tests.invoketype;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class InvokeConstructor extends TestCase {\n\n    boolean callBefore = false;\n    boolean callAfter = false;\n    public InvokeConstructor() {\n        super(\"Constructor\");\n    }\n\n    @Override\n    public void test() {\n        DexposedBridge.hookMethod(XposedHelpers.findConstructorExact(InvokeTypeTarget.class), new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                callBefore = true;\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                callAfter = true;\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        new InvokeTypeTarget();\n        return callBefore && callAfter;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeTypeTarget.java",
    "content": "package me.weishu.epic.samples.tests.invoketype;\n\n/**\n * Created by weishu on 17/11/14.\n */\n\npublic class InvokeTypeTarget {\n\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/BooleanType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class BooleanType extends TestCase {\n\n    final boolean returnType = Boolean.FALSE;\n    final boolean returnTypeModified = !returnType;\n\n    public BooleanType() {\n        super(\"Boolean\");\n    }\n\n    @Override\n    public void test() {\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnBoolean\", boolean.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnBoolean(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/ByteType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class ByteType extends TestCase {\n\n    final byte returnType = Byte.MAX_VALUE;\n    final byte returnTypeModified = Byte.MAX_VALUE - 1;\n\n    public ByteType() {\n        super(\"Byte\");\n    }\n\n    @Override\n    public void test() {\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnByte\", byte.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        final byte raw = ReturnTypeTarget.returnByte(returnType);\n        return raw == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/CharType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class CharType extends TestCase {\n\n    final char returnType = Character.MAX_VALUE;\n    final char returnTypeModified = returnType - 1;\n\n    public CharType() {\n        super(\"Char\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnChar\", char.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnChar(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/CustomType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class CustomType extends TestCase {\n\n    final ReturnTypeTarget returnType = new ReturnTypeTarget();\n    final ReturnTypeTarget returnTypeModified = new ReturnTypeTarget();\n\n    public CustomType() {\n        super(\"Custom\");\n    }\n\n    @Override\n    public void test() {\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnCustom\", ReturnTypeTarget.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnCustom(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/DoubleType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class DoubleType extends TestCase {\n\n    final double returnType = 12343748.12435;\n    final double returnTypeModified = returnType - 1;\n\n    public DoubleType() {\n        super(\"Double\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnDouble\", double.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnDouble(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/FloatType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class FloatType extends TestCase {\n\n    final float returnType = 12545.212f;\n    final float returnTypeModified = returnType - 1;\n\n    public FloatType() {\n        super(\"Float\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnFloat\", float.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnFloat(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/IntType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class IntType extends TestCase {\n\n    final int returnType = Integer.MAX_VALUE;\n    final int returnTypeModified = returnType - 1;\n\n    public IntType() {\n        super(\"Int\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnInt\", int.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        final int i = ReturnTypeTarget.returnInt(returnType);\n        return i == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/LongType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class LongType extends TestCase {\n\n    final long returnType = Long.MAX_VALUE / 2;\n    final long returnTypeModified = returnType - 1;\n\n    public LongType() {\n        super(\"Long\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnLong\", long.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnLong(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/ReturnTypeTarget.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport android.util.Log;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class ReturnTypeTarget {\n    private static final String TAG = \"ReturnTypeTarget\";\n\n    public static void returnVoid() {\n        Log.d(TAG, \"returnVoid() called\");\n    }\n\n    public static byte returnByte(byte b) {\n        Log.i(TAG, \"returnByte() called\");\n        return b;\n    }\n\n    public static char returnChar(char c) {\n        Log.i(TAG, \"returnChar() called\");\n        return c;\n    }\n\n    public static short returnShort(short s) {\n        Log.i(TAG, \"returnShort() called\");\n        return s;\n    }\n\n    public static int returnInt(int i) {\n        Log.i(TAG, \"returnInt() called\");\n        return i;\n    }\n\n    public static long returnLong(long l) {\n        Log.i(TAG, \"returnLong() called\");\n        return l;\n    }\n\n    public static float returnFloat(float f) {\n        Log.i(TAG, \"returnFloat() called\");\n        return f;\n    }\n\n    public static double returnDouble(double d) {\n        Log.i(TAG, \"returnDouble() called\");\n        return d;\n    }\n\n    public static boolean returnBoolean(boolean b) {\n        Log.i(TAG, \"returnBoolean() called\");\n        return b;\n    }\n\n    public static String returnString(String s) {\n        Log.i(TAG, \"returnString() called with: s = [\" + s + \"]\");\n        return s;\n    }\n\n    public static String[] returnStringArray(String[] a) {\n        Log.i(TAG, \"returnStringArray() called with: a = [\" + a + \"]\");\n        return a;\n    }\n\n    public static ReturnTypeTarget returnCustom(ReturnTypeTarget r) {\n        Log.i(TAG, \"returnCustom() called with: r = [\" + r + \"]\");\n        return r;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/ShortType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class ShortType extends TestCase {\n\n    final short returnType = Short.MAX_VALUE / 2;\n    final short returnTypeModified = returnType - 1;\n\n    public ShortType() {\n        super(\"Short\");\n    }\n\n    @Override\n    public void test() {\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnShort\", short.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnShort(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/StringArrayType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class StringArrayType extends TestCase {\n\n    final String[] returnType = new String[]{\"123\", \"456\"};\n    final String[] returnTypeModified = new String[]{\"123\", \"456\", \"678\"};\n\n    public StringArrayType() {\n        super(\"StringArray\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnStringArray\", String[].class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnStringArray(returnType) == returnTypeModified;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/StringType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport de.robv.android.xposed.DexposedBridge;\n\nimport me.weishu.epic.samples.tests.LogMethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\npublic class StringType extends TestCase {\n\n    final String returnType = \"123455\";\n    final String returnTypeModified = \"784fsgiulfodsg\";\n\n    public StringType() {\n        super(\"String\");\n    }\n\n    @Override\n    public void test() {\n\n\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnString\", String.class, new LogMethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                param.setResult(returnTypeModified);\n                super.beforeHookedMethod(param);\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        return ReturnTypeTarget.returnString(returnType).equals(returnTypeModified);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/weishu/epic/samples/tests/returntype/VoidType.java",
    "content": "package me.weishu.epic.samples.tests.returntype;\n\nimport android.util.Log;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XC_MethodHook;\nimport me.weishu.epic.samples.tests.TestCase;\n\n/**\n * Created by weishu on 17/11/13.\n */\n\npublic class VoidType extends TestCase {\n\n    private static final String TAG = \"VoidType\";\n\n    boolean callBefore = false;\n    boolean callAfter = false;\n\n    public VoidType() {\n        super(\"无返回值\");\n    }\n\n    @Override\n    public void test() {\n        DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, \"returnVoid\", new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {\n                super.beforeHookedMethod(param);\n                callBefore = true;\n            }\n\n            @Override\n            protected void afterHookedMethod(MethodHookParam param) throws Throwable {\n                super.afterHookedMethod(param);\n                callAfter = true;\n            }\n        });\n    }\n\n    @Override\n    public boolean predicate() {\n        ReturnTypeTarget.returnVoid();\n\n        Log.i(TAG, \"callBefore:\" + callBefore + \", callAfter:\" + callAfter);\n        return callBefore && callAfter;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:orientation=\"vertical\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <ExpandableListView\n        android:groupIndicator=\"@null\"\n        android:id=\"@+id/list\"\n        android:childDivider=\"@drawable/qmui_divider\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n    </ExpandableListView>\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/child_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:paddingLeft=\"20dp\"\n    android:background=\"@color/qmui_config_color_background\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <Button\n        android:id=\"@+id/validate\"\n        android:text=\"validate\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <Button\n        android:id=\"@+id/test\"\n        android:text=\"test\"\n        android:layout_toLeftOf=\"@+id/validate\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <TextView\n        android:layout_alignParentLeft=\"true\"\n        android:id=\"@+id/label\"\n        android:gravity=\"center\"\n        android:layout_centerVertical=\"true\"\n        android:textSize=\"12sp\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/parent_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <ImageView\n        android:layout_marginRight=\"20dp\"\n        android:layout_centerVertical=\"true\"\n        android:src=\"@drawable/arrow_down\"\n        android:id=\"@+id/indicator\"\n        android:layout_width=\"16dp\"\n        android:layout_height=\"10dp\"\n        android:layout_alignParentRight=\"true\" />\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"wrap_content\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_toLeftOf=\"@+id/indicator\"\n        android:paddingBottom=\"12dp\"\n        android:paddingLeft=\"20dp\"\n        android:paddingTop=\"12dp\"\n        android:textSize=\"20sp\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">epic</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/me/weishu/epic/ExampleUnitTest.java",
    "content": "//package me.weishu.epic;\n//\n//import org.junit.Test;\n//\n//import static org.junit.Assert.*;\n//\n///**\n// * Example local unit test, which will execute on the development machine (host).\n// *\n// * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n// */\n//public class ExampleUnitTest {\n//    @Test\n//    public void addition_isCorrect() throws Exception {\n//        assertEquals(4, 2 + 2);\n//    }\n//}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.2.1'\n        classpath 'com.github.panpf.bintray-publish:bintray-publish:1.0.0'\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        mavenLocal()\n        jcenter()\n        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n        maven { url 'https://jitpack.io' }\n    }\n    tasks.withType(Javadoc) {\n        enabled = false\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Nov 07 13:44:26 CST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.7.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "## Project-wide Gradle settings.\n#\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n#\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx1024m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n#\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "library/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "library/CMakeCache.txt",
    "content": "# This is the CMakeCache file.\n# For build in directory: /Users/weishu/dev/space/arthook/epic/library\n# It was generated by CMake: /usr/local/Cellar/cmake/3.9.1/bin/cmake\n# You can edit this file to change values found and used by cmake.\n# If you do not want to change any of the values, simply exit the editor.\n# If you do want to change a value, simply edit, save, and exit the editor.\n# The syntax for the file is as follows:\n# KEY:TYPE=VALUE\n# KEY is the name of a variable in the cache.\n# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.\n# VALUE is the current value for the KEY.\n\n########################\n# EXTERNAL cache entries\n########################\n\n//No help, variable specified on the command line.\nANDROID_TOOLCHAIN:UNINITIALIZED=gcc\n\n//Path to a program.\nCMAKE_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar\n\n//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or\n// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.\nCMAKE_BUILD_TYPE:STRING=Debug\n\n//Enable/Disable color output during build.\nCMAKE_COLOR_MAKEFILE:BOOL=ON\n\n//A wrapper around 'ar' adding the appropriate '--plugin' option\n// for the GCC compiler\nCMAKE_CXX_COMPILER_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ar\n\n//A wrapper around 'ranlib' adding the appropriate '--plugin' option\n// for the GCC compiler\nCMAKE_CXX_COMPILER_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ranlib\n\n//Flags used by the compiler during all build types.\nCMAKE_CXX_FLAGS:STRING=\n\n//Flags used by the compiler during debug builds.\nCMAKE_CXX_FLAGS_DEBUG:STRING=\n\n//Flags used by the compiler during release builds for minimum\n// size.\nCMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG\n\n//Flags used by the compiler during release builds.\nCMAKE_CXX_FLAGS_RELEASE:STRING=\n\n//Flags used by the compiler during release builds with debug info.\nCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG\n\n//Libraries linked by default with all C++ applications.\nCMAKE_CXX_STANDARD_LIBRARIES:STRING=-lm \"/Users/weishu/Library/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/libgnustl_static.a\"\n\n//A wrapper around 'ar' adding the appropriate '--plugin' option\n// for the GCC compiler\nCMAKE_C_COMPILER_AR:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ar\n\n//A wrapper around 'ranlib' adding the appropriate '--plugin' option\n// for the GCC compiler\nCMAKE_C_COMPILER_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-ranlib\n\n//Flags used by the compiler during all build types.\nCMAKE_C_FLAGS:STRING=\n\n//Flags used by the compiler during debug builds.\nCMAKE_C_FLAGS_DEBUG:STRING=\n\n//Flags used by the compiler during release builds for minimum\n// size.\nCMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG\n\n//Flags used by the compiler during release builds.\nCMAKE_C_FLAGS_RELEASE:STRING=\n\n//Flags used by the compiler during release builds with debug info.\nCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG\n\n//Libraries linked by default with all C applications.\nCMAKE_C_STANDARD_LIBRARIES:STRING=-lm\n\n//Flags used by the linker.\nCMAKE_EXE_LINKER_FLAGS:STRING=\n\n//Flags used by the linker during debug builds.\nCMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=\n\n//Flags used by the linker during release minsize builds.\nCMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=\n\n//Flags used by the linker during release builds.\nCMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=\n\n//Flags used by the linker during Release with Debug Info builds.\nCMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=\n\n//Enable/Disable output of compile commands during generation.\nCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF\n\n//Install path prefix, prepended onto install directories.\nCMAKE_INSTALL_PREFIX:PATH=/usr/local\n\n//Path to a program.\nCMAKE_LINKER:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ld\n\n//Path to a program.\nCMAKE_MAKE_PROGRAM:FILEPATH=/opt/local/bin/gmake\n\n//Flags used by the linker during the creation of modules.\nCMAKE_MODULE_LINKER_FLAGS:STRING=\n\n//Flags used by the linker during debug builds.\nCMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=\n\n//Flags used by the linker during release minsize builds.\nCMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=\n\n//Flags used by the linker during release builds.\nCMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=\n\n//Flags used by the linker during Release with Debug Info builds.\nCMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=\n\n//Path to a program.\nCMAKE_NM:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-nm\n\n//Path to a program.\nCMAKE_OBJCOPY:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objcopy\n\n//Path to a program.\nCMAKE_OBJDUMP:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-objdump\n\n//Value Computed by CMake\nCMAKE_PROJECT_NAME:STATIC=Project\n\n//Path to a program.\nCMAKE_RANLIB:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ranlib\n\n//Flags used by the linker during the creation of dll's.\nCMAKE_SHARED_LINKER_FLAGS:STRING=\n\n//Flags used by the linker during debug builds.\nCMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=\n\n//Flags used by the linker during release minsize builds.\nCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=\n\n//Flags used by the linker during release builds.\nCMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=\n\n//Flags used by the linker during Release with Debug Info builds.\nCMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=\n\n//If set, runtime paths are not added when installing shared libraries,\n// but are added when building.\nCMAKE_SKIP_INSTALL_RPATH:BOOL=NO\n\n//If set, runtime paths are not added when using shared libraries.\nCMAKE_SKIP_RPATH:BOOL=NO\n\n//Flags used by the linker during the creation of static libraries.\nCMAKE_STATIC_LINKER_FLAGS:STRING=\n\n//Flags used by the linker during debug builds.\nCMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=\n\n//Flags used by the linker during release minsize builds.\nCMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=\n\n//Flags used by the linker during release builds.\nCMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=\n\n//Flags used by the linker during Release with Debug Info builds.\nCMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=\n\n//Path to a program.\nCMAKE_STRIP:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-strip\n\n//The CMake toolchain file\nCMAKE_TOOLCHAIN_FILE:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake\n\n//If this value is on, makefiles will be generated without the\n// .SILENT directive, and all commands will be echoed to the console\n// during the make.  This is useful for debugging only. With Visual\n// Studio IDE projects all commands are done without /nologo.\nCMAKE_VERBOSE_MAKEFILE:BOOL=FALSE\n\n//Value Computed by CMake\nProject_BINARY_DIR:STATIC=/Users/weishu/dev/space/arthook/epic/library\n\n//Value Computed by CMake\nProject_SOURCE_DIR:STATIC=/Users/weishu/dev/space/arthook/epic/library\n\n//Dependencies for the target\nepic_LIB_DEPENDS:STATIC=general;/Users/weishu/Library/Android/sdk/ndk-bundle/platforms/android-9/arch-arm/usr/lib/liblog.so;\n\n//Path to a library.\nlog-lib:FILEPATH=/Users/weishu/Library/Android/sdk/ndk-bundle/platforms/android-9/arch-arm/usr/lib/liblog.so\n\n\n########################\n# INTERNAL cache entries\n########################\n\n//ADVANCED property for variable: CMAKE_AR\nCMAKE_AR-ADVANCED:INTERNAL=1\n//This is the directory where this CMakeCache.txt was created\nCMAKE_CACHEFILE_DIR:INTERNAL=/Users/weishu/dev/space/arthook/epic/library\n//Major version of cmake used to create the current loaded cache\nCMAKE_CACHE_MAJOR_VERSION:INTERNAL=3\n//Minor version of cmake used to create the current loaded cache\nCMAKE_CACHE_MINOR_VERSION:INTERNAL=9\n//Patch version of cmake used to create the current loaded cache\nCMAKE_CACHE_PATCH_VERSION:INTERNAL=1\n//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE\nCMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1\n//Path to CMake executable.\nCMAKE_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/cmake\n//Path to cpack program executable.\nCMAKE_CPACK_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/cpack\n//Path to ctest program executable.\nCMAKE_CTEST_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/ctest\n//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR\nCMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB\nCMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_FLAGS\nCMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG\nCMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL\nCMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE\nCMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO\nCMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_CXX_STANDARD_LIBRARIES\nCMAKE_CXX_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_COMPILER_AR\nCMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB\nCMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_FLAGS\nCMAKE_C_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG\nCMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL\nCMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE\nCMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO\nCMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_C_STANDARD_LIBRARIES\nCMAKE_C_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1\n//Path to cache edit program executable.\nCMAKE_EDIT_COMMAND:INTERNAL=/usr/local/Cellar/cmake/3.9.1/bin/ccmake\n//Executable file format\nCMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF\n//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS\nCMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG\nCMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL\nCMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE\nCMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO\nCMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS\nCMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1\n//Name of external makefile project generator.\nCMAKE_EXTRA_GENERATOR:INTERNAL=\n//Name of generator.\nCMAKE_GENERATOR:INTERNAL=Unix Makefiles\n//Name of generator platform.\nCMAKE_GENERATOR_PLATFORM:INTERNAL=\n//Name of generator toolset.\nCMAKE_GENERATOR_TOOLSET:INTERNAL=\n//Source directory with the top level CMakeLists.txt file for this\n// project\nCMAKE_HOME_DIRECTORY:INTERNAL=/Users/weishu/dev/space/arthook/epic/library\n//Install .so files without execute permission.\nCMAKE_INSTALL_SO_NO_EXE:INTERNAL=0\n//ADVANCED property for variable: CMAKE_LINKER\nCMAKE_LINKER-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MAKE_PROGRAM\nCMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS\nCMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG\nCMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL\nCMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE\nCMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO\nCMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_NM\nCMAKE_NM-ADVANCED:INTERNAL=1\n//number of local generators\nCMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1\n//ADVANCED property for variable: CMAKE_OBJCOPY\nCMAKE_OBJCOPY-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_OBJDUMP\nCMAKE_OBJDUMP-ADVANCED:INTERNAL=1\n//Platform information initialized\nCMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_RANLIB\nCMAKE_RANLIB-ADVANCED:INTERNAL=1\n//Path to CMake installation.\nCMAKE_ROOT:INTERNAL=/usr/local/Cellar/cmake/3.9.1/share/cmake\n//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS\nCMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG\nCMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL\nCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE\nCMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO\nCMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH\nCMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_SKIP_RPATH\nCMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS\nCMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG\nCMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL\nCMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE\nCMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO\nCMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1\n//ADVANCED property for variable: CMAKE_STRIP\nCMAKE_STRIP-ADVANCED:INTERNAL=1\n//uname command\nCMAKE_UNAME:INTERNAL=/usr/bin/uname\n//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE\nCMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1\n\n"
  },
  {
    "path": "library/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 31\n\n    defaultConfig {\n        minSdkVersion 21\n        targetSdkVersion 31\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n        ndk {\n            // Specifies the ABI configurations of your native\n            // libraries Gradle should build and package with your APK.\n            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'\n        }\n        externalNativeBuild {\n            cmake {\n                cppFlags \"-std=c++11\"\n                // arguments \"-DANDROID_TOOLCHAIN=gcc\"\n            }\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    externalNativeBuild {\n        cmake {\n            path 'src/main/cpp/CMakeLists.txt'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"com.github.tiann:FreeReflection:3.1.0\"\n    api 'me.weishu.exposed:exposed-xposedapi:0.4.5'\n}\n\napply plugin: 'com.github.panpf.bintray-publish'\n\npublish {\n    userOrg = 'twsxtd'//\n    groupId = 'me.weishu'\n    artifactId = 'epic'\n    publishVersion = '0.11.1'\n    desc = 'Android Java AOP Method Hook (Dexposed on ART)'\n    website = 'https://github.com/tiann/epic'\n}\n\n"
  },
  {
    "path": "library/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n-keepattributes Signature,SourceFile,LineNumberTable,Exceptions\n\n-keep class com.taobao.android.dexposed.** {*;}\n-keep class me.weishu.epic.art.** {*;}\n\n# delete log in release mode.\n-assumenosideeffects class com.taobao.android.dexposed.utility.Logger {\n          public static void i(...);\n          public static void w(...);\n          public static void d(...);\n          public static void e(...);\n}\n\n-assumenosideeffects class com.taobao.android.dexposed.utility.Debug {\n          public static *** hexdump(...);\n}"
  },
  {
    "path": "library/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n\n    package=\"me.weishu.epic\">\n\n    <uses-sdk tools:overrideLibrary=\"de.robv.android.xposed\" />\n    <application android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\">\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "library/src/main/cpp/.gitignore",
    "content": "build/\n"
  },
  {
    "path": "library/src/main/cpp/CMakeLists.txt",
    "content": "# For more information about using CMake with Android Studio, read the\n# documentation: https://d.android.com/studio/projects/add-native-code.html\n\n# Sets the minimum version of CMake required to build the native library.\n\ncmake_minimum_required(VERSION 3.4.1)\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n\nset (SRC_LIST epic.cpp fake_dlfcn.cpp art.cpp)\nadd_library( # Sets the name of the library.\n             epic\n\n             # Sets the library as a shared library.\n             SHARED\n\n             # Provides a relative path to your source file(s).\n             ${SRC_LIST})\n\n# Searches for a specified prebuilt library and stores the path as a\n# variable. Because CMake includes system libraries in the search path by\n# default, you only need to specify the name of the public NDK library\n# you want to add. CMake verifies that the library exists before\n# completing its build.\n\nfind_library( # Sets the name of the path variable.\n              log-lib\n\n              # Specifies the name of the NDK library that\n              # you want CMake to locate.\n              log )\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\ntarget_link_libraries( # Specifies the target library.\n                       epic\n\n                       # Links the target library to the log library\n                       # included in the NDK.\n                       ${log-lib} )\n"
  },
  {
    "path": "library/src/main/cpp/art.cpp",
    "content": "/*\n * Copyright (c) 2017, weishu\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"art.h\"\n#include <android/log.h>\n#include <cstddef>\n\n#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_INFO, \"epic.Native\", __VA_ARGS__))\n\n#define ANDROID_R_API 30\n#define MAX_SEARCH_LEN 2000\n\nvoid* ArtHelper::runtime_instance_ = nullptr;\nint ArtHelper::api = 0;\n\ntemplate <typename T>\ninline int find_offset(void* hay, int size, T needle)\n{\n  for (int i = 0; i < size; i += sizeof(T)) {\n    auto current = reinterpret_cast<T*>(reinterpret_cast<char*>(hay) + i);\n    if (*current == needle) {\n      return i;\n    }\n  }\n  return -1;\n}\n\nvoid ArtHelper::init(JNIEnv* env, int api)\n{\n  ArtHelper::api = api;\n  JavaVM* javaVM;\n  env->GetJavaVM(&javaVM);\n  JavaVMExt* javaVMExt = (JavaVMExt*)javaVM;\n\n  void* runtime = javaVMExt->runtime;\n  if (runtime == nullptr) {\n    return;\n  }\n\n  if (api < ANDROID_R_API) {\n    runtime_instance_ = runtime;\n  } else {\n    int vm_offset = find_offset(runtime, MAX_SEARCH_LEN, javaVM);\n    runtime_instance_ = reinterpret_cast<void*>(reinterpret_cast<char*>(runtime) + vm_offset - offsetof(PartialRuntimeR, java_vm_));\n  }\n}\n\nvoid* ArtHelper::getClassLinker()\n{\n  if (runtime_instance_ == nullptr || api < ANDROID_R_API) {\n    return nullptr;\n  }\n  PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;\n  return runtimeR->class_linker_;\n}\n\nvoid* ArtHelper::getJniIdManager()\n{\n  if (runtime_instance_ == nullptr || api < ANDROID_R_API) {\n    return nullptr;\n  }\n  PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;\n  return runtimeR->jni_id_manager_;\n}\n\nvoid* ArtHelper::getJitCodeCache()\n{\n  if (runtime_instance_ == nullptr || api < ANDROID_R_API) {\n    return nullptr;\n  }\n  PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;\n  return runtimeR->jit_code_cache_;\n}\n\nvoid* ArtHelper::getHeap()\n{\n  if (runtime_instance_ == nullptr) {\n    return nullptr;\n  }\n  if (api < 26) {\n    Runtime_7X* runtime7X = (Runtime_7X*)runtime_instance_;\n    return runtime7X->heap_;\n  } else if (api < ANDROID_R_API) {\n    Runtime_8X* runtime8X = (Runtime_8X*)runtime_instance_;\n    LOGV(\"bootclasspath : %s\", runtime8X->boot_class_path_string_.c_str());\n    return runtime8X->heap_;\n  } else {\n    PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;\n    return runtimeR->heap_;\n  }\n}\n"
  },
  {
    "path": "library/src/main/cpp/art.h",
    "content": "/*\n * Copyright (c) 2017, weishu\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef EPIC_ART_H\n#define EPIC_ART_H\n\n#include <jni.h>\n#include <list>\n#include <stdint.h>\n#include <string>\n#include <vector>\n\n// Android 6.0: http://androidxref.com/6.0.0_r5/xref/art/runtime/runtime.h\n// Android 7.0: http://androidxref.com/7.0.0_r1/xref/art/runtime/runtime.h\n// Android 7.1.1: http://androidxref.com/7.1.1_r6/xref/art/runtime/runtime.h\n// Android 8.0: http://androidxref.com/8.0.0_r4/xref/art/runtime/runtime.h\n\nstruct Runtime_7X {\n\n    uint64_t callee_save_methods_[3];\n    void* pre_allocated_OutOfMemoryError_;\n    void* pre_allocated_NoClassDefFoundError_;\n    void* resolution_method_;\n    void* imt_conflict_method_;\n    // Unresolved method has the same behavior as the conflict method, it is used by the class linker\n    // for differentiating between unfilled imt slots vs conflict slots in superclasses.\n    void* imt_unimplemented_method_;\n    void* sentinel_;\n\n    int instruction_set_;\n    uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3\n\n    void* compiler_callbacks_;\n    bool is_zygote_;\n    bool must_relocate_;\n    bool is_concurrent_gc_enabled_;\n    bool is_explicit_gc_disabled_;\n    bool dex2oat_enabled_;\n    bool image_dex2oat_enabled_;\n\n    std::string compiler_executable_;\n    std::string patchoat_executable_;\n    std::vector<std::string> compiler_options_;\n    std::vector<std::string> image_compiler_options_;\n    std::string image_location_;\n\n    std::string boot_class_path_string_;\n    std::string class_path_string_;\n    std::vector<std::string> properties_;\n\n    // The default stack size for managed threads created by the runtime.\n    size_t default_stack_size_;\n\n    void* heap_;\n};\n\nstruct Runtime_8X {\n\n    uint64_t callee_save_methods_[3];\n    void* pre_allocated_OutOfMemoryError_;\n    void* pre_allocated_NoClassDefFoundError_;\n    void* resolution_method_;\n    void* imt_conflict_method_;\n    // Unresolved method has the same behavior as the conflict method, it is used by the class linker\n    // for differentiating between unfilled imt slots vs conflict slots in superclasses.\n    void* imt_unimplemented_method_;\n    void* sentinel_;\n\n    int instruction_set_;\n    uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3\n\n    void* compiler_callbacks_;\n    bool is_zygote_;\n    bool must_relocate_;\n    bool is_concurrent_gc_enabled_;\n    bool is_explicit_gc_disabled_;\n    bool dex2oat_enabled_;\n    bool image_dex2oat_enabled_;\n\n    std::string compiler_executable_;\n    std::string patchoat_executable_;\n    std::vector<std::string> compiler_options_;\n    std::vector<std::string> image_compiler_options_;\n    std::string image_location_;\n\n    std::string boot_class_path_string_;\n    std::string class_path_string_;\n    std::vector<std::string> properties_;\n\n    std::list<void*> agents_;\n    std::vector<void*> plugins_;\n\n    // The default stack size for managed threads created by the runtime.\n    size_t default_stack_size_;\n\n    void* heap_;\n};\n\nstruct PartialRuntimeR {\n  void* heap_;\n\n  void* jit_arena_pool_;\n  void* arena_pool_;\n  // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are\n  // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image\n  // since the field arrays are int arrays in this case.\n  void* low_4gb_arena_pool_;\n\n  // Shared linear alloc for now.\n  void* linear_alloc_;\n\n  // The number of spins that are done before thread suspension is used to forcibly inflate.\n  size_t max_spins_before_thin_lock_inflation_;\n  void* monitor_list_;\n  void* monitor_pool_;\n\n  void* thread_list_;\n\n  void* intern_table_;\n\n  void* class_linker_;\n\n  void* signal_catcher_;\n\n  void* jni_id_manager_;\n\n  void* java_vm_;\n\n  void* jit_;\n  void* jit_code_cache_;\n};\n\nstruct JavaVMExt {\n    void* functions;\n    void* runtime;\n};\n\nclass ArtHelper {\n  public:\n    static void init(JNIEnv*, int);\n    static void* getRuntimeInstance() { return runtime_instance_; }\n    static void* getClassLinker();\n    static void* getJniIdManager();\n    static void* getJitCodeCache();\n    static void* getHeap();\n\n  private:\n    static void* runtime_instance_;\n    static int api;\n};\n\n#endif //EPIC_ART_H\n"
  },
  {
    "path": "library/src/main/cpp/build_with_cmake",
    "content": "#! /bin/sh\ncmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_SDK/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN=gcc -H. -Bbuild && cd build && make\n"
  },
  {
    "path": "library/src/main/cpp/epic.cpp",
    "content": "/*\n * Original Copyright 2014-2015 Marvin Wißfeld\n * Modified work Copyright (c) 2017, weishu\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <jni.h>\n#include <android/log.h>\n#include <sys/mman.h>\n#include <errno.h>\n#include <unistd.h>\n#include <dlfcn.h>\n#include <cstdlib>\n#include <sys/system_properties.h>\n#include <fcntl.h>\n#include \"fake_dlfcn.h\"\n#include \"art.h\"\n\n#undef NDEBUG\n#ifdef NDEBUG\n#define LOGV(...)  ((void)__android_log_print(ANDROID_LOG_INFO, \"epic.Native\", __VA_ARGS__))\n#else\n#define LOGV(...)\n#endif\n\n#define JNIHOOK_CLASS \"me/weishu/epic/art/EpicNative\"\n\njobject (*addWeakGloablReference)(JavaVM *, void *, void *) = nullptr;\n\nvoid* (*jit_load_)(bool*) = nullptr;\nvoid* jit_compiler_handle_ = nullptr;\nbool (*jit_compile_method_)(void*, void*, void*, bool) = nullptr;\nvoid* (*JitCodeCache_GetCurrentRegion)(void*) = nullptr;\n\ntypedef bool (*JIT_COMPILE_METHOD1)(void *, void *, void *, bool);\ntypedef bool (*JIT_COMPILE_METHOD2)(void *, void *, void *, bool, bool); // Android Q\ntypedef bool (*JIT_COMPILE_METHOD3)(void *, void *, void *, void *, bool, bool); // Android R\ntypedef bool (*JIT_COMPILE_METHOD4)(void *, void *, void *, void *, int); // Android S\n\nvoid (*jit_unload_)(void*) = nullptr;\n\nclass ScopedSuspendAll {};\n\nvoid (*suspendAll)(ScopedSuspendAll*, char*) = nullptr;\nvoid (*resumeAll)(ScopedSuspendAll*) = nullptr;\n\nclass ScopedJitSuspend {};\nvoid (*startJit)(ScopedJitSuspend*) = nullptr;\nvoid (*stopJit)(ScopedJitSuspend*) = nullptr;\n\nvoid (*DisableMovingGc)(void*) = nullptr;\n\nvoid* (*JniIdManager_DecodeMethodId_)(void*, jlong) = nullptr;\nvoid* (*ClassLinker_MakeInitializedClassesVisiblyInitialized_)(void*, void*, bool) = nullptr;\n\nvoid* __self() {\n\n#ifdef __arm__\n    register uint32_t r9 asm(\"r9\");\n    return (void*) r9;\n#elif defined(__aarch64__)\n    register uint64_t x19 asm(\"x19\");\n    return (void*) x19;\n#else\n#endif\n\n};\n\nstatic int api_level;\n\nvoid init_entries(JNIEnv *env) {\n    char api_level_str[5];\n    __system_property_get(\"ro.build.version.sdk\", api_level_str);\n    api_level = atoi(api_level_str);\n    LOGV(\"api level: %d\", api_level);\n    ArtHelper::init(env, api_level);\n    if (api_level < 23) {\n        // Android L, art::JavaVMExt::AddWeakGlobalReference(art::Thread*, art::mirror::Object*)\n        void *handle = dlopen(\"libart.so\", RTLD_LAZY | RTLD_GLOBAL);\n        addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym(handle,\n                                                                               \"_ZN3art9JavaVMExt22AddWeakGlobalReferenceEPNS_6ThreadEPNS_6mirror6ObjectE\");\n    } else if (api_level < 24) {\n        // Android M, art::JavaVMExt::AddWeakGlobalRef(art::Thread*, art::mirror::Object*)\n        void *handle = dlopen(\"libart.so\", RTLD_LAZY | RTLD_GLOBAL);\n        addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym(handle,\n                                                                               \"_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE\");\n    } else {\n        // Android N and O, Google disallow us use dlsym;\n        void *handle = dlopen_ex(\"libart.so\", RTLD_NOW);\n        void *jit_lib = dlopen_ex(\"libart-compiler.so\", RTLD_NOW);\n        LOGV(\"fake dlopen install: %p\", handle);\n        const char *addWeakGloablReferenceSymbol = api_level <= 25\n                                                   ? \"_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadEPNS_6mirror6ObjectE\"\n                                                   : \"_ZN3art9JavaVMExt16AddWeakGlobalRefEPNS_6ThreadENS_6ObjPtrINS_6mirror6ObjectEEE\";\n        addWeakGloablReference = (jobject (*)(JavaVM *, void *, void *)) dlsym_ex(handle, addWeakGloablReferenceSymbol);\n\n        jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, \"jit_compile_method\");\n        jit_load_ = reinterpret_cast<void* (*)(bool*)>(dlsym_ex(jit_lib, \"jit_load\"));\n        bool generate_debug_info = false;\n        jit_compiler_handle_ = (jit_load_)(&generate_debug_info);\n        LOGV(\"jit compile_method: %p\", jit_compile_method_);\n\n        suspendAll = reinterpret_cast<void (*)(ScopedSuspendAll*, char*)>(dlsym_ex(handle, \"_ZN3art16ScopedSuspendAllC1EPKcb\"));\n        resumeAll = reinterpret_cast<void (*)(ScopedSuspendAll*)>(dlsym_ex(handle, \"_ZN3art16ScopedSuspendAllD1Ev\"));\n\n        if (api_level >= 30) {\n            // Android R would not directly return ArtMethod address but an internal id\n            ClassLinker_MakeInitializedClassesVisiblyInitialized_ = reinterpret_cast<void* (*)(void*, void*, bool)>(dlsym_ex(handle, \"_ZN3art11ClassLinker40MakeInitializedClassesVisiblyInitializedEPNS_6ThreadEb\"));\n            JniIdManager_DecodeMethodId_ = reinterpret_cast<void* (*)(void*, jlong)>(dlsym_ex(handle, \"_ZN3art3jni12JniIdManager14DecodeMethodIdEP10_jmethodID\"));\n            if (api_level >= 31) {\n                // Android S CompileMethod accepts a CompilationKind enum instead of two booleans\n                // source: https://android.googlesource.com/platform/art/+/refs/heads/android12-release/compiler/jit/jit_compiler.cc\n                jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, \"_ZN3art3jit11JitCompiler13CompileMethodEPNS_6ThreadEPNS0_15JitMemoryRegionEPNS_9ArtMethodENS_15CompilationKindE\");\n            } else {\n                jit_compile_method_ = (bool (*)(void *, void *, void *, bool)) dlsym_ex(jit_lib, \"_ZN3art3jit11JitCompiler13CompileMethodEPNS_6ThreadEPNS0_15JitMemoryRegionEPNS_9ArtMethodEbb\");\n            }\n            JitCodeCache_GetCurrentRegion = (void* (*)(void*)) dlsym_ex(handle, \"_ZN3art3jit12JitCodeCache16GetCurrentRegionEv\");\n        }\n        // Disable this now.\n        // startJit = reinterpret_cast<void(*)(ScopedJitSuspend*)>(dlsym_ex(handle, \"_ZN3art3jit16ScopedJitSuspendD1Ev\"));\n        // stopJit = reinterpret_cast<void(*)(ScopedJitSuspend*)>(dlsym_ex(handle, \"_ZN3art3jit16ScopedJitSuspendC1Ev\"));\n\n        // DisableMovingGc = reinterpret_cast<void(*)(void*)>(dlsym_ex(handle, \"_ZN3art2gc4Heap15DisableMovingGcEv\"));\n    }\n\n    LOGV(\"addWeakGloablReference: %p\", addWeakGloablReference);\n}\n\njboolean epic_compile(JNIEnv *env, jclass, jobject method, jlong self) {\n    LOGV(\"self from native peer: %p, from register: %p\", reinterpret_cast<void*>(self), __self());\n    jlong art_method = (jlong) env->FromReflectedMethod(method);\n    if (art_method % 2 == 1) {\n        art_method = reinterpret_cast<jlong>(JniIdManager_DecodeMethodId_(ArtHelper::getJniIdManager(), art_method));\n    }\n    bool ret;\n    if (api_level >= 30) {\n        void* current_region = JitCodeCache_GetCurrentRegion(ArtHelper::getJitCodeCache());\n        if (api_level >= 31) {\n            ret = ((JIT_COMPILE_METHOD4)jit_compile_method_)(jit_compiler_handle_, reinterpret_cast<void*>(self),\n                                                             reinterpret_cast<void*>(current_region),\n                                                             reinterpret_cast<void*>(art_method), 1);\n        } else {\n            ret = ((JIT_COMPILE_METHOD3)jit_compile_method_)(jit_compiler_handle_, reinterpret_cast<void*>(self),\n                                                             reinterpret_cast<void*>(current_region),\n                                                             reinterpret_cast<void*>(art_method), false, false);\n        }\n    } else if (api_level >= 29) {\n        ret = ((JIT_COMPILE_METHOD2) jit_compile_method_)(jit_compiler_handle_,\n                                                          reinterpret_cast<void *>(art_method),\n                                                          reinterpret_cast<void *>(self), false, false);\n    } else {\n        ret = ((JIT_COMPILE_METHOD1) jit_compile_method_)(jit_compiler_handle_,\n                                                          reinterpret_cast<void *>(art_method),\n                                                          reinterpret_cast<void *>(self), false);\n    }\n    return (jboolean)ret;\n}\n\njlong epic_suspendAll(JNIEnv *, jclass) {\n    ScopedSuspendAll *scopedSuspendAll = (ScopedSuspendAll *) malloc(sizeof(ScopedSuspendAll));\n    suspendAll(scopedSuspendAll, \"stop_jit\");\n    return reinterpret_cast<jlong >(scopedSuspendAll);\n}\n\nvoid epic_resumeAll(JNIEnv* env, jclass, jlong obj) {\n    ScopedSuspendAll* scopedSuspendAll = reinterpret_cast<ScopedSuspendAll*>(obj);\n    resumeAll(scopedSuspendAll);\n}\n\njlong epic_stopJit(JNIEnv*, jclass) {\n    ScopedJitSuspend *scopedJitSuspend = (ScopedJitSuspend *) malloc(sizeof(ScopedJitSuspend));\n    stopJit(scopedJitSuspend);\n    return reinterpret_cast<jlong >(scopedJitSuspend);\n}\n\nvoid epic_startJit(JNIEnv*, jclass, jlong obj) {\n    ScopedJitSuspend *scopedJitSuspend = reinterpret_cast<ScopedJitSuspend *>(obj);\n    startJit(scopedJitSuspend);\n}\n\nvoid epic_disableMovingGc(JNIEnv* env, jclass ,jint api) {\n    void *heap = ArtHelper::getHeap();\n    DisableMovingGc(heap);\n}\n\njboolean epic_munprotect(JNIEnv *env, jclass, jlong addr, jlong len) {\n    long pagesize = sysconf(_SC_PAGESIZE);\n    unsigned alignment = (unsigned)((unsigned long long)addr % pagesize);\n    LOGV(\"munprotect page size: %d, alignment: %d\", pagesize, alignment);\n\n    int i = mprotect((void *) (addr - alignment), (size_t) (alignment + len),\n                     PROT_READ | PROT_WRITE | PROT_EXEC);\n    if (i == -1) {\n        LOGV(\"mprotect failed: %s (%d)\", strerror(errno), errno);\n        return JNI_FALSE;\n    }\n    return JNI_TRUE;\n}\n\njboolean epic_cacheflush(JNIEnv *env, jclass, jlong addr, jlong len) {\n#if defined(__arm__)\n    int i = cacheflush(addr, addr + len, 0);\n    LOGV(\"arm cacheflush for, %ul\", addr);\n    if (i == -1) {\n        LOGV(\"cache flush failed: %s (%d)\", strerror(errno), errno);\n        return JNI_FALSE;\n    }\n#elif defined(__aarch64__)\n    char* begin = reinterpret_cast<char*>(addr);\n    __builtin___clear_cache(begin, begin + len);\n    LOGV(\"aarch64 __builtin___clear_cache, %p\", (void*)begin);\n#endif\n    return JNI_TRUE;\n}\n\nvoid epic_MakeInitializedClassVisibilyInitialized(JNIEnv *env, jclass, jlong self) {\n  if (api_level >= 30 && ClassLinker_MakeInitializedClassesVisiblyInitialized_ && ArtHelper::getClassLinker()) {\n    ClassLinker_MakeInitializedClassesVisiblyInitialized_(ArtHelper::getClassLinker(), reinterpret_cast<void*>(self), true);\n  }\n}\n\nvoid epic_memcpy(JNIEnv *env, jclass, jlong src, jlong dest, jint length) {\n    char *srcPnt = (char *) src;\n    char *destPnt = (char *) dest;\n    for (int i = 0; i < length; ++i) {\n        destPnt[i] = srcPnt[i];\n    }\n}\n\nvoid epic_memput(JNIEnv *env, jclass, jbyteArray src, jlong dest) {\n\n    jbyte *srcPnt = env->GetByteArrayElements(src, 0);\n    jsize length = env->GetArrayLength(src);\n    unsigned char *destPnt = (unsigned char *) dest;\n    for (int i = 0; i < length; ++i) {\n        // LOGV(\"put %d with %d\", i, *(srcPnt + i));\n        destPnt[i] = (unsigned char) srcPnt[i];\n    }\n    env->ReleaseByteArrayElements(src, srcPnt, 0);\n}\n\njbyteArray epic_memget(JNIEnv *env, jclass, jlong src, jint length) {\n\n    jbyteArray dest = env->NewByteArray(length);\n    if (dest == NULL) {\n        return NULL;\n    }\n    unsigned char *destPnt = (unsigned char *) env->GetByteArrayElements(dest, 0);\n    unsigned char *srcPnt = (unsigned char *) src;\n    for (int i = 0; i < length; ++i) {\n        destPnt[i] = srcPnt[i];\n    }\n    env->ReleaseByteArrayElements(dest, (jbyte *) destPnt, 0);\n\n    return dest;\n}\n\njlong epic_mmap(JNIEnv *env, jclass, jint length) {\n    void *space = mmap(0, (size_t) length, PROT_READ | PROT_WRITE | PROT_EXEC,\n                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if (space == MAP_FAILED) {\n        LOGV(\"mmap failed: %d\", errno);\n        return 0;\n    }\n    return (jlong) space;\n}\n\nvoid epic_munmap(JNIEnv *env, jclass, jlong addr, jint length) {\n    int r = munmap((void *) addr, (size_t) length);\n    if (r == -1) {\n        LOGV(\"munmap failed: %d\", errno);\n    }\n}\n\njlong epic_malloc(JNIEnv *env, jclass, jint size) {\n    size_t length = sizeof(void *) * size;\n    void *ptr = malloc(length);\n    LOGV(\"malloc :%d of memory at: %p\", (int) length, ptr);\n    return (jlong) ptr;\n}\n\n\njobject epic_getobject(JNIEnv *env, jclass clazz, jlong self, jlong address) {\n    JavaVM *vm;\n    env->GetJavaVM(&vm);\n    LOGV(\"java vm: %p, self: %p, address: %p\", vm, (void*) self, (void*) address);\n    jobject object = addWeakGloablReference(vm, (void *) self, (void *) address);\n    jobject new_local_object = env->NewLocalRef(object);\n    env->DeleteWeakGlobalRef(object);\n    \n    return new_local_object;\n}\n\njlong epic_getMethodAddress(JNIEnv *env, jclass clazz, jobject method) {\n    jlong art_method = (jlong) env->FromReflectedMethod(method);\n    if (art_method % 2 == 1) {\n        art_method = reinterpret_cast<jlong>(JniIdManager_DecodeMethodId_(ArtHelper::getJniIdManager(), art_method));\n    }\n    return art_method;\n}\n\njboolean epic_isGetObjectAvaliable(JNIEnv *, jclass) {\n    return (jboolean) (addWeakGloablReference != nullptr);\n}\n\njboolean epic_activate(JNIEnv* env, jclass jclazz, jlong jumpToAddress, jlong pc, jlong sizeOfDirectJump,\n                       jlong sizeOfBridgeJump, jbyteArray code) {\n\n    // fetch the array, we can not call this when thread suspend(may lead deadlock)\n    jbyte *srcPnt = env->GetByteArrayElements(code, 0);\n    jsize length = env->GetArrayLength(code);\n\n    jlong cookie = 0;\n    bool isNougat = api_level >= 24;\n    if (isNougat) {\n        // We do thus things:\n        // 1. modify the code mprotect\n        // 2. modify the code\n\n        // Ideal, this two operation must be atomic. Below N, this is safe, because no one\n        // modify the code except ourselves;\n        // But in Android N, When the jit is working, between our step 1 and step 2,\n        // if we modity the mprotect of the code, and planning to write the code,\n        // the jit thread may modify the mprotect of the code meanwhile\n        // we must suspend all thread to ensure the atomic operation.\n\n        LOGV(\"suspend all thread.\");\n        cookie = epic_suspendAll(env, jclazz);\n    }\n\n    jboolean result = epic_munprotect(env, jclazz, jumpToAddress, sizeOfDirectJump);\n    if (result) {\n        unsigned char *destPnt = (unsigned char *) jumpToAddress;\n        for (int i = 0; i < length; ++i) {\n            destPnt[i] = (unsigned char) srcPnt[i];\n        }\n        jboolean ret = epic_cacheflush(env, jclazz, pc, sizeOfBridgeJump);\n        if (!ret) {\n            LOGV(\"cache flush failed!!\");\n        }\n    } else {\n        LOGV(\"Writing hook failed: Unable to unprotect memory at %d\", jumpToAddress);\n    }\n\n    if (cookie != 0) {\n        LOGV(\"resume all thread.\");\n        epic_resumeAll(env, jclazz, cookie);\n    }\n\n    env->ReleaseByteArrayElements(code, srcPnt, 0);\n    return result;\n}\n\nstatic JNINativeMethod dexposedMethods[] = {\n\n        {\"mmap\",              \"(I)J\",                          (void *) epic_mmap},\n        {\"munmap\",            \"(JI)Z\",                         (void *) epic_munmap},\n        {\"memcpy\",            \"(JJI)V\",                        (void *) epic_memcpy},\n        {\"memput\",            \"([BJ)V\",                        (void *) epic_memput},\n        {\"memget\",            \"(JI)[B\",                        (void *) epic_memget},\n        {\"munprotect\",        \"(JJ)Z\",                         (void *) epic_munprotect},\n        {\"getMethodAddress\",  \"(Ljava/lang/reflect/Member;)J\", (void *) epic_getMethodAddress},\n        {\"cacheflush\",        \"(JJ)Z\",                         (void *) epic_cacheflush},\n        {\"MakeInitializedClassVisibilyInitialized\", \"(J)V\",    (void *) epic_MakeInitializedClassVisibilyInitialized},\n        {\"malloc\",            \"(I)J\",                          (void *) epic_malloc},\n        {\"getObjectNative\",   \"(JJ)Ljava/lang/Object;\",        (void *) epic_getobject},\n        {\"compileMethod\",     \"(Ljava/lang/reflect/Member;J)Z\",(void *) epic_compile},\n        {\"suspendAll\",        \"()J\",                           (void *) epic_suspendAll},\n        {\"resumeAll\",         \"(J)V\",                          (void *) epic_resumeAll},\n        {\"stopJit\",           \"()J\",                           (void *) epic_stopJit},\n        {\"startJit\",          \"(J)V\",                          (void *) epic_startJit},\n        {\"disableMovingGc\",   \"(I)V\",                          (void *) epic_disableMovingGc},\n        {\"activateNative\",    \"(JJJJ[B)Z\",                     (void *) epic_activate},\n        {\"isGetObjectAvailable\", \"()Z\",                        (void *) epic_isGetObjectAvaliable}\n};\n\nstatic int registerNativeMethods(JNIEnv *env, const char *className,\n                                 JNINativeMethod *gMethods, int numMethods) {\n\n    jclass clazz = env->FindClass(className);\n    if (clazz == NULL) {\n        return JNI_FALSE;\n    }\n\n    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {\n        return JNI_FALSE;\n    }\n\n    return JNI_TRUE;\n}\n\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {\n\n    JNIEnv *env = NULL;\n\n    LOGV(\"JNI_OnLoad\");\n\n    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {\n        return -1;\n    }\n\n    if (!registerNativeMethods(env, JNIHOOK_CLASS, dexposedMethods,\n                               sizeof(dexposedMethods) / sizeof(dexposedMethods[0]))) {\n        return -1;\n    }\n\n    init_entries(env);\n    return JNI_VERSION_1_6;\n}\n"
  },
  {
    "path": "library/src/main/cpp/fake_dlfcn.cpp",
    "content": "// Copyright (c) 2016 avs333\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n//\t\tof this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n//\t\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//\t\tcopies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n//\t\tThe above copyright notice and this permission notice shall be included in all\n//\t\tcopies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// \t\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n//fork from https://github.com/avs333/Nougat_dlfunctions\n//do some modify\n//support all cpu abi such as x86, x86_64\n//support filename search if filename is not start with '/'\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <elf.h>\n#include <dlfcn.h>\n#include <sys/system_properties.h>\n#include <android/log.h>\n\n#define TAG_NAME    \"dlfcn_ex\"\n\n#ifdef LOG_DBG\n#define log_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG_NAME, (const char *) fmt, ##args)\n#define log_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG_NAME, (const char *) fmt, ##args)\n#define log_dbg log_info\n#else\n#define log_dbg(...)\n#define log_info(fmt, args...)\n#define log_err(fmt, args...)\n#endif\n\n#ifdef __LP64__\n#define Elf_Ehdr Elf64_Ehdr\n#define Elf_Shdr Elf64_Shdr\n#define Elf_Sym  Elf64_Sym\n#else\n#define Elf_Ehdr Elf32_Ehdr\n#define Elf_Shdr Elf32_Shdr\n#define Elf_Sym  Elf32_Sym\n#endif\n\n\nstruct ctx {\n    void *load_addr;\n    void *dynstr;\n    void *dynsym;\n    int nsyms;\n    off_t bias;\n};\n\nextern \"C\" {\n\nstatic int fake_dlclose(void *handle) {\n    if (handle) {\n        struct ctx *ctx = (struct ctx *) handle;\n        if (ctx->dynsym) free(ctx->dynsym);    /* we're saving dynsym and dynstr */\n        if (ctx->dynstr) free(ctx->dynstr);    /* from library file just in case */\n        free(ctx);\n    }\n    return 0;\n}\n\n/* flags are ignored */\nstatic void *fake_dlopen_with_path(const char *libpath, int flags) {\n    FILE *maps;\n    char buff[256];\n    struct ctx *ctx = 0;\n    off_t load_addr, size;\n    int k, fd = -1, found = 0;\n    char *shoff;\n    Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED;\n\n#define fatal(fmt, args...) do { log_err(fmt,##args); goto err_exit; } while(0)\n\n    maps = fopen(\"/proc/self/maps\", \"r\");\n    if (!maps) fatal(\"failed to open maps\");\n\n    while (!found && fgets(buff, sizeof(buff), maps)) {\n        if (strstr(buff, libpath) && (strstr(buff, \"r-xp\") || strstr(buff, \"r--p\"))) found = 1;\n    }\n    fclose(maps);\n\n    if (!found) fatal(\"%s not found in my userspace\", libpath);\n\n    if (sscanf(buff, \"%lx\", &load_addr) != 1)\n        fatal(\"failed to read load address for %s\", libpath);\n\n    log_info(\"%s loaded in Android at 0x%08lx\", libpath, load_addr);\n\n    /* Now, mmap the same library once again */\n\n    fd = open(libpath, O_RDONLY);\n    if (fd < 0) fatal(\"failed to open %s\", libpath);\n\n    size = lseek(fd, 0, SEEK_END);\n    if (size <= 0) fatal(\"lseek() failed for %s\", libpath);\n\n    elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);\n    close(fd);\n    fd = -1;\n\n    if (elf == MAP_FAILED) fatal(\"mmap() failed for %s\", libpath);\n\n    ctx = (struct ctx *) calloc(1, sizeof(struct ctx));\n    if (!ctx) fatal(\"no memory for %s\", libpath);\n\n    ctx->load_addr = (void *) load_addr;\n    shoff = ((char *) elf) + elf->e_shoff;\n\n    for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) {\n\n        Elf_Shdr *sh = (Elf_Shdr *) shoff;\n        log_dbg(\"%s: k=%d shdr=%p type=%x\", __func__, k, sh, sh->sh_type);\n\n        switch (sh->sh_type) {\n\n            case SHT_DYNSYM:\n                if (ctx->dynsym) fatal(\"%s: duplicate DYNSYM sections\", libpath); /* .dynsym */\n                ctx->dynsym = malloc(sh->sh_size);\n                if (!ctx->dynsym) fatal(\"%s: no memory for .dynsym\", libpath);\n                memcpy(ctx->dynsym, ((char *) elf) + sh->sh_offset, sh->sh_size);\n                ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym));\n                break;\n\n            case SHT_STRTAB:\n                if (ctx->dynstr) break;    /* .dynstr is guaranteed to be the first STRTAB */\n                ctx->dynstr = malloc(sh->sh_size);\n                if (!ctx->dynstr) fatal(\"%s: no memory for .dynstr\", libpath);\n                memcpy(ctx->dynstr, ((char *) elf) + sh->sh_offset, sh->sh_size);\n                break;\n\n            case SHT_PROGBITS:\n                if (!ctx->dynstr || !ctx->dynsym) break;\n                /* won't even bother checking against the section name */\n                ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset;\n                k = elf->e_shnum;  /* exit for */\n                break;\n        }\n    }\n\n    munmap(elf, size);\n    elf = 0;\n\n    if (!ctx->dynstr || !ctx->dynsym) fatal(\"dynamic sections not found in %s\", libpath);\n\n#undef fatal\n\n    log_dbg(\"%s: ok, dynsym = %p, dynstr = %p\", libpath, ctx->dynsym, ctx->dynstr);\n\n    return ctx;\n\n    err_exit:\n    if (fd >= 0) close(fd);\n    if (elf != MAP_FAILED) munmap(elf, size);\n    fake_dlclose(ctx);\n    return 0;\n}\n\n\n#if defined(__LP64__)\nstatic const char *const kSystemLibDir = \"/system/lib64/\";\nstatic const char *const kOdmLibDir = \"/odm/lib64/\";\nstatic const char *const kVendorLibDir = \"/vendor/lib64/\";\nstatic const char *const kApexLibDir = \"/apex/com.android.runtime/lib64/\";\nstatic const char *const kApexArtNsLibDir = \"/apex/com.android.art/lib64/\";\n#else\nstatic const char *const kSystemLibDir = \"/system/lib/\";\nstatic const char *const kOdmLibDir = \"/odm/lib/\";\nstatic const char *const kVendorLibDir = \"/vendor/lib/\";\nstatic const char *const kApexLibDir = \"/apex/com.android.runtime/lib/\";\nstatic const char *const kApexArtNsLibDir = \"/apex/com.android.art/lib/\";\n#endif\n\nstatic void *fake_dlopen(const char *filename, int flags) {\n    if (strlen(filename) > 0 && filename[0] == '/') {\n        return fake_dlopen_with_path(filename, flags);\n    } else {\n        char buf[512] = {0};\n        void *handle = NULL;\n        //sysmtem\n        strcpy(buf, kSystemLibDir);\n        strcat(buf, filename);\n        handle = fake_dlopen_with_path(buf, flags);\n        if (handle) {\n            return handle;\n        }\n\n        // apex in ns com.android.runtime\n        memset(buf, 0, sizeof(buf));\n        strcpy(buf, kApexLibDir);\n        strcat(buf, filename);\n        handle = fake_dlopen_with_path(buf, flags);\n        if (handle) {\n            return handle;\n        }\n\n        // apex in ns com.android.art\n        memset(buf, 0, sizeof(buf));\n        strcpy(buf, kApexArtNsLibDir);\n        strcat(buf, filename);\n        handle = fake_dlopen_with_path(buf, flags);\n        if (handle) {\n            return handle;\n        }\n\n        //odm\n        memset(buf, 0, sizeof(buf));\n        strcpy(buf, kOdmLibDir);\n        strcat(buf, filename);\n        handle = fake_dlopen_with_path(buf, flags);\n        if (handle) {\n            return handle;\n        }\n\n        //vendor\n        memset(buf, 0, sizeof(buf));\n        strcpy(buf, kVendorLibDir);\n        strcat(buf, filename);\n        handle = fake_dlopen_with_path(buf, flags);\n        if (handle) {\n            return handle;\n        }\n\n        return fake_dlopen_with_path(filename, flags);\n    }\n}\n\nstatic void *fake_dlsym(void *handle, const char *name) {\n    int k;\n    struct ctx *ctx = (struct ctx *) handle;\n    Elf_Sym *sym = (Elf_Sym *) ctx->dynsym;\n    char *strings = (char *) ctx->dynstr;\n\n    for (k = 0; k < ctx->nsyms; k++, sym++)\n        if (strcmp(strings + sym->st_name, name) == 0) {\n            /*  NB: sym->st_value is an offset into the section for relocatables,\n            but a VMA for shared libs or exe files, so we have to subtract the bias */\n            void *ret = (char *) ctx->load_addr + sym->st_value - ctx->bias;\n            log_info(\"%s found at %p\", name, ret);\n            return ret;\n        }\n    return 0;\n}\n\n\nstatic const char *fake_dlerror() {\n    return NULL;\n}\n\n// =============== implementation for compat ==========\nstatic int SDK_INT = -1;\nstatic int get_sdk_level() {\n    if (SDK_INT > 0) {\n        return SDK_INT;\n    }\n    char sdk[PROP_VALUE_MAX] = {0};;\n    __system_property_get(\"ro.build.version.sdk\", sdk);\n    SDK_INT = atoi(sdk);\n    return SDK_INT;\n}\n\nint dlclose_ex(void *handle) {\n    if (get_sdk_level() >= 24) {\n        return fake_dlclose(handle);\n    } else {\n        return dlclose(handle);\n    }\n}\n\nvoid *dlopen_ex(const char *filename, int flags) {\n    log_info(\"dlopen: %s\", filename);\n    if (get_sdk_level() >= 24) {\n        return fake_dlopen(filename, flags);\n    } else {\n        return dlopen(filename, flags);\n    }\n}\n\nvoid *dlsym_ex(void *handle, const char *symbol) {\n    if (get_sdk_level() >= 24) {\n        return fake_dlsym(handle, symbol);\n    } else {\n        return dlsym(handle, symbol);\n    }\n}\n\nconst char *dlerror_ex() {\n    if (get_sdk_level() >= 24) {\n        return fake_dlerror();\n    } else {\n        return dlerror();\n    }\n}\n}\n"
  },
  {
    "path": "library/src/main/cpp/fake_dlfcn.h",
    "content": "// Copyright (c) 2016 avs333\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n//\t\tof this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n//\t\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n//\t\tcopies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n//\t\tThe above copyright notice and this permission notice shall be included in all\n//\t\tcopies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// \t\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n#ifndef DEXPOSED_DLFCN_H\n#define DEXPOSED_DLFCN_H\n\n#include <cstdlib>\n#include <string.h>\n#include <unistd.h>\n\nextern \"C\" {\n\nvoid *dlopen_ex(const char *filename, int flags);\n\nvoid *dlsym_ex(void *handle, const char *symbol);\n\nint dlclose_ex(void *handle);\n\nconst char *dlerror_ex();\n\n};\n#endif //DEXPOSED_DLFCN_H\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/ClassUtils.java",
    "content": "/*\n * Original work Copyright (c) 2005-2008, The Android Open Source Project\n * Modified work Copyright (c) 2013, rovo89 and Tungstwenty\n * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * <p>Operates on classes without using reflection.</p>\n *\n * <p>This class handles invalid {@code null} inputs as best it can.\n * Each method documents its behaviour in more detail.</p>\n *\n * <p>The notion of a {@code canonical name} includes the human\n * readable name for the type, for example {@code int[]}. The\n * non-canonical method variants work with the JVM names, such as\n * {@code [I}. </p>\n *\n * @since 2.0\n * @version $Id: ClassUtils.java 1199894 2011-11-09 17:53:59Z ggregory $\n */\npublic class ClassUtils {\n\n    /**\n     * <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>\n     */\n    public static final char PACKAGE_SEPARATOR_CHAR = '.';\n\n    /**\n     * <p>The package separator String: {@code \"&#x2e;\"}.</p>\n     */\n    public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);\n\n    /**\n     * <p>The inner class separator character: <code>'$' == {@value}</code>.</p>\n     */\n    public static final char INNER_CLASS_SEPARATOR_CHAR = '$';\n\n    /**\n     * <p>The inner class separator String: {@code \"$\"}.</p>\n     */\n    public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);\n\n    /**\n     * <p>Empty string.</p>\n     */\n    public static final String STRING_EMPTY = \"\";\n    \n    /**\n     * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.\n     */\n    private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>();\n    static {\n         primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);\n         primitiveWrapperMap.put(Byte.TYPE, Byte.class);\n         primitiveWrapperMap.put(Character.TYPE, Character.class);\n         primitiveWrapperMap.put(Short.TYPE, Short.class);\n         primitiveWrapperMap.put(Integer.TYPE, Integer.class);\n         primitiveWrapperMap.put(Long.TYPE, Long.class);\n         primitiveWrapperMap.put(Double.TYPE, Double.class);\n         primitiveWrapperMap.put(Float.TYPE, Float.class);\n         primitiveWrapperMap.put(Void.TYPE, Void.TYPE);\n    }\n\n    /**\n     * Maps wrapper {@code Class}es to their corresponding primitive types.\n     */\n    private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>();\n    static {\n        for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) {\n            Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass);\n            if (!primitiveClass.equals(wrapperClass)) {\n                wrapperPrimitiveMap.put(wrapperClass, primitiveClass);\n            }\n        }\n    }\n\n    /**\n     * Maps a primitive class name to its corresponding abbreviation used in array class names.\n     */\n    private static final Map<String, String> abbreviationMap = new HashMap<String, String>();\n\n    /**\n     * Maps an abbreviation used in array class names to corresponding primitive class name.\n     */\n    private static final Map<String, String> reverseAbbreviationMap = new HashMap<String, String>();\n\n    /**\n     * Add primitive type abbreviation to maps of abbreviations.\n     *\n     * @param primitive Canonical name of primitive type\n     * @param abbreviation Corresponding abbreviation of primitive type\n     */\n    private static void addAbbreviation(String primitive, String abbreviation) {\n        abbreviationMap.put(primitive, abbreviation);\n        reverseAbbreviationMap.put(abbreviation, primitive);\n    }\n\n    /**\n     * Feed abbreviation maps\n     */\n    static {\n        addAbbreviation(\"int\", \"I\");\n        addAbbreviation(\"boolean\", \"Z\");\n        addAbbreviation(\"float\", \"F\");\n        addAbbreviation(\"long\", \"J\");\n        addAbbreviation(\"short\", \"S\");\n        addAbbreviation(\"byte\", \"B\");\n        addAbbreviation(\"double\", \"D\");\n        addAbbreviation(\"char\", \"C\");\n    }\n\n    /**\n     * <p>ClassUtils instances should NOT be constructed in standard programming.\n     * Instead, the class should be used as\n     * {@code ClassUtils.getShortClassName(cls)}.</p>\n     *\n     * <p>This constructor is public to permit tools that require a JavaBean\n     * instance to operate.</p>\n     */\n    public ClassUtils() {\n      super();\n    }\n\n    // Short class name\n    // ----------------------------------------------------------------------\n    /**\n     * <p>Gets the class name minus the package name for an {@code Object}.</p>\n     *\n     * @param object  the class to get the short name for, may be null\n     * @param valueIfNull  the value to return if null\n     * @return the class name of the object without the package name, or the null value\n     */\n    public static String getShortClassName(Object object, String valueIfNull) {\n        if (object == null) {\n            return valueIfNull;\n        }\n        return getShortClassName(object.getClass());\n    }\n\n    /**\n     * <p>Gets the class name minus the package name from a {@code Class}.</p>\n     * \n     * <p>Consider using the Java 5 API {@link Class#getSimpleName()} instead. \n     * The one known difference is that this code will return {@code \"Map.Entry\"} while \n     * the {@code java.lang.Class} variant will simply return {@code \"Entry\"}. </p>\n     *\n     * @param cls  the class to get the short name for.\n     * @return the class name without the package name or an empty string\n     */\n    public static String getShortClassName(Class<?> cls) {\n        if (cls == null) {\n            return \"\";\n        }\n        return getShortClassName(cls.getName());\n    }\n\n    /**\n     * <p>Gets the class name minus the package name from a String.</p>\n     *\n     * <p>The string passed in is assumed to be a class name - it is not checked.</p>\n\n     * <p>Note that this method differs from Class.getSimpleName() in that this will \n     * return {@code \"Map.Entry\"} whilst the {@code java.lang.Class} variant will simply \n     * return {@code \"Entry\"}. </p>\n     *\n     * @param className  the className to get the short name for\n     * @return the class name of the class without the package name or an empty string\n     */\n    public static String getShortClassName(String className) {\n        if (className == null) {\n            return STRING_EMPTY;\n        }\n        if (className.length() == 0) {\n            return STRING_EMPTY;\n        }\n\n        StringBuilder arrayPrefix = new StringBuilder();\n\n        // Handle array encoding\n        if (className.startsWith(\"[\")) {\n            while (className.charAt(0) == '[') {\n                className = className.substring(1);\n                arrayPrefix.append(\"[]\");\n            }\n            // Strip Object type encoding\n            if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {\n                className = className.substring(1, className.length() - 1);\n            }\n        }\n\n        if (reverseAbbreviationMap.containsKey(className)) {\n            className = reverseAbbreviationMap.get(className);\n        }\n\n        int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);\n        int innerIdx = className.indexOf(\n                INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1);\n        String out = className.substring(lastDotIdx + 1);\n        if (innerIdx != -1) {\n            out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);\n        }\n        return out + arrayPrefix;\n    }\n\n    /**\n     * <p>Null-safe version of <code>aClass.getSimpleName()</code></p>\n     *\n     * @param cls the class for which to get the simple name.\n     * @return the simple class name.\n     * @since 3.0\n     * @see Class#getSimpleName()\n     */\n    public static String getSimpleName(Class<?> cls) {\n        if (cls == null) {\n            return STRING_EMPTY;\n        }\n        return cls.getSimpleName();\n    }\n\n    /**\n     * <p>Null-safe version of <code>aClass.getSimpleName()</code></p>\n     *\n     * @param object the object for which to get the simple class name.\n     * @param valueIfNull the value to return if <code>object</code> is <code>null</code>\n     * @return the simple class name.\n     * @since 3.0\n     * @see Class#getSimpleName()\n     */\n    public static String getSimpleName(Object object, String valueIfNull) {\n        if (object == null) {\n            return valueIfNull;\n        }\n        return getSimpleName(object.getClass());\n    }\n\n    // Package name\n    // ----------------------------------------------------------------------\n    /**\n     * <p>Gets the package name of an {@code Object}.</p>\n     *\n     * @param object  the class to get the package name for, may be null\n     * @param valueIfNull  the value to return if null\n     * @return the package name of the object, or the null value\n     */\n    public static String getPackageName(Object object, String valueIfNull) {\n        if (object == null) {\n            return valueIfNull;\n        }\n        return getPackageName(object.getClass());\n    }\n\n    /**\n     * <p>Gets the package name of a {@code Class}.</p>\n     *\n     * @param cls  the class to get the package name for, may be {@code null}.\n     * @return the package name or an empty string\n     */\n    public static String getPackageName(Class<?> cls) {\n        if (cls == null) {\n            return STRING_EMPTY;\n        }\n        return getPackageName(cls.getName());\n    }\n\n    /**\n     * <p>Gets the package name from a {@code String}.</p>\n     *\n     * <p>The string passed in is assumed to be a class name - it is not checked.</p>\n     * <p>If the class is unpackaged, return an empty string.</p>\n     *\n     * @param className  the className to get the package name for, may be {@code null}\n     * @return the package name or an empty string\n     */\n    public static String getPackageName(String className) {\n        if (className == null || className.length() == 0) {\n            return STRING_EMPTY;\n        }\n\n        // Strip array encoding\n        while (className.charAt(0) == '[') {\n            className = className.substring(1);\n        }\n        // Strip Object type encoding\n        if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {\n            className = className.substring(1);\n        }\n\n        int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);\n        if (i == -1) {\n            return STRING_EMPTY;\n        }\n        return className.substring(0, i);\n    }\n\n    // Superclasses/Superinterfaces\n    // ----------------------------------------------------------------------\n    /**\n     * <p>Gets a {@code List} of superclasses for the given class.</p>\n     *\n     * @param cls  the class to look up, may be {@code null}\n     * @return the {@code List} of superclasses in order going up from this one\n     *  {@code null} if null input\n     */\n    public static List<Class<?>> getAllSuperclasses(Class<?> cls) {\n        if (cls == null) {\n            return null;\n        }\n        List<Class<?>> classes = new ArrayList<Class<?>>();\n        Class<?> superclass = cls.getSuperclass();\n        while (superclass != null) {\n            classes.add(superclass);\n            superclass = superclass.getSuperclass();\n        }\n        return classes;\n    }\n\n    /**\n     * <p>Gets a {@code List} of all interfaces implemented by the given\n     * class and its superclasses.</p>\n     *\n     * <p>The order is determined by looking through each interface in turn as\n     * declared in the source file and following its hierarchy up. Then each\n     * superclass is considered in the same way. Later duplicates are ignored,\n     * so the order is maintained.</p>\n     *\n     * @param cls  the class to look up, may be {@code null}\n     * @return the {@code List} of interfaces in order,\n     *  {@code null} if null input\n     */\n    public static List<Class<?>> getAllInterfaces(Class<?> cls) {\n        if (cls == null) {\n            return null;\n        }\n\n        LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();\n        getAllInterfaces(cls, interfacesFound);\n\n        return new ArrayList<Class<?>>(interfacesFound);\n    }\n\n    /**\n     * Get the interfaces for the specified class.\n     *\n     * @param cls  the class to look up, may be {@code null}\n     * @param interfacesFound the {@code Set} of interfaces for the class\n     */\n    private static void getAllInterfaces(Class<?> cls, HashSet<Class<?>> interfacesFound) {\n        while (cls != null) {\n            Class<?>[] interfaces = cls.getInterfaces();\n\n            for (Class<?> i : interfaces) {\n                if (interfacesFound.add(i)) {\n                    getAllInterfaces(i, interfacesFound);\n                }\n            }\n\n            cls = cls.getSuperclass();\n         }\n     }\n\n    // Convert list\n    // ----------------------------------------------------------------------\n    /**\n     * <p>Given a {@code List} of class names, this method converts them into classes.</p>\n     *\n     * <p>A new {@code List} is returned. If the class name cannot be found, {@code null}\n     * is stored in the {@code List}. If the class name in the {@code List} is\n     * {@code null}, {@code null} is stored in the output {@code List}.</p>\n     *\n     * @param classNames  the classNames to change\n     * @return a {@code List} of Class objects corresponding to the class names,\n     *  {@code null} if null input\n     * @throws ClassCastException if classNames contains a non String entry\n     */\n    public static List<Class<?>> convertClassNamesToClasses(List<String> classNames) {\n        if (classNames == null) {\n            return null;\n        }\n        List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size());\n        for (String className : classNames) {\n            try {\n                classes.add(Class.forName(className));\n            } catch (Exception ex) {\n                classes.add(null);\n            }\n        }\n        return classes;\n    }\n\n    /**\n     * <p>Given a {@code List} of {@code Class} objects, this method converts\n     * them into class names.</p>\n     *\n     * <p>A new {@code List} is returned. {@code null} objects will be copied into\n     * the returned list as {@code null}.</p>\n     *\n     * @param classes  the classes to change\n     * @return a {@code List} of class names corresponding to the Class objects,\n     *  {@code null} if null input\n     * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry\n     */\n    public static List<String> convertClassesToClassNames(List<Class<?>> classes) {\n        if (classes == null) {\n            return null;\n        }\n        List<String> classNames = new ArrayList<String>(classes.size());\n        for (Class<?> cls : classes) {\n            if (cls == null) {\n                classNames.add(null);\n            } else {\n                classNames.add(cls.getName());\n            }\n        }\n        return classNames;\n    }\n\n    // Class loading\n    // ----------------------------------------------------------------------\n    /**\n     * Returns the class represented by {@code className} using the\n     * {@code classLoader}.  This implementation supports the syntaxes\n     * \"{@code java.util.Map.Entry[]}\", \"{@code java.util.Map$Entry[]}\",\n     * \"{@code [Ljava.util.Map.Entry;}\", and \"{@code [Ljava.util.Map$Entry;}\".\n     *\n     * @param classLoader  the class loader to use to load the class\n     * @param className  the class name\n     * @param initialize  whether the class must be initialized\n     * @return the class represented by {@code className} using the {@code classLoader}\n     * @throws ClassNotFoundException if the class is not found\n     */\n    public static Class<?> getClass(\n            ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException {\n        try {\n            Class<?> clazz;\n            if (abbreviationMap.containsKey(className)) {\n                String clsName = \"[\" + abbreviationMap.get(className);\n                clazz = Class.forName(clsName, initialize, classLoader).getComponentType();\n            } else {\n                clazz = Class.forName(toCanonicalName(className), initialize, classLoader);\n            }\n            return clazz;\n        } catch (ClassNotFoundException ex) {\n            // allow path separators (.) as inner class name separators\n            int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);\n\n            if (lastDotIndex != -1) {\n                try {\n                    return getClass(classLoader, className.substring(0, lastDotIndex) +\n                            INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1),\n                            initialize);\n                } catch (ClassNotFoundException ex2) { // NOPMD\n                    // ignore exception\n                }\n            }\n\n            throw ex;\n        }\n    }\n\n    /**\n     * Returns the (initialized) class represented by {@code className}\n     * using the {@code classLoader}.  This implementation supports\n     * the syntaxes \"{@code java.util.Map.Entry[]}\",\n     * \"{@code java.util.Map$Entry[]}\", \"{@code [Ljava.util.Map.Entry;}\",\n     * and \"{@code [Ljava.util.Map$Entry;}\".\n     *\n     * @param classLoader  the class loader to use to load the class\n     * @param className  the class name\n     * @return the class represented by {@code className} using the {@code classLoader}\n     * @throws ClassNotFoundException if the class is not found\n     */\n    public static Class<?> getClass(ClassLoader classLoader, String className) throws ClassNotFoundException {\n        return getClass(classLoader, className, true);\n    }\n\n    /**\n     * Returns the (initialized) class represented by {@code className}\n     * using the current thread's context class loader. This implementation\n     * supports the syntaxes \"{@code java.util.Map.Entry[]}\",\n     * \"{@code java.util.Map$Entry[]}\", \"{@code [Ljava.util.Map.Entry;}\",\n     * and \"{@code [Ljava.util.Map$Entry;}\".\n     *\n     * @param className  the class name\n     * @return the class represented by {@code className} using the current thread's context class loader\n     * @throws ClassNotFoundException if the class is not found\n     */\n    public static Class<?> getClass(String className) throws ClassNotFoundException {\n        return getClass(className, true);\n    }\n\n    /**\n     * Returns the class represented by {@code className} using the\n     * current thread's context class loader. This implementation supports the\n     * syntaxes \"{@code java.util.Map.Entry[]}\", \"{@code java.util.Map$Entry[]}\",\n     * \"{@code [Ljava.util.Map.Entry;}\", and \"{@code [Ljava.util.Map$Entry;}\".\n     *\n     * @param className  the class name\n     * @param initialize  whether the class must be initialized\n     * @return the class represented by {@code className} using the current thread's context class loader\n     * @throws ClassNotFoundException if the class is not found\n     */\n    public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException {\n        ClassLoader contextCL = Thread.currentThread().getContextClassLoader();\n        ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;\n        return getClass(loader, className, initialize);\n    }\n\n    // ----------------------------------------------------------------------\n    /**\n     * Converts a class name to a JLS style class name.\n     *\n     * @param className  the class name\n     * @return the converted name\n     */\n    private static String toCanonicalName(String className) {\n        className = deleteWhitespace(className);\n        if (className == null) {\n            throw new NullPointerException(\"className must not be null.\");\n        } else if (className.endsWith(\"[]\")) {\n            StringBuilder classNameBuffer = new StringBuilder();\n            while (className.endsWith(\"[]\")) {\n                className = className.substring(0, className.length() - 2);\n                classNameBuffer.append(\"[\");\n            }\n            String abbreviation = abbreviationMap.get(className);\n            if (abbreviation != null) {\n                classNameBuffer.append(abbreviation);\n            } else {\n                classNameBuffer.append(\"L\").append(className).append(\";\");\n            }\n            className = classNameBuffer.toString();\n        }\n        return className;\n    }\n\n    /**\n     * <p>Converts an array of {@code Object} in to an array of {@code Class} objects.\n     * If any of these objects is null, a null element will be inserted into the array.</p>\n     *\n     * <p>This method returns {@code null} for a {@code null} input array.</p>\n     *\n     * @param array an {@code Object} array\n     * @return a {@code Class} array, {@code null} if null array input\n     * @since 2.4\n     */\n    public static Class<?>[] toClass(Object... array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Class[0];\n        }\n        Class<?>[] classes = new Class[array.length];\n        for (int i = 0; i < array.length; i++) {\n            classes[i] = array[i] == null ? null : array[i].getClass();\n        }\n        return classes;\n    }\n    \n    // Delete\n    //-----------------------------------------------------------------------\n    /**\n     * <p>Deletes all whitespaces from a String as defined by\n     * {@link Character#isWhitespace(char)}.</p>\n     *\n     * <pre>\n     * StringUtils.deleteWhitespace(null)         = null\n     * StringUtils.deleteWhitespace(\"\")           = \"\"\n     * StringUtils.deleteWhitespace(\"abc\")        = \"abc\"\n     * StringUtils.deleteWhitespace(\"   ab  c  \") = \"abc\"\n     * </pre>\n     *\n     * @param str  the String to delete whitespace from, may be null\n     * @return the String without whitespaces, {@code null} if null String input\n     */\n    public static String deleteWhitespace(String str) {\n        if (isEmpty(str)) {\n            return str;\n        }\n        int sz = str.length();\n        char[] chs = new char[sz];\n        int count = 0;\n        for (int i = 0; i < sz; i++) {\n            if (!Character.isWhitespace(str.charAt(i))) {\n                chs[count++] = str.charAt(i);\n            }\n        }\n        if (count == sz) {\n            return str;\n        }\n        return new String(chs, 0, count);\n    }\n    \n    // Empty checks\n    //-----------------------------------------------------------------------\n    /**\n     * <p>Checks if a CharSequence is empty (\"\") or null.</p>\n     *\n     * <pre>\n     * StringUtils.isEmpty(null)      = true\n     * StringUtils.isEmpty(\"\")        = true\n     * StringUtils.isEmpty(\" \")       = false\n     * StringUtils.isEmpty(\"bob\")     = false\n     * StringUtils.isEmpty(\"  bob  \") = false\n     * </pre>\n     *\n     * <p>NOTE: This method changed in Lang version 2.0.\n     * It no longer trims the CharSequence.\n     * That functionality is available in isBlank().</p>\n     *\n     * @param cs  the CharSequence to check, may be null\n     * @return {@code true} if the CharSequence is empty or null\n     * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)\n     */\n    public static boolean isEmpty(CharSequence cs) {\n        return cs == null || cs.length() == 0;\n    }\n\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/DeviceCheck.java",
    "content": "/*\n * Original work Copyright (c) 2005-2008, The Android Open Source Project\n * Modified work Copyright (c) 2013, rovo89 and Tungstwenty\n * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.Log;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\npublic class DeviceCheck {\n\t\n\tprivate static final String SELECT_RUNTIME_PROPERTY = \"persist.sys.dalvik.vm.lib\";\n\tprivate static final String LIB_DALVIK = \"libdvm.so\";\n\tprivate static final String LIB_ART = \"libart.so\";\n\tprivate static final String LIB_ART_D = \"libartd.so\";\n\t\n\tprivate static boolean isCheckedDeviceSupport = false; \n\tprivate static boolean isDeviceSupportable = false;\n\t\n\tprivate static boolean isDalvikMode() {\n        String vmMode = getCurrentRuntimeValue();\n        if(\"Dalvik\".equals(vmMode)){\n            return true;\n        }        \n        return false;\n    }\n    \n\tprivate static String getCurrentRuntimeValue() {\n\t\ttry {\n\t\t\tClass<?> systemProperties = Class\n\t\t\t\t\t.forName(\"android.os.SystemProperties\");\n\t\t\ttry {\n\t\t\t\tMethod get = systemProperties.getMethod(\"get\", String.class,\n\t\t\t\t\t\tString.class);\n\t\t\t\tif (get == null) {\n\t\t\t\t\treturn \"WTF?!\";\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tfinal String value = (String) get.invoke(systemProperties,\n\t\t\t\t\t\t\tSELECT_RUNTIME_PROPERTY,\n\t\t\t\t\t\t\t/* Assuming default is */\"Dalvik\");\n\t\t\t\t\tif (LIB_DALVIK.equals(value)) {\n\t\t\t\t\t\treturn \"Dalvik\";\n\t\t\t\t\t} else if (LIB_ART.equals(value)) {\n\t\t\t\t\t\treturn \"ART\";\n\t\t\t\t\t} else if (LIB_ART_D.equals(value)) {\n\t\t\t\t\t\treturn \"ART debug build\";\n\t\t\t\t\t}\n\n\t\t\t\t\treturn value;\n\t\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\t\treturn \"IllegalAccessException\";\n\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\treturn \"IllegalArgumentException\";\n\t\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\t\treturn \"InvocationTargetException\";\n\t\t\t\t}\n\t\t\t} catch (NoSuchMethodException e) {\n\t\t\t\treturn \"SystemProperties.get(String key, String def) method is not found\";\n\t\t\t}\n\t\t} catch (ClassNotFoundException e) {\n\t\t\treturn \"SystemProperties class is not found\";\n\t\t}\n\t}\n    \n    private static boolean isSupportSDKVersion() {\n        if (android.os.Build.VERSION.SDK_INT >= 14 && android.os.Build.VERSION.SDK_INT < 20) {\n            return true;\n        } else if(android.os.Build.VERSION.SDK_INT == 10 || android.os.Build.VERSION.SDK_INT == 9){\n        \treturn true;\n        }\n        return false;\n    }\n\n\tprivate static boolean isX86CPU() {\n\t\tProcess process = null;\n\t\tString abi = null;\n\t\tInputStreamReader ir = null;\n\t\tBufferedReader input = null;\n\t\ttry {\n\t\t\tprocess = Runtime.getRuntime().exec(\"getprop ro.product.cpu.abi\");\n\t\t\tir = new InputStreamReader(process.getInputStream());\n\t\t\tinput = new BufferedReader(ir);\n\t\t\tabi = input.readLine();\n\t\t\tif (abi.contains(\"x86\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t} finally {\n\t\t\tif (input != null) {\n\t\t\t\ttry {\n\t\t\t\t\tinput.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ir != null) {\n\t\t\t\ttry {\n\t\t\t\t\tir.close();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (process != null) {\n\t\t\t\ttry {\n\t\t\t\t\tprocess.destroy();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\t\n\tpublic static synchronized boolean isDeviceSupport(Context context) {\n\t\t// return memory checked value.\n\t\ttry {\n\t\t\tif (isCheckedDeviceSupport)\n\t\t\t\treturn isDeviceSupportable;\n\n\t\t\tif (!isX86CPU() && !isYunOS()) {\n\t\t\t\tisDeviceSupportable = true;\n\t\t\t} else {\n\t\t\t\tisDeviceSupportable = false;\n\t\t\t}\n\t\t} finally {\n\t\t\tLog.d(\"hotpatch\", \"device support is \" + isDeviceSupportable + \"checked\" + isCheckedDeviceSupport);\n\t\t\tisCheckedDeviceSupport = true;\n\t\t}\n\t\treturn isDeviceSupportable;\n\t}\n\n\t@SuppressLint(\"DefaultLocale\")\n\tpublic static boolean isYunOS() {\n\t\tString s1 = null;\n\t\tString s2 = null;\n\t\ttry {\n\t\t\tMethod m = Class.forName(\"android.os.SystemProperties\").getMethod(\n\t\t\t\t\t\"get\", String.class);\n\t\t\ts1 = (String) m.invoke(null, \"ro.yunos.version\");\n\t\t\ts2 = (String) m.invoke(null, \"java.vm.name\");\n\t\t} catch (NoSuchMethodException a) {\n\t\t} catch (ClassNotFoundException b) {\n\t\t} catch (IllegalAccessException c) {\n\t\t} catch (InvocationTargetException d) {\n\t\t}\n\t\tif ((s2 != null && s2.toLowerCase().contains(\"lemur\")) || (s1 != null && s1.trim().length() > 0)) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/Debug.java",
    "content": "/*\n * Original work Copyright (c) 2014-2015, Marvin Wißfeld\n * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed.utility;\n\nimport android.util.Log;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\n\nimport me.weishu.epic.BuildConfig;\nimport me.weishu.epic.art.method.ArtMethod;\n\npublic final class Debug {\n    private static final String TAG = \"Dexposed\";\n\n    public static final boolean DEBUG = BuildConfig.DEBUG;\n\n    private static final String RELASE_WRAN_STRING = \"none in release mode.\";\n    private Debug() {\n    }\n\n    public static String addrHex(long i) {\n        if (!DEBUG) {\n            return RELASE_WRAN_STRING;\n        }\n\n        if (Runtime.is64Bit()) {\n            return longHex(i);\n        } else {\n            return intHex((int) i);\n        }\n    }\n\n    public static String longHex(long i) {\n        return String.format(\"0x%016X\", i);\n    }\n\n    public static String intHex(int i) {\n        return String.format(\"0x%08X\", i);\n    }\n\n    public static String byteHex(byte b) {\n        return String.format(\"%02X\", b);\n    }\n\n    public static String dump(byte[] b, long start) {\n        StringBuffer sb = new StringBuffer();\n        for (int i = 0; i < b.length; i++) {\n            if (i % 8 == 0) {\n                sb.append(addrHex(start + i)).append(\":\");\n            }\n            sb.append(byteHex(b[i])).append(\" \");\n            if (i % 8 == 7) {\n                sb.append(\"\\n\");\n            }\n        }\n        return sb.toString();\n    }\n    public static String hexdump(byte[] bytes, long start) {\n        if (!DEBUG) {\n            return RELASE_WRAN_STRING;\n        }\n        StringBuffer sb = new StringBuffer();\n        for (int i = 0; i < bytes.length; i++) {\n            if (i % 8 == 0) {\n                sb.append(addrHex(start + i)).append(\":\");\n            }\n            sb.append(byteHex(bytes[i])).append(\" \");\n            if (i % 8 == 7) {\n                sb.append(\"\\n\");\n            }\n        }\n        return sb.toString();\n    }\n\n    public static String methodDescription(Method method) {\n        return method.getDeclaringClass().getName() + \"->\" + method.getName() + \" @\" +\n                addrHex(ArtMethod.of(method).getEntryPointFromQuickCompiledCode()) +\n                \" +\" + addrHex(ArtMethod.of(method).getAddress());\n    }\n\n    public static void dumpMaps() {\n        BufferedReader br = null;\n        try {\n            br = new BufferedReader(new FileReader(\"/proc/self/maps\"));\n            String line;\n            while ((line = br.readLine()) != null) {\n                Log.i(TAG, line);\n            }\n        } catch (IOException e) {\n            Log.e(TAG, \"dumpMaps error\");\n        } finally {\n            if (br != null) {\n                try {\n                    br.close();\n                } catch (IOException e) {\n                    // ignore.\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/Logger.java",
    "content": "package com.taobao.android.dexposed.utility;\n\nimport android.util.Log;\n\n/**\n * Created by weishu on 17/11/10.\n */\npublic class Logger {\n\n    private static final boolean DEBUG = Debug.DEBUG;\n\n    public static final String preFix = \"epic.\";\n\n    public static void i(String tag, String msg) {\n        if (DEBUG) {\n            Log.i(preFix + tag, msg);\n        }\n    }\n\n    public static void d(String tagSuffix, String msg) {\n        if (DEBUG) {\n            Log.d(preFix + tagSuffix, msg);\n        }\n    }\n\n    public static void w(String tag, String msg) {\n        Log.w(preFix + tag, msg);\n    }\n\n    public static void e(String tag, String msg) {\n        if (DEBUG) {\n            Log.e(preFix + tag, msg);\n        }\n    }\n\n    public static void e(String tag, String msg, Throwable e) {\n        if (DEBUG) {\n            Log.e(preFix + tag, msg, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/NeverCalled.java",
    "content": "package com.taobao.android.dexposed.utility;\n\nimport android.util.Log;\n\n/**\n * This Class is used for get the art_quick_to_interpreter_bridge address\n * Do not call this forever!!!\n */\npublic class NeverCalled {\n    private void fake(int a) {\n        Log.i(getClass().getSimpleName(), a + \"Do not inline me!!\");\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/NougatPolicy.java",
    "content": "package com.taobao.android.dexposed.utility;\n\nimport android.content.Context;\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport java.lang.reflect.Method;\n\npublic class NougatPolicy {\n\n    private static class TraceLogger {\n        static void i(String tag, String msg) {\n            Log.i(tag, msg);\n        }\n        static void e(String tag, String msg) {\n            Log.i(tag, msg);\n        }\n        static void e(String tag, String msg, Throwable e) {\n            Log.i(tag, msg, e);\n        }\n    }\n\n    private static final String TAG = \"NougatPolicy\";\n\n    public static boolean fullCompile(Context context) {\n        try {\n            long t1 = SystemClock.elapsedRealtime();\n            Object pm = getPackageManagerBinderProxy();\n            if (pm == null) {\n                TraceLogger.e(TAG, \"can not found package service\");\n                return false;\n            }\n            /*\n            @Override\n            public boolean performDexOptMode(String packageName,\n            boolean checkProfiles, String targetCompilerFilter, boolean force) {\n                int dexOptStatus = performDexOptTraced(packageName, checkProfiles,\n                        targetCompilerFilter, force);\n                return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;\n            */\n\n            final Method performDexOptMode = pm.getClass().getDeclaredMethod(\"performDexOptMode\",\n                    String.class, boolean.class, String.class, boolean.class);\n            boolean ret = (boolean) performDexOptMode.invoke(pm, context.getPackageName(), false, \"speed\", true);\n            long cost = SystemClock.elapsedRealtime() - t1;\n            Log.i(TAG, \"full Compile cost: \" + cost + \" result:\" + ret);\n            return ret;\n        } catch (Throwable e) {\n            TraceLogger.e(TAG, \"fullCompile failed:\", e);\n            return false;\n        }\n    }\n\n    public static boolean clearCompileData(Context context) {\n        boolean ret;\n        try {\n            Object pm = getPackageManagerBinderProxy();\n            final Method performDexOpt = pm.getClass().getDeclaredMethod(\"performDexOpt\", String.class,\n                    boolean.class, int.class, boolean.class);\n            ret = (Boolean) performDexOpt.invoke(pm, context.getPackageName(), false, 2 /*install*/, true);\n        } catch (Throwable e) {\n            TraceLogger.e(TAG, \"clear compile data failed\", e);\n            ret = false;\n        }\n        return ret;\n    }\n\n    private static Object getPackageManagerBinderProxy() throws Exception {\n        Class<?> activityThread = Class.forName(\"android.app.ActivityThread\");\n        final Method getPackageManager = activityThread.getDeclaredMethod(\"getPackageManager\");\n        return getPackageManager.invoke(null);\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/Platform.java",
    "content": "/*\n * Original work Copyright (c) 2016, Lody\n * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed.utility;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\npublic abstract class Platform {\n\n    /*package*/ static Platform PLATFORM_INTERNAL;\n\n    static {\n        if (Runtime.is64Bit()) {\n            PLATFORM_INTERNAL = new Platform64Bit();\n        }else {\n            PLATFORM_INTERNAL = new Platform32Bit();\n        }\n    }\n\n    public static Platform getPlatform() {\n        return PLATFORM_INTERNAL;\n    }\n\n    /**\n     * Convert a byte array to int,\n     * Use this function to get address from memory.\n     *\n     * @param data byte array\n     * @return long\n     */\n    public abstract int orderByteToInt(byte[] data);\n\n    /**\n     * Convert a byte array to long,\n     * Use this function to get address from memory.\n     *\n     * @param data byte array\n     * @return long\n     */\n    public abstract long orderByteToLong(byte[] data);\n\n    public abstract byte[] orderLongToByte(long serial, int length);\n\n    public abstract byte[] orderIntToByte(int serial);\n\n    public abstract int getIntSize();\n\n\n    static class Platform32Bit extends Platform {\n\n        @Override\n        public int orderByteToInt(byte[] data) {\n            return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();\n        }\n\n        @Override\n        public long orderByteToLong(byte[] data) {\n            return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;\n        }\n\n        @Override\n        public byte[] orderLongToByte(long serial, int length) {\n            return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putInt((int) serial).array();\n        }\n\n        @Override\n        public byte[] orderIntToByte(int serial) {\n            return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();\n        }\n\n        @Override\n        public int getIntSize() {\n            return 4;\n        }\n\n\n    }\n\n    static class Platform64Bit extends Platform {\n\n        @Override\n        public int orderByteToInt(byte[] data) {\n            return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();\n        }\n\n        @Override\n        public long orderByteToLong(byte[] data) {\n            return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getLong();\n        }\n\n        @Override\n        public byte[] orderLongToByte(long serial, int length) {\n            return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putLong(serial).array();\n        }\n\n        @Override\n        public byte[] orderIntToByte(int serial) {\n            return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();\n        }\n\n        @Override\n        public int getIntSize() {\n            return 8;\n        }\n    }\n\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/Runtime.java",
    "content": "/*\n * Original work Copyright (c) 2016, Lody\n * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed.utility;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Method;\n\nimport me.weishu.epic.art.method.ArtMethod;\n\npublic class Runtime {\n\n    private static final String TAG = \"Runtime\";\n\n    private volatile static Boolean isThumb = null;\n\n    private volatile static boolean g64 = false;\n    private volatile static boolean isArt = true;\n\n    static {\n        try {\n            g64 = (boolean) Class.forName(\"dalvik.system.VMRuntime\").getDeclaredMethod(\"is64Bit\").invoke(Class.forName(\"dalvik.system.VMRuntime\").getDeclaredMethod(\"getRuntime\").invoke(null));\n        } catch (Exception e) {\n            Log.e(TAG, \"get is64Bit failed, default not 64bit!\", e);\n            g64 = false;\n        }\n        isArt = System.getProperty(\"java.vm.version\").startsWith(\"2\");\n        Log.i(TAG, \"is64Bit: \" + g64 + \", isArt: \" + isArt);\n    }\n\n    public static boolean is64Bit() {\n        return g64;\n    }\n\n    public static boolean isArt() {\n        return isArt;\n    }\n\n    public static boolean isThumb2() {\n        if (isThumb != null) {\n            return isThumb;\n        }\n\n        try {\n            Method method = String.class.getDeclaredMethod(\"hashCode\");\n            ArtMethod artMethodStruct = ArtMethod.of(method);\n            long entryPointFromQuickCompiledCode = artMethodStruct.getEntryPointFromQuickCompiledCode();\n            Logger.w(\"Runtime\", \"isThumb2, entry: \" + Long.toHexString(entryPointFromQuickCompiledCode));\n            isThumb = ((entryPointFromQuickCompiledCode & 1) == 1);\n            return isThumb;\n        } catch (Throwable e) {\n            Logger.w(\"Runtime\", \"isThumb2, error: \" + e);\n            return true; // Default Thumb2.\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/taobao/android/dexposed/utility/Unsafe.java",
    "content": "/*\n * Copyright 2014-2015 Marvin Wißfeld\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.taobao.android.dexposed.utility;\n\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\n\npublic final class Unsafe {\n    private static final String TAG = \"Unsafe\";\n\n    private static Object unsafe;\n    private static Class unsafeClass;\n\n    static {\n        try {\n            unsafeClass = Class.forName(\"sun.misc.Unsafe\");\n            Field theUnsafe = unsafeClass.getDeclaredField(\"theUnsafe\");\n            theUnsafe.setAccessible(true);\n            unsafe = theUnsafe.get(null);\n        } catch (Exception e) {\n            try {\n                final Field theUnsafe = unsafeClass.getDeclaredField(\"THE_ONE\");\n                theUnsafe.setAccessible(true);\n                unsafe = theUnsafe.get(null);\n            } catch (Exception e2) {\n                Log.w(TAG, \"Unsafe not found o.O\");\n            }\n        }\n    }\n\n    private Unsafe() {\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static int arrayBaseOffset(Class cls) {\n        try {\n            return (int) unsafeClass.getDeclaredMethod(\"arrayBaseOffset\", Class.class).invoke(unsafe, cls);\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return 0;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static int arrayIndexScale(Class cls) {\n        try {\n            return (int) unsafeClass.getDeclaredMethod(\"arrayIndexScale\", Class.class).invoke(unsafe, cls);\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return 0;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static long objectFieldOffset(Field field) {\n        try {\n            return (long) unsafeClass.getDeclaredMethod(\"objectFieldOffset\", Field.class).invoke(unsafe, field);\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return 0;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static int getInt(Object array, long offset) {\n        try {\n            return (int) unsafeClass.getDeclaredMethod(\"getInt\", Object.class, long.class).invoke(unsafe, array, offset);\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return 0;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static long getLong(Object array, long offset) {\n        try {\n            return (long) unsafeClass.getDeclaredMethod(\"getLong\", Object.class, long.class).invoke(unsafe, array, offset);\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return 0;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static void putLong(Object array, long offset, long value) {\n        try {\n            unsafeClass.getDeclaredMethod(\"putLongVolatile\", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);\n        } catch (Exception e) {\n            try {\n                unsafeClass.getDeclaredMethod(\"putLong\", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);\n            } catch (Exception e1) {\n                Log.w(TAG, e);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static void putInt(Object array, long offset, int value) {\n        try {\n            unsafeClass.getDeclaredMethod(\"putIntVolatile\", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);\n        } catch (Exception e) {\n            try {\n                unsafeClass.getDeclaredMethod(\"putIntVolatile\", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);\n            } catch (Exception e1) {\n                Log.w(TAG, e);\n            }\n        }\n    }\n\n    public static long getObjectAddress(Object obj) {\n        try {\n            Object[] array = new Object[]{obj};\n            if (arrayIndexScale(Object[].class) == 8) {\n                return getLong(array, arrayBaseOffset(Object[].class));\n            } else {\n                return 0xffffffffL & getInt(array, arrayBaseOffset(Object[].class));\n            }\n        } catch (Exception e) {\n            Log.w(TAG, e);\n            return -1;\n        }\n    }\n\n    /**\n     * get Object from address, refer: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/\n     * @param address the address of a object.\n     * @return\n     */\n    public static Object getObject(long address) {\n        Object[] array = new Object[]{null};\n        long baseOffset = arrayBaseOffset(Object[].class);\n        if (Runtime.is64Bit()) {\n            putLong(array, baseOffset, address);\n        } else {\n            putInt(array, baseOffset, (int) address);\n        }\n        return array[0];\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/de/robv/android/xposed/DexposedBridge.java",
    "content": "/*\n * Original work Copyright (c) 2005-2008, The Android Open Source Project\n * Modified work Copyright (c) 2013, rovo89 and Tungstwenty\n * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team\n * Modified work Copyright (c) 2017, weishu\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage de.robv.android.xposed;\n\nimport android.app.AndroidAppHelper;\nimport android.os.Build;\nimport android.util.Log;\n\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.Runtime;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport me.weishu.epic.art.Epic;\nimport me.weishu.epic.art.method.ArtMethod;\nimport me.weishu.reflection.Reflection;\n\nimport static de.robv.android.xposed.XposedHelpers.getIntField;\n\npublic final class DexposedBridge {\n\n\tstatic {\n\t\ttry {\n\t\t\tif (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {\n\t\t\t\tSystem.loadLibrary(\"epic\");\n\t\t\t} else if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH){\n\t\t\t\tSystem.loadLibrary(\"dexposed\");\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(\"unsupported api level: \" + Build.VERSION.SDK_INT);\n\t\t\t}\n\t\t\tReflection.unseal(AndroidAppHelper.currentApplication());\n\t\t} catch (Throwable e) {\n\t\t\tlog(e);\n\t\t}\n\t}\n\n\tprivate static final String TAG = \"DexposedBridge\";\n\n\tprivate static final Object[] EMPTY_ARRAY = new Object[0];\n\tpublic static final ClassLoader BOOTCLASSLOADER = ClassLoader.getSystemClassLoader();\n\n\n\t// built-in handlers\n\tprivate static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> hookedMethodCallbacks\n\t\t\t\t\t\t\t\t\t= new HashMap<Member, CopyOnWriteSortedSet<XC_MethodHook>>();\n\n\tprivate static final ArrayList<XC_MethodHook.Unhook> allUnhookCallbacks = new ArrayList<XC_MethodHook.Unhook>();\n\n\n\t/**\n\t * Writes a message to BASE_DIR/log/debug.log (needs to have chmod 777)\n\t * @param text log message\n\t */\n\tpublic synchronized static void log(String text) {\n\t\tLog.i(TAG, text);\n\t}\n\n\t/**\n\t * Log the stack trace\n\t * @param t The Throwable object for the stacktrace\n\t * @see DexposedBridge#log(String)\n\t */\n\tpublic synchronized static void log(Throwable t) {\n\t\tlog(Log.getStackTraceString(t));\n\t}\n\n\t/**\n\t * Hook any method with the specified callback\n\t *\n\t * @param hookMethod The method to be hooked\n\t * @param callback\n\t */\n\tpublic static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {\n\t\tif (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {\n\t\t\tthrow new IllegalArgumentException(\"only methods and constructors can be hooked\");\n\t\t}\n\n\t\tboolean newMethod = false;\n\t\tCopyOnWriteSortedSet<XC_MethodHook> callbacks;\n\t\tsynchronized (hookedMethodCallbacks) {\n\t\t\tcallbacks = hookedMethodCallbacks.get(hookMethod);\n\t\t\tif (callbacks == null) {\n\t\t\t\tcallbacks = new CopyOnWriteSortedSet<XC_MethodHook>();\n\t\t\t\thookedMethodCallbacks.put(hookMethod, callbacks);\n\t\t\t\tnewMethod = true;\n\t\t\t}\n\t\t}\n\n\t\tLogger.w(TAG, \"hook: \" + hookMethod + \", newMethod ? \" + newMethod);\n\n\t\tcallbacks.add(callback);\n\t\tif (newMethod) {\n\t\t\tif (Runtime.isArt()) {\n\t\t\t\tif (hookMethod instanceof Method) {\n\t\t\t\t\tEpic.hookMethod(((Method) hookMethod));\n\t\t\t\t} else {\n\t\t\t\t\tEpic.hookMethod(((Constructor) hookMethod));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tClass<?> declaringClass = hookMethod.getDeclaringClass();\n\t\t\t\tint slot = getIntField(hookMethod, \"slot\");\n\n\t\t\t\tClass<?>[] parameterTypes;\n\t\t\t\tClass<?> returnType;\n\t\t\t\tif (hookMethod instanceof Method) {\n\t\t\t\t\tparameterTypes = ((Method) hookMethod).getParameterTypes();\n\t\t\t\t\treturnType = ((Method) hookMethod).getReturnType();\n\t\t\t\t} else {\n\t\t\t\t\tparameterTypes = ((Constructor<?>) hookMethod).getParameterTypes();\n\t\t\t\t\treturnType = null;\n\t\t\t\t}\n\n\t\t\t\tAdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);\n\t\t\t\thookMethodNative(hookMethod, declaringClass, slot, additionalInfo);\n\t\t\t}\n\t\t}\n\t\treturn callback.new Unhook(hookMethod);\n\t}\n\n\t/**\n\t * Removes the callback for a hooked method\n\t * @param hookMethod The method for which the callback should be removed\n\t * @param callback The reference to the callback as specified in {@link #hookMethod}\n\t */\n\tpublic static void unhookMethod(Member hookMethod, XC_MethodHook callback) {\n\t\tCopyOnWriteSortedSet<XC_MethodHook> callbacks;\n\t\tsynchronized (hookedMethodCallbacks) {\n\t\t\tcallbacks = hookedMethodCallbacks.get(hookMethod);\n\t\t\tif (callbacks == null)\n\t\t\t\treturn;\n\t\t}\n\t\tcallbacks.remove(callback);\n\t}\n\n\tpublic static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {\n\t\tSet<XC_MethodHook.Unhook> unhooks = new HashSet<XC_MethodHook.Unhook>();\n\t\tfor (Member method : hookClass.getDeclaredMethods())\n\t\t\tif (method.getName().equals(methodName))\n\t\t\t\tunhooks.add(hookMethod(method, callback));\n\t\treturn unhooks;\n\t}\n\n\tpublic static XC_MethodHook.Unhook findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {\n\t\tif (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof XC_MethodHook))\n\t\t\tthrow new IllegalArgumentException(\"no callback defined\");\n\n\t\tXC_MethodHook callback = (XC_MethodHook) parameterTypesAndCallback[parameterTypesAndCallback.length-1];\n\t\tMethod m = XposedHelpers.findMethodExact(clazz, methodName, parameterTypesAndCallback);\n\t\tXC_MethodHook.Unhook unhook = hookMethod(m, callback);\n\t\tsynchronized (allUnhookCallbacks) {\n\t\t\tallUnhookCallbacks.add(unhook);\n\t\t}\n\t\treturn unhook;\n\t}\n\n\tpublic static void unhookAllMethods() {\n\t\tsynchronized (allUnhookCallbacks) {\n\t\t\tfor (int i = 0; i < allUnhookCallbacks.size(); i++) {\n\t\t\t\t((XC_MethodHook.Unhook) allUnhookCallbacks.get(i)).unhook();\n\t\t\t}\n\t\t\tallUnhookCallbacks.clear();\n\t\t}\n\t}\n\n\tpublic static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {\n\t\tSet<XC_MethodHook.Unhook> unhooks = new HashSet<XC_MethodHook.Unhook>();\n\t\tfor (Member constructor : hookClass.getDeclaredConstructors())\n\t\t\tunhooks.add(hookMethod(constructor, callback));\n\t\treturn unhooks;\n\t}\n\n\n\tpublic static Object handleHookedArtMethod(Object artMethodObject, Object thisObject, Object[] args) {\n\n\t\tCopyOnWriteSortedSet<XC_MethodHook> callbacks;\n\n\t\tArtMethod artmethod = (ArtMethod ) artMethodObject;\n\t\tsynchronized (hookedMethodCallbacks) {\n\t\t\tcallbacks = hookedMethodCallbacks.get(artmethod.getExecutable());\n\t\t}\n\t\tObject[] callbacksSnapshot = callbacks.getSnapshot();\n\t\tfinal int callbacksLength = callbacksSnapshot.length;\n\t\t//Logger.d(TAG, \"callbacksLength:\" + callbacksLength +  \", this:\" + thisObject + \", args:\" + Arrays.toString(args));\n\t\tif (callbacksLength == 0) {\n\t\t\ttry {\n\t\t\t\tArtMethod method = Epic.getBackMethod(artmethod);\n\t\t\t\treturn method.invoke(thisObject, args);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog(e.getCause());\n\t\t\t}\n\t\t}\n\n\t\tXC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();\n\t\tparam.method  = (Member) (artmethod).getExecutable();\n\t\tparam.thisObject = thisObject;\n\t\tparam.args = args;\n\n\t\t// call \"before method\" callbacks\n\t\tint beforeIdx = 0;\n\t\tdo {\n\t\t\ttry {\n\t\t\t\t((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tlog(t);\n\n\t\t\t\t// reset result (ignoring what the unexpectedly exiting callback did)\n\t\t\t\tparam.setResult(null);\n\t\t\t\tparam.returnEarly = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (param.returnEarly) {\n\t\t\t\t// skip remaining \"before\" callbacks and corresponding \"after\" callbacks\n\t\t\t\tbeforeIdx++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} while (++beforeIdx < callbacksLength);\n\n\t\t// call original method if not requested otherwise\n\t\tif (!param.returnEarly) {\n\t\t\ttry {\n\t\t\t\tArtMethod method = Epic.getBackMethod(artmethod);\n\t\t\t\tObject result = method.invoke(thisObject, args);\n\t\t\t\tparam.setResult(result);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// log(e); origin throw exception is normal.\n\t\t\t\tparam.setThrowable(e);\n\t\t\t}\n\t\t}\n\n\t\t// call \"after method\" callbacks\n\t\tint afterIdx = beforeIdx - 1;\n\t\tdo {\n\t\t\tObject lastResult =  param.getResult();\n\t\t\tThrowable lastThrowable = param.getThrowable();\n\n\t\t\ttry {\n\t\t\t\t((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tDexposedBridge.log(t);\n\n\t\t\t\t// reset to last result (ignoring what the unexpectedly exiting callback did)\n\t\t\t\tif (lastThrowable == null)\n\t\t\t\t\tparam.setResult(lastResult);\n\t\t\t\telse\n\t\t\t\t\tparam.setThrowable(lastThrowable);\n\t\t\t}\n\t\t} while (--afterIdx >= 0);\n\n\t\tif (param.hasThrowable()) {\n\t\t\tfinal Throwable throwable = param.getThrowable();\n\t\t\tif (throwable instanceof IllegalAccessException || throwable instanceof InvocationTargetException\n\t\t\t\t\t|| throwable instanceof InstantiationException) {\n\t\t\t\t// reflect exception, get the origin cause\n\t\t\t\tfinal Throwable cause = throwable.getCause();\n\n\t\t\t\t// We can not change the exception flow of origin call, rethrow\n\t\t\t\t// Logger.e(TAG, \"origin call throw exception (not a real crash, just record for debug):\", cause);\n\t\t\t\tDexposedBridge.<RuntimeException>throwNoCheck(param.getThrowable().getCause(), null);\n\t\t\t\treturn null; //never reach.\n\t\t\t} else {\n\t\t\t\t// the exception cause by epic self, just log.\n\t\t\t\tLogger.e(TAG, \"epic cause exception in call bridge!!\", throwable);\n\t\t\t}\n\t\t\treturn null; // never reached.\n\t\t} else {\n\t\t\tfinal Object result = param.getResult();\n\t\t\t//Logger.d(TAG, \"return :\" + result);\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t/**\n\t * Just for throw an checked exception without check\n\t * @param exception The checked exception.\n\t * @param dummy dummy.\n\t * @param <T> fake type\n\t * @throws T the checked exception.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T extends Throwable> void throwNoCheck(Throwable exception, Object dummy) throws T {\n\t\tthrow (T) exception;\n\t}\n\n\t/**\n\t * This method is called as a replacement for hooked methods.\n\t */\n\tprivate static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,\n\t\t\tObject thisObject, Object[] args) throws Throwable {\n\t\tAdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;\n\n\t\tObject[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();\n\t\tfinal int callbacksLength = callbacksSnapshot.length;\n\t\tif (callbacksLength == 0) {\n\t\t\ttry {\n\t\t\t\treturn invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,\n\t\t\t\t\t\tadditionalInfo.returnType, thisObject, args);\n\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\tthrow e.getCause();\n\t\t\t}\n\t\t}\n\n\t\tXC_MethodHook.MethodHookParam param = new XC_MethodHook.MethodHookParam();\n\t\tparam.method  = method;\n\t\tparam.thisObject = thisObject;\n\t\tparam.args = args;\n\n\t\t// call \"before method\" callbacks\n\t\tint beforeIdx = 0;\n\t\tdo {\n\t\t\ttry {\n\t\t\t\t((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tlog(t);\n\n\t\t\t\t// reset result (ignoring what the unexpectedly exiting callback did)\n\t\t\t\tparam.setResult(null);\n\t\t\t\tparam.returnEarly = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (param.returnEarly) {\n\t\t\t\t// skip remaining \"before\" callbacks and corresponding \"after\" callbacks\n\t\t\t\tbeforeIdx++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} while (++beforeIdx < callbacksLength);\n\n\t\t// call original method if not requested otherwise\n\t\tif (!param.returnEarly) {\n\t\t\ttry {\n\t\t\t\tparam.setResult(invokeOriginalMethodNative(method, originalMethodId,\n\t\t\t\t\t\tadditionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));\n\t\t\t} catch (InvocationTargetException e) {\n\t\t\t\tparam.setThrowable(e.getCause());\n\t\t\t}\n\t\t}\n\n\t\t// call \"after method\" callbacks\n\t\tint afterIdx = beforeIdx - 1;\n\t\tdo {\n\t\t\tObject lastResult =  param.getResult();\n\t\t\tThrowable lastThrowable = param.getThrowable();\n\n\t\t\ttry {\n\t\t\t\t((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);\n\t\t\t} catch (Throwable t) {\n\t\t\t\tDexposedBridge.log(t);\n\n\t\t\t\t// reset to last result (ignoring what the unexpectedly exiting callback did)\n\t\t\t\tif (lastThrowable == null)\n\t\t\t\t\tparam.setResult(lastResult);\n\t\t\t\telse\n\t\t\t\t\tparam.setThrowable(lastThrowable);\n\t\t\t}\n\t\t} while (--afterIdx >= 0);\n\n\t\t// return\n\t\tif (param.hasThrowable())\n\t\t\tthrow param.getThrowable();\n\t\telse\n\t\t\treturn param.getResult();\n\t}\n\n\n\n\tprivate native static Object invokeSuperNative(Object obj, Object[] args, Member method, Class<?> declaringClass,\n            Class<?>[] parameterTypes, Class<?> returnType, int slot)\n                    throws IllegalAccessException, IllegalArgumentException,\n                            InvocationTargetException;\n\n\tpublic static Object invokeSuper(Object obj, Member method, Object... args) throws NoSuchFieldException {\n\n\t\ttry {\n\t\t\tint slot = 0;\n\t\t\tif(!Runtime.isArt()) {\n\t\t\t\t//get the super method slot\n\t\t\t\tMethod m = XposedHelpers.findMethodExact(obj.getClass().getSuperclass(), method.getName(), ((Method) method).getParameterTypes());\n\t\t\t\tslot =  (int) getIntField(m, \"slot\");\n\t\t\t}\n\n\t\t\treturn invokeSuperNative(obj, args, method, method.getDeclaringClass(), ((Method) method).getParameterTypes(), ((Method) method).getReturnType(), slot);\n\n\t\t} catch (IllegalAccessException e) {\n\t\t\tthrow new IllegalAccessError(e.getMessage());\n\t\t} catch (IllegalArgumentException e) {\n\t\t\tthrow e;\n\t\t} catch (InvocationTargetException e) {\n\t\t\tthrow new XposedHelpers.InvocationTargetError(e.getCause());\n\t\t}\n\t}\n\n\t/**\n\t * Intercept every call to the specified method and call a handler function instead.\n\t * @param method The method to intercept\n\t */\n\tprivate native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo);\n\n\tprivate native static Object invokeOriginalMethodNative(Member method, int methodId,\n\t\t\tClass<?>[] parameterTypes, Class<?> returnType, Object thisObject, Object[] args)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException;\n\n\n\t/**\n\t * Basically the same as {@link Method#invoke}, but calls the original method\n\t * as it was before the interception by Xposed. Also, access permissions are not checked.\n\t *\n\t * @param method Method to be called\n\t * @param thisObject For non-static calls, the \"this\" pointer\n\t * @param args Arguments for the method call as Object[] array\n\t * @return The result returned from the invoked method\n\t * @throws NullPointerException\n\t *             if {@code receiver == null} for a non-static method\n\t * @throws IllegalAccessException\n\t *             if this method is not accessible (see {@link AccessibleObject})\n\t * @throws IllegalArgumentException\n\t *             if the number of arguments doesn't match the number of parameters, the receiver\n\t *             is incompatible with the declaring class, or an argument could not be unboxed\n\t *             or converted by a widening conversion to the corresponding parameter type\n\t * @throws InvocationTargetException\n\t *             if an exception was thrown by the invoked method\n\n\t */\n\tpublic static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)\n\t\t\tthrows NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tif (args == null) {\n\t\t\targs = EMPTY_ARRAY;\n\t\t}\n\n\t\tClass<?>[] parameterTypes;\n\t\tClass<?> returnType;\n\t\tif (method instanceof Method) {\n\t\t\tparameterTypes = ((Method) method).getParameterTypes();\n\t\t\treturnType = ((Method) method).getReturnType();\n\t\t} else if (method instanceof Constructor) {\n\t\t\tparameterTypes = ((Constructor<?>) method).getParameterTypes();\n\t\t\treturnType = null;\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"method must be of type Method or Constructor\");\n\t\t}\n\n\t\tif (Runtime.isArt()) {\n\t\t\tArtMethod artMethod;\n\t\t\tif (method instanceof Method) {\n\t\t\t\tartMethod = ArtMethod.of((Method) method);\n\t\t\t} else {\n\t\t\t\tartMethod = ArtMethod.of((Constructor)method);\n\t\t\t}\n\t\t\ttry {\n                return Epic.getBackMethod(artMethod).invoke(thisObject, args);\n\t\t\t} catch (InstantiationException e) {\n                DexposedBridge.<RuntimeException>throwNoCheck(e, null);\n\t\t\t}\n\t\t}\n\t\treturn invokeOriginalMethodNative(method, 0, parameterTypes, returnType, thisObject, args);\n\t}\n\n\tpublic static class CopyOnWriteSortedSet<E> {\n\t\tprivate transient volatile Object[] elements = EMPTY_ARRAY;\n\n\t\tpublic synchronized boolean add(E e) {\n\t\t\tint index = indexOf(e);\n\t\t\tif (index >= 0)\n\t\t\t\treturn false;\n\n\t\t\tObject[] newElements = new Object[elements.length + 1];\n\t\t\tSystem.arraycopy(elements, 0, newElements, 0, elements.length);\n\t\t\tnewElements[elements.length] = e;\n\t\t\tArrays.sort(newElements);\n\t\t\telements = newElements;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic synchronized boolean remove(E e) {\n\t\t\tint index = indexOf(e);\n\t\t\tif (index == -1)\n\t\t\t\treturn false;\n\n\t\t\tObject[] newElements = new Object[elements.length - 1];\n\t\t\tSystem.arraycopy(elements, 0, newElements, 0, index);\n\t\t\tSystem.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);\n\t\t\telements = newElements;\n\t\t\treturn true;\n\t\t}\n\n\t\tpublic synchronized void clear(){\n\t\t\telements = EMPTY_ARRAY;\n\t\t}\n\n\t\tprivate int indexOf(Object o) {\n\t\t\tfor (int i = 0; i < elements.length; i++) {\n\t\t\t\tif (o.equals(elements[i]))\n\t\t\t\t\treturn i;\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\tpublic Object[] getSnapshot() {\n\t\t\treturn elements;\n\t\t}\n\t}\n\n\tprivate static class AdditionalHookInfo {\n\t\tfinal CopyOnWriteSortedSet<XC_MethodHook> callbacks;\n\t\tfinal Class<?>[] parameterTypes;\n\t\tfinal Class<?> returnType;\n\n\t\tprivate AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks, Class<?>[] parameterTypes, Class<?> returnType) {\n\t\t\tthis.callbacks = callbacks;\n\t\t\tthis.parameterTypes = parameterTypes;\n\t\t\tthis.returnType = returnType;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/Epic.java",
    "content": "/*\n * Copyright (c) 2017, weishu\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art;\n\nimport android.os.Build;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.Runtime;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport me.weishu.epic.art.arch.Arm64;\nimport me.weishu.epic.art.arch.ShellCode;\nimport me.weishu.epic.art.arch.Thumb2;\nimport me.weishu.epic.art.method.ArtMethod;\n\n/**\n * Hook Center.\n */\npublic final class Epic {\n\n    private static final String TAG = \"Epic\";\n\n    private static final Map<String, ArtMethod> backupMethodsMapping = new ConcurrentHashMap<>();\n\n    private static final Map<Long, MethodInfo> originSigs = new ConcurrentHashMap<>();\n\n    private static final Map<Long, Trampoline> scripts = new HashMap<>();\n    private static ShellCode ShellCode;\n\n    static {\n        boolean isArm = true; // TODO: 17/11/21 TODO\n        int apiLevel = Build.VERSION.SDK_INT;\n        boolean thumb2 = true;\n        if (isArm) {\n            if (Runtime.is64Bit()) {\n                ShellCode = new Arm64();\n            } else if (Runtime.isThumb2()) {\n                ShellCode = new Thumb2();\n            } else {\n                thumb2 = false;\n                ShellCode = new Thumb2();\n                Logger.w(TAG, \"ARM32, not support now.\");\n            }\n        }\n        if (ShellCode == null) {\n            throw new RuntimeException(\"Do not support this ARCH now!! API LEVEL:\" + apiLevel + \" thumb2 ? : \" + thumb2);\n        }\n        Logger.i(TAG, \"Using: \" + ShellCode.getName());\n    }\n\n    public static boolean hookMethod(Constructor origin) {\n        return hookMethod(ArtMethod.of(origin));\n    }\n\n    public static boolean hookMethod(Method origin) {\n        ArtMethod artOrigin = ArtMethod.of(origin);\n        return hookMethod(artOrigin);\n    }\n\n    private static boolean hookMethod(ArtMethod artOrigin) {\n\n        MethodInfo methodInfo = new MethodInfo();\n        methodInfo.isStatic = Modifier.isStatic(artOrigin.getModifiers());\n        final Class<?>[] parameterTypes = artOrigin.getParameterTypes();\n        if (parameterTypes != null) {\n            methodInfo.paramNumber = parameterTypes.length;\n            methodInfo.paramTypes = parameterTypes;\n        } else {\n            methodInfo.paramNumber = 0;\n            methodInfo.paramTypes = new Class<?>[0];\n        }\n        methodInfo.returnType = artOrigin.getReturnType();\n        methodInfo.method = artOrigin;\n        originSigs.put(artOrigin.getAddress(), methodInfo);\n\n        if (!artOrigin.isAccessible()) {\n            artOrigin.setAccessible(true);\n        }\n\n        artOrigin.ensureResolved();\n\n        long originEntry = artOrigin.getEntryPointFromQuickCompiledCode();\n        if (originEntry == ArtMethod.getQuickToInterpreterBridge()) {\n            Logger.i(TAG, \"this method is not compiled, compile it now. current entry: 0x\" + Long.toHexString(originEntry));\n            boolean ret = artOrigin.compile();\n            if (ret) {\n                originEntry = artOrigin.getEntryPointFromQuickCompiledCode();\n                Logger.i(TAG, \"compile method success, new entry: 0x\" + Long.toHexString(originEntry));\n            } else {\n                Logger.e(TAG, \"compile method failed...\");\n                return false;\n                // return hookInterpreterBridge(artOrigin);\n            }\n        }\n\n        ArtMethod backupMethod = artOrigin.backup();\n\n        Logger.i(TAG, \"backup method address:\" + Debug.addrHex(backupMethod.getAddress()));\n        Logger.i(TAG, \"backup method entry :\" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));\n\n        ArtMethod backupList = getBackMethod(artOrigin);\n        if (backupList == null) {\n            setBackMethod(artOrigin, backupMethod);\n        }\n\n        final long key = originEntry;\n        final EntryLock lock = EntryLock.obtain(originEntry);\n        //noinspection SynchronizationOnLocalVariableOrMethodParameter\n        synchronized (lock) {\n            if (!scripts.containsKey(key)) {\n                scripts.put(key, new Trampoline(ShellCode, originEntry));\n            }\n            Trampoline trampoline = scripts.get(key);\n            boolean ret = trampoline.install(artOrigin);\n            // Logger.d(TAG, \"hook Method result:\" + ret);\n            return ret;\n        }\n    }\n\n    /*\n    private static boolean hookInterpreterBridge(ArtMethod artOrigin) {\n\n        String identifier = artOrigin.getIdentifier();\n        ArtMethod backupMethod = artOrigin.backup();\n\n        Logger.d(TAG, \"backup method address:\" + Debug.addrHex(backupMethod.getAddress()));\n        Logger.d(TAG, \"backup method entry :\" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));\n\n        List<ArtMethod> backupList = backupMethodsMapping.get(identifier);\n        if (backupList == null) {\n            backupList = new LinkedList<ArtMethod>();\n            backupMethodsMapping.put(identifier, backupList);\n        }\n        backupList.add(backupMethod);\n\n        long originalEntryPoint = ShellCode.toMem(artOrigin.getEntryPointFromQuickCompiledCode());\n        Logger.d(TAG, \"originEntry Point(bridge):\" + Debug.addrHex(originalEntryPoint));\n\n        originalEntryPoint += 16;\n        Logger.d(TAG, \"originEntry Point(offset8):\" + Debug.addrHex(originalEntryPoint));\n\n        if (!scripts.containsKey(originalEntryPoint)) {\n            scripts.put(originalEntryPoint, new Trampoline(ShellCode, artOrigin));\n        }\n        Trampoline trampoline = scripts.get(originalEntryPoint);\n\n        boolean ret = trampoline.install();\n        Logger.i(TAG, \"hook Method result:\" + ret);\n        return ret;\n\n    }*/\n\n    public synchronized static ArtMethod getBackMethod(ArtMethod origin) {\n        String identifier = origin.getIdentifier();\n        return backupMethodsMapping.get(identifier);\n    }\n\n    public static synchronized void setBackMethod(ArtMethod origin, ArtMethod backup) {\n        String identifier = origin.getIdentifier();\n        backupMethodsMapping.put(identifier, backup);\n    }\n\n    public static MethodInfo getMethodInfo(long address) {\n        return originSigs.get(address);\n    }\n\n    public static int getQuickCompiledCodeSize(ArtMethod method) {\n\n        long entryPoint = ShellCode.toMem(method.getEntryPointFromQuickCompiledCode());\n        long sizeInfo1 = entryPoint - 4;\n        byte[] bytes = EpicNative.get(sizeInfo1, 4);\n        int size = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();\n        Logger.d(TAG, \"getQuickCompiledCodeSize: \" + size);\n        return size;\n    }\n\n\n    public static class MethodInfo {\n        public boolean isStatic;\n        public int paramNumber;\n        public Class<?>[] paramTypes;\n        public Class<?> returnType;\n        public ArtMethod method;\n\n        @Override\n        public String toString() {\n            return method.toGenericString();\n        }\n    }\n\n    private static class EntryLock {\n        static Map<Long, EntryLock> sLockPool = new HashMap<>();\n\n        static synchronized EntryLock obtain(long entry) {\n            if (sLockPool.containsKey(entry)) {\n                return sLockPool.get(entry);\n            } else {\n                EntryLock entryLock = new EntryLock();\n                sLockPool.put(entry, entryLock);\n                return entryLock;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/EpicNative.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art;\n\nimport android.util.Log;\n\nimport com.taobao.android.dexposed.DeviceCheck;\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.Unsafe;\n\nimport java.lang.reflect.Member;\n\nimport de.robv.android.xposed.XposedHelpers;\n\nimport static com.taobao.android.dexposed.utility.Debug.addrHex;\n\n\npublic final class EpicNative {\n\n    private static final String TAG = \"EpicNative\";\n    private static volatile boolean useUnsafe = false;\n    static {\n        try {\n            System.loadLibrary(\"epic\");\n            useUnsafe = DeviceCheck.isYunOS() || !isGetObjectAvailable();\n            Log.i(TAG, \"use unsafe ? \" + useUnsafe);\n        } catch (Throwable e) {\n            Log.e(TAG, \"init EpicNative error\", e);\n        }\n    }\n\n    public static native long mmap(int length);\n\n    public static native boolean munmap(long address, int length);\n\n    public static native void memcpy(long src, long dest, int length);\n\n    public static native void memput(byte[] bytes, long dest);\n\n    public static native byte[] memget(long src, int length);\n\n    public static native boolean munprotect(long addr, long len);\n\n    public static native long getMethodAddress(Member method);\n\n    public static native void MakeInitializedClassVisibilyInitialized(long self);\n\n    public static native boolean cacheflush(long addr, long len);\n\n    public static native long malloc(int sizeOfPtr);\n\n    public static native Object getObjectNative(long self, long address);\n\n    private static native boolean isGetObjectAvailable();\n\n    public static Object getObject(long self, long address) {\n        if (useUnsafe) {\n            return Unsafe.getObject(address);\n        } else {\n            return getObjectNative(self, address);\n        }\n    }\n\n    public static native boolean compileMethod(Member method, long self);\n\n    /**\n     * suspend all running thread momently\n     * @return a handle to resume all thread, used by {@link #resumeAll(long)}\n     */\n    public static native long suspendAll();\n\n    /**\n     * resume all thread which are suspend by {@link #suspendAll()}\n     * only work abobe Android N\n     * @param cookie the cookie return by {@link #suspendAll()}\n     */\n    public static native void resumeAll(long cookie);\n\n    /**\n     * stop jit compiler in runtime.\n     * Warning: Just for experiment Do not call this now!!!\n     * @return cookie use by {@link #startJit(long)}\n     */\n    public static native long stopJit();\n\n    /**\n     * start jit compiler stop by {@link #stopJit()}\n     * Warning: Just for experiment Do not call this now!!!\n     * @param cookie the cookie return by {@link #stopJit()}\n     */\n    public static native void startJit(long cookie);\n\n    // FIXME: 17/12/29 reimplement it with pure native code.\n    static native boolean activateNative(long jumpToAddress, long pc, long sizeOfTargetJump, long sizeOfBridgeJump, byte[] code);\n\n    /**\n     * Disable the moving gc of runtime.\n     * Warning: Just for experiment Do not call this now!!!\n     * @param api the api level\n     */\n    public static native void disableMovingGc(int api);\n\n\n    private EpicNative() {\n    }\n\n    public static boolean compileMethod(Member method) {\n        final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\");\n        return compileMethod(method, nativePeer);\n    }\n\n    public static Object getObject(long address) {\n        final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\");\n        return getObject(nativePeer, address);\n    }\n\n    public static void MakeInitializedClassVisibilyInitialized() {\n        final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\");\n        MakeInitializedClassVisibilyInitialized(nativePeer);\n    }\n\n    public static long map(int length) {\n        long m = mmap(length);\n        Logger.i(TAG, \"Mapped memory of size \" + length + \" at \" + addrHex(m));\n        return m;\n    }\n\n    public static boolean unmap(long address, int length) {\n        Logger.d(TAG, \"Removing mapped memory of size \" + length + \" at \" + addrHex(address));\n        return munmap(address, length);\n    }\n\n    public static void put(byte[] bytes, long dest) {\n        if (Debug.DEBUG) {\n            Logger.d(TAG, \"Writing memory to: \" + addrHex(dest));\n            Logger.d(TAG, Debug.hexdump(bytes, dest));\n        }\n        memput(bytes, dest);\n    }\n\n    public static byte[] get(long src, int length) {\n        Logger.d(TAG, \"Reading \" + length + \" bytes from: \" + addrHex(src));\n        byte[] bytes = memget(src, length);\n        Logger.d(TAG, Debug.hexdump(bytes, src));\n        return bytes;\n    }\n\n    public static boolean unprotect(long addr, long len) {\n        Logger.d(TAG, \"Disabling mprotect from \" + addrHex(addr));\n        return munprotect(addr, len);\n    }\n\n    public static void copy(long src, long dst, int length) {\n        Logger.d(TAG, \"Copy \" + length + \" bytes form \" + addrHex(src) + \" to \" + addrHex(dst));\n        memcpy(src, dst, length);\n    }\n\n}\n\n\n\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/Trampoline.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.Runtime;\n\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport me.weishu.epic.art.arch.ShellCode;\nimport me.weishu.epic.art.entry.Entry;\nimport me.weishu.epic.art.entry.Entry64;\nimport me.weishu.epic.art.method.ArtMethod;\n\nclass Trampoline {\n    private static final String TAG = \"Trampoline\";\n\n    private final ShellCode shellCode;\n    private final long jumpToAddress;\n    private final byte[] originalCode;\n    private int trampolineSize;\n    private long trampolineAddress;\n    private boolean active;\n\n    // private ArtMethod artOrigin;\n    private Set<ArtMethod> segments = new HashSet<>();\n\n    Trampoline(ShellCode shellCode, long entryPoint) {\n        this.shellCode = shellCode;\n        this.jumpToAddress = shellCode.toMem(entryPoint);\n        this.originalCode = EpicNative.get(jumpToAddress, shellCode.sizeOfDirectJump());\n    }\n\n    public boolean install(ArtMethod originMethod){\n        boolean modified = segments.add(originMethod);\n        if (!modified) {\n            // Already hooked, ignore\n            Logger.d(TAG, originMethod + \" is already hooked, return.\");\n            return true;\n        }\n\n        byte[] page = create();\n        EpicNative.put(page, getTrampolineAddress());\n\n        int quickCompiledCodeSize = Epic.getQuickCompiledCodeSize(originMethod);\n        int sizeOfDirectJump = shellCode.sizeOfDirectJump();\n        if (quickCompiledCodeSize < sizeOfDirectJump) {\n            Logger.w(TAG, originMethod.toGenericString() + \" quickCompiledCodeSize: \" + quickCompiledCodeSize);\n            originMethod.setEntryPointFromQuickCompiledCode(getTrampolinePc());\n            return true;\n        }\n        // 这里是绝对不能改EntryPoint的，碰到GC就挂(GC暂停线程的时候，遍历所有线程堆栈，如果被hook的方法在堆栈上，那就GG)\n        // source.setEntryPointFromQuickCompiledCode(script.getTrampolinePc());\n        return activate();\n    }\n\n    private long getTrampolineAddress() {\n        if (getSize() != trampolineSize) {\n            alloc();\n        }\n        return trampolineAddress;\n    }\n\n    private long getTrampolinePc() {\n        return shellCode.toPC(getTrampolineAddress());\n    }\n\n    private void alloc() {\n        if (trampolineAddress != 0) {\n            free();\n        }\n        trampolineSize = getSize();\n        trampolineAddress = EpicNative.map(trampolineSize);\n        Logger.d(TAG, \"Trampoline alloc:\" + trampolineSize + \", addr: 0x\" + Long.toHexString(trampolineAddress));\n    }\n\n    private void free() {\n        if (trampolineAddress != 0) {\n            EpicNative.unmap(trampolineAddress, trampolineSize);\n            trampolineAddress = 0;\n            trampolineSize = 0;\n        }\n\n        if (active) {\n            EpicNative.put(originalCode, jumpToAddress);\n        }\n    }\n\n    private int getSize() {\n        int count = 0;\n        count += shellCode.sizeOfBridgeJump() * segments.size();\n        count += shellCode.sizeOfCallOrigin();\n        return count;\n    }\n\n    private byte[] create() {\n        Logger.d(TAG, \"create trampoline.\" + segments);\n        byte[] mainPage = new byte[getSize()];\n\n        int offset = 0;\n        for (ArtMethod method : segments) {\n            byte[] bridgeJump = createTrampoline(method);\n            int length = bridgeJump.length;\n            System.arraycopy(bridgeJump, 0, mainPage, offset, length);\n            offset += length;\n        }\n\n        byte[] callOriginal = shellCode.createCallOrigin(jumpToAddress, originalCode);\n        System.arraycopy(callOriginal, 0, mainPage, offset, callOriginal.length);\n\n        return mainPage;\n    }\n\n    private boolean activate() {\n        long pc = getTrampolinePc();\n        Logger.d(TAG, \"Writing direct jump entry \" + Debug.addrHex(pc) + \" to origin entry: 0x\" + Debug.addrHex(jumpToAddress));\n        synchronized (Trampoline.class) {\n            return EpicNative.activateNative(jumpToAddress, pc, shellCode.sizeOfDirectJump(),\n                    shellCode.sizeOfBridgeJump(), shellCode.createDirectJump(pc));\n        }\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        free();\n        super.finalize();\n    }\n\n    private byte[] createTrampoline(ArtMethod source){\n        final Epic.MethodInfo methodInfo = Epic.getMethodInfo(source.getAddress());\n        final Class<?> returnType = methodInfo.returnType;\n\n//        Method bridgeMethod = Runtime.is64Bit() ? (Build.VERSION.SDK_INT == 23 ? Entry64_2.getBridgeMethod(methodInfo) : Entry64.getBridgeMethod(returnType))\n//                : Entry.getBridgeMethod(returnType);\n        Method bridgeMethod = Runtime.is64Bit() ? Entry64.getBridgeMethod(returnType)\n                : Entry.getBridgeMethod(returnType);\n\n        final ArtMethod target = ArtMethod.of(bridgeMethod);\n        long targetAddress = target.getAddress();\n        long targetEntry = target.getEntryPointFromQuickCompiledCode();\n        long sourceAddress = source.getAddress();\n        long structAddress = EpicNative.malloc(4);\n\n        Logger.d(TAG, \"targetAddress:\"+ Debug.longHex(targetAddress));\n        Logger.d(TAG, \"sourceAddress:\"+ Debug.longHex(sourceAddress));\n        Logger.d(TAG, \"targetEntry:\"+ Debug.longHex(targetEntry));\n        Logger.d(TAG, \"structAddress:\"+ Debug.longHex(structAddress));\n\n        return shellCode.createBridgeJump(targetAddress, targetEntry, sourceAddress, structAddress);\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/arch/Arm64.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.arch;\n\nimport java.nio.ByteOrder;\n\npublic class Arm64 extends ShellCode {\n\n    @Override\n    public int sizeOfDirectJump() {\n        return 4 * 4;\n    }\n\n    @Override\n    public byte[] createDirectJump(long targetAddress) {\n        byte[] instructions = new byte[]{\n                0x50, 0x00, 0x00, 0x58,         // ldr x9, _targetAddress\n                0x00, 0x02, 0x1F, (byte) 0xD6,  // br x9\n                0x00, 0x00, 0x00, 0x00,         // targetAddress\n                0x00, 0x00, 0x00, 0x00          // targetAddress\n        };\n        writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);\n        return instructions;\n    }\n\n    @Override\n    public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {\n\n        byte[] instructions = new byte[]{\n                0x1f, 0x20, 0x03, (byte) 0xd5,         // nop\n                0x69, 0x02, 0x00, 0x58,                // ldr x9, source_method\n                0x1f, 0x00, 0x09, (byte) 0xeb,         // cmp x0, x9\n                (byte) 0xa1, 0x02, 0x00, 0x54,         // bne 5f\n                (byte) 0x80, 0x01, 0x00, 0x58,         // ldr x0, target_method\n\n                0x29, 0x02, 0x00, 0x58,                // ldr x9, struct\n                (byte) 0xea, 0x03, 0x00, (byte) 0x91,  // mov x10, sp\n\n                0x2a, 0x01, 0x00, (byte) 0xf9,         // str x10, [x9, #0]\n                0x22, 0x05, 0x00, (byte) 0xf9,         // str x2, [x9, #8]\n\n                0x23, 0x09, 0x00, (byte) 0xf9,         // str x3, [x9, #16]\n                (byte) 0xe3, 0x03, 0x09, (byte) 0xaa,  // mov x3, x9\n                0x22, 0x01, 0x00, 0x58,                // ldr x2, source_method\n                0x22, 0x0d, 0x00, (byte) 0xf9,         // str x2, [x9, #24]\n                (byte) 0xe2, 0x03, 0x13, (byte) 0xaa,  // mov x2, x19\n                (byte) 0x89, 0x00, 0x00, 0x58,         // ldr x9, target_method_entry\n                0x20, 0x01, 0x1f, (byte) 0xd6,         // br x9\n\n                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address\n                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry\n                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method\n                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // struct\n\n        };\n\n\n        writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 32);\n        writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 24);\n        writeLong(srcAddress,\n                ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);\n        writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 8);\n\n        return instructions;\n    }\n\n    @Override\n    public int sizeOfBridgeJump() {\n        return 24 * 4;\n    }\n\n\n    @Override\n    public long toPC(long code) {\n        return code;\n    }\n\n    @Override\n    public long toMem(long pc) {\n        return pc;\n    }\n\n    @Override\n    public String getName() {\n        return \"64-bit ARM\";\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/arch/Arm64_2.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.arch;\n\nimport java.nio.ByteOrder;\n\npublic class Arm64_2 extends ShellCode {\n\n    @Override\n    public int sizeOfDirectJump() {\n        return 4 * 4;\n    }\n\n    @Override\n    public byte[] createDirectJump(long targetAddress) {\n        byte[] instructions = new byte[]{\n                0x50, 0x00, 0x00, 0x58,         // ldr x16, _targetAddress\n                0x00, 0x02, 0x1F, (byte) 0xD6,  // br x16\n                0x00, 0x00, 0x00, 0x00,         // targetAddress\n                0x00, 0x00, 0x00, 0x00          // targetAddress\n        };\n        writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);\n        return instructions;\n    }\n\n    @Override\n    public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {\n\n        byte[] instructions = new byte[] {\n\n                    /*\n                    ldr x17, 3f\n                    cmp x0, x17\n                    bne 5f\n                    ldr x0, 1f\n                    ldr x17, 4f\n                    mov x16, sp\n                    str x16, [x17, #0]\n                    str x2, [x17, #8]\n                    ldr x16, 3f\n                    str x16, [x17, #16]\n                    mov x2, x17\n                    ldr x17, 2f\n                    br x17\n\n                    1:\n                    .quad 0x0\n                    2:\n                    .quad 0x0\n                    3:\n                    .quad 0x0\n                    4:\n                    .quad 0x0\n\n                    5:\n                    mov x0, x17\n\n                    */\n                    0x1f, 0x20, 0x03, (byte) 0xd5,         // nop\n                    0x31, 0x02, 0x00, 0x58,\n                    0x1f, 0x00, 0x11, (byte)0xeb,\n                    (byte)0x61, 0x02, 0x00, 0x54,\n                    (byte)0x40, 0x01, 0x00, 0x58,\n                    (byte)0xf1, 0x01, 0x00, 0x58,\n                    (byte)0xf0, 0x03, 0x00, (byte)0x91,\n                    (byte)0x30, 0x02, 0x00, (byte)0xf9,\n                    0x22, 0x06, 0x00, (byte)0xf9,\n                    0x30, 0x01, 0x00, (byte)0x58,\n                    (byte)0x30, 0x0a, 0x00, (byte)0xf9,\n                    (byte)0xe2, 0x03, 0x11, (byte)0xaa,\n                    (byte)0x91, 0x00, 0x00, 0x58,\n                    (byte)0x20, 0x02, 0x1f, (byte)0xd6,\n\n                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address\n                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry\n                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method\n                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // struct\n            };\n\n        writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 32);\n        writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 24);\n        writeLong(srcAddress,\n                ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);\n        writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 8);\n\n        return instructions;\n    }\n\n    @Override\n    public int sizeOfBridgeJump() {\n        return 22 * 4;\n    }\n\n\n    @Override\n    public long toPC(long code) {\n        return code;\n    }\n\n    @Override\n    public long toMem(long pc) {\n        return pc;\n    }\n\n    @Override\n    public String getName() {\n        return \"64-bit ARM(Android M)\";\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/arch/ShellCode.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.arch;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\npublic abstract class ShellCode {\n\n    public abstract byte[] createDirectJump(long targetAddress);\n\n    public abstract int sizeOfDirectJump();\n\n    public abstract long toPC(long code);\n\n    public abstract long toMem(long pc);\n\n    public byte[] createCallOrigin(long originalAddress, byte[] originalPrologue) {\n        byte[] callOriginal = new byte[sizeOfCallOrigin()];\n        System.arraycopy(originalPrologue, 0, callOriginal, 0, sizeOfDirectJump());\n        byte[] directJump = createDirectJump(toPC(originalAddress + sizeOfDirectJump()));\n        System.arraycopy(directJump, 0, callOriginal, sizeOfDirectJump(), directJump.length);\n        return callOriginal;\n    }\n\n    public int sizeOfCallOrigin() {\n        return sizeOfDirectJump() * 2;\n    }\n\n    public abstract int sizeOfBridgeJump();\n\n    public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {\n        throw new RuntimeException(\"not impled\");\n    }\n\n    static void writeInt(int i, ByteOrder order, byte[] target, int pos) {\n        System.arraycopy(ByteBuffer.allocate(4).order(order).putInt(i).array(), 0, target, pos, 4);\n    }\n\n    static void writeLong(long i, ByteOrder order, byte[] target, int pos) {\n        System.arraycopy(ByteBuffer.allocate(8).order(order).putLong(i).array(), 0, target, pos, 8);\n    }\n\n    public abstract String getName();\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/arch/Thumb2.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.arch;\n\nimport java.nio.ByteOrder;\n\n\npublic class Thumb2 extends ShellCode {\n\n    @Override\n    public int sizeOfDirectJump() {\n        return 12;\n    }\n\n    @Override\n    public byte[] createDirectJump(long targetAddress) {\n        byte[] instructions = new byte[] {\n                (byte) 0xdf, (byte) 0xf8, 0x00, (byte) 0xf0,        // ldr pc, [pc]\n                0, 0, 0, 0\n        };\n        writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 4);\n        return instructions;\n    }\n\n    @Override\n    public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {\n        // 有问题，参数丢失。\n//        byte[] instructions = new byte[] {\n//                (byte) 0xdf, (byte) 0xf8, 0x18, (byte) 0xc0,    // ldr ip, [pc, #24], ip = source_method_address\n//\n//                (byte) 0x01, (byte) 0x91,                       // str r1, [sp, #4]\n//                (byte) 0x02, (byte) 0x92,                       // str r2, [sp, #8]\n//\n//                (byte) 0x03, (byte) 0x93,                       // str r3, [sp, #12]\n//                (byte) 0x62, (byte) 0x46,                       // mov r2, ip\n//\n//                0x01, 0x48,                                     // ldr r0, [pc, #4]\n//                0x6b, 0x46,                                     // mov r3, sp\n//\n//                (byte) 0xdf, (byte) 0xf8, 0x04, (byte) 0xf0,    // ldr pc, [pc, #4]\n//\n//                0x0, 0x0, 0x0, 0x0,                             // target_method_pos_x\n//                0x0, 0x0, 0x0, 0x0,                             // target_method_entry_point\n//                0x0, 0x0, 0x0, 0x0,                             // src_method_pos_x\n//        };\n//        writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n//                instructions.length - 12);\n//        writeInt((int) targetEntry,\n//                ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);\n//        writeInt((int) srcAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n//                instructions.length - 4);\n\n        byte[] instructions = new byte[]{\n\n                (byte) 0xdf, (byte) 0xf8, (byte) 0x30, (byte) 0xc0, // ldr ip, [pc, #48] ip = source method address\n\n                (byte) 0x60, (byte) 0x45,                           // cmp r0, ip        if r0 != ip\n                (byte) 0x40, (byte) 0xf0, (byte) 0x19, (byte) 0x80, // bne.w 1f          jump label 1:\n                (byte) 0x08, (byte) 0x48,                           // ldr r0, [pc, #28] r0 = target_method_address\n                (byte) 0xdf, (byte) 0xf8, (byte) 0x28, (byte) 0xc0, // ldr ip, [pc, #38] ip = struct address\n                (byte) 0xcc, (byte) 0xf8, (byte) 0x00, (byte) 0xd0, // str sp, [ip, #0]\n                (byte) 0xcc, (byte) 0xf8, (byte) 0x04, (byte) 0x20, // str r2, [ip, #4]\n                (byte) 0xcc, (byte) 0xf8, (byte) 0x08, (byte) 0x30, // str r3, [ip, #8]\n\n                (byte) 0x63, (byte) 0x46,                           // mov r3, ip\n                (byte) 0x05, (byte) 0x4a,                           // ldr r2, [pc, #16] r2 = source_method_address\n                (byte) 0xcc, (byte) 0xf8, (byte) 0x0c, (byte) 0x20, // str r2, [ip, #12]\n                (byte) 0x4a, (byte) 0x46,                           // move r2, r9\n                (byte) 0x4a, (byte) 0x46,                           // move r2, r9\n\n                (byte) 0xdf, (byte) 0xf8, (byte) 0x04, (byte) 0xf0, // ldr pc, [pc, #4]\n\n                0x0, 0x0, 0x0, 0x0,                             // target_method_pos_x\n                0x0, 0x0, 0x0, 0x0,                             // target_method_entry_point\n                0x0, 0x0, 0x0, 0x0,                             // src_method_address\n                0x0, 0x0, 0x0, 0x0,                             // struct address (sp, r1, r2)\n                // 1:\n        };\n\n        writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 16);\n        writeInt((int) targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 12);\n        writeInt((int) srcAddress,\n                ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);\n        writeInt((int) structAddress, ByteOrder.LITTLE_ENDIAN, instructions,\n                instructions.length - 4);\n\n        return instructions;\n    }\n\n    @Override\n    public int sizeOfBridgeJump() {\n        return 15 * 4;\n    }\n\n    @Override\n    public long toPC(long code) {\n        return toMem(code) + 1;\n    }\n\n    @Override\n    public long toMem(long pc) {\n        return pc & ~0x1;\n    }\n\n    @Override\n    public String getName() {\n        return \"Thumb2\";\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/entry/Entry.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.entry;\n\nimport android.os.Build;\nimport android.util.Pair;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\n\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport me.weishu.epic.art.Epic;\nimport me.weishu.epic.art.EpicNative;\n\n@SuppressWarnings({\"unused\", \"ConstantConditions\"})\npublic class Entry {\n\n    private final static String TAG = \"Entry\";\n\n    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];\n\n    //region ---------------callback---------------\n    private static int onHookInt(Object artmethod, Object receiver, Object[] args) {\n        return (Integer) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static long onHookLong(Object artmethod, Object receiver, Object[] args) {\n        return (Long) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static double onHookDouble(Object artmethod, Object receiver, Object[] args) {\n        return (Double) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static char onHookChar(Object artmethod, Object receiver, Object[] args) {\n        return (Character) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static short onHookShort(Object artmethod, Object receiver, Object[] args) {\n        return (Short) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static float onHookFloat(Object artmethod, Object receiver, Object[] args) {\n        return (Float) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static Object onHookObject(Object artmethod, Object receiver, Object[] args) {\n        return DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static void onHookVoid(Object artmethod, Object receiver, Object[] args) {\n        DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static boolean onHookBoolean(Object artmethod, Object receiver, Object[] args) {\n        return (Boolean) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static byte onHookByte(Object artmethod, Object receiver, Object[] args) {\n        return (Byte) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n    //endregion\n\n    //region ---------------bridge---------------\n    private static void voidBridge(int r1, int self, int struct) {\n        referenceBridge(r1, self, struct);\n    }\n\n    private static boolean booleanBridge(int r1, int self, int struct) {\n        return (Boolean) referenceBridge(r1, self, struct);\n    }\n\n    private static byte byteBridge(int r1, int self, int struct) {\n        return (Byte) referenceBridge(r1, self, struct);\n    }\n\n    private static short shortBridge(int r1, int self, int struct) {\n        return (Short) referenceBridge(r1, self, struct);\n    }\n\n    private static char charBridge(int r1, int self, int struct) {\n        return (Character) referenceBridge(r1, self, struct);\n    }\n\n    private static int intBridge(int r1, int self, int struct) {\n        return (Integer) referenceBridge(r1, self, struct);\n    }\n\n    private static long longBridge(int r1, int self, int struct) {\n        return (Long) referenceBridge(r1, self, struct);\n    }\n\n    private static float floatBridge(int r1, int self, int struct) {\n        return (Float) referenceBridge(r1, self, struct);\n    }\n\n    private static double doubleBridge(int r1, int self, int struct) {\n        return (Double) referenceBridge(r1, self, struct);\n    }\n    //endregion\n\n    private static Object referenceBridge(int r1, int self, int struct) {\n        Logger.i(TAG, \"enter bridge function.\");\n\n        // struct {\n        //     void* sp;\n        //     void* r2;\n        //     void* r3;\n        //     void* sourceMethod\n        // }\n        // sp + 16 = r4\n\n        Logger.i(TAG, \"struct:\" + Long.toHexString(struct));\n\n        final int sp = ByteBuffer.wrap(EpicNative.get(struct, 4)).order(ByteOrder.LITTLE_ENDIAN).getInt();\n\n        // Logger.i(TAG, \"stack:\" + Debug.hexdump(EpicNative.get(sp, 96), 0));\n\n        final byte[] rr1 = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(r1).array();\n        final byte[] r2 = EpicNative.get(struct + 4, 4);\n\n        final byte[] r3 = EpicNative.get(struct + 8, 4);\n\n        Logger.d(TAG, \"r1:\" + Debug.hexdump(rr1, 0));\n        Logger.d(TAG, \"r2:\" + Debug.hexdump(r2, 0));\n        Logger.d(TAG, \"r3:\" + Debug.hexdump(r3, 0));\n\n        final byte[] sourceAddr = EpicNative.get(struct + 12, 4);\n\n        ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[8]);\n        byteBuffer.put(sourceAddr);\n        byteBuffer.put(new byte[] {0, 0, 0, 0});\n        byteBuffer.flip();\n\n        final long sourceMethod = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).getLong();\n        \n        Logger.i(TAG, \"sourceMethod:\" + Long.toHexString(sourceMethod));\n\n        Epic.MethodInfo originMethodInfo = Epic.getMethodInfo(sourceMethod);\n        Logger.i(TAG, \"originMethodInfo :\" + originMethodInfo);\n\n        final Pair<Object, Object[]> constructArguments = constructArguments(originMethodInfo, self, rr1, r2, r3, sp);\n        Object receiver = constructArguments.first;\n        Object[] arguments = constructArguments.second;\n\n        Logger.i(TAG, \"arguments:\" + Arrays.toString(arguments));\n\n        Class<?> returnType = originMethodInfo.returnType;\n        Object artMethod = originMethodInfo.method;\n\n        Logger.i(TAG, \"leave bridge function\");\n\n        if (returnType == void.class) {\n            onHookVoid(artMethod, receiver, arguments);\n            return 0;\n        } else if (returnType == char.class) {\n            return onHookChar(artMethod, receiver, arguments);\n        } else if (returnType == byte.class) {\n            return onHookByte(artMethod, receiver, arguments);\n        } else if (returnType == short.class) {\n            return onHookShort(artMethod, receiver, arguments);\n        } else if (returnType == int.class) {\n            return onHookInt(artMethod, receiver, arguments);\n        } else if (returnType == long.class) {\n            return onHookLong(artMethod, receiver, arguments);\n        } else if (returnType == float.class) {\n            return onHookFloat(artMethod, receiver, arguments);\n        } else if (returnType == double.class) {\n            return onHookDouble(artMethod, receiver, arguments);\n        } else if (returnType == boolean.class) {\n            return onHookBoolean(artMethod, receiver, arguments);\n        } else {\n            return onHookObject(artMethod, receiver, arguments);\n        }\n    }\n\n    /**\n     * construct the method arguments from register r1, r2, r3 and stack\n     * @param r1 register r1 value\n     * @param r2 register r2 value\n     * @param r3 register r3 value\n     * @param sp stack pointer\n     * @return arguments passed to the callee method\n     */\n    private static Pair<Object, Object[]> constructArguments(Epic.MethodInfo originMethodInfo, int self,\n                                                             byte[] r1, byte[] r2, byte[] r3, int sp) {\n        boolean isStatic = originMethodInfo.isStatic;\n\n        int numberOfArgs;\n        Class<?>[] typeOfArgs;\n        if (isStatic) {\n\n            // static argument, r1, r2, r3, sp + 16\n\n            // sp + 0 = ArtMethod (ourself)\n            // sp + 4 = r1 (may be earased)\n            // sp + 8 = r2 (may be earased)\n            // sp + 12 = r3 (may be earased)\n            // sp + 16 = r4, remain\n\n            numberOfArgs = originMethodInfo.paramNumber;\n            typeOfArgs = originMethodInfo.paramTypes;\n        } else {\n            // non-static, r1 = receiver; r2, r3, sp + 16 is arguments.\n\n            // sp + 0 = ArtMethod (ourself)\n            // sp + 4 = r1 = this (may be earased)\n            // sp + 8 = r2 = first argument (may be earased)\n            // sp + 12 = r3 = second argument (may be earased)\n            // sp + 16 = third argument, remain\n            numberOfArgs = 1 + originMethodInfo.paramNumber;\n            typeOfArgs = new Class<?>[numberOfArgs];\n            typeOfArgs[0] = Object.class; // this\n            System.arraycopy(originMethodInfo.paramTypes, 0, typeOfArgs, 1, originMethodInfo.paramTypes.length);\n        }\n\n        Object[] arguments = new Object[numberOfArgs];\n\n        int currentStackPosition = 4; // sp + 0 = ArtMethod, sp + 4... start store arguments.\n        final int argumentStackBegin = 16; // sp + 4 = r1, sp + 8 = r2, sp + 12 = r3, sp + 16 start in stack.\n\n        int[] argStartPos = new int[numberOfArgs];\n\n        for (int i = 0; i < numberOfArgs; i++) {\n            Class<?> typeOfArg = typeOfArgs[i];\n            int typeLength = getTypeLength(typeOfArg);\n            argStartPos[i] = currentStackPosition;\n            currentStackPosition += typeLength;\n        }\n\n        int argTotalLength = currentStackPosition;\n        byte[] argBytes = new byte[argTotalLength];\n\n        do {\n            if (argTotalLength <= 4) break;\n\n            boolean align = Build.VERSION.SDK_INT >= 23 && numberOfArgs > 0 && getTypeLength(typeOfArgs[0]) == 8;\n            if (align) {\n                System.arraycopy(r2, 0, argBytes, 4, 4);\n                System.arraycopy(r3, 0, argBytes, 8, 4);\n                if (argTotalLength <= 12) break;\n                System.arraycopy(EpicNative.get(sp + 12, 4), 0, argBytes, 12, 4);\n            } else {\n                System.arraycopy(r1, 0, argBytes, 4, 4);\n\n                if (argTotalLength <= 8) break;\n                System.arraycopy(r2, 0, argBytes, 8, 4);\n                if (argTotalLength <= 12) break;\n                System.arraycopy(r3, 0, argBytes, 12, 4);\n            }\n\n            if (argTotalLength <= 16) break;\n\n            byte[] argInStack = EpicNative.get(sp + 16, argTotalLength - 16);\n            System.arraycopy(argInStack, 0, argBytes, 16, argTotalLength - 16);\n        } while (false);\n\n        //region ---------------Process Arguments passing in Android M---------------\n        if (Build.VERSION.SDK_INT == 23) {\n            // Android M, fix sp + 12\n            if (argTotalLength <= 12) {\n                // Nothing\n            } else {\n                if (argTotalLength <= 16) {\n                    if (getTypeLength(typeOfArgs[0]) == 8) {\n                        // first is 8byte\n                        System.arraycopy(EpicNative.get(sp + 44, 4), 0, argBytes, 12, 4);\n                    } else {\n                        // 48, 444: normal.\n                    }\n                } else {\n                    boolean isR3Grabbed = true;\n                    if (numberOfArgs >= 2) {\n                        int arg1TypeLength = getTypeLength(typeOfArgs[0]);\n                        int arg2TypeLength = getTypeLength(typeOfArgs[1]);\n                        if (arg1TypeLength == 4 && arg2TypeLength == 8) {\n                            isR3Grabbed = false;\n                        }\n\n                        if (numberOfArgs == 2 && arg1TypeLength == 8 && arg2TypeLength == 8) {\n                            // in this case, we have no reference register to local r3, just hard code now :(\n                            System.arraycopy(EpicNative.get(sp + 44, 4), 0, argBytes, 12, 4);\n                            isR3Grabbed = false;\n                        }\n                    }\n                    if (numberOfArgs >= 3) {\n                        int arg1TypeLength = getTypeLength(typeOfArgs[0]);\n                        int arg2TypeLength = getTypeLength(typeOfArgs[1]);\n                        int arg3TypeLength = getTypeLength(typeOfArgs[2]);\n                        if (arg1TypeLength == 4 && arg2TypeLength == 4 && arg3TypeLength == 4) {\n                            // in this case: r1 = arg1; r2 = arg2; r3 = arg3, normal.\n                            isR3Grabbed = false;\n                        }\n                        if (numberOfArgs == 3 && arg1TypeLength == 8 && arg2TypeLength == 4 && arg3TypeLength == 8) {\n                            // strange case :)\n                            System.arraycopy(EpicNative.get(sp + 52, 4), 0, argBytes, 12, 4);\n                            isR3Grabbed = false;\n                        }\n                    }\n                    if (isR3Grabbed) {\n                        byte[] otherStoreInStack = Arrays.copyOfRange(argBytes, argumentStackBegin, argBytes.length);\n                        int otherStoreInStackLength = otherStoreInStack.length;\n                        int searchRegion = 0;\n                        for (int i = argumentStackBegin + otherStoreInStackLength; ; i = i + 4) {\n                            final byte[] bytes = EpicNative.get(sp + i, otherStoreInStackLength);\n                            searchRegion += otherStoreInStackLength;\n                            if (Arrays.equals(bytes, otherStoreInStack)) {\n                                int originR3Index = sp + i - 4;\n                                final byte[] originR3 = EpicNative.get(originR3Index, 4);\n                                Logger.d(TAG, \"found other arguments in stack, index:\" + i + \", origin r3:\" + Arrays.toString(originR3));\n                                System.arraycopy(originR3, 0, argBytes, 12, 4);\n                                break;\n                            }\n                            if (searchRegion > (1 << 10)) {\n                                throw new RuntimeException(\"can not found the modify r3 register!!!\");\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        //endregion\n\n        Logger.d(TAG, \"argBytes: \" + Debug.hexdump(argBytes, 0));\n\n        for (int i = 0; i < numberOfArgs; i++) {\n            final Class<?> typeOfArg = typeOfArgs[i];\n            final int startPos = argStartPos[i];\n            final int typeLength = getTypeLength(typeOfArg);\n            byte[] argWithBytes = Arrays.copyOfRange(argBytes, startPos, startPos + typeLength);\n            arguments[i] = wrapArgument(typeOfArg, self, argWithBytes);\n//            Logger.d(TAG, \"argument[\" + i + \"], startPos:\" + startPos + \", typeOfLength:\" + typeLength);\n//            Logger.d(TAG, \"argWithBytes:\" + Debug.hexdump(argWithBytes, 0) + \", value:\" + arguments[i]);\n        }\n\n        Object thiz = null;\n        Object[] parameters = EMPTY_OBJECT_ARRAY;\n        if (isStatic) {\n            parameters = arguments;\n        } else {\n            thiz = arguments[0];\n            int argumentLength = arguments.length;\n            if (argumentLength > 1) {\n                parameters = Arrays.copyOfRange(arguments, 1, argumentLength);\n            }\n        }\n\n        return Pair.create(thiz, parameters);\n    }\n\n    private static Object wrapArgument(Class<?> type, int self, byte[] value) {\n        final ByteBuffer byteBuffer = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);\n        Logger.d(TAG, \"wrapArgument: type:\" + type);\n        if (type.isPrimitive()) {\n            if (type == int.class) {\n                return byteBuffer.getInt();\n            } else if (type == long.class) {\n                return byteBuffer.getLong();\n            } else if (type == float.class) {\n                return byteBuffer.getFloat();\n            } else if (type == short.class) {\n                return byteBuffer.getShort();\n            } else if (type == byte.class) {\n                return byteBuffer.get();\n            } else if (type == char.class) {\n                return byteBuffer.getChar();\n            } else if (type == double.class) {\n                return byteBuffer.getDouble();\n            } else if (type == boolean.class) {\n                return byteBuffer.getInt() != 0;\n            } else {\n                throw new RuntimeException(\"unknown type:\" + type);\n            }\n        } else {\n            int address = byteBuffer.getInt();\n            Object object = EpicNative.getObject(self, address);\n            // Logger.i(TAG, \"wrapArgument, address: 0x\" + Long.toHexString(address) + \", value:\" + object);\n            return object;\n        }\n    }\n\n    private static Map<Class<?>, String> bridgeMethodMap = new HashMap<>();\n\n    static {\n        Class<?>[] primitiveTypes = new Class[]{boolean.class, byte.class, char.class, short.class,\n                int.class, long.class, float.class, double.class};\n        for (Class<?> primitiveType : primitiveTypes) {\n            bridgeMethodMap.put(primitiveType, primitiveType.getName() + \"Bridge\");\n        }\n        bridgeMethodMap.put(Object.class, \"referenceBridge\");\n        bridgeMethodMap.put(void.class, \"voidBridge\");\n    }\n\n    public static Method getBridgeMethod(Class<?> returnType) {\n        try {\n            final String bridgeMethod = bridgeMethodMap.get(returnType.isPrimitive() ? returnType : Object.class);\n            Logger.i(TAG, \"bridge method:\" + bridgeMethod + \", map:\" + bridgeMethodMap);\n            Method method = Entry.class.getDeclaredMethod(bridgeMethod, int.class, int.class, int.class);\n            method.setAccessible(true);\n            return method;\n        } catch (Throwable e) {\n            throw new RuntimeException(\"error\", e);\n        }\n    }\n\n    private static int getTypeLength(Class<?> clazz) {\n        if (clazz == long.class || clazz == double.class) {\n            return 8; // double & long are 8 bytes.\n        } else {\n            return 4;\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/entry/Entry64.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.entry;\n\nimport com.taobao.android.dexposed.utility.Logger;\n\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.Epic;\nimport me.weishu.epic.art.EpicNative;\n\n@SuppressWarnings({\"unused\", \"ConstantConditions\"})\npublic class Entry64 {\n\n    private final static String TAG = \"Entry64\";\n\n    //region ---------------callback---------------\n    private static int onHookInt(Object artmethod, Object receiver, Object[] args) {\n        return (Integer) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static long onHookLong(Object artmethod, Object receiver, Object[] args) {\n        return (Long) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static double onHookDouble(Object artmethod, Object receiver, Object[] args) {\n        return (Double) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static char onHookChar(Object artmethod, Object receiver, Object[] args) {\n        return (Character) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static short onHookShort(Object artmethod, Object receiver, Object[] args) {\n        return (Short) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static float onHookFloat(Object artmethod, Object receiver, Object[] args) {\n        return (Float) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static Object onHookObject(Object artmethod, Object receiver, Object[] args) {\n        return DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static void onHookVoid(Object artmethod, Object receiver, Object[] args) {\n        DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static boolean onHookBoolean(Object artmethod, Object receiver, Object[] args) {\n        return (Boolean) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static byte onHookByte(Object artmethod, Object receiver, Object[] args) {\n        return (Byte) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n    //endregion\n\n    //region ---------------bridge---------------\n    private static void voidBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n         referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static boolean booleanBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Boolean) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static byte byteBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Byte) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static short shortBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Short) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static char charBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Character) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static int intBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Integer) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static long longBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Long) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static float floatBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Float) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n\n    private static double doubleBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) {\n        return (Double) referenceBridge(r1, self, struct, x4, x5, x6, x7);\n    }\n    //endregion\n\n    private static Object referenceBridge(long x1, long self, long struct, long x4, long x5, long x6, long x7) {\n        Logger.i(TAG, \"enter bridge function.\");\n\n        // struct {\n        //     void* sp;\n        //     void* r2;\n        //     void* r3;\n        //     void* sourceMethod\n        // }\n        // sp + 16 = r4\n\n        Logger.d(TAG, \"self:\" + Long.toHexString(self));\n        final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\");\n        Logger.d(TAG, \"java thread native peer:\" + Long.toHexString(nativePeer));\n\n        Logger.d(TAG, \"struct:\" + Long.toHexString(struct));\n\n        final long sp = ByteBuffer.wrap(EpicNative.get(struct, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();\n\n        Logger.d(TAG, \"stack:\" + sp);\n\n        final byte[] rr1 = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(x1).array();\n        final byte[] r2 = EpicNative.get(struct + 8, 8);\n\n        final byte[] r3 = EpicNative.get(struct + 16, 8);\n\n        final long sourceMethod = ByteBuffer.wrap(EpicNative.get(struct + 24, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();\n        Logger.d(TAG, \"sourceMethod:\" + Long.toHexString(sourceMethod));\n\n        Epic.MethodInfo originMethodInfo = Epic.getMethodInfo(sourceMethod);\n        Logger.d(TAG, \"originMethodInfo :\" + originMethodInfo);\n\n        boolean isStatic = originMethodInfo.isStatic;\n\n        int numberOfArgs = originMethodInfo.paramNumber;\n        Class<?>[] typeOfArgs = originMethodInfo.paramTypes;\n        Object[] arguments = new Object[numberOfArgs];\n\n        Object receiver;\n\n        self = nativePeer;\n\n        if (isStatic) {\n            receiver = null;\n            do {\n                if (numberOfArgs == 0) break;\n                arguments[0] = wrapArgument(typeOfArgs[0], self, rr1);\n                if (numberOfArgs == 1) break;\n                arguments[1] = wrapArgument(typeOfArgs[1], self, r2);\n                if (numberOfArgs == 2) break;\n                arguments[2] = wrapArgument(typeOfArgs[2], self, r3);\n                if (numberOfArgs == 3) break;\n                arguments[3] = wrapArgument(typeOfArgs[3], self, x4);\n                if (numberOfArgs == 4) break;\n                arguments[4] = wrapArgument(typeOfArgs[4], self, x5);\n                if (numberOfArgs == 5) break;\n                arguments[5] = wrapArgument(typeOfArgs[5], self, x6);\n                if (numberOfArgs == 6) break;\n                arguments[6] = wrapArgument(typeOfArgs[6], self, x7);\n                if (numberOfArgs == 7) break;\n\n                for (int i = 7; i < numberOfArgs; i++) {\n                    byte[] argsInStack = EpicNative.get(sp + i * 8 + 8, 8);\n                    arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);\n                }\n            } while (false);\n\n        } else {\n\n            receiver = EpicNative.getObject(self, x1);\n            //Logger.i(TAG, \"this :\" + receiver);\n\n            do {\n                if (numberOfArgs == 0) break;\n                arguments[0] = wrapArgument(typeOfArgs[0], self, r2);\n                if (numberOfArgs == 1) break;\n                arguments[1] = wrapArgument(typeOfArgs[1], self, r3);\n                if (numberOfArgs == 2) break;\n                arguments[2] = wrapArgument(typeOfArgs[2], self, x4);\n                if (numberOfArgs == 3) break;\n                arguments[3] = wrapArgument(typeOfArgs[3], self, x5);\n                if (numberOfArgs == 4) break;\n                arguments[4] = wrapArgument(typeOfArgs[4], self, x6);\n                if (numberOfArgs == 5) break;\n                arguments[5] = wrapArgument(typeOfArgs[5], self, x7);\n                if (numberOfArgs == 6) break;\n\n                for (int i = 6; i < numberOfArgs; i++) {\n                    byte[] argsInStack = EpicNative.get(sp + i * 8 + 16, 8);\n                    arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);\n                }\n            } while (false);\n        }\n\n        Logger.i(TAG, \"arguments:\" + Arrays.toString(arguments));\n\n        Class<?> returnType = originMethodInfo.returnType;\n        Object artMethod = originMethodInfo.method;\n\n        Logger.d(TAG, \"leave bridge function\");\n\n        if (returnType == void.class) {\n            onHookVoid(artMethod, receiver, arguments);\n            return 0;\n        } else if (returnType == char.class) {\n            return onHookChar(artMethod, receiver, arguments);\n        } else if (returnType == byte.class) {\n            return onHookByte(artMethod, receiver, arguments);\n        } else if (returnType == short.class) {\n            return onHookShort(artMethod, receiver, arguments);\n        } else if (returnType == int.class) {\n            return onHookInt(artMethod, receiver, arguments);\n        } else if (returnType == long.class) {\n            return onHookLong(artMethod, receiver, arguments);\n        } else if (returnType == float.class) {\n            return onHookFloat(artMethod, receiver, arguments);\n        } else if (returnType == double.class) {\n            return onHookDouble(artMethod, receiver, arguments);\n        } else if (returnType == boolean.class) {\n            return onHookBoolean(artMethod, receiver, arguments);\n        } else {\n            return onHookObject(artMethod, receiver, arguments);\n        }\n    }\n\n    private static Object wrapArgument(Class<?> type, long self, long value) {\n        return wrapArgument(type, self, ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array());\n    }\n\n    private static Object wrapArgument(Class<?> type, long self, byte[] value) {\n        final ByteBuffer byteBuffer = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);\n        if (type.isPrimitive()) {\n            if (type == int.class) {\n                return byteBuffer.getInt();\n            } else if (type == long.class) {\n                return byteBuffer.getLong();\n            } else if (type == float.class) {\n                return byteBuffer.getFloat();\n            } else if (type == short.class) {\n                return byteBuffer.getShort();\n            } else if (type == byte.class) {\n                return byteBuffer.get();\n            } else if (type == char.class) {\n                return byteBuffer.getChar();\n            } else if (type == double.class) {\n                return byteBuffer.getDouble();\n            } else if (type == boolean.class) {\n                return byteBuffer.getInt() == 0;\n            } else {\n                throw new RuntimeException(\"unknown type:\" + type);\n            }\n        } else {\n            long address = byteBuffer.getLong();\n            Object object = EpicNative.getObject(self, address);\n            //Logger.i(TAG, \"wrapArgument, address: 0x\" + Long.toHexString(address) + \", value:\" + object);\n            return object;\n        }\n    }\n\n    private static Map<Class<?>, String> bridgeMethodMap = new HashMap<>();\n    static {\n        Class<?>[] primitiveTypes = new Class[]{boolean.class, byte.class, char.class, short.class,\n                int.class, long.class, float.class, double.class};\n        for (Class<?> primitiveType : primitiveTypes) {\n            bridgeMethodMap.put(primitiveType, primitiveType.getName() + \"Bridge\");\n        }\n        bridgeMethodMap.put(void.class, \"voidBridge\");\n        bridgeMethodMap.put(Object.class, \"referenceBridge\");\n    }\n\n    public static Method getBridgeMethod(Class<?> returnType) {\n        try {\n            final String bridgeMethod = bridgeMethodMap.get(returnType.isPrimitive() ? returnType : Object.class);\n            Logger.d(TAG, \"bridge method:\" + bridgeMethod + \", map:\" + bridgeMethodMap);\n            Method method = Entry64.class.getDeclaredMethod(bridgeMethod, long.class, long.class,\n                    long.class, long.class, long.class, long.class, long.class);\n            method.setAccessible(true);\n            return method;\n        } catch (Throwable e) {\n            throw new RuntimeException(\"error\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/entry/Entry64_2.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.entry;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\n\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport de.robv.android.xposed.DexposedBridge;\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.Epic;\nimport me.weishu.epic.art.EpicNative;\n\n@SuppressWarnings({\"unused\", \"ConstantConditions\"})\npublic class Entry64_2 {\n\n    private final static String TAG = \"Entry64\";\n\n    //region ---------------callback---------------\n    private static int onHookInt(Object artmethod, Object receiver, Object[] args) {\n        return (Integer) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static long onHookLong(Object artmethod, Object receiver, Object[] args) {\n        return (Long) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static double onHookDouble(Object artmethod, Object receiver, Object[] args) {\n        return (Double) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static char onHookChar(Object artmethod, Object receiver, Object[] args) {\n        return (Character) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static short onHookShort(Object artmethod, Object receiver, Object[] args) {\n        return (Short) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static float onHookFloat(Object artmethod, Object receiver, Object[] args) {\n        return (Float) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static Object onHookObject(Object artmethod, Object receiver, Object[] args) {\n        return DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static void onHookVoid(Object artmethod, Object receiver, Object[] args) {\n        DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static boolean onHookBoolean(Object artmethod, Object receiver, Object[] args) {\n        return (Boolean) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n\n    private static byte onHookByte(Object artmethod, Object receiver, Object[] args) {\n        return (Byte) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args);\n    }\n    //endregion\n\n    //region ---------------voidBridge---------------\n    private static void voidBridge(long x1, long struct) {\n        referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static void voidBridge(long x1, long struct, long x3) {\n        referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static void voidBridge(long x1, long struct, long x3, long x4) {\n        referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static void voidBridge(long r1, long struct, long x3, long x4, long x5) {\n        referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static void voidBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static void voidBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------booleanBridge---------------\n    private static boolean booleanBridge(long x1, long struct) {\n        return (Boolean) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static boolean booleanBridge(long x1, long struct, long x3) {\n        return (Boolean) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static boolean booleanBridge(long x1, long struct, long x3, long x4) {\n        return (Boolean) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static boolean booleanBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Boolean) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static boolean booleanBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Boolean) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static boolean booleanBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Boolean) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------byteBridge---------------\n    private static byte byteBridge(long x1, long struct) {\n        return (Byte) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static byte byteBridge(long x1, long struct, long x3) {\n        return (Byte) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static byte byteBridge(long x1, long struct, long x3, long x4) {\n        return (Byte) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static byte byteBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Byte) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static byte byteBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Byte) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static byte byteBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Byte) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------shortBridge---------------\n    private static short shortBridge(long x1, long struct) {\n        return (Short) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static short shortBridge(long x1, long struct, long x3) {\n        return (Short) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static short shortBridge(long x1, long struct, long x3, long x4) {\n        return (Short) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static short shortBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Short) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static short shortBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Short) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static short shortBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Short) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------charBridge---------------\n    private static char charBridge(long x1, long struct) {\n        return (Character) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static char charBridge(long x1, long struct, long x3) {\n        return (Character) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static char charBridge(long x1, long struct, long x3, long x4) {\n        return (Character) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static char charBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Character) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static char charBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Character) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static char charBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Character) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------intBridge---------------\n    private static int intBridge(long x1, long struct) {\n        return (Integer) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static int intBridge(long x1, long struct, long x3) {\n        return (Integer) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static int intBridge(long x1, long struct, long x3, long x4) {\n        return (Integer) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static int intBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Integer) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static int intBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Integer) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static int intBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Integer) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------longBridge---------------\n    private static long longBridge(long x1, long struct) {\n        return (Long) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static long longBridge(long x1, long struct, long x3) {\n        return (Long) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static long longBridge(long x1, long struct, long x3, long x4) {\n        return (Long) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static long longBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Long) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static long longBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Long) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static long longBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Long) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------floatBridge---------------\n    private static float floatBridge(long x1, long struct) {\n        return (Float) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static float floatBridge(long x1, long struct, long x3) {\n        return (Float) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static float floatBridge(long x1, long struct, long x3, long x4) {\n        return (Float) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static float floatBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Float) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static float floatBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Float) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static float floatBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Float) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------doubleBridge---------------\n    private static double doubleBridge(long x1, long struct) {\n        return (Double) referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static double doubleBridge(long x1, long struct, long x3) {\n        return (Double) referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static double doubleBridge(long x1, long struct, long x3, long x4) {\n        return (Double) referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static double doubleBridge(long r1, long struct, long x3, long x4, long x5) {\n        return (Double) referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static double doubleBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return (Double) referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n\n    private static double doubleBridge(long r1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        return (Double) referenceBridge(r1, struct, x3, x4, x5, x6, x7);\n    }\n    //endregion\n\n    //region ---------------referenceBridge---------------\n    private static Object referenceBridge(long x1, long struct) {\n        return referenceBridge(x1, struct, 0, 0, 0, 0, 0);\n    }\n\n    private static Object referenceBridge(long x1, long struct, long x3) {\n        return referenceBridge(x1, struct, x3, 0, 0, 0, 0);\n    }\n\n    private static Object referenceBridge(long x1, long struct, long x3, long x4) {\n        return referenceBridge(x1, struct, x3, x4, 0, 0, 0);\n    }\n\n    private static Object referenceBridge(long r1, long struct, long x3, long x4, long x5) {\n        return referenceBridge(r1, struct, x3, x4, x5, 0, 0);\n    }\n\n    private static Object referenceBridge(long r1, long struct, long x3, long x4, long x5, long x6) {\n        return referenceBridge(r1, struct, x3, x4, x5, x6, 0);\n    }\n    //endregion\n\n    private static Object referenceBridge(long x1, long struct, long x3, long x4, long x5, long x6, long x7) {\n        Logger.i(TAG, \"enter bridge function.\");\n\n        // struct {\n        //     void* sp;\n        //     void* x2;\n        //     void* sourceMethod\n        // }\n\n        final long self = XposedHelpers.getLongField(Thread.currentThread(), \"nativePeer\");\n        Logger.d(TAG, \"java thread native peer:\" + Long.toHexString(self));\n\n        Logger.d(TAG, \"struct:\" + Long.toHexString(struct));\n        Logger.d(TAG, \"struct:\" + Debug.hexdump(EpicNative.get(struct, 24), struct));\n\n        final long sp = ByteBuffer.wrap(EpicNative.get(struct, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();\n\n        Logger.d(TAG, \"stack:\" + sp);\n\n        final byte[] rr1 = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(x1).array();\n        final byte[] r2 = EpicNative.get(struct + 8, 8);\n\n        final long sourceMethod = ByteBuffer.wrap(EpicNative.get(struct + 16, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong();\n        Logger.d(TAG, \"sourceMethod:\" + Long.toHexString(sourceMethod));\n\n        Epic.MethodInfo originMethodInfo = Epic.getMethodInfo(sourceMethod);\n        Logger.d(TAG, \"originMethodInfo :\" + originMethodInfo);\n\n        boolean isStatic = originMethodInfo.isStatic;\n\n        int numberOfArgs = originMethodInfo.paramNumber;\n        Class<?>[] typeOfArgs = originMethodInfo.paramTypes;\n        Object[] arguments = new Object[numberOfArgs];\n\n        Object receiver;\n\n        if (isStatic) {\n            receiver = null;\n            do {\n                if (numberOfArgs == 0) break;\n                arguments[0] = wrapArgument(typeOfArgs[0], self, rr1);\n                if (numberOfArgs == 1) break;\n                arguments[1] = wrapArgument(typeOfArgs[1], self, r2);\n                if (numberOfArgs == 2) break;\n                arguments[2] = wrapArgument(typeOfArgs[2], self, x3);\n                if (numberOfArgs == 3) break;\n                arguments[3] = wrapArgument(typeOfArgs[3], self, x4);\n                if (numberOfArgs == 4) break;\n                arguments[4] = wrapArgument(typeOfArgs[4], self, x5);\n                if (numberOfArgs == 5) break;\n                arguments[5] = wrapArgument(typeOfArgs[5], self, x6);\n                if (numberOfArgs == 6) break;\n                arguments[6] = wrapArgument(typeOfArgs[6], self, x7);\n                if (numberOfArgs == 7) break;\n\n                for (int i = 7; i < numberOfArgs; i++) {\n                    byte[] argsInStack = EpicNative.get(sp + i * 8 + 8, 8);\n                    arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);\n                }\n            } while (false);\n\n        } else {\n\n            receiver = EpicNative.getObject(self, x1);\n            Logger.i(TAG, \"this :\" + receiver);\n\n            do {\n                if (numberOfArgs == 0) break;\n                arguments[0] = wrapArgument(typeOfArgs[0], self, r2);\n                if (numberOfArgs == 1) break;\n                arguments[1] = wrapArgument(typeOfArgs[1], self, x3);\n                if (numberOfArgs == 2) break;\n                arguments[2] = wrapArgument(typeOfArgs[2], self, x4);\n                if (numberOfArgs == 3) break;\n                arguments[3] = wrapArgument(typeOfArgs[3], self, x5);\n                if (numberOfArgs == 4) break;\n                arguments[4] = wrapArgument(typeOfArgs[4], self, x6);\n                if (numberOfArgs == 5) break;\n                arguments[5] = wrapArgument(typeOfArgs[5], self, x7);\n                if (numberOfArgs == 6) break;\n\n                for (int i = 6; i < numberOfArgs; i++) {\n                    byte[] argsInStack = EpicNative.get(sp + i * 8 + 16, 8);\n                    arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack);\n                }\n            } while (false);\n        }\n\n        Logger.i(TAG, \"arguments:\" + Arrays.toString(arguments));\n\n        Class<?> returnType = originMethodInfo.returnType;\n        Object artMethod = originMethodInfo.method;\n\n        Logger.d(TAG, \"leave bridge function\");\n\n        if (returnType == void.class) {\n            onHookVoid(artMethod, receiver, arguments);\n            return 0;\n        } else if (returnType == char.class) {\n            return onHookChar(artMethod, receiver, arguments);\n        } else if (returnType == byte.class) {\n            return onHookByte(artMethod, receiver, arguments);\n        } else if (returnType == short.class) {\n            return onHookShort(artMethod, receiver, arguments);\n        } else if (returnType == int.class) {\n            return onHookInt(artMethod, receiver, arguments);\n        } else if (returnType == long.class) {\n            return onHookLong(artMethod, receiver, arguments);\n        } else if (returnType == float.class) {\n            return onHookFloat(artMethod, receiver, arguments);\n        } else if (returnType == double.class) {\n            return onHookDouble(artMethod, receiver, arguments);\n        } else if (returnType == boolean.class) {\n            return onHookBoolean(artMethod, receiver, arguments);\n        } else {\n            return onHookObject(artMethod, receiver, arguments);\n        }\n    }\n\n    private static Object wrapArgument(Class<?> type, long self, long value) {\n        return wrapArgument(type, self, ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array());\n    }\n\n    private static Object wrapArgument(Class<?> type, long self, byte[] value) {\n        final ByteBuffer byteBuffer = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);\n        if (type.isPrimitive()) {\n            if (type == int.class) {\n                return byteBuffer.getInt();\n            } else if (type == long.class) {\n                return byteBuffer.getLong();\n            } else if (type == float.class) {\n                return byteBuffer.getFloat();\n            } else if (type == short.class) {\n                return byteBuffer.getShort();\n            } else if (type == byte.class) {\n                return byteBuffer.get();\n            } else if (type == char.class) {\n                return byteBuffer.getChar();\n            } else if (type == double.class) {\n                return byteBuffer.getDouble();\n            } else if (type == boolean.class) {\n                return byteBuffer.getInt() == 0;\n            } else {\n                throw new RuntimeException(\"unknown type:\" + type);\n            }\n        } else {\n            long address = byteBuffer.getLong();\n            Object object = EpicNative.getObject(self, address);\n            // Logger.d(TAG, \"wrapArgument, address: 0x\" + Long.toHexString(address) + \", value:\" + object);\n            return object;\n        }\n    }\n\n    private static Map<Class<?>, String> bridgeMethodMap = new HashMap<>();\n    static {\n        Class<?>[] primitiveTypes = new Class[]{boolean.class, byte.class, char.class, short.class,\n                int.class, long.class, float.class, double.class};\n        for (Class<?> primitiveType : primitiveTypes) {\n            bridgeMethodMap.put(primitiveType, primitiveType.getName() + \"Bridge\");\n        }\n        bridgeMethodMap.put(void.class, \"voidBridge\");\n        bridgeMethodMap.put(Object.class, \"referenceBridge\");\n    }\n\n    public static Method getBridgeMethod(Epic.MethodInfo methodInfo) {\n        try {\n            Class<?> returnType = methodInfo.returnType;\n            int paramNumber = methodInfo.isStatic ? methodInfo.paramNumber : methodInfo.paramNumber + 1;\n            Class<?>[] bridgeParamTypes;\n            if (paramNumber <= 2) {\n                paramNumber = 2;\n            }\n            bridgeParamTypes = new Class[paramNumber];\n            for (int i = 0; i < paramNumber; i++) {\n                bridgeParamTypes[i] = long.class;\n            }\n\n            final String bridgeMethod = bridgeMethodMap.get(returnType.isPrimitive() ? returnType : Object.class);\n            Logger.d(TAG, \"bridge method:\" + bridgeMethod + \", map:\" + bridgeMethodMap);\n            Method method = Entry64_2.class.getDeclaredMethod(bridgeMethod, bridgeParamTypes);\n            method.setAccessible(true);\n            return method;\n        } catch (Throwable e) {\n            throw new RuntimeException(\"can not found bridge.\" , e);\n        }\n    }\n}"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/method/ArtMethod.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.method;\n\nimport android.os.Build;\nimport android.util.Log;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.NeverCalled;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\n\nimport de.robv.android.xposed.XposedHelpers;\nimport me.weishu.epic.art.EpicNative;\n\n/**\n * Object stands for a Java Method, may be a constructor or a method.\n */\npublic class ArtMethod {\n\n    private static final String TAG = \"ArtMethod\";\n\n    /**\n     * The address of the Art method. this is not the real memory address of the java.lang.reflect.Method\n     * But the address used by VM which stand for the Java method.\n     * generally, it was the address of art::mirror::ArtMethod. @{link #objectAddress}\n     */\n    private long address;\n\n    /**\n     * the origin object if this is a constructor\n     */\n    private Constructor constructor;\n\n    /**\n     * the origin object if this is a method;\n     */\n    private Method method;\n\n    /**\n     * the origin ArtMethod if this method is a backup of someone, null when this is not backup\n     */\n    private ArtMethod origin;\n\n    /**\n     * The size of ArtMethod, usually the java part of ArtMethod may not stand for the whole one\n     * may be some native field is placed in the end of header.\n     */\n    private static int artMethodSize = -1;\n\n    private ArtMethod(Constructor constructor) {\n        if (constructor == null) {\n            throw new IllegalArgumentException(\"constructor can not be null\");\n        }\n        this.constructor = constructor;\n        init();\n    }\n\n    private ArtMethod(Method method, long address) {\n        if (method == null) {\n            throw new IllegalArgumentException(\"method can not be null\");\n        }\n        this.method = method;\n        if (address != -1) {\n            this.address = address;\n        } else {\n            init();\n        }\n    }\n\n    private void init() {\n        if (constructor != null) {\n            address = EpicNative.getMethodAddress(constructor);\n        } else {\n            address = EpicNative.getMethodAddress(method);\n        }\n    }\n\n    public static ArtMethod of(Method method) {\n        return new ArtMethod(method, -1);\n    }\n\n    public static ArtMethod of(Method method, long address) {\n        return new ArtMethod(method, address);\n    }\n\n    public static ArtMethod of(Constructor constructor) {\n        return new ArtMethod(constructor);\n    }\n\n\n    public ArtMethod backup() {\n        try {\n            // Before Oreo, it is: java.lang.reflect.AbstractMethod\n            // After Oreo, it is: java.lang.reflect.Executable\n            Class<?> abstractMethodClass = Method.class.getSuperclass();\n\n            Object executable = this.getExecutable();\n            ArtMethod artMethod;\n            if (Build.VERSION.SDK_INT < 23) {\n                Class<?> artMethodClass = Class.forName(\"java.lang.reflect.ArtMethod\");\n                //Get the original artMethod field\n                Field artMethodField = abstractMethodClass.getDeclaredField(\"artMethod\");\n                if (!artMethodField.isAccessible()) {\n                    artMethodField.setAccessible(true);\n                }\n                Object srcArtMethod = artMethodField.get(executable);\n\n                Constructor<?> constructor = artMethodClass.getDeclaredConstructor();\n                constructor.setAccessible(true);\n                Object destArtMethod = constructor.newInstance();\n\n                //Fill the fields to the new method we created\n                for (Field field : artMethodClass.getDeclaredFields()) {\n                    if (!field.isAccessible()) {\n                        field.setAccessible(true);\n                    }\n                    field.set(destArtMethod, field.get(srcArtMethod));\n                }\n                Method newMethod = Method.class.getConstructor(artMethodClass).newInstance(destArtMethod);\n                newMethod.setAccessible(true);\n                artMethod = ArtMethod.of(newMethod);\n\n                artMethod.setEntryPointFromQuickCompiledCode(getEntryPointFromQuickCompiledCode());\n                artMethod.setEntryPointFromJni(getEntryPointFromJni());\n            } else {\n                Constructor<Method> constructor = Method.class.getDeclaredConstructor();\n                // we can't use constructor.setAccessible(true); because Google does not like it\n                // AccessibleObject.setAccessible(new AccessibleObject[]{constructor}, true);\n                Field override = AccessibleObject.class.getDeclaredField(\n                        Build.VERSION.SDK_INT == Build.VERSION_CODES.M ? \"flag\" : \"override\");\n                override.setAccessible(true);\n                override.set(constructor, true);\n\n                Method m = constructor.newInstance();\n                m.setAccessible(true);\n                for (Field field : abstractMethodClass.getDeclaredFields()) {\n                    field.setAccessible(true);\n                    field.set(m, field.get(executable));\n                }\n                Field artMethodField = abstractMethodClass.getDeclaredField(\"artMethod\");\n                artMethodField.setAccessible(true);\n                int artMethodSize = getArtMethodSize();\n                long memoryAddress = EpicNative.map(artMethodSize);\n\n                byte[] data = EpicNative.get(address, artMethodSize);\n                EpicNative.put(data, memoryAddress);\n                artMethodField.set(m, memoryAddress);\n                // From Android R, getting method address may involve the jni_id_manager which uses\n                // ids mapping instead of directly returning the method address. During resolving the\n                // id->address mapping, it will assume the art method to be from the \"methods_\" array\n                // in class. However this address may be out of the range of the methods array. Thus\n                // it will cause a crash during using the method offset to resolve method array.\n                artMethod = ArtMethod.of(m, memoryAddress);\n            }\n            artMethod.makePrivate();\n            artMethod.setAccessible(true);\n            artMethod.origin = this; // save origin method.\n            return artMethod;\n\n\n        } catch (Throwable e) {\n            Log.e(TAG, \"backup method error:\", e);\n            throw new IllegalStateException(\"Cannot create backup method from :: \" + getExecutable(), e);\n        }\n    }\n\n    /**\n     * @return is method/constructor accessible\n     */\n    public boolean isAccessible() {\n        if (constructor != null) {\n            return constructor.isAccessible();\n        } else {\n            return method.isAccessible();\n        }\n    }\n\n    /**\n     * make the constructor or method accessible\n     * @param accessible accessible\n     */\n    public void setAccessible(boolean accessible) {\n        if (constructor != null) {\n            constructor.setAccessible(accessible);\n        } else {\n            method.setAccessible(accessible);\n        }\n    }\n\n    /**\n     * get the origin method's name\n     * @return constructor name of method name\n     */\n    public String getName() {\n        if (constructor != null) {\n            return constructor.getName();\n        } else {\n            return method.getName();\n        }\n    }\n\n    public Class<?> getDeclaringClass() {\n        if (constructor != null) {\n            return constructor.getDeclaringClass();\n        } else {\n            return method.getDeclaringClass();\n        }\n    }\n\n    /**\n     * Force compile the method to avoid interpreter mode.\n     * This is only used above Android N\n     * @return if compile success return true, otherwise false.\n     */\n    public boolean compile() {\n        if (constructor != null) {\n            return EpicNative.compileMethod(constructor);\n        } else {\n            return EpicNative.compileMethod(method);\n        }\n    }\n\n    /**\n     * invoke the origin method\n     * @param receiver the receiver\n     * @param args origin method/constructor's parameters\n     * @return origin method's return value.\n     * @throws IllegalAccessException throw if no access, impossible.\n     * @throws InvocationTargetException invoke target error.\n     * @throws InstantiationException throw when the constructor can not create instance.\n     */\n    public Object invoke(Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException {\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            if (origin != null) {\n                byte[] currentAddress = EpicNative.get(origin.address, 4);\n                byte[] backupAddress = EpicNative.get(address, 4);\n                if (!Arrays.equals(currentAddress, backupAddress)) {\n                    if (Debug.DEBUG) {\n                        Logger.i(TAG, \"the address of java method was moved by gc, backup it now! origin address: 0x\"\n                                + Arrays.toString(currentAddress) + \" , currentAddress: 0x\" + Arrays.toString(backupAddress));\n                    }\n                    EpicNative.put(currentAddress, address);\n                    return invokeInternal(receiver, args);\n                } else {\n                    Logger.i(TAG, \"the address is same with last invoke, not moved by gc\");\n                }\n            }\n        }\n\n        return invokeInternal(receiver, args);\n    }\n\n    private Object invokeInternal(Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException {\n        if (constructor != null) {\n            return constructor.newInstance(args);\n        } else {\n            return method.invoke(receiver, args);\n        }\n    }\n\n    /**\n     * get the modifiers of origin method/constructor\n     * @return the modifiers\n     */\n    public int getModifiers() {\n        if (constructor != null) {\n            return constructor.getModifiers();\n        } else {\n            return method.getModifiers();\n        }\n    }\n\n    /**\n     * get the parameter type of origin method/constructor\n     * @return the parameter types.\n     */\n    public Class<?>[] getParameterTypes() {\n        if (constructor != null) {\n            return constructor.getParameterTypes();\n        } else {\n            return method.getParameterTypes();\n        }\n    }\n\n    /**\n     * get the return type of origin method/constructor\n     * @return the return type, if it is a constructor, return Object.class\n     */\n    public Class<?> getReturnType() {\n        if (constructor != null) {\n            return Object.class;\n        } else {\n            return method.getReturnType();\n        }\n    }\n\n    /**\n     * get the exception declared by the method/constructor\n     * @return the array of declared exception.\n     */\n    public Class<?>[] getExceptionTypes() {\n        if (constructor != null) {\n            return constructor.getExceptionTypes();\n        } else {\n            return method.getExceptionTypes();\n        }\n    }\n\n    public String toGenericString() {\n        if (constructor != null) {\n            return constructor.toGenericString();\n        } else {\n            return method.toGenericString();\n        }\n    }\n\n    /**\n     * @return the origin method/constructor\n     */\n    public Object getExecutable() {\n        if (constructor != null) {\n            return constructor;\n        } else {\n            return method;\n        }\n    }\n\n    /**\n     * get the memory address of the inner constructor/method\n     * @return the method address, in general, it was the pointer of art::mirror::ArtMethod\n     */\n    public long getAddress() {\n        return address;\n    }\n\n    /**\n     * get the unique identifier of the constructor/method\n     * @return the method identifier\n     */\n    public String getIdentifier() {\n        // Can we use address, may gc move it??\n        return String.valueOf(getAddress());\n    }\n\n    /**\n     * force set the private flag of the method.\n     */\n    public void makePrivate() {\n        int accessFlags = getAccessFlags();\n        accessFlags &= ~Modifier.PUBLIC;\n        accessFlags |= Modifier.PRIVATE;\n        setAccessFlags(accessFlags);\n    }\n\n    /**\n     * the static method is lazy resolved, when not resolved, the entry point is a trampoline of\n     * a bridge, we can not hook these entry. this method force the static method to be resolved.\n     */\n    public void ensureResolved() {\n        if (!Modifier.isStatic(getModifiers())) {\n            Logger.d(TAG, \"not static, ignore.\");\n            return;\n        }\n\n        try {\n            invoke(null);\n            Logger.d(TAG, \"ensure resolved\");\n        } catch (Exception ignored) {\n            // we should never make a successful call.\n        } finally {\n            EpicNative.MakeInitializedClassVisibilyInitialized();\n        }\n    }\n\n    /**\n     * The entry point of the quick compiled code.\n     * @return the entry point.\n     */\n    public long getEntryPointFromQuickCompiledCode() {\n        return Offset.read(address, Offset.ART_QUICK_CODE_OFFSET);\n    }\n\n    /**\n     * @param pointer_entry_point_from_quick_compiled_code the entry point.\n     */\n    public void setEntryPointFromQuickCompiledCode(long pointer_entry_point_from_quick_compiled_code) {\n        Offset.write(address, Offset.ART_QUICK_CODE_OFFSET, pointer_entry_point_from_quick_compiled_code);\n    }\n\n    /**\n     * @return the access flags of the method/constructor, not only stand for the modifiers.\n     */\n    public int getAccessFlags() {\n        return (int) Offset.read(address, Offset.ART_ACCESS_FLAG_OFFSET);\n    }\n\n    public void setAccessFlags(int newFlags) {\n        Offset.write(address, Offset.ART_ACCESS_FLAG_OFFSET, newFlags);\n    }\n\n    public void setEntryPointFromJni(long entryPointFromJni) {\n        Offset.write(address, Offset.ART_JNI_ENTRY_OFFSET, entryPointFromJni);\n    }\n\n    public long getEntryPointFromJni() {\n        return Offset.read(address, Offset.ART_JNI_ENTRY_OFFSET);\n    }\n\n    /**\n     * The size of an art::mirror::ArtMethod, we use two rule method to measure the size\n     * @return the size\n     */\n    public static int getArtMethodSize() {\n        if (artMethodSize > 0) {\n            return artMethodSize;\n        }\n        final Method rule1 = XposedHelpers.findMethodExact(ArtMethod.class, \"rule1\");\n        final Method rule2 = XposedHelpers.findMethodExact(ArtMethod.class, \"rule2\");\n        final long rule2Address = EpicNative.getMethodAddress(rule2);\n        final long rule1Address = EpicNative.getMethodAddress(rule1);\n        final long size = Math.abs(rule2Address - rule1Address);\n        artMethodSize = (int) size;\n        Logger.d(TAG, \"art Method size: \" + size);\n        return artMethodSize;\n    }\n\n    private void rule1() {\n        Log.i(TAG, \"do not inline me!!\");\n    }\n\n    private void rule2() {\n        Log.i(TAG, \"do not inline me!!\");\n    }\n    public static long getQuickToInterpreterBridge() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            return -1L;\n        }\n        final Method fake = XposedHelpers.findMethodExact(NeverCalled.class, \"fake\", int.class);\n        return ArtMethod.of(fake).getEntryPointFromQuickCompiledCode();\n    }\n\n    public long getFieldOffset() {\n        // searchOffset(address, )\n        return 0L;\n    }\n\n    /**\n     * search Offset in memory\n     * @param base base address\n     * @param range search range\n     * @param value search value\n     * @return the first address of value if found\n     */\n    public static long searchOffset(long base, long range, int value) {\n        final int align = 4;\n        final long step = range / align;\n        for (long i = 0; i < step; i++) {\n            long offset = i * align;\n            final byte[] bytes = EpicNative.memget(base + i * align, align);\n            final int valueInOffset = ByteBuffer.allocate(4).put(bytes).getInt();\n            if (valueInOffset == value) {\n                return offset;\n            }\n        }\n        return -1;\n    }\n\n    public static long searchOffset(long base, long range, long value) {\n        final int align = 4;\n        final long step = range / align;\n        for (long i = 0; i < step; i++) {\n            long offset = i * align;\n            final byte[] bytes = EpicNative.memget(base + i * align, align);\n            final long valueInOffset = ByteBuffer.allocate(8).put(bytes).getLong();\n            if (valueInOffset == value) {\n                return offset;\n            }\n        }\n        return -1;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/me/weishu/epic/art/method/Offset.java",
    "content": "/*\n * Copyright (c) 2017, weishu twsxtd@gmail.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage me.weishu.epic.art.method;\n\nimport android.os.Build;\n\nimport com.taobao.android.dexposed.utility.Debug;\nimport com.taobao.android.dexposed.utility.Logger;\nimport com.taobao.android.dexposed.utility.Runtime;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\nimport me.weishu.epic.art.EpicNative;\n\n/**\n * The Offset of field in an ArtMethod\n */\nclass Offset {\n\n    private static final String TAG = \"Offset\";\n\n    /**\n     * the offset of the entry point\n     */\n    static Offset ART_QUICK_CODE_OFFSET;\n\n    /**\n     * the offset of the access flag\n     */\n    static Offset ART_ACCESS_FLAG_OFFSET;\n\n    /**\n     * the offset of a jni entry point\n     */\n    static Offset ART_JNI_ENTRY_OFFSET;\n\n    static {\n        initFields();\n    }\n\n    private enum BitWidth {\n        DWORD(4),\n        QWORD(8);\n\n        BitWidth(int width) {\n            this.width = width;\n        }\n\n        int width;\n    }\n\n    private long offset;\n    private BitWidth length;\n\n    public long getOffset() {\n        return offset;\n    }\n\n    public void setOffset(long offset) {\n        this.offset = offset;\n    }\n\n    public BitWidth getLength() {\n        return length;\n    }\n\n    public void setLength(BitWidth length) {\n        this.length = length;\n    }\n\n    public static long read(long base, Offset offset) {\n        long address = base + offset.offset;\n        byte[] bytes = EpicNative.get(address, offset.length.width);\n        if (offset.length == BitWidth.DWORD) {\n            return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;\n        } else {\n            return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong();\n        }\n    }\n\n    public static void write(long base, Offset offset, long value) {\n        long address = base + offset.offset;\n        byte[] bytes;\n        if (offset.length == BitWidth.DWORD) {\n            if (value > 0xFFFFFFFFL) {\n                throw new IllegalStateException(\"overflow may occur\");\n            } else {\n                bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array();\n            }\n        } else {\n            bytes = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array();\n        }\n        EpicNative.put(bytes, address);\n    }\n\n    private static void initFields() {\n        ART_QUICK_CODE_OFFSET = new Offset();\n        ART_ACCESS_FLAG_OFFSET = new Offset();\n        ART_JNI_ENTRY_OFFSET = new Offset();\n\n        ART_ACCESS_FLAG_OFFSET.setLength(Offset.BitWidth.DWORD);\n\n        final int apiLevel = Build.VERSION.SDK_INT;\n\n        if (Runtime.is64Bit()) {\n            ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.QWORD);\n            ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);\n            switch (apiLevel) {\n                case Build.VERSION_CODES.S:\n                    // source: https://android.googlesource.com/platform/art/+/refs/heads/android12-release/runtime/art_method.h\n                    ART_QUICK_CODE_OFFSET.setOffset(24);\n                    ART_JNI_ENTRY_OFFSET.setOffset(16);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.R:\n                case Build.VERSION_CODES.Q:\n                case Build.VERSION_CODES.P:\n                    ART_QUICK_CODE_OFFSET.setOffset(32);\n                    ART_JNI_ENTRY_OFFSET.setOffset(24);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.O_MR1:\n                case Build.VERSION_CODES.O:\n                    ART_QUICK_CODE_OFFSET.setOffset(40);\n                    ART_JNI_ENTRY_OFFSET.setOffset(32);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.N_MR1:\n                case Build.VERSION_CODES.N:\n                    ART_QUICK_CODE_OFFSET.setOffset(48);\n                    ART_JNI_ENTRY_OFFSET.setOffset(40);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.M:\n                    ART_QUICK_CODE_OFFSET.setOffset(48);\n                    ART_JNI_ENTRY_OFFSET.setOffset(40);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(12);\n                    break;\n                case Build.VERSION_CODES.LOLLIPOP_MR1:\n                    ART_QUICK_CODE_OFFSET.setOffset(56);\n                    ART_JNI_ENTRY_OFFSET.setOffset(44);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(20);\n                    break;\n                case Build.VERSION_CODES.LOLLIPOP:\n                    ART_QUICK_CODE_OFFSET.setOffset(40);\n                    ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD);\n                    ART_JNI_ENTRY_OFFSET.setOffset(32);\n                    ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(56);\n                    break;\n                case Build.VERSION_CODES.KITKAT:\n                    ART_QUICK_CODE_OFFSET.setOffset(32);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(28);\n                    break;\n                default:\n                    throw new RuntimeException(\"API LEVEL: \" + apiLevel + \" is not supported now : (\");\n            }\n        } else {\n            ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.DWORD);\n            ART_JNI_ENTRY_OFFSET.setLength(BitWidth.DWORD);\n            switch (apiLevel) {\n                case Build.VERSION_CODES.S:\n                    ART_QUICK_CODE_OFFSET.setOffset(20);\n                    ART_JNI_ENTRY_OFFSET.setOffset(16);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.R:\n                case Build.VERSION_CODES.Q:\n                case Build.VERSION_CODES.P:\n                    ART_QUICK_CODE_OFFSET.setOffset(24);\n                    ART_JNI_ENTRY_OFFSET.setOffset(20);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.O_MR1:\n                case Build.VERSION_CODES.O:\n                    ART_QUICK_CODE_OFFSET.setOffset(28);\n                    ART_JNI_ENTRY_OFFSET.setOffset(24);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.N_MR1:\n                case Build.VERSION_CODES.N:\n                    ART_QUICK_CODE_OFFSET.setOffset(32);\n                    ART_JNI_ENTRY_OFFSET.setOffset(28);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(4);\n                    break;\n                case Build.VERSION_CODES.M:\n                    ART_QUICK_CODE_OFFSET.setOffset(36);\n                    ART_JNI_ENTRY_OFFSET.setOffset(32);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(12);\n                    break;\n                case Build.VERSION_CODES.LOLLIPOP_MR1:\n                    ART_QUICK_CODE_OFFSET.setOffset(44);\n                    ART_JNI_ENTRY_OFFSET.setOffset(40);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(20);\n                    break;\n                case Build.VERSION_CODES.LOLLIPOP:\n                    ART_QUICK_CODE_OFFSET.setOffset(40);\n                    ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD);\n                    ART_JNI_ENTRY_OFFSET.setOffset(32);\n                    ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(56);\n                    break;\n                case Build.VERSION_CODES.KITKAT:\n                    ART_QUICK_CODE_OFFSET.setOffset(32);\n                    ART_ACCESS_FLAG_OFFSET.setOffset(28);\n                    break;\n                default:\n                    throw new RuntimeException(\"API LEVEL: \" + apiLevel + \" is not supported now : (\");\n            }\n        }\n        if (Debug.DEBUG) {\n            Logger.i(TAG, \"quick code offset: \" + ART_QUICK_CODE_OFFSET.getOffset());\n            Logger.i(TAG, \"access flag offset: \" + ART_ACCESS_FLAG_OFFSET.getOffset());\n            Logger.i(TAG, \"jni code offset: \" + ART_JNI_ENTRY_OFFSET.getOffset());\n\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">epic</string>\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':library'\n"
  }
]