├── .gitignore
├── .gitmodules
├── LICENSE
├── README.en.md
├── README.md
├── VirtualApkPatch
├── builds_test
│ ├── codeblocks
│ │ └── VirtualApkPatch.cbp
│ ├── main.cpp
│ ├── vc
│ │ ├── VirtualApkPatch.vcxproj
│ │ └── VirtualApkPatch.vcxproj.filters
│ └── xcode
│ │ └── VirtualApkPatch.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
└── patch
│ ├── virtual_apk_patch.cpp
│ └── virtual_apk_patch.h
├── project_fix_unity_jar
├── fix_unity_jar
│ ├── builds
│ │ ├── codeblocks
│ │ │ └── FixUnityJar.cbp
│ │ ├── vc
│ │ │ └── FixUnityJar
│ │ │ │ ├── FixUnityJar.vcxproj
│ │ │ │ └── FixUnityJar.vcxproj.filters
│ │ └── xcode
│ │ │ └── FixUnityJar.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ └── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ └── src
│ │ ├── fix_unity_jar.cpp
│ │ ├── fix_unity_jar.h
│ │ └── fix_unity_jar_main.cpp
└── null_lib
│ ├── Android.mk
│ ├── Application.mk
│ ├── build_libs.bat
│ ├── build_libs.sh
│ └── null_lib_jni.cpp
├── project_hook_unity_jni
├── build_libs
│ ├── Android.mk
│ ├── Application.mk
│ ├── build_libs.bat
│ ├── build_libs.sh
│ └── hook_unity_src_xcode.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── src
│ ├── hook_unity.cpp
│ ├── hook_unity.h
│ ├── hook_unity_jni.cpp
│ ├── java
│ └── com
│ │ └── github
│ │ └── sisong
│ │ └── HotUnity.java
│ └── virtual_apk_patch_jni.cpp
└── project_unity_demo
├── newV
├── Assets
│ ├── MenuClick.cs
│ ├── MenuClick.cs.meta
│ ├── Move.cs
│ ├── Move.cs.meta
│ ├── SMove.unity
│ └── SMove.unity.meta
├── Packages
│ └── manifest.json
└── ProjectSettings
│ ├── AudioManager.asset
│ ├── ClusterInputManager.asset
│ ├── DynamicsManager.asset
│ ├── EditorBuildSettings.asset
│ ├── EditorSettings.asset
│ ├── GraphicsSettings.asset
│ ├── InputManager.asset
│ ├── NavMeshAreas.asset
│ ├── NetworkManager.asset
│ ├── Physics2DSettings.asset
│ ├── PresetManager.asset
│ ├── ProjectSettings.asset
│ ├── ProjectVersion.txt
│ ├── QualitySettings.asset
│ ├── TagManager.asset
│ ├── TimeManager.asset
│ ├── UPRSettings.asset
│ ├── UnityConnectSettings.asset
│ └── VFXManager.asset
└── oldV
├── Assets
├── CMove.unity
├── CMove.unity.meta
├── MenuClick.cs
├── MenuClick.cs.meta
├── Move.cs
├── Move.cs.meta
├── Plugins.meta
└── Plugins
│ └── Android.meta
├── Packages
└── manifest.json
└── ProjectSettings
├── AudioManager.asset
├── ClusterInputManager.asset
├── DynamicsManager.asset
├── EditorBuildSettings.asset
├── EditorSettings.asset
├── GraphicsSettings.asset
├── InputManager.asset
├── NavMeshAreas.asset
├── NetworkManager.asset
├── Physics2DSettings.asset
├── PresetManager.asset
├── ProjectSettings.asset
├── ProjectVersion.txt
├── QualitySettings.asset
├── TagManager.asset
├── TimeManager.asset
├── UPRSettings.asset
├── UnityConnectSettings.asset
└── VFXManager.asset
/.gitignore:
--------------------------------------------------------------------------------
1 | [Ll]ibrary/
2 | [Tt]emp/
3 | [Oo]bj/
4 | [Bb]uild/
5 | Assets/AssetStoreTools*
6 |
7 | # Visual Studio cache directory
8 | .vs/
9 |
10 | # Autogenerated VS/MD/Consulo solution and project files
11 | ExportedObj/
12 | .consulo/
13 | *.csproj
14 | *.unityproj
15 | *.sln
16 | *.suo
17 | *.tmp
18 | *.user
19 | *.userprefs
20 | *.pidb
21 | *.booproj
22 | *.svd
23 | *.pdb
24 | *.opendb
25 |
26 | # Unity3D generated meta files
27 | *.pidb.meta
28 | *.pdb.meta
29 |
30 | # Unity3D Generated File On Crash Reports
31 | sysinfo.txt
32 |
33 | # Builds
34 | *.apk
35 | *.unitypackage
36 | project_hook_unity_jni/build_libs/libs
37 | tool_hot_cache_lib/build_demos/android_ndk_jni_mk/libs
38 | tool_hot_cache_lib/build_demos/android_ndk_jni_mk/obj
39 | project_fix_unity_jar/null_lib/libs
40 | # xcode
41 | xcshareddata
42 | Debug
43 | Release
44 | Release_x86
45 | FixUnityJar.depend
46 | FixUnityJar.layout
47 | VirtualApkPatch.depend
48 | VirtualApkPatch.layout
49 | project_unity_demo/oldV/.vscode
50 | Packages-Update.log
51 | project_unity_demo/newV/.vscode
52 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "project_hook_unity_jni/xHook"]
2 | path = project_hook_unity_jni/xHook
3 | url = https://github.com/iqiyi/xHook.git
4 | [submodule "ApkDiffPatch"]
5 | path = ApkDiffPatch
6 | url = https://github.com/sisong/ApkDiffPatch.git
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 housisong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # [UnityAndroidHotUpdate]
2 | [](https://github.com/sisong/UnityAndroidHotUpdate/releases)
3 | [](LICENSE)
4 | [](https://github.com/sisong/UnityAndroidHotUpdate/issues)
5 |
6 | [[README 中文版](README.md)]
7 | provide a way to hot update Unity app on Android, support code & resources, not need lua js or IL runtime etc..., will not disturb your project development; just loading the new version apk file to achieve.
8 | ( dependent libraries [ApkDiffPatch], [xHook]. )
9 |
10 | [UnityAndroidHotUpdate]: https://github.com/sisong/UnityAndroidHotUpdate
11 | [ApkDiffPatch]: https://github.com/sisong/ApkDiffPatch
12 | [xHook]: https://github.com/iqiyi/xHook
13 | [UnityAndroidIl2cppPatchDemo]: https://github.com/noodle1983/UnityAndroidIl2cppPatchDemo
14 | [HDiffPatch]: https://github.com/sisong/HDiffPatch
15 | [bsdiff]: http://www.daemonology.net/bsdiff/
16 | [archive-patcher]: https://github.com/andrewhayden/archive-patcher
17 | [v2+ sign]: https://source.android.com/security/apksigning/v2
18 |
19 |
20 | ## The effect of this solution
21 | When the android user running the app, update logic can complete the task of downloaded the new version app in the background; When the app is restarted, user run with new version app (not need reinstall through system, the "hot update" means this)!
22 |
23 | ## Theory of hot update
24 | When installing the Android app, the apk file is stored in path ```getApplicationContext().getPackageResourcePath()```, and the native abi library files are decompressed and stored in path ```getApplicationContext().getApplicationInfo().nativeLibraryDir``` , including libmain.so, libunity.so, libil2cpp.so or libmono*.so, etc...
25 | This solution will store the updated version apk file to the update path in ```getApplicationContext().getFilesDir().getPath()```, and decompress the changed local abi library files from apk to the update path; When the app restarts, use the hook and java layer code to intercept the c file API in Unity's library files, map access path from the apk file and library files to the new apk file and new library files.
26 | (The idea Hook Unity library's C file API to implement hot update, comes from [UnityAndroidIl2cppPatchDemo], thanks.)
27 |
28 | ## Feature
29 | * **Implement simple and run fast**
30 | The theory and implement are simple, and support hot update of code and resources; support il2cpp and mono two code backend, support libunity.so, libmono.so and other library files changes; support libmain.so little(RELEASE) version changes, not support Unity main(MAJOR & MINOR) version changes;
31 | Solution import into project is simple, easy to modify by yourself; (the description of import project is at back of this document.)
32 | After the hot update, the execution performance of the app is not affected, unlike the slow performance and Activity compatibility problems after using the plugin (or virtual) apk solution;
33 | This solution will not disturb your Unity project development, not need other program language or constraints, such as lua js or IL runtime etc...
34 | * **The patch package by downloaded is small**
35 | Not need to download the complete apk file, you just need to download the difference between the existing version and the latest version; then you can create the latest version apk file on device;
36 | You can select [ApkDiffPatch] for apk file diff&patch, it can generate very small patch package; for example, just some simple code changes, the patch package size about hundreds of KB.
37 | * **Development environment and compatibility**
38 | Test used Unity5.6, Unity2017, Unity2018, Unity2019;
39 | Test used mono and il2cpp backend;
40 | Test on armeabi-v7a arm64-v8a and x86 device;
41 | Test supported Andorid4.1+; but maybe only supported Andorid5+ when using Unity5.6 or Unity2017 + il2cpp backend, you need tests;
42 | It is generally compatible, if the main(MAJOR & MINOR) version of Unity unchanged (jni lib's API will not change) and no new .so library files been added;
43 | The project's other .so libraries can be added to the list of libraries that are allowed to be advance load, for compatible with the hot update (added in the HotUnity.java file, see the description of improt project);
44 | Tested to create a simple app with the same Unity main version, and successfully hot updated to an existing complex game app;
45 | Of course, those involving permissions or third-party services, etc., their compatibility need more testing.
46 |
47 |
48 | ## How to import into your Unity project for testing
49 | * Export project: select Gradle and export project in your Unity project, and then modify it and do packaging by Android Studio;
50 | * Modify the exported project:
51 | add libhotunity.so(rebuild in path ```project_hook_unity_jni```) to project jniLibs;
52 | add ```com/github/sisong/HotUnity.java``` to project; (You can add the .so in this file that need hot update, which will be loaded if exist new version;)
53 | edit file UnityPlayerActivity.java in project; add code: ```import com.github.sisong.HotUnity;```, and add code: ```HotUnity.hotUnity(this);``` before ```mUnityPlayer = new UnityPlayer(this);```
54 | * If you need to support upgrading little(RELEASE) version of Unity, you need to use the FixUnityJar program(code in path ```project_fix_unity_jar/fix_unity_jar```) to modify the file unity-classes.jar in the export project, and add libnull.so(rebuild in path ```project_fix_unity_jar/null_lib ```) to project jniLibs;
55 | * APK installation for compatibility with Android 10, need:
56 | Edit AndroidProject's ```AndroidManifest.xml``` file's ```application``` node, add:
57 | ```xml
58 |
59 |
61 |
62 |
63 | ```
64 | Create ```/xml/update_files.xml``` file in AndroidProject's ```res``` path, Enter the following content:
65 | ```xml
66 |
67 |
68 |
69 |
70 | ```
71 | * Package the test project by Android Studio (you can automate the process of exporting modifying and packaging in Unity with the editor extension), the app should be able to running normally on the device; now you need test the app "hot" update to a new version;
72 | * Manual hot update test process: you have a new version app named update.apk, placed it in the HotUpdate subdirectory of the ```getApplicationContext().getFilesDir().getPath()``` (device path ```/data/data//files/HotUpdate/```); Decompress the *.so file in ```lib//``` from update.apk, and place it in the directory ```HotUpdate/update.apk_lib/``` (can also dispose only changed .so file); restart the app on the device, you should be able to see that the new version is already running!
73 |
74 |
75 | ## How to automate hot update by app
76 | (the following info provides an optional way to do this)
77 | * We have a new version apk, I can use the diff tool to generate patch between the old and new versions; put the version upgrade info and patch files on the server;
78 | * When the app running, it checks the upgrade info and download the patch file; the app call patch algorithm to generate a new version apk(ie ```HotUpdate/update.apk```) using the latest apk and patch file; At the same time, choose to cache the changed .so files in this new apk to the ```HotUpdate/update.apk_lib/``` path; If you need a "cold" re-install choose not to cache .so files;
79 | * Generate patch file between apks and merge it on device, used the [ApkDiffPatch]; if you want test diff program, you can download it at [releases](https://github.com/sisong/ApkDiffPatch/releases); The patch process needs to be executed on the user device. provided function virtualApkPatch() in java, and native function virtual_apk_patch() can be used in C#.
80 | * Note: ApkDiffPatch is specially optimized for zip file. Generally, it generates smaller patch file size than [bsdiff] or [HDiffPatch]; The ZipDiff tool has special requirements for the input apk file, if the apk has [v2+ sign], then you need to normalized the apk files by the ApkNormalized tool; Then use AndroidSDK#apksigner to re-sign the apk; All the apks released for user need to done this process, this is to byte by byte restore the apk when patching; (apk after this processed, it is also compatible with the patch size optimization scheme [archive-patcher] of the Google Play Store)
81 |
82 |
83 | ## Recommend a process for multi version update
84 | ```
85 | v0 -> new install(v0)
86 |
87 | v1 -> diff(v0,v1) -> pat01 ; v0: patch(v0,download(pat01)) -> v1 + cache_lib(v1)
88 | ( if error on patch then: download(v1) -> v1 + install(v1) )
89 |
90 | v2 -> diff(v0,v2) -> pat02 ; v0: patch(v0,download(pat02)) -> v2 + cache_lib(v2)
91 | diff(v1,v2) -> pat12 ; v1: patch(v1,download(pat12)) -> v2 + cache_lib(v2)
92 |
93 | v3 -> (if no patch for v0) ; v0: download(v3) -> v3 + install(v3)
94 | diff(v1,v3) -> pat13 ; v1: patch(v1,download(pat13)) -> v3 + cache_lib(v3)
95 | diff(v2,v3) -> pat23 ; v2: patch(v2,download(pat23)) -> v3 + cache_lib(v3)
96 |
97 | if is_need_install(v3,v4):
98 | v4 -> (if no patch for v0) ; v0: download(v4) -> v4 + install(v4)
99 | diff(v1,v4) -> pat14 ; v1: patch(v1,download(pat14)) -> v4 + install(v4)
100 | diff(v2,v4) -> pat24 ; v2: patch(v2,download(pat24)) -> v4 + install(v4)
101 | diff(v3,v4) -> pat34 ; v3: patch(v3,download(pat34)) -> v4 + install(v4)
102 |
103 | v5 -> (if no patch for v0) ; v0: download(v5) -> v5 + install(v5)
104 | (if no patch for v1) ; v1: download(v5) -> v5 + install(v5)
105 | diff(v2,v5) -> pat25 ; v2: patch(v2,download(pat25)) -> v5 + install(v5)
106 | diff(v3,v5) -> pat35 ; v3: patch(v3,download(pat35)) -> v5 + install(v5)
107 | diff(v4,v5) -> pat45 ; v4: patch(v4,download(pat45)) -> v5 + cache_lib(v5)
108 | ```
109 | This new version and patch release process needs to be automated: normalized apk, re-signing, creating patches, generate config, etc...
110 |
111 |
112 | ## Defect
113 | * After changed API between Java and Unity, apk can't be hot update, the new apk needs to be installed; After changed the Unity main(MAJOR & MINOR) version, apk can't be hot update, and the same Unity main version can continue to hot update;
114 | * Can not switch between il2cpp and mono backend apks by hot update, the new apk needs to be installed;
115 | * The solution can only support Android and can't be applied on iOS; (The app developed by Unity run on PC, if needs difference update, you can using [HDiffPatch] to diff&patch between directory.)
116 | * The diff&patch algorithm selected [ApkDiffPatch], which may not support this situation: apk must support [v2+ sign], but released apk cannot be signed by self, then it is impossible to version control and diff;
117 | * The new apk file and cached .so files take up disk space; A recommended solution: fixed Unity version, initial version with a minimized apk (or obb split mode), subsequent updates are the full version; (Another possible improvement is to use the concept of virtual apk: Use hook to map the access to the original apk file when file API access unchanged entry files in virtual apk; the patch process also requires a new implementation; a similar implementation see [UnityAndroidIl2cppPatchDemo].)
118 |
119 |
120 | by housisong@gmail.com
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [UnityAndroidHotUpdate]
2 | [](https://github.com/sisong/UnityAndroidHotUpdate/releases)
3 | [](LICENSE)
4 | [](https://github.com/sisong/UnityAndroidHotUpdate/issues)
5 |
6 | [[README English](README.en.md)]
7 | 提供了一个在Android上热更新Unity开发的app的方案,支持代码和资源;不依赖其他语言(lua,js等)不干涉项目开发过程;它通过直接加载新版本apk文件来实现的。
8 | ( 依赖的库 [ApkDiffPatch], [xHook]. )
9 |
10 | [UnityAndroidHotUpdate]: https://github.com/sisong/UnityAndroidHotUpdate
11 | [ApkDiffPatch]: https://github.com/sisong/ApkDiffPatch
12 | [xHook]: https://github.com/iqiyi/xHook
13 | [UnityAndroidIl2cppPatchDemo]: https://github.com/noodle1983/UnityAndroidIl2cppPatchDemo
14 | [HDiffPatch]: https://github.com/sisong/HDiffPatch
15 | [bsdiff]: http://www.daemonology.net/bsdiff/
16 | [archive-patcher]: https://github.com/andrewhayden/archive-patcher
17 | [v2版及以上签名]: https://source.android.com/security/apksigning/v2
18 |
19 |
20 | ## 该方案的效果
21 | Android用户在使用app过程中,更新逻辑可以在后台完成下载新版本的任务,当app重新启动后用户看到的是新版本的app(不需要系统安装); 这里的“热更”就是指不需要经过系统的重新安装就拥有了新版本!
22 |
23 |
24 | ## 热更原理
25 | Android程序在安装后,apk文件存放在```getApplicationContext().getPackageResourcePath()```;并将其中相应abi的库文件解压存放到了```getApplicationContext().getApplicationInfo().nativeLibraryDir```,其中包括libmain.so,libunity.so,libil2cpp.so or libmono*.so,等;
26 | 本方案会在app运行中,将更新到的新版本apk存放到```getApplicationContext().getFilesDir().getPath()```的更新路径,并将apk中本机abi并发生了改变的库文件解压到更新路径; 当app重启后用hook和java层代码配合,将unity相关库访问apk文件和lib库文件路径的c文件访问函数映射到访问我们准备好的新的apk文件和lib路径。
27 | ( Hook Unity库的C文件API来实现热更的思路来源于[UnityAndroidIl2cppPatchDemo],感谢,这里做了一些简化和改进。 )
28 |
29 |
30 | ## 特性
31 | * **简单快速**
32 | 原理和实现简单,并且支持代码和资源的热更新;同时支持il2cpp与mono这2种代码打包方式,支持libunity.so、libmono.so等库文件变动,支持libmain.so的RELEASE小版本改动,不支持Unity大版本(MAJOR和MINOR版本)变动;
33 | 接入项目简单,容易修改定制;(本文后面有接入项目说明)
34 | 热更新后app执行速度不受影响,不像使用插件化方案后遇到的速度慢和Activity兼容等问题;
35 | 这是一个几乎不改变Unity开发流程的方案,不需要引入额外的语言(如lua或js等),也不需要一个额外的中间层解释器(如ILRuntime)、动态加载反射、手工绑定等工作。
36 | * **下载流量小**
37 | 不需要下载完整的apk文件,而只需要下载已有版本和最新版本之间的差异就可以了;然后在用户本地合成新的apk文件;
38 | 这里选择用的[ApkDiffPatch]方案,可以生成非常小的补丁;比如只是一些简单代码修改,补丁一般在百k级别。
39 | * **运行环境和兼容性**
40 | 在Unity5.6、Unity2017、Unity2018、Unity2019的多个版本上测试过;
41 | 测试过分别使用mono代码后端和il2cpp代码后端;
42 | 测试过armeabi-v7a、arm64-v8a和x86的设备;
43 | 一般支持Andorid4.1及以上的系统了;但使用Unity5.6或Unity2017和il2cpp代码后端时可能只支持Andorid5及以上,你需要测试;
44 | 在发布使用的Unity大版本(MAJOR & MINOR)没有变化(jni库的API不变)和没有新增.so库文件的情况下,一般都可以兼容;
45 | 项目的常规.so库可以添加到允许更新的库列表中提前加载从而兼容热更(在HotUnity.java文件中添加,参见接入项目说明);
46 | 测试过用相同的Unity大版本新建了一个最简单的app,也可以更新到一个已有的复杂的游戏app;
47 | 当然某些涉及权限、特殊第三方业务等是否兼容还需要更多测试。
48 |
49 |
50 | ## 如何接入你的项目测试
51 | * 导出项目: 将你的Unity项目选择Gradle导出项目,以便修改后交给Android Studio进行打包;
52 | * 修改导出的项目:
53 | 将libhotunity.so文件(注意abi路径对应,build项目在```project_hook_unity_jni```目录里)复制到项目的jniLibs下的相应子目录中;
54 | 将```com/github/sisong/HotUnity.java```文件复制到项目源代码的java相应路径中; (可以在这个文件中添加你需要支持热更的.so,这会立即加载可能存在的新版本库)
55 | 在项目的UnityPlayerActivity.java文件中```import com.github.sisong.HotUnity;```,并且在```mUnityPlayer = new UnityPlayer(this);```代码之前添加代码```HotUnity.hotUnity(this);```
56 | * 如果你需要支持升级发布用的Unity的小版本,那么需要用FixUnityJar程序(代码在```project_fix_unity_jar/fix_unity_jar```路径里)修改导出项目中文件unity-classes.jar,并且将libnull.so文件(build项目在```project_fix_unity_jar/null_lib```目录里)复制到项目的jniLibs下的相应子目录中;
57 | * 为了兼容Android10安装APK,需要:
58 | 在安卓工程的```AndroidManifest.xml```文件的```application```节点中添加
59 | ```xml
60 |
61 |
63 |
64 |
65 | ```
66 | 在安卓工程的```res```目录创建```/xml/update_files.xml```文件,内容为:
67 | ```xml
68 |
69 |
70 |
71 |
72 | ```
73 | * 用Android Studio打包测试项目(你可以把这个导出、修改、打包的过程在Unity中利用编辑器扩展自动化下来;后续本仓库会更新到Demo中),app在设备上应该能够正常运行; 现在你需要测试热更新到一个新版本;
74 | * 手工热更新测试过程:假设有了修改过的新版本apk命名成update.apk,放置到```getApplicationContext().getFilesDir().getPath()```目录的HotUpdate子目录下(一般设备上路径```/data/data//files/HotUpdate/```); 将update.apk包中```lib/<本测试设备abi>/```中的*.so文件解压后直接放置到```HotUpdate/update.apk_lib/```目录下(可以只放置有修改过的.so文件); 重新运行设备上安装的app,你应该可以看到,已经运行的是新版本!
75 |
76 |
77 | ## 如何在正式app里自动化热更
78 | app热更应该是一个程序自动化的过程,它将代替我们上面的手工测试过程;(本方案提供了一种可选实现路径)
79 | * 假设开发了新版本,我可以用diff工具生成新旧版本之间的补丁;将版本升级信息和补丁文件放到服务器上;
80 | * 客户运行app时检查到升级信息并下载到相应的补丁文件,app用patch算法将本机最新apk和补丁生成新版本apk(即```HotUpdate/update.apk```), 并选择将apk中有修改过的.so文件缓存到```HotUpdate/update.apk_lib/```路径;需要“冷”安装时选择不缓存.so文件;
81 | * 生成apk间的补丁和在设备上合并,使用了[ApkDiffPatch]项目; diff程序如果不想自己编译,可以在[releases](https://github.com/sisong/ApkDiffPatch/releases)下载到;patch过程需要在用户设备上执行,java中提供了virtualApkPatch()函数,C#中可以使用native的virtual_apk_patch()函数;
82 | * 注意:ApkDiffPatch针对zip进行了特别的优化,一般比[bsdiff]和[HDiffPatch]生成更小的补丁,其ZipDiff工具对输入的apk文件有特别的要求,如果apk有[v2版及以上签名],那需要用库提供的ApkNormalized工具对apk进行标准化,然后再用AndroidSDK#apksigner对apk重新进行签名;所有对用户发布的apk都需要经过这个处理,这是为了patch时能够原样还原apk;(经过这个处理过的apk,也兼容谷歌Play商店的补丁大小优化方案[archive-patcher])
83 |
84 |
85 | ## 推荐一个多版本更新流程(较复杂的示例,考虑了支持需要重新安装的情况)
86 | ```
87 | v0 -> new install(v0)
88 |
89 | v1 -> diff(v0,v1) -> pat01 ; v0: patch(v0,download(pat01)) -> v1 + cache_lib(v1)
90 | ( 如果patch时发生错误: download(v1) -> v1 + install(v1) )
91 |
92 | v2 -> diff(v0,v2) -> pat02 ; v0: patch(v0,download(pat02)) -> v2 + cache_lib(v2)
93 | diff(v1,v2) -> pat12 ; v1: patch(v1,download(pat12)) -> v2 + cache_lib(v2)
94 |
95 | v3 -> (假设放弃了对v0的补丁) ; v0: download(v3) -> v3 + install(v3)
96 | diff(v1,v3) -> pat13 ; v1: patch(v1,download(pat13)) -> v3 + cache_lib(v3)
97 | diff(v2,v3) -> pat23 ; v2: patch(v2,download(pat23)) -> v3 + cache_lib(v3)
98 |
99 | if is_need_install(v3,v4):
100 | v4 -> (假设放弃了对v0的补丁) ; v0: download(v4) -> v4 + install(v4)
101 | diff(v1,v4) -> pat14 ; v1: patch(v1,download(pat14)) -> v4 + install(v4)
102 | diff(v2,v4) -> pat24 ; v2: patch(v2,download(pat24)) -> v4 + install(v4)
103 | diff(v3,v4) -> pat34 ; v3: patch(v3,download(pat34)) -> v4 + install(v4)
104 |
105 | v5 -> (假设放弃了对v0的补丁) ; v0: download(v5) -> v5 + install(v5)
106 | (假设放弃了对v1的补丁) ; v1: download(v5) -> v5 + install(v5)
107 | diff(v2,v5) -> pat25 ; v2: patch(v2,download(pat25)) -> v5 + install(v5)
108 | diff(v3,v5) -> pat35 ; v3: patch(v3,download(pat35)) -> v5 + install(v5)
109 | diff(v4,v5) -> pat45 ; v4: patch(v4,download(pat45)) -> v5 + cache_lib(v5)
110 | ```
111 | 这个新版本和补丁的发布过程,需要自动化:Apk标准化、重新签名、创建补丁、生成配置等。
112 |
113 |
114 | ## 已知缺点
115 | * Java和Unity之间的API变动后不支持热更,新apk需要安装;一般切换升级Unity大版本后无法热更, 小版本切换可以热更;
116 | * 不能随意切换il2cpp与mono代码打包方式,否则无法热更新,apk需要重新安装;
117 | * 方案只能支持android,无法应用到iOS上;(PC上Unity开发的app需要支持差异更新可以考虑使用[HDiffPatch]之类支持目录间diff和patch的方案就可以了)
118 | * diff&patch方案选择了[ApkDiffPatch]方案,该方案可能不能支持这种情况:apk必须要支持[v2版及以上签名]发布,但签名权又不在自己手中,而在渠道手中,并且造成了无法进行版本控制和diff的;
119 | * 得到的新apk文件和库缓存会长期占用磁盘空间;一个实践方案是:固定Unity版本,初始版本用一个最小化的apk(或者obb分离模式),后续更新后才是完整版本;(另一个可能的改进方案是使用虚拟apk的概念:将没有改变的entry文件利用hook将访问映射到原apk文件里,补丁逻辑也需要另外实现;类似的实现参见[UnityAndroidIl2cppPatchDemo])。
120 |
121 |
122 | by housisong@gmail.com
123 |
--------------------------------------------------------------------------------
/VirtualApkPatch/builds_test/codeblocks/VirtualApkPatch.cbp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/VirtualApkPatch/builds_test/main.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // main.cpp
3 | // VirtualApkPatch
4 | //
5 | // Created by sisong on 2019/09/12.
6 | // Copyright © 2019 sisong. All rights reserved.
7 | //
8 | #include "../patch/virtual_apk_patch.h"
9 | #include "../../ApkDiffPatch/HDiffPatch/_clock_for_demo.h"
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | int main(int argc, const char * argv[]) {
17 | double time0=clock_s();
18 | if (argc!=1+9){
19 | printf(" virtual_apk_patch()\\virtual_apk_merge() is run on android;\n"
20 | " VirtualApkPatch for test them on PC;\n"
21 | "usage: VirtualApkPatch baseApk baseSoDir hotApk hotSoDir"
22 | " out_newApk out_newChangedSoDir zipDiffPath arch_abi threadNum\n");
23 | return -1;
24 | }
25 | int threadNum=atoi(argv[9]);
26 | const char* hotApk=argv[3];
27 | const char* newSoDir=argv[6];
28 | int result=virtual_apk_patch(argv[1],argv[2],hotApk,argv[4],
29 | argv[5],newSoDir,argv[7],argv[8],
30 | threadNum);
31 | if (result==kVApkPatch_ok)
32 | printf("virtual_apk_patch() ok!\n");
33 | else
34 | printf("virtual_apk_patch() return error: %d !\n",result);
35 | double time1=clock_s();
36 | printf("virtual_apk_patch time: %.3f s\n\n",(time1-time0));
37 |
38 | if ((result==kVApkPatch_ok)&&(newSoDir!=0)&&(strlen(newSoDir)>0)
39 | &&(hotApk!=0)&&(strlen(hotApk)>0)){
40 | result=virtual_apk_merge(argv[1],argv[2],hotApk,argv[4],
41 | argv[5],newSoDir,argv[8]);
42 | printf("\n");
43 | if (result==kVApkMerge_ok)
44 | printf("virtual_apk_merge() ok!\n");
45 | else
46 | printf("virtual_apk_merge() return error: %d !\n",result);
47 | double time2=clock_s();
48 | printf("virtual_apk_merge time: %.3f s\n",(time2-time1));
49 | }
50 | return result;
51 | }
52 |
--------------------------------------------------------------------------------
/VirtualApkPatch/builds_test/vc/VirtualApkPatch.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {A1F31BE8-0454-41F8-A2AE-9A25531F5C1B}
23 | v4.0
24 | ManagedCProj
25 | VirtualApkPatch
26 |
27 |
28 |
29 | Application
30 | true
31 | false
32 | Unicode
33 | v143
34 |
35 |
36 | Application
37 | true
38 | false
39 | Unicode
40 | v143
41 |
42 |
43 | Application
44 | false
45 | false
46 | Unicode
47 | true
48 | v143
49 |
50 |
51 | Application
52 | false
53 | false
54 | Unicode
55 | true
56 | v143
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | true
76 |
77 |
78 | true
79 |
80 |
81 | false
82 |
83 |
84 | false
85 |
86 |
87 |
88 | Level3
89 | Disabled
90 | WIN32;_IS_NEED_VIRTUAL_ZIP=1;_IS_NEED_FIXED_ZLIB_VERSION=1;_DEBUG;%(PreprocessorDefinitions)
91 | NotUsing
92 |
93 |
94 |
95 |
96 | true
97 |
98 |
99 |
100 |
101 |
102 |
103 | Level3
104 | Disabled
105 | WIN32;_IS_NEED_VIRTUAL_ZIP=1;_IS_NEED_FIXED_ZLIB_VERSION=1;_DEBUG;%(PreprocessorDefinitions)
106 | NotUsing
107 |
108 |
109 |
110 |
111 | true
112 |
113 |
114 |
115 |
116 |
117 |
118 | Level3
119 | WIN32;_IS_NEED_VIRTUAL_ZIP=1;_IS_NEED_FIXED_ZLIB_VERSION=1;NDEBUG;%(PreprocessorDefinitions)
120 | NotUsing
121 |
122 |
123 | Size
124 |
125 |
126 | false
127 |
128 |
129 |
130 |
131 |
132 |
133 | Level3
134 | WIN32;_IS_NEED_VIRTUAL_ZIP=1;_IS_NEED_FIXED_ZLIB_VERSION=1;NDEBUG;%(PreprocessorDefinitions)
135 | NotUsing
136 |
137 |
138 | Size
139 |
140 |
141 | false
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 | {0849cd0e-1b9b-44eb-8937-a278b6a7e2cf}
188 |
189 |
190 |
191 |
192 |
193 |
--------------------------------------------------------------------------------
/VirtualApkPatch/builds_test/vc/VirtualApkPatch.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ApkDiffPatch
8 |
9 |
10 | ApkDiffPatch
11 |
12 |
13 | ApkDiffPatch
14 |
15 |
16 | ApkDiffPatch
17 |
18 |
19 | ApkDiffPatch
20 |
21 |
22 | zlib
23 |
24 |
25 | zlib
26 |
27 |
28 | zlib
29 |
30 |
31 | zlib
32 |
33 |
34 | zlib
35 |
36 |
37 | zlib
38 |
39 |
40 | zlib
41 |
42 |
43 | zlib
44 |
45 |
46 | zlib
47 |
48 |
49 | zlib
50 |
51 |
52 | zlib
53 |
54 |
55 | HDiffPatch
56 |
57 |
58 | HDiffPatch
59 |
60 |
61 | HDiffPatch
62 |
63 |
64 | HDiffPatch
65 |
66 |
67 |
68 |
69 |
70 | ApkDiffPatch
71 |
72 |
73 | ApkDiffPatch
74 |
75 |
76 | ApkDiffPatch
77 |
78 |
79 | ApkDiffPatch
80 |
81 |
82 | ApkDiffPatch
83 |
84 |
85 | ApkDiffPatch
86 |
87 |
88 | ApkDiffPatch
89 |
90 |
91 |
92 |
93 | {172e7ddf-de29-421e-a9bf-8f08b5bae838}
94 |
95 |
96 | {52c2d553-1c2e-46d7-853e-9fad42537d0e}
97 |
98 |
99 | {2f4024ec-afe5-4867-aaec-ab4607a6095d}
100 |
101 |
102 |
--------------------------------------------------------------------------------
/VirtualApkPatch/builds_test/xcode/VirtualApkPatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VirtualApkPatch/patch/virtual_apk_patch.h:
--------------------------------------------------------------------------------
1 | // virtual_apk_patch.h
2 | // Created by sisong on 2019-09-11.
3 | #ifndef _virtual_apk_patch_h_
4 | #define _virtual_apk_patch_h_
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | enum TVirtualApkPatch_result{
10 | kVApkPatch_ok=0,
11 | kVApkPatch_memError,
12 | kVApkPatch_baseApkFileError,
13 | kVApkPatch_apkFileError,
14 | kVApkPatch_abiError,
15 | kVApkPatch_tempFileError,
16 | kVApkPatch_mkNewSoDirError,
17 | kVApkPatch_catchedUnknowError,
18 | kVApkPatch_newApkBadError,
19 |
20 | kVApkMerge_ok=0,
21 | kVApkMerge_ApkPathsError=50,
22 | kVApkMerge_lostNewApkError,
23 | kVApkMerge_removeHotApkError,
24 | kVApkMerge_renameNewApkError,
25 | kVApkMerge_baseApkFileError,
26 | kVApkMerge_abiError,
27 | kVApkMerge_newApkFileError,
28 | kVApkMerge_removeLibFilesError,
29 | kVApkMerge_renameLibDirError,
30 | kVApkMerge_moveLibFilesError,
31 | kVApkMerge_removeNewSoDirError,
32 | kVApkMerge_catchedUnknowError,
33 | kVApkPatch_hotApkBadError,
34 |
35 | kVApkPatch_patchError_base=100, // kVApkPatch_patchError_base + TPatchResult error
36 | };
37 |
38 | # ifdef __ANDROID__
39 | # define VIRTUAL_APK_PATCH_EXPORT __attribute__((visibility("default")))
40 | # else
41 | # define VIRTUAL_APK_PATCH_EXPORT
42 | # endif
43 |
44 | // patch hot&base + zipDiff to new
45 | // baseApk&baseSoDir read only
46 | // out_newChangedSoDir can NULL or "", meen no need cache .so files
47 | // if first patch hotApk/hotSoDir can NULL or "", and not need use merge
48 | // if threadNum>1 then used more and more memory, you need test it
49 | int virtual_apk_patch(const char* baseApk,const char* baseSoDir,
50 | const char* hotApk,const char* hotSoDir,
51 | const char* out_newApk,const char* out_newChangedSoDir,
52 | const char* zipDiffPath,const char* arch_abi,
53 | int threadNum) VIRTUAL_APK_PATCH_EXPORT;
54 |
55 | //merge new(patch result) to hot
56 | //if return error then need update new version from base version
57 | int virtual_apk_merge(const char* baseApk,const char* baseSoDir,
58 | const char* hotApk,const char* hotSoDir,
59 | const char* newApk,const char* newChangedSoDir,
60 | const char* arch_abi) VIRTUAL_APK_PATCH_EXPORT;
61 |
62 | #ifdef __cplusplus
63 | }
64 | #endif
65 | #endif //_virtual_apk_patch_h_
66 |
67 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/builds/codeblocks/FixUnityJar.cbp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/builds/vc/FixUnityJar/FixUnityJar.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | x64
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {209AF3B6-B62D-4FD6-B2A6-28A5E8DEFE73}
23 | v4.0
24 | ManagedCProj
25 | FixUnityJar
26 |
27 |
28 |
29 | Application
30 | true
31 | false
32 | Unicode
33 | v143
34 |
35 |
36 | Application
37 | true
38 | false
39 | Unicode
40 | v143
41 |
42 |
43 | Application
44 | false
45 | false
46 | Unicode
47 | true
48 | v143
49 |
50 |
51 | Application
52 | false
53 | false
54 | Unicode
55 | true
56 | v143
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | true
76 | ..\..\..\..\..\ApkDiffPatch\zlib1.2.11;$(IncludePath)
77 |
78 |
79 | true
80 | ..\..\..\..\..\ApkDiffPatch\zlib1.2.11;$(IncludePath)
81 |
82 |
83 | false
84 | ..\..\..\..\..\ApkDiffPatch\zlib1.2.11;$(IncludePath)
85 |
86 |
87 | false
88 | ..\..\..\..\..\ApkDiffPatch\zlib1.2.11;$(IncludePath)
89 |
90 |
91 |
92 | Level3
93 | Disabled
94 | WIN32;_IS_USED_MULTITHREAD=0;_IS_NEED_FIXED_ZLIB_VERSION=0;_DEBUG;%(PreprocessorDefinitions)
95 | NotUsing
96 |
97 |
98 |
99 |
100 | true
101 |
102 |
103 |
104 |
105 |
106 |
107 | Level3
108 | Disabled
109 | WIN32;_IS_USED_MULTITHREAD=0;_IS_NEED_FIXED_ZLIB_VERSION=0;_DEBUG;%(PreprocessorDefinitions)
110 | NotUsing
111 |
112 |
113 |
114 |
115 | true
116 |
117 |
118 |
119 |
120 |
121 |
122 | Level3
123 | WIN32;_IS_USED_MULTITHREAD=0;_IS_NEED_FIXED_ZLIB_VERSION=0;NDEBUG;%(PreprocessorDefinitions)
124 | NotUsing
125 |
126 |
127 |
128 |
129 | false
130 |
131 |
132 |
133 |
134 |
135 |
136 | Level3
137 | WIN32;_IS_USED_MULTITHREAD=0;_IS_NEED_FIXED_ZLIB_VERSION=0;NDEBUG;%(PreprocessorDefinitions)
138 | NotUsing
139 |
140 |
141 |
142 |
143 | false
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/builds/vc/FixUnityJar/FixUnityJar.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {5232775d-fe73-4dc1-8e6d-8b1d0db39c7b}
6 |
7 |
8 | {63dfd856-c2d5-4bcd-b54b-e61432b223e9}
9 |
10 |
11 |
12 |
13 |
14 |
15 | zipper
16 |
17 |
18 | zipper
19 |
20 |
21 | zipper
22 |
23 |
24 | zlib
25 |
26 |
27 | zlib
28 |
29 |
30 | zlib
31 |
32 |
33 | zlib
34 |
35 |
36 | zlib
37 |
38 |
39 | zlib
40 |
41 |
42 | zlib
43 |
44 |
45 | zlib
46 |
47 |
48 | zlib
49 |
50 |
51 | zlib
52 |
53 |
54 | zlib
55 |
56 |
57 |
58 |
59 |
60 | zipper
61 |
62 |
63 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/builds/xcode/FixUnityJar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | D6D0B8372327437900E4FDD0 /* fix_unity_jar_main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6D0B8352327437900E4FDD0 /* fix_unity_jar_main.cpp */; };
11 | D6D0B8382327437900E4FDD0 /* fix_unity_jar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6D0B8362327437900E4FDD0 /* fix_unity_jar.cpp */; };
12 | D6D0B83C232764D500E4FDD0 /* Zipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6D0B83A232764D500E4FDD0 /* Zipper.cpp */; };
13 | D6D0B8422327679B00E4FDD0 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D6D0B8412327679B00E4FDD0 /* libz.tbd */; };
14 | D6D0B844232767BD00E4FDD0 /* file_for_patch.c in Sources */ = {isa = PBXBuildFile; fileRef = D6D0B843232767BC00E4FDD0 /* file_for_patch.c */; };
15 | D6D0B846232767E400E4FDD0 /* patch.c in Sources */ = {isa = PBXBuildFile; fileRef = D6D0B845232767E300E4FDD0 /* patch.c */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | D6D0B8242327427D00E4FDD0 /* CopyFiles */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = /usr/share/man/man1/;
23 | dstSubfolderSpec = 0;
24 | files = (
25 | );
26 | runOnlyForDeploymentPostprocessing = 1;
27 | };
28 | /* End PBXCopyFilesBuildPhase section */
29 |
30 | /* Begin PBXFileReference section */
31 | D6D0B8262327427D00E4FDD0 /* FixUnityJar */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = FixUnityJar; sourceTree = BUILT_PRODUCTS_DIR; };
32 | D6D0B8342327437900E4FDD0 /* fix_unity_jar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fix_unity_jar.h; sourceTree = ""; };
33 | D6D0B8352327437900E4FDD0 /* fix_unity_jar_main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fix_unity_jar_main.cpp; sourceTree = ""; };
34 | D6D0B8362327437900E4FDD0 /* fix_unity_jar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fix_unity_jar.cpp; sourceTree = ""; };
35 | D6D0B83A232764D500E4FDD0 /* Zipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Zipper.cpp; path = ../../../../../ApkDiffPatch/src/patch/Zipper.cpp; sourceTree = ""; };
36 | D6D0B83B232764D500E4FDD0 /* Zipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Zipper.h; path = ../../../../../ApkDiffPatch/src/patch/Zipper.h; sourceTree = ""; };
37 | D6D0B8412327679B00E4FDD0 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
38 | D6D0B843232767BC00E4FDD0 /* file_for_patch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = file_for_patch.c; path = ../../../../../ApkDiffPatch/HDiffPatch/file_for_patch.c; sourceTree = ""; };
39 | D6D0B845232767E300E4FDD0 /* patch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = patch.c; path = ../../../../../ApkDiffPatch/HDiffPatch/libHDiffPatch/HPatch/patch.c; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | D6D0B8232327427D00E4FDD0 /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | D6D0B8422327679B00E4FDD0 /* libz.tbd in Frameworks */,
48 | );
49 | runOnlyForDeploymentPostprocessing = 0;
50 | };
51 | /* End PBXFrameworksBuildPhase section */
52 |
53 | /* Begin PBXGroup section */
54 | D6D0B81D2327427D00E4FDD0 = {
55 | isa = PBXGroup;
56 | children = (
57 | D6D0B839232764B700E4FDD0 /* zipper */,
58 | D6D0B8332327437900E4FDD0 /* src */,
59 | D6D0B8272327427D00E4FDD0 /* Products */,
60 | D6D0B8402327679B00E4FDD0 /* Frameworks */,
61 | );
62 | sourceTree = "";
63 | };
64 | D6D0B8272327427D00E4FDD0 /* Products */ = {
65 | isa = PBXGroup;
66 | children = (
67 | D6D0B8262327427D00E4FDD0 /* FixUnityJar */,
68 | );
69 | name = Products;
70 | sourceTree = "";
71 | };
72 | D6D0B8332327437900E4FDD0 /* src */ = {
73 | isa = PBXGroup;
74 | children = (
75 | D6D0B8342327437900E4FDD0 /* fix_unity_jar.h */,
76 | D6D0B8352327437900E4FDD0 /* fix_unity_jar_main.cpp */,
77 | D6D0B8362327437900E4FDD0 /* fix_unity_jar.cpp */,
78 | );
79 | name = src;
80 | path = ../../src;
81 | sourceTree = "";
82 | };
83 | D6D0B839232764B700E4FDD0 /* zipper */ = {
84 | isa = PBXGroup;
85 | children = (
86 | D6D0B83A232764D500E4FDD0 /* Zipper.cpp */,
87 | D6D0B83B232764D500E4FDD0 /* Zipper.h */,
88 | D6D0B845232767E300E4FDD0 /* patch.c */,
89 | D6D0B843232767BC00E4FDD0 /* file_for_patch.c */,
90 | );
91 | path = zipper;
92 | sourceTree = "";
93 | };
94 | D6D0B8402327679B00E4FDD0 /* Frameworks */ = {
95 | isa = PBXGroup;
96 | children = (
97 | D6D0B8412327679B00E4FDD0 /* libz.tbd */,
98 | );
99 | name = Frameworks;
100 | sourceTree = "";
101 | };
102 | /* End PBXGroup section */
103 |
104 | /* Begin PBXNativeTarget section */
105 | D6D0B8252327427D00E4FDD0 /* FixUnityJar */ = {
106 | isa = PBXNativeTarget;
107 | buildConfigurationList = D6D0B82D2327427D00E4FDD0 /* Build configuration list for PBXNativeTarget "FixUnityJar" */;
108 | buildPhases = (
109 | D6D0B8222327427D00E4FDD0 /* Sources */,
110 | D6D0B8232327427D00E4FDD0 /* Frameworks */,
111 | D6D0B8242327427D00E4FDD0 /* CopyFiles */,
112 | );
113 | buildRules = (
114 | );
115 | dependencies = (
116 | );
117 | name = FixUnityJar;
118 | productName = FixUnityJar;
119 | productReference = D6D0B8262327427D00E4FDD0 /* FixUnityJar */;
120 | productType = "com.apple.product-type.tool";
121 | };
122 | /* End PBXNativeTarget section */
123 |
124 | /* Begin PBXProject section */
125 | D6D0B81E2327427D00E4FDD0 /* Project object */ = {
126 | isa = PBXProject;
127 | attributes = {
128 | LastUpgradeCheck = 1030;
129 | ORGANIZATIONNAME = sisong;
130 | TargetAttributes = {
131 | D6D0B8252327427D00E4FDD0 = {
132 | CreatedOnToolsVersion = 10.3;
133 | };
134 | };
135 | };
136 | buildConfigurationList = D6D0B8212327427D00E4FDD0 /* Build configuration list for PBXProject "FixUnityJar" */;
137 | compatibilityVersion = "Xcode 9.3";
138 | developmentRegion = en;
139 | hasScannedForEncodings = 0;
140 | knownRegions = (
141 | en,
142 | );
143 | mainGroup = D6D0B81D2327427D00E4FDD0;
144 | productRefGroup = D6D0B8272327427D00E4FDD0 /* Products */;
145 | projectDirPath = "";
146 | projectRoot = "";
147 | targets = (
148 | D6D0B8252327427D00E4FDD0 /* FixUnityJar */,
149 | );
150 | };
151 | /* End PBXProject section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | D6D0B8222327427D00E4FDD0 /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | D6D0B8372327437900E4FDD0 /* fix_unity_jar_main.cpp in Sources */,
159 | D6D0B83C232764D500E4FDD0 /* Zipper.cpp in Sources */,
160 | D6D0B844232767BD00E4FDD0 /* file_for_patch.c in Sources */,
161 | D6D0B846232767E400E4FDD0 /* patch.c in Sources */,
162 | D6D0B8382327437900E4FDD0 /* fix_unity_jar.cpp in Sources */,
163 | );
164 | runOnlyForDeploymentPostprocessing = 0;
165 | };
166 | /* End PBXSourcesBuildPhase section */
167 |
168 | /* Begin XCBuildConfiguration section */
169 | D6D0B82B2327427D00E4FDD0 /* Debug */ = {
170 | isa = XCBuildConfiguration;
171 | buildSettings = {
172 | ALWAYS_SEARCH_USER_PATHS = NO;
173 | CLANG_ANALYZER_NONNULL = YES;
174 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
176 | CLANG_CXX_LIBRARY = "libc++";
177 | CLANG_ENABLE_MODULES = YES;
178 | CLANG_ENABLE_OBJC_ARC = YES;
179 | CLANG_ENABLE_OBJC_WEAK = YES;
180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
181 | CLANG_WARN_BOOL_CONVERSION = YES;
182 | CLANG_WARN_COMMA = YES;
183 | CLANG_WARN_CONSTANT_CONVERSION = YES;
184 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
185 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
186 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
187 | CLANG_WARN_EMPTY_BODY = YES;
188 | CLANG_WARN_ENUM_CONVERSION = YES;
189 | CLANG_WARN_INFINITE_RECURSION = YES;
190 | CLANG_WARN_INT_CONVERSION = YES;
191 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
192 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
193 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
195 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
196 | CLANG_WARN_STRICT_PROTOTYPES = YES;
197 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
198 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | CODE_SIGN_IDENTITY = "-";
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu11;
206 | GCC_DYNAMIC_NO_PIC = NO;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_OPTIMIZATION_LEVEL = 0;
209 | GCC_PREPROCESSOR_DEFINITIONS = (
210 | "DEBUG=1",
211 | "$(inherited)",
212 | "_IS_USED_MULTITHREAD=0",
213 | "_IS_NEED_FIXED_ZLIB_VERSION=0",
214 | );
215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
217 | GCC_WARN_UNDECLARED_SELECTOR = YES;
218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
219 | GCC_WARN_UNUSED_FUNCTION = YES;
220 | GCC_WARN_UNUSED_VARIABLE = YES;
221 | MACOSX_DEPLOYMENT_TARGET = 10.14;
222 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
223 | MTL_FAST_MATH = YES;
224 | ONLY_ACTIVE_ARCH = YES;
225 | SDKROOT = macosx;
226 | };
227 | name = Debug;
228 | };
229 | D6D0B82C2327427D00E4FDD0 /* Release */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | ALWAYS_SEARCH_USER_PATHS = NO;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
236 | CLANG_CXX_LIBRARY = "libc++";
237 | CLANG_ENABLE_MODULES = YES;
238 | CLANG_ENABLE_OBJC_ARC = YES;
239 | CLANG_ENABLE_OBJC_WEAK = YES;
240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
241 | CLANG_WARN_BOOL_CONVERSION = YES;
242 | CLANG_WARN_COMMA = YES;
243 | CLANG_WARN_CONSTANT_CONVERSION = YES;
244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
247 | CLANG_WARN_EMPTY_BODY = YES;
248 | CLANG_WARN_ENUM_CONVERSION = YES;
249 | CLANG_WARN_INFINITE_RECURSION = YES;
250 | CLANG_WARN_INT_CONVERSION = YES;
251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
256 | CLANG_WARN_STRICT_PROTOTYPES = YES;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
259 | CLANG_WARN_UNREACHABLE_CODE = YES;
260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
261 | CODE_SIGN_IDENTITY = "-";
262 | DEAD_CODE_STRIPPING = YES;
263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
264 | DEPLOYMENT_POSTPROCESSING = YES;
265 | ENABLE_NS_ASSERTIONS = NO;
266 | ENABLE_STRICT_OBJC_MSGSEND = YES;
267 | GCC_C_LANGUAGE_STANDARD = gnu11;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "_IS_USED_MULTITHREAD=0",
271 | "_IS_NEED_FIXED_ZLIB_VERSION=0",
272 | );
273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
275 | GCC_WARN_UNDECLARED_SELECTOR = YES;
276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
277 | GCC_WARN_UNUSED_FUNCTION = YES;
278 | GCC_WARN_UNUSED_VARIABLE = YES;
279 | MACOSX_DEPLOYMENT_TARGET = 10.14;
280 | MTL_ENABLE_DEBUG_INFO = NO;
281 | MTL_FAST_MATH = YES;
282 | SDKROOT = macosx;
283 | };
284 | name = Release;
285 | };
286 | D6D0B82E2327427D00E4FDD0 /* Debug */ = {
287 | isa = XCBuildConfiguration;
288 | buildSettings = {
289 | CODE_SIGN_STYLE = Automatic;
290 | PRODUCT_NAME = "$(TARGET_NAME)";
291 | };
292 | name = Debug;
293 | };
294 | D6D0B82F2327427D00E4FDD0 /* Release */ = {
295 | isa = XCBuildConfiguration;
296 | buildSettings = {
297 | CODE_SIGN_STYLE = Automatic;
298 | PRODUCT_NAME = "$(TARGET_NAME)";
299 | };
300 | name = Release;
301 | };
302 | /* End XCBuildConfiguration section */
303 |
304 | /* Begin XCConfigurationList section */
305 | D6D0B8212327427D00E4FDD0 /* Build configuration list for PBXProject "FixUnityJar" */ = {
306 | isa = XCConfigurationList;
307 | buildConfigurations = (
308 | D6D0B82B2327427D00E4FDD0 /* Debug */,
309 | D6D0B82C2327427D00E4FDD0 /* Release */,
310 | );
311 | defaultConfigurationIsVisible = 0;
312 | defaultConfigurationName = Release;
313 | };
314 | D6D0B82D2327427D00E4FDD0 /* Build configuration list for PBXNativeTarget "FixUnityJar" */ = {
315 | isa = XCConfigurationList;
316 | buildConfigurations = (
317 | D6D0B82E2327427D00E4FDD0 /* Debug */,
318 | D6D0B82F2327427D00E4FDD0 /* Release */,
319 | );
320 | defaultConfigurationIsVisible = 0;
321 | defaultConfigurationName = Release;
322 | };
323 | /* End XCConfigurationList section */
324 | };
325 | rootObject = D6D0B81E2327427D00E4FDD0 /* Project object */;
326 | }
327 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/builds/xcode/FixUnityJar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/project_fix_unity_jar/fix_unity_jar/src/fix_unity_jar.cpp:
--------------------------------------------------------------------------------
1 | // fix_unity_jar.cpp
2 | // Created by sisong on 2019-09-10.
3 |
4 | #include "fix_unity_jar.h"
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "../../../ApkDiffPatch/src/patch/Zipper.h"
10 | #include "../../../ApkDiffPatch/HDiffPatch/file_for_patch.h"
11 |
12 | static const char* kUnityPlayer_class="com/unity3d/player/UnityPlayer.class";
13 |
14 | static inline std::string zipFile_name(UnZipper* self,int fileIndex){
15 | int nameLen=UnZipper_file_nameLen(self,fileIndex);
16 | const char* nameBegin=UnZipper_file_nameBegin(self,fileIndex);
17 | return std::string(nameBegin,nameBegin+nameLen);
18 | }
19 |
20 |
21 | #define check(value,err_code) { \
22 | if (!(value)){ printf(#value" ERROR!\n"); \
23 | result=err_code; if (!_isInClear){ goto clear; } } }
24 |
25 | static int fixClass(Zipper* zipper,UnZipper* srcZip,int srcFileIndex,uint32_t* out_newCrc32){
26 | int result=kFixUnityJar_ok;
27 | bool _isInClear=false;
28 |
29 | size_t _codeSize=(size_t)UnZipper_file_uncompressedSize(srcZip,srcFileIndex);
30 | std::vector code(_codeSize,0);
31 | TByte* code_end=code.data()+code.size();
32 | {//decompress
33 | hpatch_TStreamOutput _codeStream;
34 | mem_as_hStreamOutput(&_codeStream,code.data(),code_end);
35 | check(UnZipper_fileData_decompressTo(srcZip,srcFileIndex,&_codeStream),
36 | kFixUnityJar_decompressError);
37 | }
38 | {//edit
39 | //find one only
40 | const TByte kLibMain[]={1,0,4,'m','a','i','n'};
41 | const TByte kLibNull[]={1,0,4,'n','u','l','l'};
42 | const TByte* _temp_pos=0;
43 | TByte* pos= std::search(code.data(),code_end,kLibMain,kLibMain+sizeof(kLibMain));
44 | check(pos!=code_end, kFixUnityJar_fixClassError);
45 | _temp_pos=std::search(pos+1,code_end,kLibMain,kLibMain+sizeof(kLibMain));
46 | check(_temp_pos==code_end, kFixUnityJar_fixClassError);
47 | //replace
48 | assert(sizeof(kLibMain)==sizeof(kLibNull));
49 | memcpy(pos,kLibNull,sizeof(kLibNull));
50 | }
51 | {//new crc32
52 | *out_newCrc32=(uint32_t)crc32(crc32(0,0,0),code.data(),(uInt)code.size());
53 | check(Zipper_file_append_set_new_crc32(zipper,*out_newCrc32),
54 | kFixUnityJar_outputJarFileError)
55 | }
56 | {//save
57 | check(Zipper_file_append_begin(zipper,srcZip,srcFileIndex,false,code.size(),0),
58 | kFixUnityJar_outputJarFileError);
59 | check(Zipper_file_append_part(zipper,code.data(),code.size()),
60 | kFixUnityJar_outputJarFileError);
61 | check(Zipper_file_append_end(zipper),
62 | kFixUnityJar_outputJarFileError);
63 | }
64 | clear:
65 | _isInClear=true;
66 | return result;
67 | }
68 |
69 |
70 | const int kZipAlignSize=1;
71 | const int kCompressLevel=6;
72 | const int kDefaultMemLevel=MAX_MEM_LEVEL;
73 | const bool isAlwaysReCompress=false;
74 |
75 | int fix_unity_jar(const char* unity_classes_jar,const char* out_new_jar){
76 | int result=kFixUnityJar_ok;
77 | bool _isInClear=false;
78 | int fileCount=0;
79 | int foundClassIndex=-1;
80 | uint32_t newCrc32=0;
81 | UnZipper unzipper;
82 | Zipper zipper;
83 | UnZipper_init(&unzipper);
84 | Zipper_init(&zipper);
85 |
86 | check(UnZipper_openFile(&unzipper,unity_classes_jar), kFixUnityJar_inputJarFileError);
87 | fileCount=UnZipper_fileCount(&unzipper);
88 |
89 | check(Zipper_openFile(&zipper,out_new_jar,fileCount,kZipAlignSize,false,
90 | kCompressLevel,kDefaultMemLevel), kFixUnityJar_outputJarFileError);
91 |
92 | for (int i=0; i=0, kFixUnityJar_classFileError);
108 |
109 | //not need check(Zipper_copyExtra_before_fileHeader(&zipper,&unzipper),kFixUnityJar_inputJarFileError);
110 | for (int i=0; i
11 |
12 | int main(int argc, const char * argv[]) {
13 | if (argc!=1+2) return -1;
14 | int result=fix_unity_jar(argv[1],argv[2]);
15 | if (result==kFixUnityJar_ok)
16 | std::cout << "fix_unity_jar() ok!\n";
17 | else
18 | std::cout << "fix_unity_jar() return error: "<
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | /* null
10 | JNIEXPORT void
11 | Java_com_github_sisong_FixUnityJar_null(JNIEnv* jenv,jobject jobj){
12 | //null
13 | } //*/
14 |
15 | #ifdef __cplusplus
16 | }
17 | #endif
18 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 | include $(CLEAR_VARS)
3 |
4 | LOCAL_MODULE := hotunity
5 |
6 | ADP_PATH := $(LOCAL_PATH)/../../ApkDiffPatch
7 |
8 | Lzma_Files := $(ADP_PATH)/lzma/C/LzmaDec.c \
9 | $(ADP_PATH)/lzma/C/Lzma2Dec.c
10 | ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
11 | Lzma_Files += $(ADP_PATH)/lzma/Asm/arm64/LzmaDecOpt.S
12 | endif
13 |
14 | ZLIB_PATH := $(ADP_PATH)/zlib1.2.11
15 | Zlib_Files := $(ZLIB_PATH)/crc32.c \
16 | $(ZLIB_PATH)/deflate.c \
17 | $(ZLIB_PATH)/inflate.c \
18 | $(ZLIB_PATH)/zutil.c \
19 | $(ZLIB_PATH)/adler32.c \
20 | $(ZLIB_PATH)/trees.c \
21 | $(ZLIB_PATH)/inftrees.c \
22 | $(ZLIB_PATH)/inffast.c
23 |
24 | HDP_PATH := $(ADP_PATH)/HDiffPatch
25 | Hdp_Files := $(HDP_PATH)/file_for_patch.c \
26 | $(HDP_PATH)/libHDiffPatch/HPatch/patch.c \
27 | $(HDP_PATH)/libParallel/parallel_import.cpp \
28 | $(HDP_PATH)/libParallel/parallel_channel.cpp
29 |
30 | ADP_PATCH_PATH := $(ADP_PATH)/src/patch
31 | Adp_Files := $(ADP_PATCH_PATH)/NewStream.cpp \
32 | $(ADP_PATCH_PATH)/OldStream.cpp \
33 | $(ADP_PATCH_PATH)/Patcher.cpp \
34 | $(ADP_PATCH_PATH)/ZipDiffData.cpp \
35 | $(ADP_PATCH_PATH)/Zipper.cpp
36 |
37 | XHOOK_PATH := $(LOCAL_PATH)/../xHook/libxhook/jni
38 | xHook_Files := \
39 | $(XHOOK_PATH)/xh_core.c \
40 | $(XHOOK_PATH)/xh_elf.c \
41 | $(XHOOK_PATH)/xh_log.c \
42 | $(XHOOK_PATH)/xh_util.c \
43 | $(XHOOK_PATH)/xh_version.c
44 |
45 |
46 | Src_Files := $(LOCAL_PATH)/../src/hook_unity.cpp \
47 | $(LOCAL_PATH)/../src/hook_unity_jni.cpp \
48 | $(LOCAL_PATH)/../src/virtual_apk_patch_jni.cpp \
49 | $(LOCAL_PATH)/../../VirtualApkPatch/patch/virtual_apk_patch.cpp
50 |
51 |
52 | LOCAL_SRC_FILES := $(Src_Files) $(xHook_Files) $(Lzma_Files) $(Zlib_Files) $(Hdp_Files) $(Adp_Files)
53 |
54 | DEF_FLAGS := -DZ7_ST -D_IS_USED_MULTITHREAD=1 -D_IS_USED_PTHREAD=1 -D_IS_NEED_CACHE_OLD_BY_COVERS=0 \
55 | -D_IS_NEED_FIXED_ZLIB_VERSION=1 -D_IS_NEED_VIRTUAL_ZIP=1
56 | ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
57 | DEF_FLAGS += -DZ7_LZMA_DEC_OPT
58 | endif
59 |
60 | LOCAL_LDLIBS := -llog -landroid
61 | LOCAL_CFLAGS := -Os -DANDROID_NDK -DNDEBUG -D_LARGEFILE_SOURCE -DTARGET_ARCH_ABI=\"$(TARGET_ARCH_ABI)\" $(DEF_FLAGS)
62 |
63 | include $(BUILD_SHARED_LIBRARY)
64 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/Application.mk:
--------------------------------------------------------------------------------
1 | APP_PLATFORM := android-14
2 | APP_CFLAGS += -Wno-error=format-security
3 | APP_CFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
4 | APP_CFLAGS += -ffunction-sections -fdata-sections
5 | APP_LDFLAGS += -Wl,--gc-sections
6 | APP_STL := stlport_static
7 | APP_CPPFLAGS := -fexceptions
8 | APP_BUILD_SCRIPT := Android.mk
9 | APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/build_libs.bat:
--------------------------------------------------------------------------------
1 | ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk NDK_APPLICATION_MK=Application.mk
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/build_libs.sh:
--------------------------------------------------------------------------------
1 | ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk NDK_APPLICATION_MK=Application.mk
2 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/hook_unity_src_xcode.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXContainerItemProxy section */
10 | D63B9886233333AA003D38A5 /* PBXContainerItemProxy */ = {
11 | isa = PBXContainerItemProxy;
12 | containerPortal = D63B987F233333A9003D38A5 /* VirtualApkPatch.xcodeproj */;
13 | proxyType = 2;
14 | remoteGlobalIDString = D65C69C523293AB300A3C89C;
15 | remoteInfo = VirtualApkPatch;
16 | };
17 | D658935023331568003BB2F4 /* PBXContainerItemProxy */ = {
18 | isa = PBXContainerItemProxy;
19 | containerPortal = D658934C23331567003BB2F4 /* FixUnityJar.xcodeproj */;
20 | proxyType = 2;
21 | remoteGlobalIDString = D6D0B8262327427D00E4FDD0;
22 | remoteInfo = FixUnityJar;
23 | };
24 | D6ACBC3924E4C99B0008166E /* PBXContainerItemProxy */ = {
25 | isa = PBXContainerItemProxy;
26 | containerPortal = D6ACBC3024E4C99B0008166E /* ZipPatch.xcodeproj */;
27 | proxyType = 2;
28 | remoteGlobalIDString = D65119052041A9B400805001;
29 | remoteInfo = ZipPatch;
30 | };
31 | D6BA8F74235FDA4100AFC34A /* PBXContainerItemProxy */ = {
32 | isa = PBXContainerItemProxy;
33 | containerPortal = D6BA8F6E235FDA4100AFC34A /* ZipDiff.xcodeproj */;
34 | proxyType = 2;
35 | remoteGlobalIDString = D651191B2041ADA900805001;
36 | remoteInfo = ZipDiff;
37 | };
38 | D6BA8F7A235FDA4100AFC34A /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = D6BA8F76235FDA4100AFC34A /* ApkNormalized.xcodeproj */;
41 | proxyType = 2;
42 | remoteGlobalIDString = D65119312041AF8400805001;
43 | remoteInfo = ApkNormalized;
44 | };
45 | D6BA8F80235FDA9300AFC34A /* PBXContainerItemProxy */ = {
46 | isa = PBXContainerItemProxy;
47 | containerPortal = D6BA8F7C235FDA9300AFC34A /* HDiffPatch.xcodeproj */;
48 | proxyType = 2;
49 | remoteGlobalIDString = D62A9FC22054020C00A61D43;
50 | remoteInfo = HDiffPatch;
51 | };
52 | /* End PBXContainerItemProxy section */
53 |
54 | /* Begin PBXCopyFilesBuildPhase section */
55 | D6703D062314D8420095F791 /* CopyFiles */ = {
56 | isa = PBXCopyFilesBuildPhase;
57 | buildActionMask = 2147483647;
58 | dstPath = /usr/share/man/man1/;
59 | dstSubfolderSpec = 0;
60 | files = (
61 | );
62 | runOnlyForDeploymentPostprocessing = 1;
63 | };
64 | /* End PBXCopyFilesBuildPhase section */
65 |
66 | /* Begin PBXFileReference section */
67 | D63B987F233333A9003D38A5 /* VirtualApkPatch.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = VirtualApkPatch.xcodeproj; path = ../../VirtualApkPatch/builds_test/xcode/VirtualApkPatch.xcodeproj; sourceTree = ""; };
68 | D658934C23331567003BB2F4 /* FixUnityJar.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FixUnityJar.xcodeproj; path = ../../project_fix_unity_jar/fix_unity_jar/builds/xcode/FixUnityJar.xcodeproj; sourceTree = ""; };
69 | D6703D082314D8420095F791 /* hook_unity_src_xcode */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = hook_unity_src_xcode; sourceTree = BUILT_PRODUCTS_DIR; };
70 | D6703D152314D8A10095F791 /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../src; sourceTree = ""; };
71 | D689587E24D4FC4F009D8CD3 /* README.en.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.en.md; path = ../../../README.en.md; sourceTree = ""; };
72 | D689587F24D4FC4F009D8CD3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../../README.md; sourceTree = ""; };
73 | D689588024D5085C009D8CD3 /* Application.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = Application.mk; sourceTree = SOURCE_ROOT; };
74 | D689588124D5085C009D8CD3 /* build_libs.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = build_libs.sh; sourceTree = SOURCE_ROOT; };
75 | D689588224D5085C009D8CD3 /* build_libs.bat */ = {isa = PBXFileReference; lastKnownFileType = text; path = build_libs.bat; sourceTree = SOURCE_ROOT; };
76 | D689588324D5085C009D8CD3 /* Android.mk */ = {isa = PBXFileReference; lastKnownFileType = text; path = Android.mk; sourceTree = SOURCE_ROOT; };
77 | D6ACBC3024E4C99B0008166E /* ZipPatch.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ZipPatch.xcodeproj; path = ../../ApkDiffPatch/builds/xcode/ZipPatch.xcodeproj; sourceTree = ""; };
78 | D6BA8F6E235FDA4100AFC34A /* ZipDiff.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ZipDiff.xcodeproj; path = ../../ApkDiffPatch/builds/xcode/ZipDiff.xcodeproj; sourceTree = ""; };
79 | D6BA8F76235FDA4100AFC34A /* ApkNormalized.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ApkNormalized.xcodeproj; path = ../../ApkDiffPatch/builds/xcode/ApkNormalized.xcodeproj; sourceTree = ""; };
80 | D6BA8F7C235FDA9300AFC34A /* HDiffPatch.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HDiffPatch.xcodeproj; path = ../../ApkDiffPatch/builds/xcode/HDiffPatch.xcodeproj; sourceTree = ""; };
81 | /* End PBXFileReference section */
82 |
83 | /* Begin PBXFrameworksBuildPhase section */
84 | D6703D052314D8420095F791 /* Frameworks */ = {
85 | isa = PBXFrameworksBuildPhase;
86 | buildActionMask = 2147483647;
87 | files = (
88 | );
89 | runOnlyForDeploymentPostprocessing = 0;
90 | };
91 | /* End PBXFrameworksBuildPhase section */
92 |
93 | /* Begin PBXGroup section */
94 | D63B9880233333A9003D38A5 /* Products */ = {
95 | isa = PBXGroup;
96 | children = (
97 | D63B9887233333AA003D38A5 /* VirtualApkPatch */,
98 | );
99 | name = Products;
100 | sourceTree = "";
101 | };
102 | D658934D23331567003BB2F4 /* Products */ = {
103 | isa = PBXGroup;
104 | children = (
105 | D658935123331568003BB2F4 /* FixUnityJar */,
106 | );
107 | name = Products;
108 | sourceTree = "";
109 | };
110 | D6703CFF2314D8420095F791 = {
111 | isa = PBXGroup;
112 | children = (
113 | D6ACBC3024E4C99B0008166E /* ZipPatch.xcodeproj */,
114 | D6BA8F7C235FDA9300AFC34A /* HDiffPatch.xcodeproj */,
115 | D63B987F233333A9003D38A5 /* VirtualApkPatch.xcodeproj */,
116 | D6BA8F76235FDA4100AFC34A /* ApkNormalized.xcodeproj */,
117 | D6BA8F6E235FDA4100AFC34A /* ZipDiff.xcodeproj */,
118 | D658934C23331567003BB2F4 /* FixUnityJar.xcodeproj */,
119 | D6703D152314D8A10095F791 /* src */,
120 | D689587824D4FC16009D8CD3 /* sets */,
121 | D6703D092314D8420095F791 /* Products */,
122 | );
123 | sourceTree = "";
124 | };
125 | D6703D092314D8420095F791 /* Products */ = {
126 | isa = PBXGroup;
127 | children = (
128 | D6703D082314D8420095F791 /* hook_unity_src_xcode */,
129 | );
130 | name = Products;
131 | sourceTree = "";
132 | };
133 | D689587824D4FC16009D8CD3 /* sets */ = {
134 | isa = PBXGroup;
135 | children = (
136 | D689588324D5085C009D8CD3 /* Android.mk */,
137 | D689588024D5085C009D8CD3 /* Application.mk */,
138 | D689588224D5085C009D8CD3 /* build_libs.bat */,
139 | D689588124D5085C009D8CD3 /* build_libs.sh */,
140 | D689587E24D4FC4F009D8CD3 /* README.en.md */,
141 | D689587F24D4FC4F009D8CD3 /* README.md */,
142 | );
143 | path = sets;
144 | sourceTree = "";
145 | };
146 | D6ACBC3124E4C99B0008166E /* Products */ = {
147 | isa = PBXGroup;
148 | children = (
149 | D6ACBC3A24E4C99B0008166E /* ZipPatch */,
150 | );
151 | name = Products;
152 | sourceTree = "";
153 | };
154 | D6BA8F6F235FDA4100AFC34A /* Products */ = {
155 | isa = PBXGroup;
156 | children = (
157 | D6BA8F75235FDA4100AFC34A /* ZipDiff */,
158 | );
159 | name = Products;
160 | sourceTree = "";
161 | };
162 | D6BA8F77235FDA4100AFC34A /* Products */ = {
163 | isa = PBXGroup;
164 | children = (
165 | D6BA8F7B235FDA4100AFC34A /* ApkNormalized */,
166 | );
167 | name = Products;
168 | sourceTree = "";
169 | };
170 | D6BA8F7D235FDA9300AFC34A /* Products */ = {
171 | isa = PBXGroup;
172 | children = (
173 | D6BA8F81235FDA9300AFC34A /* libHDiffPatch.a */,
174 | );
175 | name = Products;
176 | sourceTree = "";
177 | };
178 | /* End PBXGroup section */
179 |
180 | /* Begin PBXNativeTarget section */
181 | D6703D072314D8420095F791 /* hook_unity_src_xcode */ = {
182 | isa = PBXNativeTarget;
183 | buildConfigurationList = D6703D0F2314D8420095F791 /* Build configuration list for PBXNativeTarget "hook_unity_src_xcode" */;
184 | buildPhases = (
185 | D6703D042314D8420095F791 /* Sources */,
186 | D6703D052314D8420095F791 /* Frameworks */,
187 | D6703D062314D8420095F791 /* CopyFiles */,
188 | );
189 | buildRules = (
190 | );
191 | dependencies = (
192 | );
193 | name = hook_unity_src_xcode;
194 | productName = hook_unity_src_xcode;
195 | productReference = D6703D082314D8420095F791 /* hook_unity_src_xcode */;
196 | productType = "com.apple.product-type.tool";
197 | };
198 | /* End PBXNativeTarget section */
199 |
200 | /* Begin PBXProject section */
201 | D6703D002314D8420095F791 /* Project object */ = {
202 | isa = PBXProject;
203 | attributes = {
204 | LastUpgradeCheck = 1030;
205 | ORGANIZATIONNAME = sisong;
206 | TargetAttributes = {
207 | D6703D072314D8420095F791 = {
208 | CreatedOnToolsVersion = 10.3;
209 | };
210 | };
211 | };
212 | buildConfigurationList = D6703D032314D8420095F791 /* Build configuration list for PBXProject "hook_unity_src_xcode" */;
213 | compatibilityVersion = "Xcode 9.3";
214 | developmentRegion = en;
215 | hasScannedForEncodings = 0;
216 | knownRegions = (
217 | en,
218 | );
219 | mainGroup = D6703CFF2314D8420095F791;
220 | productRefGroup = D6703D092314D8420095F791 /* Products */;
221 | projectDirPath = "";
222 | projectReferences = (
223 | {
224 | ProductGroup = D6BA8F77235FDA4100AFC34A /* Products */;
225 | ProjectRef = D6BA8F76235FDA4100AFC34A /* ApkNormalized.xcodeproj */;
226 | },
227 | {
228 | ProductGroup = D658934D23331567003BB2F4 /* Products */;
229 | ProjectRef = D658934C23331567003BB2F4 /* FixUnityJar.xcodeproj */;
230 | },
231 | {
232 | ProductGroup = D6BA8F7D235FDA9300AFC34A /* Products */;
233 | ProjectRef = D6BA8F7C235FDA9300AFC34A /* HDiffPatch.xcodeproj */;
234 | },
235 | {
236 | ProductGroup = D63B9880233333A9003D38A5 /* Products */;
237 | ProjectRef = D63B987F233333A9003D38A5 /* VirtualApkPatch.xcodeproj */;
238 | },
239 | {
240 | ProductGroup = D6BA8F6F235FDA4100AFC34A /* Products */;
241 | ProjectRef = D6BA8F6E235FDA4100AFC34A /* ZipDiff.xcodeproj */;
242 | },
243 | {
244 | ProductGroup = D6ACBC3124E4C99B0008166E /* Products */;
245 | ProjectRef = D6ACBC3024E4C99B0008166E /* ZipPatch.xcodeproj */;
246 | },
247 | );
248 | projectRoot = "";
249 | targets = (
250 | D6703D072314D8420095F791 /* hook_unity_src_xcode */,
251 | );
252 | };
253 | /* End PBXProject section */
254 |
255 | /* Begin PBXReferenceProxy section */
256 | D63B9887233333AA003D38A5 /* VirtualApkPatch */ = {
257 | isa = PBXReferenceProxy;
258 | fileType = "compiled.mach-o.executable";
259 | path = VirtualApkPatch;
260 | remoteRef = D63B9886233333AA003D38A5 /* PBXContainerItemProxy */;
261 | sourceTree = BUILT_PRODUCTS_DIR;
262 | };
263 | D658935123331568003BB2F4 /* FixUnityJar */ = {
264 | isa = PBXReferenceProxy;
265 | fileType = "compiled.mach-o.executable";
266 | path = FixUnityJar;
267 | remoteRef = D658935023331568003BB2F4 /* PBXContainerItemProxy */;
268 | sourceTree = BUILT_PRODUCTS_DIR;
269 | };
270 | D6ACBC3A24E4C99B0008166E /* ZipPatch */ = {
271 | isa = PBXReferenceProxy;
272 | fileType = "compiled.mach-o.executable";
273 | path = ZipPatch;
274 | remoteRef = D6ACBC3924E4C99B0008166E /* PBXContainerItemProxy */;
275 | sourceTree = BUILT_PRODUCTS_DIR;
276 | };
277 | D6BA8F75235FDA4100AFC34A /* ZipDiff */ = {
278 | isa = PBXReferenceProxy;
279 | fileType = "compiled.mach-o.executable";
280 | path = ZipDiff;
281 | remoteRef = D6BA8F74235FDA4100AFC34A /* PBXContainerItemProxy */;
282 | sourceTree = BUILT_PRODUCTS_DIR;
283 | };
284 | D6BA8F7B235FDA4100AFC34A /* ApkNormalized */ = {
285 | isa = PBXReferenceProxy;
286 | fileType = "compiled.mach-o.executable";
287 | path = ApkNormalized;
288 | remoteRef = D6BA8F7A235FDA4100AFC34A /* PBXContainerItemProxy */;
289 | sourceTree = BUILT_PRODUCTS_DIR;
290 | };
291 | D6BA8F81235FDA9300AFC34A /* libHDiffPatch.a */ = {
292 | isa = PBXReferenceProxy;
293 | fileType = archive.ar;
294 | path = libHDiffPatch.a;
295 | remoteRef = D6BA8F80235FDA9300AFC34A /* PBXContainerItemProxy */;
296 | sourceTree = BUILT_PRODUCTS_DIR;
297 | };
298 | /* End PBXReferenceProxy section */
299 |
300 | /* Begin PBXSourcesBuildPhase section */
301 | D6703D042314D8420095F791 /* Sources */ = {
302 | isa = PBXSourcesBuildPhase;
303 | buildActionMask = 2147483647;
304 | files = (
305 | );
306 | runOnlyForDeploymentPostprocessing = 0;
307 | };
308 | /* End PBXSourcesBuildPhase section */
309 |
310 | /* Begin XCBuildConfiguration section */
311 | D6703D0D2314D8420095F791 /* Debug */ = {
312 | isa = XCBuildConfiguration;
313 | buildSettings = {
314 | ALWAYS_SEARCH_USER_PATHS = NO;
315 | CLANG_ANALYZER_NONNULL = YES;
316 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
318 | CLANG_CXX_LIBRARY = "libc++";
319 | CLANG_ENABLE_MODULES = YES;
320 | CLANG_ENABLE_OBJC_ARC = YES;
321 | CLANG_ENABLE_OBJC_WEAK = YES;
322 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
323 | CLANG_WARN_BOOL_CONVERSION = YES;
324 | CLANG_WARN_COMMA = YES;
325 | CLANG_WARN_CONSTANT_CONVERSION = YES;
326 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
327 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
328 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
329 | CLANG_WARN_EMPTY_BODY = YES;
330 | CLANG_WARN_ENUM_CONVERSION = YES;
331 | CLANG_WARN_INFINITE_RECURSION = YES;
332 | CLANG_WARN_INT_CONVERSION = YES;
333 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
334 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
335 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
338 | CLANG_WARN_STRICT_PROTOTYPES = YES;
339 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
340 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
341 | CLANG_WARN_UNREACHABLE_CODE = YES;
342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
343 | CODE_SIGN_IDENTITY = "-";
344 | COPY_PHASE_STRIP = NO;
345 | DEBUG_INFORMATION_FORMAT = dwarf;
346 | ENABLE_STRICT_OBJC_MSGSEND = YES;
347 | ENABLE_TESTABILITY = YES;
348 | GCC_C_LANGUAGE_STANDARD = gnu11;
349 | GCC_DYNAMIC_NO_PIC = NO;
350 | GCC_NO_COMMON_BLOCKS = YES;
351 | GCC_OPTIMIZATION_LEVEL = 0;
352 | GCC_PREPROCESSOR_DEFINITIONS = (
353 | "DEBUG=1",
354 | "$(inherited)",
355 | );
356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
358 | GCC_WARN_UNDECLARED_SELECTOR = YES;
359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
360 | GCC_WARN_UNUSED_FUNCTION = YES;
361 | GCC_WARN_UNUSED_VARIABLE = YES;
362 | MACOSX_DEPLOYMENT_TARGET = 10.14;
363 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
364 | MTL_FAST_MATH = YES;
365 | ONLY_ACTIVE_ARCH = YES;
366 | SDKROOT = macosx;
367 | };
368 | name = Debug;
369 | };
370 | D6703D0E2314D8420095F791 /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ALWAYS_SEARCH_USER_PATHS = NO;
374 | CLANG_ANALYZER_NONNULL = YES;
375 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
377 | CLANG_CXX_LIBRARY = "libc++";
378 | CLANG_ENABLE_MODULES = YES;
379 | CLANG_ENABLE_OBJC_ARC = YES;
380 | CLANG_ENABLE_OBJC_WEAK = YES;
381 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
382 | CLANG_WARN_BOOL_CONVERSION = YES;
383 | CLANG_WARN_COMMA = YES;
384 | CLANG_WARN_CONSTANT_CONVERSION = YES;
385 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
387 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
388 | CLANG_WARN_EMPTY_BODY = YES;
389 | CLANG_WARN_ENUM_CONVERSION = YES;
390 | CLANG_WARN_INFINITE_RECURSION = YES;
391 | CLANG_WARN_INT_CONVERSION = YES;
392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
397 | CLANG_WARN_STRICT_PROTOTYPES = YES;
398 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
399 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
400 | CLANG_WARN_UNREACHABLE_CODE = YES;
401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
402 | CODE_SIGN_IDENTITY = "-";
403 | COPY_PHASE_STRIP = NO;
404 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
405 | ENABLE_NS_ASSERTIONS = NO;
406 | ENABLE_STRICT_OBJC_MSGSEND = YES;
407 | GCC_C_LANGUAGE_STANDARD = gnu11;
408 | GCC_NO_COMMON_BLOCKS = YES;
409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
410 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
411 | GCC_WARN_UNDECLARED_SELECTOR = YES;
412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
413 | GCC_WARN_UNUSED_FUNCTION = YES;
414 | GCC_WARN_UNUSED_VARIABLE = YES;
415 | MACOSX_DEPLOYMENT_TARGET = 10.14;
416 | MTL_ENABLE_DEBUG_INFO = NO;
417 | MTL_FAST_MATH = YES;
418 | SDKROOT = macosx;
419 | };
420 | name = Release;
421 | };
422 | D6703D102314D8420095F791 /* Debug */ = {
423 | isa = XCBuildConfiguration;
424 | buildSettings = {
425 | CODE_SIGN_STYLE = Automatic;
426 | PRODUCT_NAME = "$(TARGET_NAME)";
427 | };
428 | name = Debug;
429 | };
430 | D6703D112314D8420095F791 /* Release */ = {
431 | isa = XCBuildConfiguration;
432 | buildSettings = {
433 | CODE_SIGN_STYLE = Automatic;
434 | PRODUCT_NAME = "$(TARGET_NAME)";
435 | };
436 | name = Release;
437 | };
438 | /* End XCBuildConfiguration section */
439 |
440 | /* Begin XCConfigurationList section */
441 | D6703D032314D8420095F791 /* Build configuration list for PBXProject "hook_unity_src_xcode" */ = {
442 | isa = XCConfigurationList;
443 | buildConfigurations = (
444 | D6703D0D2314D8420095F791 /* Debug */,
445 | D6703D0E2314D8420095F791 /* Release */,
446 | );
447 | defaultConfigurationIsVisible = 0;
448 | defaultConfigurationName = Release;
449 | };
450 | D6703D0F2314D8420095F791 /* Build configuration list for PBXNativeTarget "hook_unity_src_xcode" */ = {
451 | isa = XCConfigurationList;
452 | buildConfigurations = (
453 | D6703D102314D8420095F791 /* Debug */,
454 | D6703D112314D8420095F791 /* Release */,
455 | );
456 | defaultConfigurationIsVisible = 0;
457 | defaultConfigurationName = Release;
458 | };
459 | /* End XCConfigurationList section */
460 | };
461 | rootObject = D6703D002314D8420095F791 /* Project object */;
462 | }
463 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/hook_unity_src_xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/build_libs/hook_unity_src_xcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/src/hook_unity.cpp:
--------------------------------------------------------------------------------
1 | // hook_unity.cpp
2 | // Created by sisong on 2019-08-15.
3 |
4 | #include "hook_unity.h"
5 | #include
6 | #include
7 | #include //exit
8 | #include
9 | #include
10 | #include "../xHook/libxhook/jni/xh_core.h"
11 | #include
12 | #include // dlopen
13 | #ifdef __cplusplus
14 | extern "C" {
15 | #endif
16 | static const char* kLibMain="libmain.so";
17 | static const char* kLibUnity="libunity.so";
18 | static const int kLibUnityLen=11;//strlen(kLibUnity);
19 | //for il2cpp
20 | static const char* kLibIL2cpp="libil2cpp.so";
21 |
22 | #define _IsDebug 0
23 | #define _LogTag "HotUnity"
24 | #define LOG_INFO(fmt,args...) __android_log_print(ANDROID_LOG_INFO,_LogTag,fmt, ##args)
25 | #define LOG_ERROR(fmt,args...) __android_log_print(ANDROID_LOG_ERROR,_LogTag,fmt, ##args)
26 | #define LOG_DEBUG(fmt,args...) do { if (_IsDebug) LOG_INFO(fmt, ##args); } while(0)
27 |
28 | static const int kMaxPathLen=512-1;
29 | static const char kDirTag='/';
30 |
31 | static bool g_isMapPath =false;
32 | static char g_baseApkPath[kMaxPathLen+1]={0};
33 | static int g_baseApkPathLen=0;
34 | static char g_baseSoDir[kMaxPathLen+1]={0};
35 | static int g_baseSoDirLen=0;
36 | static char g_hotApkPath[kMaxPathLen+1]={0};
37 | static int g_hotApkPathLen=0;
38 | static char g_hotSoDir[kMaxPathLen+1]={0};
39 | static int g_hotSoDirLen=0;
40 |
41 |
42 | static const char* getFileName(const char* filePath){
43 | int pathLen=(int)strlen(filePath);
44 | int i=pathLen-1;
45 | for (;i>=0;--i) {
46 | if (filePath[i]==kDirTag) break;
47 | }
48 | return filePath+(i+1);
49 | }
50 |
51 | static bool pathIsExists(const char* path){
52 | struct stat path_stat;
53 | memset(&path_stat,0,sizeof(path_stat));
54 | int ret=stat(path,&path_stat);
55 | return (ret==0)&&( (path_stat.st_mode&(S_IFREG|S_IFDIR))!=0 );
56 | }
57 |
58 | static bool appendPath(char* dstPath,int dstPathSize,
59 | const char* dir,int dirLen,const char*subPath,int subPathLen){
60 | if (dirLen+1+subPathLen+1>dstPathSize){
61 | LOG_ERROR("appendPath() error len %s + %s",dir,subPath);
62 | return false;
63 | }
64 | memcpy(dstPath,dir,dirLen);
65 | dstPath+=dirLen;
66 | int dirTagCount= (((dirLen>0)&&(dir[dirLen-1]==kDirTag))?1:0)
67 | +(((subPathLen>0)&&(subPath[0]==kDirTag))?1:0);
68 | switch (dirTagCount) {
69 | case 2:{ ++subPath; --subPathLen; } break;
70 | case 0:{ if ((dirLen>0)&&(subPathLen>0)) { dstPath[0]=kDirTag; ++dstPath; } } break;
71 | }
72 | memcpy(dstPath,subPath,subPathLen);
73 | dstPath[subPathLen]='\0';
74 | return true;
75 | }
76 |
77 | static const char* map_path(const char* src,int srcLen,
78 | const char* dst,int dstLen,
79 | const char* path,int pathLen,char* newPath,const int newPathSize,
80 | bool needPathIsExists,bool* isCanMap=NULL,int clipLen=-1){
81 | const char* const errValue=NULL;
82 | if (isCanMap) *isCanMap=false;
83 | if (!g_isMapPath) return path;
84 | if ((pathLen=2)
148 | static void _DEBUG_log_libmaps(){
149 | const char* path="/proc/self/maps";
150 | FILE* fp=fopen(path,"r");
151 | char line[(kMaxPathLen+1)*2]={0};
152 | while(fgets(line,sizeof(line)-1, fp)){
153 | LOG_DEBUG(" lib maps : %s\n",line);
154 | }
155 | fclose(fp);
156 | }
157 | #else
158 | #define _DEBUG_log_libmaps()
159 | #endif
160 |
161 | //dlopen
162 | static bool hook_lib(const char* libPath);
163 | static void* my_new_dlopen(const char* path,int flags,bool isMustLoad,bool isMustHookOk){
164 | void* const errValue=NULL;
165 | LOG_DEBUG("new_dlopen() %d %s",flags,path);
166 | MAP_PATH(path,errValue);
167 | if ((!isMustLoad)&&(!pathIsExists(path)))
168 | return errValue;
169 |
170 | void* result=::dlopen(path,flags);
171 | LOG_INFO("dlopen() result 0x%08x %s",(unsigned int)(size_t)result,path);
172 | _DEBUG_log_libmaps();
173 |
174 | if ((result!=errValue)&&isSoDirCanMap){
175 | bool ret=hook_lib(path);
176 | if ((!ret)&&isMustHookOk)
177 | return errValue;
178 | }
179 | return result;
180 | }
181 |
182 | static void* new_dlopen(const char* path,int flags){
183 | return my_new_dlopen(path,flags,true,false);
184 | }
185 |
186 |
187 | #define HOOK(lib,errValue,symbol){ \
188 | if (0!=xh_core_register(lib,#symbol,(void*)new_##symbol,NULL)){ \
189 | LOG_ERROR("hook_lib() failed to find function:%s in %s",#symbol,lib); return errValue; } }
190 |
191 | static bool hook_lib(const char* libPath){
192 | LOG_INFO("hook_lib() to hook %s",libPath);
193 | libPath=getFileName(libPath);
194 | #if (_IsDebug>=2)
195 | xh_core_enable_debug(1);
196 | #endif
197 | const bool errValue=false;
198 | HOOK(libPath,errValue,stat);
199 | HOOK(libPath,errValue,fopen);
200 | HOOK(libPath,errValue,open);
201 | HOOK(libPath,errValue,dlopen);
202 | if(0!= xh_core_refresh(0)){
203 | LOG_ERROR("hook_lib() failed to hook %s",libPath);
204 | return errValue;
205 | }
206 | xh_core_clear();
207 | return true;
208 | }
209 |
210 |
211 | static bool loadUnityLib(const char* libName,bool isMustLoad,bool isMustHookOk){
212 | char libPath[kMaxPathLen+1];
213 | if (!appendPath(libPath,sizeof(libPath),
214 | g_baseSoDir,g_baseSoDirLen,libName,(int)strlen(libName))) return false;
215 |
216 | const int flags= RTLD_NOW;
217 | void* result=my_new_dlopen(libPath,flags,isMustLoad,isMustHookOk);
218 | return (result!=NULL)||(!isMustLoad);
219 | }
220 |
221 | static bool loadUnityLibs(){
222 | if (!hook_lib(kLibMain)) return false; // loaded in java, only hook
223 | if (!hook_lib(kLibUnity)) return false; // loaded in java, only hook
224 | if (!loadUnityLib(kLibIL2cpp,false,false)) return false; // pre-load for il2cpp
225 | //test found : not need pre-load libs for mono
226 | return true;
227 | }
228 |
229 |
230 | #define _ERR_RETURN() { g_isMapPath=false; exit(-1); return; }
231 |
232 | #define _COPY_PATH(dst,src) { \
233 | dst##Len=strlen(src); \
234 | if (dst##Len>kMaxPathLen) \
235 | { LOG_ERROR("hook_unity_doHook() strlen("#src") %d",dst##Len); _ERR_RETURN(); } \
236 | memcpy(dst,src,dst##Len+1); }
237 |
238 | void hook_unity_doHook(const char* baseApkPath,const char* baseSoDir,
239 | const char* hotApkPath,const char* hotSoDir){
240 | LOG_INFO("hook_unity_doHook() from: %s %s",baseApkPath,baseSoDir);
241 | LOG_INFO("hook_unity_doHook() to: %s %s",hotApkPath,hotSoDir);
242 |
243 | _COPY_PATH(g_baseApkPath,baseApkPath);
244 | _COPY_PATH(g_baseSoDir,baseSoDir);
245 | _COPY_PATH(g_hotApkPath,hotApkPath);
246 | _COPY_PATH(g_hotSoDir,hotSoDir);
247 |
248 | g_isMapPath=true;
249 | if (!loadUnityLibs()) _ERR_RETURN();
250 | }
251 |
252 | #ifdef __cplusplus
253 | }
254 | #endif
255 |
256 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/src/hook_unity.h:
--------------------------------------------------------------------------------
1 | // hook_unity.h
2 | // Created by sisong on 2019-08-15.
3 | #ifndef _hook_unity_h_
4 | #define _hook_unity_h_
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 | void hook_unity_doHook(const char* baseApkPath,const char* baseSoDir,
9 | const char* hotApkPath,const char* hotSoDir);
10 | #ifdef __cplusplus
11 | }
12 | #endif
13 | #endif //_hook_unity_h_
14 |
15 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/src/hook_unity_jni.cpp:
--------------------------------------------------------------------------------
1 | // hook_unity_jni.cpp
2 | // Created by sisong on 2019-08-14.
3 |
4 | #include
5 | #include "hook_unity.h"
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | JNIEXPORT void
11 | Java_com_github_sisong_HotUnity_doHot(JNIEnv* jenv,jobject jobj,
12 | jstring baseApkPath,jstring baseSoDir,
13 | jstring hotApkPath,jstring hotSoDir){
14 | const char* cBaseApkPath= jenv->GetStringUTFChars(baseApkPath, NULL);
15 | const char* cBaseSoDir = jenv->GetStringUTFChars(baseSoDir, NULL);
16 | const char* cHotApkPath = jenv->GetStringUTFChars(hotApkPath, NULL);
17 | const char* cHotSoDir= jenv->GetStringUTFChars(hotSoDir, NULL);
18 | hook_unity_doHook(cBaseApkPath,cBaseSoDir,cHotApkPath,cHotSoDir);
19 | jenv->ReleaseStringUTFChars(hotSoDir,cHotSoDir);
20 | jenv->ReleaseStringUTFChars(hotApkPath,cHotApkPath);
21 | jenv->ReleaseStringUTFChars(baseSoDir,cBaseSoDir);
22 | jenv->ReleaseStringUTFChars(baseApkPath,cBaseApkPath);
23 | }
24 |
25 | #ifdef __cplusplus
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/src/java/com/github/sisong/HotUnity.java:
--------------------------------------------------------------------------------
1 | package com.github.sisong;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Intent;
6 | import android.content.Context;
7 | import android.content.pm.PackageManager;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.util.Log;
11 | import java.io.File;
12 |
13 | import android.support.v4.content.FileProvider;
14 | //import androidx.core.content.FileProvider;
15 |
16 | /*
17 | customize edit for Unity export android project:
18 | 1. add this file HotUnity.java to project;
19 | 2. add libhotunity.so to project jniLibs;
20 | 3. edit file UnityPlayerActivity.java in project;
21 | add code: `import com.github.sisong.HotUnity;`
22 | add code: `HotUnity.hotUnity(this);` before `mUnityPlayer = new UnityPlayer(this);`
23 | */
24 |
25 | public class HotUnity{
26 | private static native void doHot(String baseApk,String baseSoDir,String hotApk,String hotSoDir);
27 | private static native int virtualApkPatch(String baseApk,String baseSoDir,
28 | String hotApk,String hotSoDir,
29 | String out_newApk,String out_newSoDir,//if need install(out_newApk), out_newSoDir set ""
30 | String zipDiffPath,int threadNum);
31 | private static native int virtualApkMerge(String baseApk,String baseSoDir,
32 | String hotApk,String hotSoDir,
33 | String newApk,String newSoDir);
34 |
35 | private static final String kHotUnityLib ="hotunity";
36 | private static final String kLogTag ="HotUnity";
37 | public static Context app=null;
38 | public static String baseApk=null;
39 | public static String baseSoDir=null;
40 | public static String updateDirPath=null;
41 | public static String hotApk=null;
42 | public static String hotSoDir=null;
43 | public static String newApk=null;
44 | public static String newSoDir=null;
45 | public static void hotUnity(Context _app){
46 | app=_app;
47 | baseApk=app.getPackageResourcePath();
48 | baseSoDir=app.getApplicationInfo().nativeLibraryDir;
49 | updateDirPath=app.getFilesDir().getPath() + "/HotUpdate";
50 | if (!makeDir(updateDirPath)) { runByBaseApk(); return; }
51 | hotApk=updateDirPath+"/update.apk";
52 | hotSoDir=hotApk+"_lib";
53 | newApk=updateDirPath+"/new_update.apk";//ApkPatch temp out
54 | newSoDir=newApk+"_lib";
55 |
56 | //for DEBUG test
57 | testHotUpdate(app, baseApk,baseSoDir,hotApk,hotSoDir,newApk,newSoDir);
58 |
59 | //merge new to hot for patch result
60 | if (!mergeNewUpdate(baseApk,baseSoDir,hotApk,hotSoDir,newApk,newSoDir))
61 | revertToBaseApk();
62 |
63 | boolean isRunHot=pathIsExists(hotApk)&&pathIsExists(hotSoDir);
64 | if (isRunHot){
65 | if (!isVersionRise())
66 | isRunHot=false;
67 | }
68 | if (isRunHot) {
69 | runByHotApk();
70 | }else{
71 | removeHotToBaseApk();
72 | runByBaseApk();
73 | }
74 | }
75 |
76 | //version check: hotApk's versionCode > baseApk's versionCode
77 | private static boolean isVersionRise() {
78 | boolean isRise=false;
79 | try {
80 | int hotApkVersion=getVersionCodeFromApk(app, hotApk);
81 | int baseApkVersion=getVersionCode(app, app.getPackageName());
82 | isRise = hotApkVersion>baseApkVersion;
83 | }catch (Exception e){
84 | }
85 | return isRise;
86 | }
87 |
88 | private static void runByBaseApk(){
89 | System.loadLibrary("main");
90 | System.loadLibrary(kHotUnityLib);
91 | }
92 | private static void runByHotApk(){
93 | mapPathLoadLib(hotSoDir,"main");
94 | mapPathLoadLib(hotSoDir,"unity");
95 | mapPathLoadLib(hotSoDir,kHotUnityLib);
96 | //note: You can load other your lib(not unity's) by mapPathLoadLib, can use newVersion lib;
97 |
98 | doHot(baseApk,baseSoDir,hotApk,hotSoDir);
99 | }
100 |
101 | //public funcs for call by C#
102 | public static int apkPatch(String zipDiffPath,int threadNum,String installApkPath){
103 | removeFile(newApk);
104 | removeLibDirWithLibs(newSoDir);
105 | boolean isHotUpdate=(installApkPath==null)||(installApkPath.isEmpty());
106 | //kHotUnityLib is loaded, not need: mapPathLoadLib(hotSoDir,kHotUnityLib);
107 | String tempApkPath=isHotUpdate?(updateDirPath+"/new_apk.tmp"):(installApkPath+".tmp");
108 | removeFile(tempApkPath);
109 | if ((!isHotUpdate)&&pathIsExists(installApkPath)) removeFile(installApkPath);
110 | int ret=virtualApkPatch(baseApk,baseSoDir,hotApk,hotSoDir,
111 | tempApkPath,isHotUpdate?newSoDir:"",zipDiffPath,threadNum);
112 | Log.w(kLogTag, "virtualApkPatch() result " +String.valueOf(ret));
113 | if (ret!=0) {
114 | removeFile(tempApkPath);
115 | if (isHotUpdate) removeLibDirWithLibs(newSoDir);
116 | }else {
117 | String targetApkFile=isHotUpdate?newApk:installApkPath;
118 | if (!moveFileTo(tempApkPath,targetApkFile)){
119 | Log.w(kLogTag, tempApkPath + " moveFileTo " + targetApkFile + " failed.");
120 | return -1;
121 | }
122 | if (isHotUpdate) makeDir(hotSoDir);
123 | }
124 | return ret;
125 | }
126 | public static void installApk(String installApkPath) {
127 | File apkFile=new File(installApkPath);
128 | Intent intent=new Intent("android.intent.action.INSTALL_PACKAGE");
129 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
130 | intent.setAction(Intent.ACTION_VIEW);
131 |
132 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
133 | intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
134 | Uri uri = FileProvider.getUriForFile(app, app.getApplicationContext().getPackageName() + ".fileprovider", apkFile);
135 | intent.setDataAndType(uri, "application/vnd.android.package-archive");
136 | } else {
137 | intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
138 | }
139 | app.startActivity(intent);
140 | }
141 | public static void restartApp() {
142 | Intent intent = app.getPackageManager().getLaunchIntentForPackage(app.getPackageName());
143 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
144 | PendingIntent pendingIntent = PendingIntent.getActivity(app.getApplicationContext(),
145 | 0, intent, PendingIntent.FLAG_ONE_SHOT);
146 | AlarmManager mgr = (AlarmManager)app.getSystemService(Context.ALARM_SERVICE);
147 | long alarmTimeMillis = System.currentTimeMillis() + 1000;
148 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
149 | mgr.setExactAndAllowWhileIdle(AlarmManager.RTC, alarmTimeMillis, pendingIntent);
150 | else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
151 | mgr.setExact(AlarmManager.RTC, alarmTimeMillis, pendingIntent);
152 | else
153 | mgr.set(AlarmManager.RTC, alarmTimeMillis, pendingIntent);
154 | System.exit(0);
155 | }
156 | public static void exitApp(int errCode) {
157 | Intent mainIntent = new Intent(Intent.ACTION_MAIN);
158 | mainIntent.addCategory(Intent.CATEGORY_HOME);
159 | mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
160 | app.startActivity(mainIntent);
161 | System.exit(errCode);
162 | }
163 | private static void removeHotToBaseApk(){
164 | removeFile(newApk);
165 | removeLibDirWithLibs(newSoDir);
166 | removeFile(hotApk);
167 | removeLibDirWithLibs(hotSoDir);
168 | clearCache();
169 | }
170 | public static void revertToBaseApk() {
171 | removeHotToBaseApk();
172 | restartApp();
173 | }
174 |
175 | private static void testHotUpdate(Context app,
176 | String baseApk,String baseSoDir,
177 | String hotApk,String hotSoDir,
178 | String newApk,String newSoDir){
179 | if (pathIsExists(newApk)||(pathIsExists(newSoDir))) return;
180 | String testDir=app.getExternalFilesDir("").getAbsolutePath()+"/testHotUpdate";
181 | //default: testDir=="/sdcard/Android/data//files/testHotUpdate";
182 | //NOTE: put the files you need test into the testDir directory
183 | String testPatFile=testDir+"/new.pat"; //test pat file
184 | if (!pathIsExists(testPatFile)) return;
185 | Log.w(kLogTag, "testHotUpdate() with "+testPatFile);
186 | mapPathLoadLib(hotSoDir,kHotUnityLib); //for native function: virtualApkPatch()
187 |
188 | String testBase=testDir+"/base.apk";
189 | if (pathIsExists(testBase)) {
190 | baseApk = testBase;
191 | baseSoDir= testBase + "_lib";
192 | }
193 | String testHot=testDir+"/update.apk";
194 | if (pathIsExists(testBase)||pathIsExists(testHot)) {
195 | hotApk = testHot;
196 | hotSoDir= testHot + "_lib";
197 | }
198 | int ret=virtualApkPatch(baseApk,baseSoDir,hotApk,hotSoDir,
199 | newApk,newSoDir,testPatFile,3);
200 | Log.w(kLogTag, "virtualApkPatch() result " +String.valueOf(ret));
201 | if ((ret==0) && removeFile(testPatFile)){ //update ok
202 | Log.w(kLogTag, "testHotUpdate() ok");
203 | }else{
204 | Log.w(kLogTag, "testHotUpdate() ERROR, exitApp");
205 | exitApp(ret);
206 | }
207 | }
208 |
209 | // merge new to hot
210 | private static boolean mergeNewUpdate(String baseApk,String baseSoDir,
211 | String hotApk,String hotSoDir,
212 | String newApk,String newSoDir){
213 | if (!pathIsExists(hotApk))
214 | removeLibDirWithLibs(hotSoDir); //continue
215 | if (!pathIsExists(newApk)) {
216 | removeLibDirWithLibs(newSoDir);
217 | return true; //not need merge, continue run app
218 | }
219 |
220 | clearCache();
221 | if (!mergeHotUnityLib(newSoDir,hotSoDir)){
222 | Log.w(kLogTag,"mergeHotUnityLib() error! "+newSoDir);
223 | return false;
224 | }
225 | mapPathLoadLib(hotSoDir,kHotUnityLib); //for native function: virtualApkMerge()
226 | int rt=virtualApkMerge(baseApk,baseSoDir,hotApk,hotSoDir,newApk,newSoDir);
227 | if (rt!=0){
228 | Log.w(kLogTag,"virtualApkMerge() error code "+String.valueOf(rt)+"! "+newApk);
229 | return false;
230 | }
231 | return true;
232 | }
233 |
234 | private static boolean mergeHotUnityLib(String newSoDir,String hotSoDir){
235 | String newLibHotUnity=getLibPath(newSoDir,kHotUnityLib);
236 | if (!pathIsExists(newLibHotUnity)) return true;
237 | if (!makeDir(hotSoDir)) return false;
238 | String hotLibHotUnity=getLibPath(hotSoDir,kHotUnityLib);
239 | if (!removeFile(hotLibHotUnity)) return false;
240 | return moveFileTo(newLibHotUnity,hotLibHotUnity);
241 | }
242 |
243 | private static void clearCache() {
244 | Log.i(kLogTag, "clearCache");
245 | File cacheDir = app.getExternalFilesDir("il2cpp");
246 | if (!removeDir(cacheDir))
247 | Log.w(kLogTag, "clearCache() error, can't clear dir \"" + cacheDir.getPath() + "\"! ");
248 | }
249 |
250 | private static void removeLibDirWithLibs(String libDir) {
251 | File soDir=new File(libDir);
252 | if (!removeDir(soDir))
253 | Log.w(kLogTag,"removeLibDirWithLibs() error, can't clear dir \""+libDir+"\"! ");
254 | }
255 |
256 | private static boolean moveFileTo(String oldFilePath,String newFilePath) {
257 | File df=new File(oldFilePath);
258 | File newdf=new File(newFilePath);
259 | return df.renameTo(newdf);
260 | }
261 | private static boolean removeFile(String fileName) {
262 | File f=new File(fileName);
263 | return removeFile(f);
264 | }
265 | private static boolean removeFile(File f) {
266 | if ((f==null)||(!f.exists()))
267 | return true;
268 | return f.delete();
269 | }
270 | private static boolean removeDir(File dir) {
271 | if ((null == dir) || (!dir.exists()))
272 | return true;
273 | if (!dir.isDirectory())
274 | throw new RuntimeException("\"" + dir.getAbsolutePath() + "\" should be a directory!");
275 | boolean result=true;
276 | File[] files = dir.listFiles();
277 | if ((null != files)&&(files.length>0)) {
278 | for (File f : files) {
279 | if (f.isDirectory())
280 | result &=removeDir(f);
281 | else
282 | result &=removeFile(f);
283 | }
284 | }
285 | return result & dir.delete();
286 | }
287 |
288 | private static boolean makeDir(String dirPath) {
289 | File df=new File(dirPath);
290 | if (df.exists()) return true;
291 | if (!df.mkdir()) return false;
292 | return true;
293 | }
294 |
295 | private static void mapPathLoadLib(String hotSoDir, String libName){
296 | String cachedLibPath=getLibPath(hotSoDir,libName);
297 | if (pathIsExists(cachedLibPath)) {
298 | Log.w(kLogTag,"java map_path() to "+cachedLibPath);
299 | System.load(cachedLibPath);
300 | } else {
301 | Log.w(kLogTag,"java map_path() not found "+cachedLibPath);
302 | System.loadLibrary(libName);
303 | }
304 | }
305 | private static String getLibPath(String dir,String libName){
306 | return dir+"/"+System.mapLibraryName(libName);
307 | }
308 | private static boolean pathIsExists(String path) {
309 | File file = new File(path);
310 | return (file!=null)&&file.exists();
311 | }
312 |
313 | private static int getVersionCode(Context context,String packageName) throws PackageManager.NameNotFoundException {
314 | return context.getPackageManager().getPackageInfo(packageName, 0).versionCode;
315 | }
316 | private static int getVersionCodeFromApk(Context context,String apkFilePath) {
317 | return context.getPackageManager().
318 | getPackageArchiveInfo(apkFilePath,PackageManager.GET_ACTIVITIES).versionCode;
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/project_hook_unity_jni/src/virtual_apk_patch_jni.cpp:
--------------------------------------------------------------------------------
1 | // virtual_apk_patch_jni.cpp
2 | // Created by sisong on 2019-09-11.
3 |
4 | #include
5 | #include "../../VirtualApkPatch/patch/virtual_apk_patch.h"
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | JNIEXPORT int
11 | Java_com_github_sisong_HotUnity_virtualApkPatch(JNIEnv* jenv,jobject jobj,
12 | jstring baseApk,jstring baseSoDir,
13 | jstring hotApk,jstring hotSoDir,
14 | jstring out_newApk,jstring out_newSoDir,
15 | jstring zipDiffPath,int threadNum){
16 | const char* cBaseApk = jenv->GetStringUTFChars(baseApk, NULL);
17 | const char* cBaseSoDir = jenv->GetStringUTFChars(baseSoDir, NULL);
18 | const char* cHotApk = hotApk? jenv->GetStringUTFChars(hotApk, NULL):0;
19 | const char* cHotSoDir = hotSoDir? jenv->GetStringUTFChars(hotSoDir, NULL):0;
20 | const char* out_cNewApk = jenv->GetStringUTFChars(out_newApk, NULL);
21 | const char* out_cNewSoDir= jenv->GetStringUTFChars(out_newSoDir, NULL);
22 | const char* cZipDiffPath = jenv->GetStringUTFChars(zipDiffPath, NULL);
23 | const char* arch_abi=TARGET_ARCH_ABI; // "armeabi-v7a" "x86" ...
24 | int result =virtual_apk_patch(cBaseApk,cBaseSoDir,cHotApk,cHotSoDir,
25 | out_cNewApk,out_cNewSoDir,cZipDiffPath,arch_abi,threadNum);
26 | jenv->ReleaseStringUTFChars(zipDiffPath,cZipDiffPath);
27 | jenv->ReleaseStringUTFChars(out_newSoDir,out_cNewSoDir);
28 | jenv->ReleaseStringUTFChars(out_newApk,out_cNewApk);
29 | if (hotSoDir) jenv->ReleaseStringUTFChars(hotSoDir,cHotSoDir);
30 | if (hotApk) jenv->ReleaseStringUTFChars(hotApk,cHotApk);
31 | jenv->ReleaseStringUTFChars(baseSoDir,cBaseSoDir);
32 | jenv->ReleaseStringUTFChars(baseApk,cBaseApk);
33 | return result;
34 | }
35 |
36 | JNIEXPORT int
37 | Java_com_github_sisong_HotUnity_virtualApkMerge(JNIEnv* jenv,jobject jobj,
38 | jstring baseApk,jstring baseSoDir,
39 | jstring hotApk,jstring hotSoDir,
40 | jstring newApk,jstring newSoDir){
41 | const char* cBaseApk = jenv->GetStringUTFChars(baseApk, NULL);
42 | const char* cBaseSoDir= jenv->GetStringUTFChars(baseSoDir, NULL);
43 | const char* cHotApk = jenv->GetStringUTFChars(hotApk, NULL);
44 | const char* cHotSoDir = jenv->GetStringUTFChars(hotSoDir, NULL);
45 | const char* cNewApk = jenv->GetStringUTFChars(newApk, NULL);
46 | const char* cNewSoDir = jenv->GetStringUTFChars(newSoDir, NULL);
47 | const char* arch_abi=TARGET_ARCH_ABI; // "armeabi-v7a" "x86" ...
48 | int result =virtual_apk_merge(cBaseApk,cBaseSoDir,cHotApk,cHotSoDir,
49 | cNewApk,cNewSoDir,arch_abi);
50 | jenv->ReleaseStringUTFChars(newSoDir,cNewSoDir);
51 | jenv->ReleaseStringUTFChars(newApk,cNewApk);
52 | jenv->ReleaseStringUTFChars(hotSoDir,cHotSoDir);
53 | jenv->ReleaseStringUTFChars(hotApk,cHotApk);
54 | jenv->ReleaseStringUTFChars(baseSoDir,cBaseSoDir);
55 | jenv->ReleaseStringUTFChars(baseApk,cBaseApk);
56 | return result;
57 | }
58 |
59 | #ifdef __cplusplus
60 | }
61 | #endif
62 |
--------------------------------------------------------------------------------
/project_unity_demo/newV/Assets/MenuClick.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 | using UnityEngine.UI;
5 |
6 | public class MenuClick : MonoBehaviour
7 | {
8 | void Start()
9 | {
10 | Button btn = GetComponent