diff --git a/README.md b/README.md
index fdd164f32..416d08381 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@

[](https://github.com/Tencent/matrix/blob/master/LICENSE)
[](https://github.com/Tencent/matrix/pulls)
-[](https://github.com/Tencent/matrix/wiki)
+[](https://github.com/Tencent/matrix/wiki)
[](https://app.circleci.com/pipelines/github/Tencent/matrix)
(中文版本请参看[这里](#matrix_cn))
@@ -9,17 +9,19 @@
[Matrix for iOS/macOS 中文版](#matrix_ios_cn)
[Matrix for android 中文版](#matrix_android_cn)
[Matrix for iOS/macOS](#matrix_ios_en)
-[Matrix for android ](#matrix_android_en)
+[Matrix for android](#matrix_android_en)
**Matrix** is an APM (Application Performance Manage) used in Wechat to monitor, locate and analyse performance problems. It is a **plugin style**, **non-invasive** solution and is currently available on iOS, macOS and Android.
# Matrix for iOS/macOS
-The monitoring scope of the current tool includes: crash, lag, and out-of-memory, which includes the following two plugins:
+The monitoring scope of the current tool includes: crash, lag, and memory, which includes the following three plugins:
* **WCCrashBlockMonitorPlugin:** Based on [KSCrash](https://github.com/kstenerud/KSCrash) framework, it features cutting-edge lag stack capture capabilities with crash capture.
-* **WCMemoryStatPlugin:** A performance-optimized out-of-memory monitoring tool that captures memory allocation and the callstack of an application's out-of-memory event.
+* **WCMemoryStatPlugin:** A memory monitoring tool that captures memory allocation and the callstack of an application's memory event.
+
+* **WCFPSMonitorPlugin:** A fps monitoring tool that captures main thread's callstack while user scrolling.
## Features
@@ -32,18 +34,10 @@ The monitoring scope of the current tool includes: crash, lag, and out-of-memory
#### WCMemoryStatPlugin
* Live recording every object's creating and the corresponding callstack of its creation, and report it when the application out-of-memory is detected.
-* Use a balanced binary tree to store living objects and a hash table to store the callstack to optimize performance to the extreme
## Getting Started
#### Install
-* **Install via Cocoapods**
- 1. Install [CocoaPods](https://guides.cocoapods.org/using/getting-started.html);
- 2. Run `pod repo update` to make CocoaPods aware of the latest available `matrix` versions;
- 3. In your Podfile, add `pod 'matrix-wechat'` to your app target, from the command line, run `pod install`;
- 4. Use the .xcworkspace file generated by CocoaPods to work on your project;
- 5. Add `#import ` , then you can use the performance probe tool of WeChat.
-
* **Install with static framework**
1. Get source code of Matrix;
2. Open terminal, execute `make` in the `matrix/matrix-iOS` directory to compile and generate static library. After compiling, the iOS platform library is in the `matrix/matrix-iOS/build_ios` directory, and the macOS platform library is in the `matrix/matrix-iOS/build_macos` directory.
@@ -73,15 +67,16 @@ WCCrashBlockMonitorPlugin *crashBlockPlugin = [[WCCrashBlockMonitorPlugin alloc]
[curBuilder addPlugin:crashBlockPlugin]; // add lag and crash monitor.
WCMemoryStatPlugin *memoryStatPlugin = [[WCMemoryStatPlugin alloc] init];
-[curBuilder addPlugin:memoryStatPlugin]; // add out-of-memory monitor.
+[curBuilder addPlugin:memoryStatPlugin]; // add memory monitor.
+
+WCFPSMonitorPlugin *fpsMonitorPlugin = [[WCFPSMonitorPlugin alloc] init];
+[curBuilder addPlugin:fpsMonitorPlugin]; // add fps monitor.
[matrix addMatrixBuilder:curBuilder];
[crashBlockPlugin start]; // start the lag and crash monitor.
-// [memoryStatPlugin start];
-// start out-of-memory monitor
-// Be careful, WCMemoryStatPlugin has a large performance loss after it is turned on. It is recommended to turn it on as needed.
-
+[memoryStatPlugin start]; // start memory monitor
+[fpsMonitorPlugin start]; // start fps monitor
```
#### Receive callbacks to obtain monitoring data
@@ -109,7 +104,7 @@ Each plugin added to `MatrixBuilder` will call back the corresponding event via
## Tutorials
-At this point, Matrix has been integrated into the app and is beginning to collect crash, lag, and out-of-memory data. If you still have questions, check out the example: `samples/sample-apple/MatrixDemo`.
+At this point, Matrix has been integrated into the app and is beginning to collect crash, lag, and memory data. If you still have questions, check out the example: `samples/sample-iOS/MatrixDemo`.
@@ -220,7 +215,7 @@ At this point, Matrix has been integrated into the app and is beginning to colle
1. Configure `MATRIX_VERSION` in gradle.properties.
``` gradle
- MATRIX_VERSION=2.0.5
+ MATRIX_VERSION=2.1.0
```
2. Add `matrix-gradle-plugin` in your build.gradle:
@@ -369,11 +364,11 @@ Then other components in Matrix could use Quikcen Backtrace to unwind stacktrace
#### APK Checker Usage
-APK Checker can run independently in Jar ([matrix-apk-canary-2.0.5.jar](https://repo.maven.apache.org/maven2/com/tencent/matrix/matrix-apk-canary/2.0.5/matrix-apk-canary-2.0.5.jar)) mode, usage:
+APK Checker can run independently in Jar ([matrix-apk-canary-2.1.0.jar](https://repo.maven.apache.org/maven2/com/tencent/matrix/matrix-apk-canary/2.1.0/matrix-apk-canary-2.1.0.jar)) mode, usage:
```shell
-java -jar matrix-apk-canary-2.0.5.jar
+java -jar matrix-apk-canary-2.1.0.jar
Usages:
--config CONFIG-FILE-PATH
or
@@ -432,18 +427,20 @@ Matrix is under the BSD license. See the [LICENSE](https://github.com/Tencent/Ma
# Matrix

-[](https://github.com/Tencent/matrix/blob/master/LICENSE)[](https://github.com/Tencent/matrix/pulls) [](https://github.com/Tencent/matrix/wiki)
+[](https://github.com/Tencent/matrix/blob/master/LICENSE)[](https://github.com/Tencent/matrix/pulls) [](https://github.com/Tencent/matrix/wiki)
**Matrix** 是一款微信研发并日常使用的应用性能接入框架,支持iOS, macOS和Android。
Matrix 通过接入各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
# Matrix for iOS/macOS
-当前工具监控范围包括:崩溃、卡顿和爆内存,包含以下两款插件:
+Matrix-iOS 当前工具监控范围包括:崩溃、卡顿和内存,包含以下三款插件:
+
+* **WCCrashBlockMonitorPlugin:** 基于 [KSCrash](https://github.com/kstenerud/KSCrash) 框架开发,具有业界领先的卡顿堆栈捕获能力,同时兼备崩溃捕获能力
-* **WCCrashBlockMonitorPlugin:** 基于 [KSCrash](https://github.com/kstenerud/KSCrash) 框架开发,具有业界领先的卡顿堆栈捕获能力,同时兼备崩溃捕获能力。
+* **WCMemoryStatPlugin:** 一款性能极致的内存监控工具,能够全面捕获应用 OOM 时的内存分配以及调用堆栈情况
-* **WCMemoryStatPlugin:** 一款性能优化到极致的爆内存监控工具,能够全面捕获应用爆内存时的内存分配以及调用堆栈情况。
+* **WCFPSMonitorPlugin:** 一款 FPS 监控工具,当用户滑动界面时,记录主线程调用栈
## 特性
@@ -455,18 +452,11 @@ Matrix 通过接入各种性能监控方案,对性能监控项的异常数据
#### WCMemoryStatPlugin
-* 在应用运行期间获取对象存活以及相应的堆栈信息,在检测到应用爆内存时进行上报
-* 使用平衡二叉树存储存活对象,使用 Hash Table 存储堆栈,将性能优化到极致
+* 在应用运行期间获取对象存活以及相应的堆栈信息,在检测到应用 OOM 时进行上报
## 使用方法
#### 安装
-* **通过 Cocoapods 安装**
- 1. 先安装 [CocoaPods](https://guides.cocoapods.org/using/getting-started.html);
- 2. 通过 pod repo update 更新 matrix 的 Cocoapods 版本;
- 3. 在 Podfile 对应的 target 中,添加 pod 'matrix-wechat',并执行 pod install;
- 4. 在项目中使用 Cocoapods 生成的 .xcworkspace运行工程;
- 5. 在你的代码文件头引入头文件 #import ,就可以接入微信的性能探针工具了!
* **通过静态库安装**
1. 获取 Matrix 源码;
@@ -499,13 +489,15 @@ WCCrashBlockMonitorPlugin *crashBlockPlugin = [[WCCrashBlockMonitorPlugin alloc]
WCMemoryStatPlugin *memoryStatPlugin = [[WCMemoryStatPlugin alloc] init];
[curBuilder addPlugin:memoryStatPlugin]; // 添加内存监控功能
+
+WCFPSMonitorPlugin *fpsMonitorPlugin = [[WCFPSMonitorPlugin alloc] init];
+[curBuilder addPlugin:fpsMonitorPlugin]; // 添加 fps 监控功能
[matrix addMatrixBuilder:curBuilder];
[crashBlockPlugin start]; // 开启卡顿和崩溃监控
-// [memoryStatPlugin start];
-// 开启内存监控,注意 memoryStatPlugin 开启之后对性能损耗较大,建议按需开启
-
+[memoryStatPlugin start]; // 开启内存监控
+[fpsMonitorPlugin start]; // 开启 fps 监控
```
#### 接收回调获得监控数据
@@ -533,7 +525,7 @@ curBuilder.pluginListener = <一个遵循 MatrixPluginListenerDelegate 的对象
## Demo
-至此,Matrix 已经集成到应用中并且开始收集崩溃、ANR、卡顿和爆内存数据,如仍有疑问,请查看示例:`samples/sample-apple/MatrixDemo`。
+至此,Matrix 已经集成到应用中并且开始收集崩溃、卡顿和爆内存数据,如仍有疑问,请查看示例:`samples/sample-iOS/MatrixDemo`
# Matrix for Android
@@ -636,7 +628,7 @@ Matrix-android 当前监控范围包括:应用安装包大小,帧率变化
1. 在你项目根目录下的 gradle.properties 中配置要依赖的 Matrix 版本号,如:
``` gradle
- MATRIX_VERSION=2.0.5
+ MATRIX_VERSION=2.1.0
```
2. 在你项目根目录下的 build.gradle 文件添加 Matrix 依赖,如:
@@ -782,10 +774,10 @@ WeChatBacktrace.instance().configure(getApplicationContext()).commit();
#### APK Checker
-APK Check 以独立的 jar 包提供 ([matrix-apk-canary-2.0.5.jar](https://repo.maven.apache.org/maven2/com/tencent/matrix/matrix-apk-canary/2.0.5/matrix-apk-canary-2.0.5.jar)),你可以运行:
+APK Check 以独立的 jar 包提供 ([matrix-apk-canary-2.1.0.jar](https://repo.maven.apache.org/maven2/com/tencent/matrix/matrix-apk-canary/2.1.0/matrix-apk-canary-2.1.0.jar)),你可以运行:
```cmd
-java -jar matrix-apk-canary-2.0.5.jar
+java -jar matrix-apk-canary-2.1.0.jar
```
查看 Usages 来使用它。
@@ -846,3 +838,12 @@ Options:
# License
Matrix is under the BSD license. See the [LICENSE](https://github.com/Tencent/Matrix/blob/master/LICENSE) file for details
+
+# 信息公示
+
+- SDK名称:Matrix
+- 版本号:2.1.0
+- 开发者:深圳市腾讯计算机系统有限公司
+- 主要功能:Matrix是微信研发并日常使用的应用性能监控工具,支持iOS、macOS和Android。Matrix通过接入闪退、卡顿、耗电、内存等方面的监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。
+- [Matrix SDK使用说明](https://github.com/Tencent/matrix)
+- [Matrix SDK个人信息保护规则](https://support.weixin.qq.com/cgi-bin/mmsupportacctnodeweb-bin/pages/yTezupX6yF028Mpf)
diff --git a/matrix/matrix-android/.gitignore b/matrix/matrix-android/.gitignore
index 631c8277c..ff0340f47 100644
--- a/matrix/matrix-android/.gitignore
+++ b/matrix/matrix-android/.gitignore
@@ -8,4 +8,5 @@
.idea/
**/.*
**/bin/
-/gradle/oss-android-template.gradle
\ No newline at end of file
+/gradle/oss-android-template.gradle
+local.gradle
\ No newline at end of file
diff --git a/matrix/matrix-android/build.gradle b/matrix/matrix-android/build.gradle
index 944ea24a8..6d26068e3 100644
--- a/matrix/matrix-android/build.gradle
+++ b/matrix/matrix-android/build.gradle
@@ -8,7 +8,7 @@ buildscript {
}
- gradle.ext.KOTLIN_VERSION = "1.3.72"
+ gradle.ext.KOTLIN_VERSION = "1.4.32"
dependencies {
// classpath
@@ -52,7 +52,7 @@ ext {
// For android sub-projects
minSdkVersion = 19
targetSdkVersion = 29
- compileSdkVersion = 29
+ compileSdkVersion = 31
buildToolsVersion = '29.0.2'
MIN_SDK_VERSION_FOR_HOOK = 21
@@ -86,6 +86,8 @@ ext {
ABI_FILTERS = ['armeabi-v7a', 'arm64-v8a']
LOGGER_VERSION = 1.1 // fixme logger
+
+ LIFECYCLE_VERSION = '2.3.1'
}
// Build sample project
diff --git a/matrix/matrix-android/gradle.properties b/matrix/matrix-android/gradle.properties
index d0733879a..55933a473 100644
--- a/matrix/matrix-android/gradle.properties
+++ b/matrix/matrix-android/gradle.properties
@@ -14,7 +14,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
# org.gradle.parallel=true
#Tue Jun 20 10:24:33 CST 2017
-VERSION_NAME_PREFIX=2.0.5
+VERSION_NAME_PREFIX=2.1.0
VERSION_NAME_SUFFIX=
## two options: Internal (for wechat), External (for public repo)
PUBLISH_CHANNEL=Internal
diff --git a/matrix/matrix-android/gradle/android-publish-private.gradle b/matrix/matrix-android/gradle/android-publish-private.gradle
index 913642040..fdf3dc885 100644
--- a/matrix/matrix-android/gradle/android-publish-private.gradle
+++ b/matrix/matrix-android/gradle/android-publish-private.gradle
@@ -206,4 +206,19 @@ task buildAndPublishRepo(type: Copy, dependsOn: ['build', 'publish']) {
}
}
+task publishRepo(type: Copy, dependsOn: ['publish']) {
+ group = "publishing"
+
+ // save artifacts files to artifacts folder
+ from configurations.archives.allArtifacts.files
+ into "${rootProject.buildDir}/outputs/artifacts/"
+ rename { String fileName ->
+ fileName.replace("release.aar", "${version}.aar")
+ }
+
+ doLast {
+ println "* published to repo: ${project.group}:${project.name}:${project.version}"
+ }
+}
+
apply from: rootProject.file('gradle/check.gradle')
\ No newline at end of file
diff --git a/matrix/matrix-android/gradle/java-publish-private.gradle b/matrix/matrix-android/gradle/java-publish-private.gradle
index 5a5000fdf..c3d0f8cad 100644
--- a/matrix/matrix-android/gradle/java-publish-private.gradle
+++ b/matrix/matrix-android/gradle/java-publish-private.gradle
@@ -143,4 +143,19 @@ task buildAndPublishRepo(type: Copy, dependsOn: ['build', 'publish']) {
}
}
+task publishRepo(type: Copy, dependsOn: ['publish']) {
+ group = "publishing"
+
+ // save artifacts files to artifacts folder
+ from configurations.archives.allArtifacts.files
+ into "${rootProject.buildDir}/outputs/artifacts/"
+ rename { String fileName ->
+ fileName.replace("release.aar", "${version}.aar")
+ }
+
+ doLast {
+ println "* published to repo: ${project.group}:${project.name}:${project.version}"
+ }
+}
+
apply from: rootProject.file('gradle/check.gradle')
\ No newline at end of file
diff --git a/matrix/matrix-android/gradle/java-publish.gradle b/matrix/matrix-android/gradle/java-publish.gradle
index 65cb10554..7d751f877 100644
--- a/matrix/matrix-android/gradle/java-publish.gradle
+++ b/matrix/matrix-android/gradle/java-publish.gradle
@@ -141,7 +141,7 @@ task buildAndPublishToLocalMaven(type: Copy, dependsOn: ['build', 'publishToMave
}
}
-task buildAndPublishRepo(type: Copy, dependsOn: ['build', 'publish']) {
+task buildAndPublishRepo(type: Copy, dependsOn: ['publish']) {
group = "publishing"
// save artifacts files to artifacts folder
diff --git a/matrix/matrix-android/matrix-android-commons/CMakeLists.txt b/matrix/matrix-android/matrix-android-commons/CMakeLists.txt
index a9b8c0b65..1cdd8e69a 100644
--- a/matrix/matrix-android/matrix-android-commons/CMakeLists.txt
+++ b/matrix/matrix-android/matrix-android-commons/CMakeLists.txt
@@ -1,6 +1,11 @@
cmake_minimum_required(VERSION 3.4.1)
-project(android-commons C)
+project(android-commons)
+
+option(EnableLOG "Enable Logs" ON)
+if(EnableLOG)
+ add_definitions(-DEnableLOG)
+endif()
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/libsemi_dlfcn)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/libenhance_dlsym)
+add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/libsemi_dlfcn)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/libxhook)
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/build.gradle b/matrix/matrix-android/matrix-android-commons/build.gradle
index 62aef0853..ea33532cc 100644
--- a/matrix/matrix-android/matrix-android-commons/build.gradle
+++ b/matrix/matrix-android/matrix-android-commons/build.gradle
@@ -19,6 +19,7 @@ android {
externalNativeBuild {
cmake {
targets = ['xhook', 'semi_dlfcn', 'enhance_dlsym']
+ arguments = ["-DEnableLOG=${gradle.enableLog() ? "ON" : "OFF"}" as String]
}
exportHeaders {
from('src/main/cpp/libxhook') {
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/AndroidManifest.xml b/matrix/matrix-android/matrix-android-commons/src/main/AndroidManifest.xml
index 8d3d02285..44d0b0d6e 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/AndroidManifest.xml
+++ b/matrix/matrix-android/matrix-android-commons/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/CMakeLists.txt b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/CMakeLists.txt
index 52b7c5b46..0443da00d 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/CMakeLists.txt
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/CMakeLists.txt
@@ -3,17 +3,21 @@ project(libenhance_dlsym CXX)
set(enhance_dlsym_source_dir ${CMAKE_CURRENT_SOURCE_DIR})
-set(enhance_dlsym_source
- ${enhance_dlsym_source_dir}/EnhanceDlsym.cpp)
+set(
+ enhance_dlsym_source
+ ${enhance_dlsym_source_dir}/EnhanceDlsym.cpp
+)
add_library(enhance_dlsym STATIC ${enhance_dlsym_source})
+find_library(log-lib log)
+
target_include_directories(
- enhance_dlsym
- PUBLIC ${enhance_dlsym_source_dir}
+ enhance_dlsym
+ PUBLIC ${enhance_dlsym_source_dir}
)
target_link_libraries(
- enhance_dlsym
- PUBLIC ${log-lib}
+ enhance_dlsym
+ PUBLIC ${log-lib}
)
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/EnhanceDlsym.cpp b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/EnhanceDlsym.cpp
index 6eb5b0cd4..15c4fc7a4 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/EnhanceDlsym.cpp
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libenhance_dlsym/EnhanceDlsym.cpp
@@ -32,10 +32,15 @@
#include
#include
#include "EnhanceDlsym.h"
-#include "../../../../../matrix-jectl/src/main/cpp/jectl/JeLog.h"
#define TAG "Matrix.EnhanceDl"
+#include
+
+#define LOGD(TAG, FMT, args...) //__android_log_print(ANDROID_LOG_DEBUG, TAG, FMT, ##args)
+#define LOGI(TAG, FMT, args...) //__android_log_print(ANDROID_LOG_INFO, TAG, FMT, ##args)
+#define LOGE(TAG, FMT, args...) //__android_log_print(ANDROID_LOG_ERROR, TAG, FMT, ##args)
+
namespace enhance {
static std::set m_opened_info;
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/CMakeLists.txt b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/CMakeLists.txt
index 893bf07ba..fb352f0d5 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/CMakeLists.txt
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/CMakeLists.txt
@@ -3,18 +3,22 @@ project(libsemi_dlfcn C)
set(semi_dlfcn_source_dir ${CMAKE_CURRENT_SOURCE_DIR})
-set(semi_dlfcn_source
- ${semi_dlfcn_source_dir}/semi_dlfcn.c
- ${semi_dlfcn_source_dir}/sd_log.c)
+set(
+ semi_dlfcn_source
+ ${semi_dlfcn_source_dir}/semi_dlfcn.c
+ ${semi_dlfcn_source_dir}/sd_log.c
+)
add_library(semi_dlfcn STATIC ${semi_dlfcn_source})
+find_library(log-lib log)
+
target_include_directories(
- semi_dlfcn
- PUBLIC ${semi_dlfcn_source_dir}
+ semi_dlfcn
+ PUBLIC ${semi_dlfcn_source_dir}
)
target_link_libraries(
- semi_dlfcn
- PUBLIC ${log-lib}
+ semi_dlfcn
+ PUBLIC ${log-lib}
)
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/sd_log.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/sd_log.c
index a94d33faa..3e89448b6 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/sd_log.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/sd_log.c
@@ -4,5 +4,10 @@
#include "sd_log.h"
+#ifdef EnableLOG
bool g_semi_dlfcn_log_enabled = true;
+#else
+bool g_semi_dlfcn_log_enabled = false;
+#endif
+
int g_semi_dlfcn_log_level = ANDROID_LOG_DEBUG;
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/semi_dlfcn.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/semi_dlfcn.c
index 3ac926db9..abe0a2b6d 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/semi_dlfcn.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libsemi_dlfcn/semi_dlfcn.c
@@ -1,3 +1,4 @@
+#include
//
// Created by YinSheng Tang on 2021/7/21.
//
@@ -27,9 +28,9 @@ typedef struct {
uint32_t magic;
const char* pathname;
- const void* base_addr;
+ const ElfW(Ehdr)* ehdr;
- ElfW(Phdr)* phdrs;
+ const ElfW(Phdr)* phdrs;
ElfW(Word) phdr_count;
ElfW(Addr) load_bias;
@@ -38,6 +39,9 @@ typedef struct {
ElfW(Sym)* syms;
ElfW(Word) sym_count;
+
+ ElfW(Sym)* dynsyms;
+ ElfW(Word) dynsym_count;
} semi_dlinfo_t;
// Copy from xhook.
@@ -82,11 +86,21 @@ static bool validate_elf_header(uintptr_t base_addr) {
return true;
}
+static ElfW(Addr) find_load_bias(ElfW(Addr) base, const ElfW(Phdr*) phdrs, ElfW(Half) phnum) {
+ for (int i = 0; i < phnum; ++i) {
+ const ElfW(Phdr*) phdr = phdrs + i;
+ if (phdr->p_type == PT_LOAD) {
+ return base - phdr->p_vaddr;
+ }
+ }
+ return 0;
+}
+
static int legacy_iterate_maps(iterate_callback cb, void* data) {
int res = 0;
- FILE* fp = fopen("/proc/thread-self/maps", "r");
+ FILE* fp = fopen("/proc/self/maps", "r");
if (fp == NULL) {
- LOGE(LOG_TAG, "Fail to open /proc/thread-self/maps.");
+ LOGE(LOG_TAG, "Fail to open /proc/self/maps.");
return res;
}
char line[512] = {};
@@ -96,7 +110,8 @@ static int legacy_iterate_maps(iterate_callback cb, void* data) {
int offset = 0;
int pathname_pos = 0;
- if (sscanf(line, "%"PRIxPTR"-%*" PRIxPTR " %4s %x %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) {
+ if (sscanf(line, "%"PRIxPTR"-%*" PRIxPTR " %4s %x %*x:%*x %*d%n", // NOLINT(cert-err34-c)
+ &base_addr, perm, &offset, &pathname_pos) != 3) {
continue;
}
@@ -147,11 +162,12 @@ static int legacy_iterate_maps(iterate_callback cb, void* data) {
continue;
}
- struct dl_phdr_info info;
- info.dlpi_addr = base_addr;
+ struct dl_phdr_info info = {0};
info.dlpi_name = pathname;
- info.dlpi_phdr = (ElfW(Phdr)*) (base_addr + ((ElfW(Ehdr)*) base_addr)->e_phoff);
- info.dlpi_phnum = ((ElfW(Ehdr)*) base_addr)->e_phnum;
+ const ElfW(Ehdr)* ehdr = (const ElfW(Ehdr)*) base_addr;
+ info.dlpi_phdr = (ElfW(Phdr)*) (base_addr + ehdr->e_phoff);
+ info.dlpi_phnum = ehdr->e_phnum;
+ info.dlpi_addr = find_load_bias(base_addr, info.dlpi_phdr, info.dlpi_phnum);
res = cb(&info, sizeof(struct dl_phdr_info), data);
if (res != 0) {
@@ -164,8 +180,12 @@ static int legacy_iterate_maps(iterate_callback cb, void* data) {
return res;
}
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "readability-redundant-declaration"
+#pragma ide diagnostic ignored "bugprone-reserved-identifier"
// Make compiler happy.
extern int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data) __attribute__((weak));
+#pragma clang diagnostic pop
#ifdef __LP64__
#define LINKER_PATHNAME_SUFFIX "/system/bin/linker64"
@@ -176,11 +196,11 @@ extern int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*
#define LINKER_PATHNAME_SUFFIX_LEN (sizeof(LINKER_PATHNAME_SUFFIX) - 1)
static pthread_mutex_t s_linker_base_mutex = PTHREAD_MUTEX_INITIALIZER;
-static const void* s_linker_base = NULL;
+static ElfW(Addr) s_linker_base = 0;
static pthread_mutex_t s_dl_mutex_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t* s_dl_mutex_address = NULL;
-static int find_linker_base_iter_cb(struct dl_phdr_info* info, size_t info_size, void* data) {
+static int find_linker_base_iter_cb(struct dl_phdr_info* info, __unused size_t info_size, void* data) {
const char* pathname = info->dlpi_name;
size_t pathname_len = sizeof(pathname);
if (pathname_len < LINKER_PATHNAME_SUFFIX_LEN) {
@@ -194,14 +214,14 @@ static int find_linker_base_iter_cb(struct dl_phdr_info* info, size_t info_size,
return 0;
}
-static const void* get_linker_base_address() {
+static ElfW(Addr) get_linker_base_address() {
pthread_mutex_lock(&s_linker_base_mutex);
- if (s_linker_base != NULL) {
+ if (s_linker_base != 0) {
goto bail;
}
- s_linker_base = (const void*) getauxval(AT_BASE);
- if (s_linker_base == NULL) {
+ s_linker_base = (ElfW(Addr)) getauxval(AT_BASE);
+ if (s_linker_base == 0) {
legacy_iterate_maps(find_linker_base_iter_cb, &s_linker_base);
}
@@ -210,6 +230,8 @@ static const void* get_linker_base_address() {
return s_linker_base;
}
+static bool fill_rest_neccessary_data(semi_dlinfo_t* semi_dlinfo);
+
#define LINKER_DL_MUTEX_SYMNAME "__dl__ZL10g_dl_mutex"
static pthread_mutex_t* get_dl_mutex_address() {
@@ -218,36 +240,65 @@ static pthread_mutex_t* get_dl_mutex_address() {
goto bail;
}
- const void* linker_base = get_linker_base_address();
- if (linker_base == NULL) {
+ ElfW(Addr) linker_base = get_linker_base_address();
+ if (linker_base == 0) {
goto bail;
}
+ semi_dlinfo_t semi_dlinfo = {0};
+ semi_dlinfo.magic = SEMI_DLINFO_MAGIC;
+ semi_dlinfo.pathname = LINKER_PATHNAME_SUFFIX;
+ semi_dlinfo.ehdr = (ElfW(Ehdr)*) linker_base;
+ semi_dlinfo.phdrs = (const ElfW(Phdr)*) (((ElfW(Addr)) linker_base) + semi_dlinfo.ehdr->e_phoff);
+ semi_dlinfo.phdr_count = semi_dlinfo.ehdr->e_phnum;
+ semi_dlinfo.load_bias = find_load_bias(linker_base, semi_dlinfo.phdrs, semi_dlinfo.phdr_count);
+ fill_rest_neccessary_data(&semi_dlinfo);
- s_dl_mutex_address = semi_dlsym(linker_base, LINKER_DL_MUTEX_SYMNAME);
+ s_dl_mutex_address = semi_dlsym(&semi_dlinfo, LINKER_DL_MUTEX_SYMNAME);
bail:
pthread_mutex_unlock(&s_dl_mutex_mutex);
return s_dl_mutex_address;
}
+typedef struct {
+ void* original_data;
+ iterate_callback original_cb;
+} dl_iter_data_wrapper_t;
+
+static int dl_iterate_phdr_npe_avoidance_cb(struct dl_phdr_info* info, size_t info_size, void* wrapped_data) {
+ if (info->dlpi_name == NULL) {
+ LOGW(LOG_TAG, "Path is null, skip it.");
+ return 0;
+ }
+ dl_iter_data_wrapper_t* casted_wrapped_data = (dl_iter_data_wrapper_t*) wrapped_data;
+ return casted_wrapped_data->original_cb(info, info_size, casted_wrapped_data->original_data);
+}
+
int semi_dl_iterate_phdr(iterate_callback cb, void* data) {
int sdk = android_get_device_api_level();
- if (sdk < 21) {
+ bool fallback_to_legacy = false;
+ Dl_info tmp_info = {};
+ dladdr(&semi_dl_iterate_phdr, &tmp_info);
+ if (tmp_info.dli_fname != NULL && tmp_info.dli_fname[0] != '/') {
+ LOGW(LOG_TAG, "dladdr only tell us relative path of loaded so, fallbacl to legacy iterate mode.");
+ fallback_to_legacy = true;
+ }
+ if (sdk < 21 || fallback_to_legacy) {
return legacy_iterate_maps(cb, data);
} else if (sdk >= 21 && sdk <= 22) {
pthread_mutex_t *dl_mutex = get_dl_mutex_address();
if (dl_mutex != NULL) {
pthread_mutex_lock(dl_mutex);
}
- const void *linker_base = get_linker_base_address();
+ ElfW(Addr) linker_base = get_linker_base_address();
int ret = 0;
- if (linker_base != NULL) {
- struct dl_phdr_info dlinfo;
- dlinfo.dlpi_addr = (ElfW(Addr)) linker_base;
+ if (linker_base != 0) {
+ struct dl_phdr_info dlinfo = {0};
dlinfo.dlpi_name = LINKER_PATHNAME_SUFFIX;
ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*) linker_base;
dlinfo.dlpi_phdr = (ElfW(Phdr)*) (((uintptr_t) linker_base) + ehdr->e_phoff);
dlinfo.dlpi_phnum = ehdr->e_phnum;
+ dlinfo.dlpi_addr = find_load_bias(linker_base, dlinfo.dlpi_phdr, dlinfo.dlpi_phnum);
ret = cb(&dlinfo, sizeof(struct dl_phdr_info), data);
} else {
LOGW(LOG_TAG, "Cannot find base of linker.");
@@ -256,23 +307,27 @@ int semi_dl_iterate_phdr(iterate_callback cb, void* data) {
goto bail_sdk_21_22;
}
- ret = dl_iterate_phdr(cb, data);
+ dl_iter_data_wrapper_t wrapped_data = {
+ .original_cb = cb,
+ .original_data = data
+ };
+ ret = dl_iterate_phdr(dl_iterate_phdr_npe_avoidance_cb, &wrapped_data);
bail_sdk_21_22:
if (dl_mutex != NULL) {
pthread_mutex_unlock(dl_mutex);
}
return ret;
- } else if (sdk >= 23 && sdk <= 26) {
- const void *linker_base = get_linker_base_address();
+ } else if (sdk >= 23) {
+ ElfW(Addr) linker_base = get_linker_base_address();
int ret = 0;
- if (linker_base != NULL) {
- struct dl_phdr_info dlinfo;
- dlinfo.dlpi_addr = (ElfW(Addr)) linker_base;
+ if (linker_base != 0) {
+ struct dl_phdr_info dlinfo = {0};
dlinfo.dlpi_name = LINKER_PATHNAME_SUFFIX;
ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*) linker_base;
dlinfo.dlpi_phdr = (ElfW(Phdr)*) (((uintptr_t) linker_base) + ehdr->e_phoff);
dlinfo.dlpi_phnum = ehdr->e_phnum;
+ dlinfo.dlpi_addr = find_load_bias((ElfW(Addr)) linker_base, dlinfo.dlpi_phdr, dlinfo.dlpi_phnum);
ret = cb(&dlinfo, sizeof(struct dl_phdr_info), data);
} else {
LOGW(LOG_TAG, "Cannot find base of linker.");
@@ -281,12 +336,14 @@ int semi_dl_iterate_phdr(iterate_callback cb, void* data) {
goto bail_sdk_23_26;
}
- ret = dl_iterate_phdr(cb, data);
+ dl_iter_data_wrapper_t wrapped_data = {
+ .original_cb = cb,
+ .original_data = data
+ };
+ ret = dl_iterate_phdr(dl_iterate_phdr_npe_avoidance_cb, &wrapped_data);
bail_sdk_23_26:
return ret;
- } else {
- return dl_iterate_phdr(cb, data);
}
}
@@ -296,7 +353,7 @@ typedef struct {
semi_dlinfo_t* semi_dlinfo;
} semi_dlopen_iter_info;
-static int dlopen_iter_cb(struct dl_phdr_info* info, size_t info_size, void* data) {
+static int dlopen_iter_cb(struct dl_phdr_info* info, __unused size_t info_size, void* data) {
semi_dlopen_iter_info* iter_info = (semi_dlopen_iter_info*) data;
semi_dlinfo_t* semi_dlinfo = iter_info->semi_dlinfo;
@@ -311,14 +368,23 @@ static int dlopen_iter_cb(struct dl_phdr_info* info, size_t info_size, void* dat
return 0;
}
- LOGD(LOG_TAG, "dlopen_iter_cb, pathname: %s, name_suffix: %s, suffix_len: %zu", pathname, name_suffix, name_suffix_len);
+ LOGD(LOG_TAG, "pathname: %s, suffix_to_find: %s", info->dlpi_name, name_suffix);
if (strncmp(pathname + pathname_len - name_suffix_len, name_suffix, name_suffix_len) == 0) {
semi_dlinfo->pathname = pathname;
- semi_dlinfo->base_addr = (const void*) info->dlpi_addr;
- ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*) info->dlpi_addr;
- semi_dlinfo->phdrs = (ElfW(Phdr)*) (((uintptr_t) info->dlpi_addr) + ehdr->e_phoff);
- semi_dlinfo->phdr_count = ehdr->e_phnum;
+ semi_dlinfo->phdrs = info->dlpi_phdr;
+ semi_dlinfo->phdr_count = info->dlpi_phnum;
+ semi_dlinfo->load_bias = info->dlpi_addr;
+ for (int phdrIdx = 0; phdrIdx < info->dlpi_phnum; ++phdrIdx) {
+ const ElfW(Phdr)* phdr = info->dlpi_phdr + phdrIdx;
+ if (phdr->p_type == PT_LOAD) {
+ // dlpi_addr is actually load_bias.
+ semi_dlinfo->ehdr = (ElfW(Ehdr)*) (info->dlpi_addr + phdr->p_vaddr);
+ break;
+ }
+ }
+ LOGD(LOG_TAG, "dlopen_iter_cb, pathname: %s, name_suffix: %s, suffix_len: %zu, dlpi_addr: %p, ehdr: %p, phdr: %p",
+ pathname, name_suffix, name_suffix_len, (void*) info->dlpi_addr, semi_dlinfo->ehdr, info->dlpi_phdr);
return 1;
}
@@ -346,7 +412,10 @@ static bool load_section(int fd, off_t file_offset, size_t section_size, void**
goto fail;
}
+ #pragma clang diagnostic push
+ #pragma ide diagnostic ignored "UnreachableCode"
goto bail;
+ #pragma clang diagnostic pop
fail:
res = false;
@@ -359,17 +428,11 @@ static bool load_section(int fd, off_t file_offset, size_t section_size, void**
}
static bool fill_rest_neccessary_data(semi_dlinfo_t* semi_dlinfo) {
- ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*) semi_dlinfo->base_addr;
- ElfW(Phdr)* phdrs = semi_dlinfo->phdrs;
- for (int i = 0; i < semi_dlinfo->phdr_count; ++i) {
- if (phdrs[i].p_type == PT_LOAD) {
- if ((ElfW(Addr)) semi_dlinfo->base_addr < phdrs[i].p_vaddr) {
- LOGE(LOG_TAG, "Unexpected first program header.");
- return false;
- }
- semi_dlinfo->load_bias = ((uintptr_t) semi_dlinfo->base_addr) - phdrs[i].p_vaddr;
- break;
- }
+ const ElfW(Ehdr)* ehdr = semi_dlinfo->ehdr;
+
+ if (semi_dlinfo->phdr_count != semi_dlinfo->ehdr->e_phnum) {
+ LOGE(LOG_TAG, "Phdr count mismatch in \"%s\".", semi_dlinfo->pathname);
+ return false;
}
int fd = open(semi_dlinfo->pathname, O_RDONLY);
@@ -387,6 +450,7 @@ static bool fill_rest_neccessary_data(semi_dlinfo_t* semi_dlinfo) {
bool strtbl_ok = false;
bool symtbl_ok = false;
+ bool dynsym_ok = false;
for (int shdr_idx = ehdr->e_shnum - 1; shdr_idx >= 0; --shdr_idx) {
ElfW(Shdr)* shdr = shdrs + shdr_idx;
if (shdr->sh_type == SHT_STRTAB && shdr_idx != ehdr->e_shstrndx) {
@@ -412,8 +476,20 @@ static bool fill_rest_neccessary_data(semi_dlinfo_t* semi_dlinfo) {
semi_dlinfo->sym_count = 0;
symtbl_ok = false;
}
+ } else if (shdr->sh_type == SHT_DYNSYM) {
+ LOGD(LOG_TAG, "load dynsym, sh_off: %" PRIxPTR ", sh_size: %" PRIuPTR,
+ (uintptr_t) shdr->sh_offset, (uintptr_t) shdr->sh_size);
+ if (LIKELY(load_section(fd, shdr->sh_offset, shdr->sh_size, (void **) &semi_dlinfo->dynsyms))) {
+ semi_dlinfo->dynsym_count = shdr->sh_size / shdr->sh_entsize;
+ dynsym_ok = true;
+ } else {
+ LOGE(LOG_TAG, "Fail to map dynamic symbol table of \"%s\"", semi_dlinfo->pathname);
+ semi_dlinfo->dynsyms = NULL;
+ semi_dlinfo->dynsym_count = 0;
+ dynsym_ok = false;
+ }
}
- if (strtbl_ok && symtbl_ok) {
+ if (strtbl_ok && (symtbl_ok || dynsym_ok)) {
break;
}
}
@@ -423,10 +499,24 @@ static bool fill_rest_neccessary_data(semi_dlinfo_t* semi_dlinfo) {
free(shdrs);
}
- if (LIKELY(strtbl_ok && symtbl_ok)) {
+ if (LIKELY(strtbl_ok && (symtbl_ok || dynsym_ok))) {
return true;
} else {
LOGE(LOG_TAG, "Failure in fill_rest_neccessary_data.");
+ if (semi_dlinfo->strs != NULL) {
+ free(semi_dlinfo->strs);
+ semi_dlinfo->strs = NULL;
+ }
+ if (semi_dlinfo->syms != NULL) {
+ free(semi_dlinfo->syms);
+ semi_dlinfo->syms = NULL;
+ semi_dlinfo->sym_count = 0;
+ }
+ if (semi_dlinfo->dynsyms != NULL) {
+ free(semi_dlinfo->dynsyms);
+ semi_dlinfo->dynsyms = NULL;
+ semi_dlinfo->dynsym_count = 0;
+ }
return false;
}
}
@@ -471,7 +561,7 @@ void* semi_dlopen(const char* pathname) {
semi_dl_iterate_phdr(dlopen_iter_cb, &iter_info);
- if (result->base_addr != NULL) {
+ if (result->ehdr != NULL) {
if (!fill_rest_neccessary_data(result)) {
goto fail;
}
@@ -499,7 +589,7 @@ void* semi_dlopen(const char* pathname) {
void* semi_dlsym(const void* semi_hlib, const char* sym_name) {
semi_dlinfo_t* semi_dlinfo = (semi_dlinfo_t*) semi_hlib;
if (UNLIKELY(semi_dlinfo->magic != SEMI_DLINFO_MAGIC)) {
- LOGE(LOG_TAG, "Invalid semi_hlib, skip doing dlsym.");
+ LOGE(LOG_TAG, "Invalid semi_hlib, skip doing dlsym. %x", semi_dlinfo->magic);
return NULL;
}
@@ -512,6 +602,15 @@ void* semi_dlsym(const void* semi_hlib, const char* sym_name) {
}
}
+ for (int i = 0; i < semi_dlinfo->dynsym_count; ++i) {
+ ElfW(Sym) *curr_sym = semi_dlinfo->dynsyms + i;
+ int sym_type = ELF_ST_TYPE(curr_sym->st_info);
+ const char *curr_sym_name = semi_dlinfo->strs + curr_sym->st_name;
+ if ((sym_type == STT_FUNC || sym_type == STT_OBJECT) && strcmp(curr_sym_name, sym_name) == 0) {
+ return (void *) semi_dlinfo->load_bias + curr_sym->st_value;
+ }
+ }
+
LOGE(LOG_TAG, "Cannot find symbol \"%s\" in \"%s\"", sym_name, semi_dlinfo->pathname);
return NULL;
}
@@ -528,9 +627,17 @@ void semi_dlclose(void* semi_hlib) {
}
if (semi_dlinfo->strs != NULL) {
free(semi_dlinfo->strs);
+ semi_dlinfo->strs = NULL;
}
if (semi_dlinfo->syms != NULL) {
free(semi_dlinfo->syms);
+ semi_dlinfo->syms = NULL;
+ semi_dlinfo->sym_count = 0;
+ }
+ if (semi_dlinfo->dynsyms != NULL) {
+ free(semi_dlinfo->dynsyms);
+ semi_dlinfo->dynsyms = NULL;
+ semi_dlinfo->dynsym_count = 0;
}
free(semi_hlib);
}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/CMakeLists.txt b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/CMakeLists.txt
index 429117605..33425c5c4 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/CMakeLists.txt
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/CMakeLists.txt
@@ -3,31 +3,31 @@ project(libxhook C)
set(xhook_source_dir ${CMAKE_CURRENT_SOURCE_DIR})
-set(xhook_source
- ${xhook_source_dir}/xh_core.c
- ${xhook_source_dir}/xh_elf.c
- ${xhook_source_dir}/xh_jni.c
- ${xhook_source_dir}/xh_log.c
- ${xhook_source_dir}/xh_util.c
- ${xhook_source_dir}/xh_version.c
- ${xhook_source_dir}/xh_maps.c
- ${xhook_source_dir}/xhook.c
- ${xhook_source_dir}/xhook_ext.c)
+set(
+ xhook_source
+ ${xhook_source_dir}/xh_core.c
+ ${xhook_source_dir}/xh_elf.c
+ ${xhook_source_dir}/xh_jni.c
+ ${xhook_source_dir}/xh_log.c
+ ${xhook_source_dir}/xh_util.c
+ ${xhook_source_dir}/xh_version.c
+ ${xhook_source_dir}/xh_maps.c
+ ${xhook_source_dir}/xhook.c
+ ${xhook_source_dir}/xhook_ext.c
+)
add_library(xhook STATIC ${xhook_source})
-find_library(
- log-lib
- log
-)
+find_library(log-lib log)
target_include_directories(
- xhook
- PUBLIC ${xhook_source_dir}
+ xhook
+ PUBLIC ${xhook_source_dir}
+ PUBLIC ${xhook_source_dir}/../libsemi_dlfcn
)
target_link_libraries(
- xhook
- ${log-lib}
-)
-
+ xhook
+ PUBLIC ${log-lib}
+ PUBLIC semi_dlfcn
+)
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_core.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_core.c
index 23b645b6c..892b41fe1 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_core.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_core.c
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include "queue.h"
#include "tree.h"
#include "xh_errno.h"
@@ -133,9 +134,11 @@ static void xh_core_init_request_group(xh_core_request_group_t* request_group, i
//required info from /proc/self/maps
typedef struct xh_core_map_info
{
- char *pathname;
- uintptr_t base_addr;
- xh_elf_t elf;
+ char* pathname;
+ uintptr_t bias_addr;
+ ElfW(Phdr)* phdrs;
+ ElfW(Half) phdr_count;
+ xh_elf_t elf;
RB_ENTRY(xh_core_map_info) link;
} xh_core_map_info_t;
static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b)
@@ -387,7 +390,7 @@ static void xh_core_hook_impl_with_spec_info(xh_core_map_info_t *mi,
static void xh_core_hook_impl(xh_core_map_info_t *mi)
{
//init
- if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;
+ if(0 != xh_elf_init(&(mi->elf), mi->bias_addr, mi->phdrs, mi->phdr_count, mi->pathname)) return;
xh_core_hook_impl_with_spec_info(mi, &xh_core_hook_info, &xh_core_ignore_info);
@@ -463,24 +466,18 @@ static int is_current_pathname_matches_any_requests(xh_core_hook_info_queue_t *h
return match;
}
-static int xh_core_maps_iterate_cb(void* data, uintptr_t start, uintptr_t end, char* perms, int offset, char* pathname)
+static int xh_core_maps_iterate_cb(struct dl_phdr_info* info, size_t info_size, void* data)
{
xh_core_map_info_tree_t* map_info_refreshed = (xh_core_map_info_tree_t*) data;
- //check permission
- if (perms[0] != 'r') return 0;
- if (perms[3] != 'p') return 0; //do not touch the shared memory
-
- //check offset
- //
- //We are trying to find ELF header in memory.
- //It can only be found at the beginning of a mapped memory regions
- //whose offset is 0.
- if(0 != offset) return 0;
-
- if('[' == pathname[0]) return 0;
+ const char* pathname = info->dlpi_name;
//check pathname
+ if (pathname[0] == '[') {
+ XH_LOG_DEBUG("'%s' is not a lib, skip it.", pathname);
+ return 0;
+ }
+
//if we need to hook this elf?
if (!is_current_pathname_matches_any_requests(&xh_core_hook_info, &xh_core_ignore_info, pathname))
{
@@ -504,18 +501,9 @@ static int xh_core_maps_iterate_cb(void* data, uintptr_t start, uintptr_t end, c
check_finished:
XH_LOG_INFO("'%s' matches hook request, do further checks.", pathname);
- if (0 == xh_check_loaded_so((void *) start)) {
- XH_LOG_ERROR("%p is not loaded by linker %s", (void *) start, pathname);
- return 0; // do not touch the so that not loaded by linker
- }
-
- //check elf header format
- //We are trying to do ELF header checking as late as possible.
- if(0 != xh_core_check_elf_header(start, pathname)) return 0;
-
//check existed map item
xh_core_map_info_t mi_key;
- mi_key.pathname = pathname;
+ mi_key.pathname = (char*) pathname;
xh_core_map_info_t* mi;
if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))
{
@@ -532,9 +520,11 @@ static int xh_core_maps_iterate_cb(void* data, uintptr_t start, uintptr_t end, c
}
//re-hook if base_addr changed
- if(mi->base_addr != start)
+ if(mi->bias_addr != info->dlpi_addr)
{
- mi->base_addr = start;
+ mi->bias_addr = info->dlpi_addr;
+ mi->phdrs = (ElfW(Phdr)*) info->dlpi_phdr;
+ mi->phdr_count = info->dlpi_phnum;
xh_core_hook(mi);
}
}
@@ -547,7 +537,9 @@ static int xh_core_maps_iterate_cb(void* data, uintptr_t start, uintptr_t end, c
free(mi);
return 0;
}
- mi->base_addr = start;
+ mi->bias_addr = info->dlpi_addr;
+ mi->phdrs = (ElfW(Phdr)*) info->dlpi_phdr;
+ mi->phdr_count = info->dlpi_phnum;
//repeated?
//We only keep the first one, that is the real base address
@@ -568,10 +560,8 @@ static void xh_core_refresh_impl()
{
pthread_rwlock_rdlock(&xh_core_refresh_blocker);
- xh_maps_invalidate();
-
xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);
- xh_maps_iterate((xh_maps_iterate_cb_t) xh_core_maps_iterate_cb, &map_info_refreshed);
+ semi_dl_iterate_phdr((iterate_callback) xh_core_maps_iterate_cb, &map_info_refreshed);
xh_core_map_info_t* mi;
xh_core_map_info_t* mi_tmp;
@@ -801,106 +791,69 @@ void xh_core_enable_sigsegv_protection(int flag)
xh_core_sigsegv_enable = (flag ? 1 : 0);
}
-void* xh_core_elf_open(const char *path_suffix) {
- char line[512];
- FILE* fp;
- uintptr_t base_addr;
- char perm[5];
- unsigned long offset;
- int pathname_pos;
- char *pathname;
- size_t pathname_len;
- xh_core_map_info_t *mi;
- size_t path_suffix_len;
- int found;
-
- if (path_suffix == NULL)
- {
- return NULL;
- }
+typedef struct xh_single_so_iterate_args {
+ const char* path_suffix;
+ xh_core_map_info_t* mi;
+} xh_single_so_iterate_args_t;
- if(NULL == (fp = fopen("/proc/self/maps", "r")))
- {
- XH_LOG_ERROR("fopen /proc/self/maps failed");
- return NULL;
- }
+static int xh_single_so_search_iterate_cb(struct dl_phdr_info* info, size_t info_size, void* data) {
+ xh_single_so_iterate_args_t* args = (xh_single_so_iterate_args_t*) data;
- path_suffix_len = strlen(path_suffix);
- if (path_suffix_len == 0)
- {
- fclose(fp);
- return NULL;
+ const char* pathname = info->dlpi_name;
+ size_t path_len = strlen(pathname);
+ size_t path_suffix_len = strlen(args->path_suffix);
+ if (strncmp(pathname + path_len - path_suffix_len, args->path_suffix, path_suffix_len) != 0) {
+ // Continue to process next entry.
+ return 0;
}
- found = 0;
- while(fgets(line, sizeof(line), fp))
- {
- if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;
-
- //check permission
- if(perm[0] != 'r') continue;
- if(perm[3] != 'p') continue; //do not touch the shared memory
-
- //check offset
- //
- //We are trying to find ELF header in memory.
- //It can only be found at the beginning of a mapped memory regions
- //whose offset is 0.
- if(0 != offset) continue;
-
- //get pathname
- while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))
- pathname_pos += 1;
- if(pathname_pos >= (int)(sizeof(line) - 1)) continue;
- pathname = line + pathname_pos;
- pathname_len = strlen(pathname);
- if(0 == pathname_len) continue;
- if(pathname[pathname_len - 1] == '\n')
- {
- pathname[pathname_len - 1] = '\0';
- pathname_len -= 1;
- }
- if(0 == pathname_len) continue;
- if('[' == pathname[0]) continue;
- if (path_suffix_len > pathname_len) continue;
-
- if (strncmp(pathname + pathname_len - path_suffix_len, path_suffix, path_suffix_len) != 0)
- {
- continue;
- }
-
- if (0 != xh_core_check_elf_header(base_addr, pathname)) continue;
+ int check_elf_ret = xh_core_check_elf_header(info->dlpi_addr, pathname);
+ if (0 != check_elf_ret) {
+ XH_LOG_ERROR("Fail to check elf header: %s, ret: %d.", pathname, check_elf_ret);
+ // Continue to process next entry.
+ return 0;
+ }
- found = 1;
- break;
+ args->mi->pathname = strdup(pathname);
+ if (args->mi->pathname == NULL) {
+ XH_LOG_ERROR("Fail to allocate memory to store path: %s.", pathname);
+ return -1;
}
+ args->mi->bias_addr = info->dlpi_addr;
+ args->mi->phdrs = (ElfW(Phdr)*) info->dlpi_phdr;
+ args->mi->phdr_count = info->dlpi_phnum;
+ return 1;
+}
- if (found != 1)
- {
- fclose(fp);
+void* xh_core_elf_open(const char *path_suffix) {
+ if (path_suffix == NULL) {
+ XH_LOG_ERROR("path_suffix is null.");
return NULL;
}
-
- mi = malloc(sizeof(xh_core_map_info_t));
- if (mi == NULL)
- {
- fclose(fp);
+ xh_core_map_info_t* mi = malloc(sizeof(xh_core_map_info_t));
+ if (mi == NULL) {
+ XH_LOG_ERROR("Fail to allocate memory.");
return NULL;
}
memset(mi, 0, sizeof(xh_core_map_info_t));
- if ((mi->pathname = strdup(pathname)) == NULL)
- {
- fclose(fp);
+ xh_single_so_iterate_args_t iter_args = {
+ .path_suffix = path_suffix,
+ .mi = mi
+ };
+ int iter_ret = semi_dl_iterate_phdr(xh_single_so_search_iterate_cb, &iter_args);
+ if (iter_ret > 0) {
+ XH_LOG_INFO("Open so with path suffix %s successfully, realpath: %s.", path_suffix, mi->pathname);
+ return mi;
+ } else {
+ if (mi->pathname != NULL) {
+ free(mi->pathname);
+ mi->pathname = NULL;
+ }
free(mi);
- mi = NULL;
+ XH_LOG_ERROR("Fail to open %s", path_suffix);
return NULL;
}
-
- mi->base_addr = base_addr;
- fclose(fp);
-
- return mi;
}
static int xh_core_hook_single_sym_impl(xh_core_map_info_t *mi, const char *symbol, void *new_func,
@@ -914,7 +867,7 @@ static int xh_core_hook_single_sym_impl(xh_core_map_info_t *mi, const char *symb
}
//init
- ret = xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname);
+ ret = xh_elf_init(&(mi->elf), mi->bias_addr, mi->phdrs, mi->phdr_count, mi->pathname);
if (ret != 0)
{
return ret;
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.c
index 85cbf8af5..bce5ce804 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.c
@@ -771,20 +771,27 @@ static void xh_elf_dump(xh_elf_t *self)
#endif
-int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname)
+int xh_elf_init(xh_elf_t *self, uintptr_t bias_addr, ElfW(Phdr)* phdrs, ElfW(Half) phdr_count, const char *pathname)
{
- if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL;
+ if(0 == bias_addr || NULL == pathname) return XH_ERRNO_INVAL;
//always reset
memset(self, 0, sizeof(xh_elf_t));
self->pathname = pathname;
- self->base_addr = (ElfW(Addr))base_addr;
- self->ehdr = (ElfW(Ehdr) *)base_addr;
- self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes
+ self->bias_addr = (ElfW(Addr)) bias_addr;
+ self->phdr = phdrs;
- //find the first load-segment with offset 0
- ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0);
+ XH_LOG_DEBUG("xh_elf_init: pathname: %s, phdr: %p, phdr_count: %u", pathname, phdrs, phdr_count);
+
+ ElfW(Phdr)* phdr0 = NULL;
+ for (int i = 0; i < phdr_count; ++i) {
+ ElfW(Phdr)* phdr = phdrs + i;
+ if (phdr->p_type == PT_LOAD) {
+ phdr0 = phdr;
+ break;
+ }
+ }
if(NULL == phdr0)
{
XH_LOG_ERROR("Can NOT found the first load segment. %s", pathname);
@@ -797,9 +804,9 @@ int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname)
(void *)(phdr0->p_vaddr), pathname);
#endif
- //save load bias addr
+ self->base_addr = self->bias_addr + phdr0->p_vaddr;
if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT;
- self->bias_addr = self->base_addr - phdr0->p_vaddr;
+ self->ehdr = (ElfW(Ehdr) *) self->base_addr;
//find dynamic-segment
ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC);
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.h b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.h
index b73312089..9670afe22 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.h
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_elf.h
@@ -73,7 +73,7 @@ typedef struct
int is_use_gnu_hash;
} xh_elf_t;
-int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname);
+int xh_elf_init(xh_elf_t *self, uintptr_t bias_addr, ElfW(Phdr)* phdrs, ElfW(Half) phdr_count, const char *pathname);
int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func);
int xh_elf_check_elfheader(uintptr_t base_addr);
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.c
index ee7ce70bb..0359417fe 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.c
@@ -24,5 +24,10 @@
#include
#include "xh_log.h"
-android_LogPriority xh_log_priority = ANDROID_LOG_WARN;
+#ifdef EnableLOG
+int enable_log = 1;
+#else
int enable_log = 0;
+#endif
+
+android_LogPriority xh_log_priority = ANDROID_LOG_DEBUG;
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.h b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.h
index 06ce20abc..b8a8ee615 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.h
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_log.h
@@ -35,15 +35,15 @@ extern android_LogPriority xh_log_priority;
extern int enable_log;
#define XH_LOG_TAG "xhook"
-//#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-//#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-//#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-//#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-
-#define XH_LOG_DEBUG(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-#define XH_LOG_INFO(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-#define XH_LOG_WARN(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
-#define XH_LOG_ERROR(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_DEBUG(fmt, ...) do{if(enable_log && (xh_log_priority <= ANDROID_LOG_DEBUG)) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_INFO(fmt, ...) do{if(enable_log && (xh_log_priority <= ANDROID_LOG_INFO)) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_WARN(fmt, ...) do{if(enable_log && (xh_log_priority <= ANDROID_LOG_WARN)) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_ERROR(fmt, ...) do{if(enable_log && (xh_log_priority <= ANDROID_LOG_ERROR)) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+
+// #define XH_LOG_DEBUG(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+// #define XH_LOG_INFO(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+// #define XH_LOG_WARN(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+// #define XH_LOG_ERROR(fmt, ...) do{if(enable_log) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
#define XH_LOG_ABORT(fmt, ...) do{ __android_log_assert(NULL, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_util.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_util.c
index 97fe11c69..21192f0cb 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_util.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xh_util.c
@@ -52,7 +52,10 @@ static int xh_util_get_mem_protect_maps_iter_cb(void* data, uintptr_t start, uin
int* load0 = (int*) ((void**) data)[4];
int* found_all = (int*) ((void**) data)[5];
- if (pathname != NULL && strcmp(expected_pathname, pathname) != 0) return 0;
+ // Use addr to locate map entry is enough.
+ // Use pathname to locate map entry may fail if expected map entry was mapped directly from apk. In this case
+ // map entry name is always the apk path while our expect name is the so path.
+ // if (pathname != NULL && strcmp(expected_pathname, pathname) != 0) return 0;
if(perms[3] != 'p') return 0;
diff --git a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xhook_ext.c b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xhook_ext.c
index eb909b4e5..aeeb89610 100644
--- a/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xhook_ext.c
+++ b/matrix/matrix-android/matrix-android-commons/src/main/cpp/libxhook/xhook_ext.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include "xhook_ext.h"
#include "xh_core.h"
#include "xh_elf.h"
@@ -62,13 +63,20 @@ void xhook_elf_close(void *h_lib)
xh_core_elf_close(h_lib);
}
-static int xh_export_symtable_hook(const char* pathname, const void* base_addr, const char* symbol_name, void* handler,
- void** original_address) {
+static int xh_export_symtable_hook(
+ const char* pathname,
+ const void* bias_addr,
+ ElfW(Phdr)* phdrs,
+ ElfW(Half) phdr_count,
+ const char* symbol_name,
+ void* handler,
+ void** original_address
+) {
if(NULL == symbol_name || NULL == handler) return XH_ERRNO_INVAL;
xh_elf_t self = {};
{
- int error = xh_elf_init(&self, (uintptr_t) base_addr, pathname);
+ int error = xh_elf_init(&self, (uintptr_t) bias_addr, phdrs, phdr_count, pathname);
if (error != 0) return error;
}
XH_LOG_INFO("hooking %s in %s using export table hook.\n", symbol_name, pathname);
@@ -107,97 +115,79 @@ static int xh_export_symtable_hook(const char* pathname, const void* base_addr,
return 0;
}
-static int xhook_find_library_base_addr(const char* owner_lib_name, char path_name_out[PATH_MAX + 1], const void** base_addr_out) {
- FILE* fp = fopen("/proc/self/maps", "r");
- if(fp == NULL) {
- XH_LOG_ERROR("fopen /proc/self/maps failed");
- return XH_ERRNO_ELFINIT;
+typedef struct found_lib_info {
+ struct {
+ const char* owner_lib_name;
+ } args_in;
+
+ struct {
+ char path_name[PATH_MAX + 1];
+ const void* bias_addr;
+ ElfW(Phdr)* phdrs;
+ ElfW(Half) phdr_count;
+ } args_out;
+} found_lib_info_t;
+
+#define MAPS_ITER_RET_NOTFOUND 0
+#define MAPS_ITER_RET_FOUND 1
+
+static int find_owner_library_cb(struct dl_phdr_info* info, size_t info_size, void* data) {
+ found_lib_info_t* found_lib_info = (found_lib_info_t*) data;
+
+ const char* owner_lib_name = found_lib_info->args_in.owner_lib_name;
+ size_t owner_name_len = strlen(owner_lib_name);
+ if (owner_name_len == 0) return MAPS_ITER_RET_NOTFOUND;
+
+ char real_suffix[PATH_MAX + 1];
+ if (owner_lib_name[0] != '/') {
+ real_suffix[0] = '/';
+ strncpy(real_suffix + 1, owner_lib_name, PATH_MAX);
+ ++owner_name_len;
+ } else {
+ strncpy(real_suffix, owner_lib_name, PATH_MAX);
}
-
- char line[512] = {};
- while(fgets(line, sizeof(line), fp)) {
- uintptr_t base_addr = 0;
- char perm[5] = {};
- unsigned long offset = 0;
- int pathname_pos = 0;
- if (sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) {
- continue;
- }
-
- //check permission
- if (perm[0] != 'r') continue;
- if (perm[3] != 'p') continue; //do not touch the shared memory
-
- //check offset
- //
- //We are trying to find ELF header in memory.
- //It can only be found at the beginning of a mapped memory regions
- //whose offset is 0.
- if (0 != offset) continue;
-
- // Skip normal mmapped region.
- // Some libraries may mmap specific elf file into memory for
- // accessing as a normal file.
- {
- Dl_info info;
- if (dladdr((void*) base_addr, &info) == 0) {
- continue;
- }
- }
-
- //get pathname
- while (isspace(line[pathname_pos]) && pathname_pos < (int) (sizeof(line) - 1)) {
- pathname_pos += 1;
- }
- if (pathname_pos >= (int) (sizeof(line) - 1)) continue;
- char* pathname = line + pathname_pos;
- size_t pathname_len = strlen(pathname);
- if (0 == pathname_len) continue;
- if (pathname[pathname_len - 1] == '\n') {
- pathname[pathname_len - 1] = '\0';
- pathname_len -= 1;
- }
- if (0 == pathname_len) continue;
- if ('[' == pathname[0]) continue;
-
- //check pathname
- //if we need to hook this elf?
- char real_suffix[PATH_MAX + 1] = {};
- size_t real_suffix_len = snprintf(real_suffix, sizeof(real_suffix), "/%s", owner_lib_name);
- if (pathname_len < real_suffix_len) {
- continue;
- }
- if (strncmp(pathname + pathname_len - real_suffix_len, real_suffix, real_suffix_len) != 0) {
- continue;
- }
-
- //check elf header format
- //We are trying to do ELF header checking as late as possible.
- if (0 != xh_elf_check_elfheader(base_addr)) continue;
-
- XH_LOG_DEBUG("found library, owner_lib_name: %s, path: %s, base: %" PRIxPTR,
- owner_lib_name, pathname, base_addr);
-
- if (path_name_out != NULL) {
- strncpy(path_name_out, pathname, PATH_MAX);
- }
- if (base_addr_out != NULL) {
- *base_addr_out = (const void*) base_addr;
- }
- fclose(fp);
- return 0;
+ if (owner_name_len > PATH_MAX) owner_name_len = PATH_MAX;
+ real_suffix[owner_name_len] = '\0';
+
+ XH_LOG_DEBUG("find_owner_library_cb: curr_pathname: %s, real_suffix: %s", info->dlpi_name, real_suffix);
+
+ size_t curr_pathname_len = strlen(info->dlpi_name);
+ if (strncmp(info->dlpi_name + curr_pathname_len - owner_name_len, real_suffix, owner_name_len) == 0) {
+ strcpy(found_lib_info->args_out.path_name, info->dlpi_name);
+ found_lib_info->args_out.bias_addr = (const void*) info->dlpi_addr;
+ found_lib_info->args_out.phdrs = (ElfW(Phdr)*) info->dlpi_phdr;
+ found_lib_info->args_out.phdr_count = info->dlpi_phnum;
+ XH_LOG_INFO("Found owner lib '%s' by suffix '%s'.", info->dlpi_name, real_suffix);
+ return MAPS_ITER_RET_FOUND;
+ } else {
+ return MAPS_ITER_RET_NOTFOUND;
}
- fclose(fp);
- return XH_ERRNO_NOTFND;
}
int xhook_export_symtable_hook(const char* owner_lib_name, const char* symbol_name, void* handler,
void** original_address) {
- char path_name[PATH_MAX + 1] = {};
- const void* base_addr = NULL;
- if (xhook_find_library_base_addr(owner_lib_name, path_name, &base_addr) == 0) {
- return xh_export_symtable_hook(path_name, base_addr, symbol_name, handler, original_address);
- } else {
- return XH_ERRNO_NOTFND;
+ found_lib_info_t found_lib_info = {};
+ found_lib_info.args_in.owner_lib_name = owner_lib_name;
+ switch (semi_dl_iterate_phdr(find_owner_library_cb, &found_lib_info)) {
+ case MAPS_ITER_RET_FOUND: {
+ return xh_export_symtable_hook(
+ found_lib_info.args_out.path_name,
+ found_lib_info.args_out.bias_addr,
+ found_lib_info.args_out.phdrs,
+ found_lib_info.args_out.phdr_count,
+ symbol_name,
+ handler,
+ original_address
+ );
+ }
+ case MAPS_ITER_RET_NOTFOUND: {
+ return XH_ERRNO_NOTFND;
+ }
+ case XH_ERRNO_NOMEM: {
+ return XH_ERRNO_NOMEM;
+ }
+ default: {
+ return XH_ERRNO_UNKNOWN;
+ }
}
}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/build.gradle b/matrix/matrix-android/matrix-android-lib/build.gradle
index 63d453515..176b6b377 100644
--- a/matrix/matrix-android/matrix-android-lib/build.gradle
+++ b/matrix/matrix-android/matrix-android-lib/build.gradle
@@ -1,14 +1,18 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
+ useLibrary 'android.test.base'
+
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName rootProject.ext.VERSION_NAME
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
@@ -20,11 +24,14 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
+ api 'androidx.lifecycle:lifecycle-common:2.3.1'
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.annotation:annotation:1.0.0'
- api 'androidx.annotation:annotation:1.0.0'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+ androidTestImplementation "org.mockito:mockito-core:2.8.9"
+ androidTestImplementation "org.mockito:mockito-android:2.8.9"
}
version = rootProject.ext.VERSION_NAME
@@ -36,10 +43,8 @@ if (rootProject.file('gradle/WeChatPublish.gradle').exists()) {
}else {
//uploading to WeChat maven repo
apply from: rootProject.file('gradle/WeChatPublish.gradle')
- apply from: rootProject.file('gradle/WeChatNativeDepend.gradle')
wechatPublish {
artifactId=POM_ARTIFACT_ID
}
}
}
-
diff --git a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ExampleInstrumentedTest.java b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ExampleInstrumentedTest.java
index d8617eb9a..fe3003b81 100644
--- a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ExampleInstrumentedTest.java
+++ b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ExampleInstrumentedTest.java
@@ -35,7 +35,7 @@ public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.tencent.matrix.test", appContext.getPackageName());
}
diff --git a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/FibonacciTest.kt b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/FibonacciTest.kt
new file mode 100644
index 000000000..590782f72
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/FibonacciTest.kt
@@ -0,0 +1,36 @@
+package com.tencent.matrix
+
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.math.min
+
+/**
+ * Created by Yves on 2021/11/25
+ */
+@RunWith(AndroidJUnit4::class)
+class FibonacciTest {
+
+ companion object {
+ private const val TAG = "FibonacciTest"
+ }
+
+ class FibonacciInterval(private val maxVal: Long) {
+ private var n = 34L // 10th
+ private var m = 55L // 11th
+
+ fun next(): Long {
+ return min((n + m).also { n = m; m = it }, maxVal)
+ }
+ }
+
+ @Test
+ fun test() {
+ val f = FibonacciInterval(60 * 1000L)
+
+ for (i in 0..25) {
+ Log.d(TAG, "${f.next()}")
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ShadowStatefulOwnerTest.kt b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ShadowStatefulOwnerTest.kt
new file mode 100644
index 000000000..0889603be
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ShadowStatefulOwnerTest.kt
@@ -0,0 +1,82 @@
+package com.tencent.matrix
+
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.tencent.matrix.lifecycle.IStateObserver
+import com.tencent.matrix.lifecycle.StatefulOwner
+import com.tencent.matrix.lifecycle.reverse
+import com.tencent.matrix.lifecycle.shadow
+import org.junit.runner.RunWith
+import org.junit.Test
+
+/**
+ * Created by Yves on 2022/1/7
+ */
+@RunWith(AndroidJUnit4::class)
+class ShadowStatefulOwnerTest {
+
+ companion object {
+ private const val TAG = "ShadowStatefulOwnerTest"
+ }
+
+ @Test
+ fun test() {
+ val origin = object : StatefulOwner() {
+ fun on() = turnOn()
+ fun off() = turnOff()
+ }
+
+
+ val shadow = origin.shadow()
+ val reverse = origin.reverse()
+
+ origin.observeForever(object :IStateObserver {
+ override fun on() {
+ Log.d(TAG, "origin on")
+// origin.removeObserver(this)
+ }
+
+ override fun off() {
+ Log.d(TAG, "origin off")
+// origin.removeObserver(this)
+ }
+ })
+
+ shadow.observeForever(object :IStateObserver {
+ override fun on() {
+ Log.d(TAG, "shadow on")
+// shadow.removeObserver(this)
+ }
+
+ override fun off() {
+ Log.d(TAG, "shadow off")
+// shadow.removeObserver(this)
+ }
+ })
+
+
+ reverse.observeForever(object :IStateObserver {
+ override fun on() {
+ Log.d(TAG, "reverse on")
+ reverse.removeObserver(this)
+ }
+
+ override fun off() {
+ Log.d(TAG, "reverse off")
+ reverse.removeObserver(this)
+ }
+ })
+
+ origin.on()
+ origin.off()
+
+ Log.d(TAG, "============")
+
+ origin.on()
+ origin.off()
+
+ Log.d(TAG, "done")
+
+
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/StatefulOwnerTest.kt b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/StatefulOwnerTest.kt
new file mode 100644
index 000000000..db2d09551
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/StatefulOwnerTest.kt
@@ -0,0 +1,131 @@
+package com.tencent.matrix
+
+import android.os.SystemClock
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.tencent.matrix.lifecycle.*
+import com.tencent.matrix.util.MatrixLog
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Created by Yves on 2021/11/11
+ */
+@RunWith(AndroidJUnit4::class)
+class StatefulOwnerTest {
+
+ companion object {
+ private const val TAG = "Matrix.test.StatefulOwnerTest"
+ }
+
+ @Test
+ fun testRemoveSource() {
+
+ class TestMsOwner: MultiSourceStatefulOwner(ReduceOperators.OR)
+
+ class TestStatefulOwner: StatefulOwner() {
+ fun handleOn() = turnOn()
+ fun handleOff() = turnOff()
+ }
+
+ val msOwner = TestMsOwner()
+ msOwner.observeForever(object : IStateObserver {
+ override fun on() {
+ MatrixLog.d(TAG, "test ON")
+ }
+
+ override fun off() {
+ MatrixLog.d(TAG, "test OFF")
+ }
+
+ })
+ assertEquals(msOwner.active(), false)
+
+ val s1 = TestStatefulOwner().apply {
+ handleOn()
+ MatrixLog.d(TAG, "add s1")
+ msOwner.addSourceOwner(this)
+ }
+
+ assertEquals(msOwner.active(), true)
+
+ val s2 = TestStatefulOwner().apply {
+ handleOff()
+ MatrixLog.d(TAG, "add s2")
+ msOwner.addSourceOwner(this)
+ }
+
+ assertEquals(msOwner.active(), true)
+
+ MatrixLog.d(TAG, "remove s1")
+ msOwner.removeSourceOwner(s1)
+
+ assertEquals(msOwner.active(), false)
+
+ MatrixLog.d(TAG, "turn off s2")
+ s2.handleOff()
+
+ }
+
+ @Test
+ fun scheduleTest() {
+ val o1 = object : StatefulOwner(true) {
+ fun handleOn() = turnOn()
+ fun handleOff() = turnOff()
+ }
+
+ o1.observeForever(object : IStateObserver {
+ override fun on() {
+ Log.d(TAG, "on: normal observe at ${Thread.currentThread().name}")
+ }
+
+ override fun off() {
+ Log.d(TAG, "off: normal observe at ${Thread.currentThread().name}")
+ }
+ })
+
+ o1.observeForever(object : ISerialObserver {
+ override fun on() {
+ Log.d(TAG, "on: serial observe at ${Thread.currentThread().name} pool size = ${MatrixLifecycleThread.executor.poolSize}")
+ }
+
+ override fun off() {
+ Log.d(TAG, "off: serial observe at ${Thread.currentThread().name} pool size = ${MatrixLifecycleThread.executor.poolSize}")
+ }
+ })
+
+ o1.handleOn()
+ o1.handleOff()
+ SystemClock.sleep(100)
+ o1.handleOn()
+ o1.handleOff()
+ SystemClock.sleep(100)
+ o1.handleOn()
+ o1.handleOff()
+
+ val m1 = MultiSourceStatefulOwner(ReduceOperators.AND, o1)
+ m1.observeForever(object : IStateObserver {
+ override fun on() {
+ Log.d(TAG, "Multi on: normal observe at ${Thread.currentThread().name}")
+ }
+
+ override fun off() {
+ Log.d(TAG, "Multi off: normal observe at ${Thread.currentThread().name}")
+ }
+ })
+ m1.observeForever(object : ISerialObserver {
+ override fun on() {
+ Log.d(TAG, "Multi on: serial observe at ${Thread.currentThread().name} pool size = ${MatrixLifecycleThread.executor.poolSize}")
+ }
+
+ override fun off() {
+ Log.d(TAG, "Multi off: serial observe at ${Thread.currentThread().name} pool size = ${MatrixLifecycleThread.executor.poolSize}")
+ }
+ })
+
+ o1.handleOn()
+ o1.handleOff()
+ }
+
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ThreadTest.kt b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ThreadTest.kt
new file mode 100644
index 000000000..14a89ee8d
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/androidTest/java/com/tencent/matrix/ThreadTest.kt
@@ -0,0 +1,193 @@
+package com.tencent.matrix
+
+import android.os.SystemClock
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.tencent.matrix.lifecycle.MatrixLifecycleThread
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.*
+
+/**
+ * Created by Yves on 2022/1/17
+ */
+@RunWith(AndroidJUnit4::class)
+class ThreadTest {
+ companion object {
+ private const val TAG = "ThreadTest"
+ }
+
+ @Test
+ fun executorTest() {
+ Log.d(TAG, "executorTest: - 1")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 1 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 2")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 2 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 3")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 3 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 4")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 4 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 5 ${Thread.currentThread().name}")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 5 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 6")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 6 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 7")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 7 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "executorTest: - 8")
+ MatrixLifecycleThread.executor.execute {
+// SystemClock.sleep(1000)
+ Log.d(TAG, "executorTest: 8 ${Thread.currentThread().name}")
+ while (true) {}
+ }
+
+
+
+ SystemClock.sleep(15000)
+ }
+
+ @Test
+ fun forkJoinPoolTest() {
+ val pool = ForkJoinPool(
+ Runtime.getRuntime().availableProcessors(),
+ { pool ->
+ val thread = object : ForkJoinWorkerThread(pool) {}
+ thread.name = "matrix_di_${(pool?.poolSize ?: 0) + 1}"
+ Log.d(TAG, "forkJoinPoolTest: create ${thread.name}")
+ thread
+ },
+ null,
+ true
+ )
+
+ for (i in 0..10) {
+ pool.execute {
+ SystemClock.sleep(10)
+ Log.d(TAG, "forkJoinPoolTest: task done")
+ }
+ }
+
+ SystemClock.sleep(2)
+
+ Log.d(TAG, "forkJoinPoolTest: ${pool.poolSize}")
+
+ SystemClock.sleep(1000)
+
+ Log.d(TAG, "forkJoinPoolTest: ${pool.poolSize}")
+ }
+
+ @Test
+ fun executorTest2() {
+ val c = MatrixLifecycleThread.executor
+
+ c.execute {
+ Log.d(TAG, "cachedThreadPoolTest: costly task")
+ SystemClock.sleep(1000 * 100L)
+ Log.d(TAG, "cachedThreadPoolTest: costly task done")
+ }
+
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 2, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 3, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 4, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 5, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 6, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 7, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 8, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 9, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 10, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(510); Log.d(TAG, "cachedThreadPoolTest: task 11, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(20000); Log.d(TAG, "cachedThreadPoolTest: task 12, size = ${c.poolSize}, $this") } })
+
+ Log.d(TAG, "cachedThreadPoolTest: size 1 = ${c.poolSize}")
+
+// SystemClock.sleep(35 * 1000L)
+
+ Log.d(TAG, "cachedThreadPoolTest: size 2 = ${c.poolSize}")
+
+ SystemClock.sleep(15 * 1000L)
+
+ Log.d(TAG, "cachedThreadPoolTest: size 3 = ${c.poolSize}")
+
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 13, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 14, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 15, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 16, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 17, size = ${c.poolSize}, $this") } })
+ c.execute (object : Runnable { override fun run() { SystemClock.sleep(10); Log.d(TAG, "cachedThreadPoolTest: task 18, size = ${c.poolSize}, $this") } })
+
+ SystemClock.sleep(60 * 1000L)
+ }
+
+ @Test
+ fun executorTest3() {
+ val c = MatrixLifecycleThread.executor
+ for (i in 0..10) {
+
+ SystemClock.sleep(1000L)
+
+ c.execute {
+ SystemClock.sleep(1 * 1000L)
+ }
+ }
+
+ SystemClock.sleep(30 * 1000L)
+ Log.d(TAG, "=======")
+
+ for (i in 0..10) {
+ c.execute {
+ SystemClock.sleep(10);
+ }
+ }
+
+ SystemClock.sleep(1000 * 1000L)
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/AndroidManifest.xml b/matrix/matrix-android/matrix-android-lib/src/main/AndroidManifest.xml
index c8c224592..98508a696 100644
--- a/matrix/matrix-android/matrix-android-lib/src/main/AndroidManifest.xml
+++ b/matrix/matrix-android/matrix-android-lib/src/main/AndroidManifest.xml
@@ -2,7 +2,20 @@
xmlns:android="http://schemas.android.com/apk/res/android"
>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISubordinateProxy.aidl b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISubordinateProxy.aidl
new file mode 100644
index 000000000..74e3fbd5b
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISubordinateProxy.aidl
@@ -0,0 +1,13 @@
+// ISubordinateProxy.aidl
+package com.tencent.matrix.lifecycle.supervisor;
+
+// Declare any non-default types here with import statements
+import com.tencent.matrix.util.MemInfo;
+
+interface ISubordinateProxy {
+ void dispatchState(in String scene, in String stateName, in boolean state);
+ void dispatchKill(in String scene, in String targetProcess, in int targetPid);
+ void dispatchDeath(in String scene, in String targetProcess, in int targetPid, in boolean isLruKill);
+
+ MemInfo getMemInfo();
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISupervisorProxy.aidl b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISupervisorProxy.aidl
new file mode 100644
index 000000000..42120bda7
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ISupervisorProxy.aidl
@@ -0,0 +1,21 @@
+// ISupervisorProxy.aidl
+package com.tencent.matrix.lifecycle.supervisor;
+
+// Declare any non-default types here with import statements
+
+import com.tencent.matrix.lifecycle.supervisor.ProcessToken;
+import com.tencent.matrix.lifecycle.supervisor.ISubordinateProxy;
+
+interface ISupervisorProxy {
+ void registerSubordinate(in ProcessToken[] tokens, in ISubordinateProxy subordinateProxy);
+
+ void onStateChanged(in ProcessToken token);
+
+ void onSceneChanged(in String scene);
+
+ void onProcessKilled(in ProcessToken token);
+ void onProcessRescuedFromKill(in ProcessToken token);
+ void onProcessKillCanceled(in ProcessToken token);
+
+ String getRecentScene();
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ProcessToken.aidl b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ProcessToken.aidl
new file mode 100644
index 000000000..5e5190ff2
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/lifecycle/supervisor/ProcessToken.aidl
@@ -0,0 +1,6 @@
+// ProcessToken.aidl
+package com.tencent.matrix.lifecycle.supervisor;
+
+// Declare any non-default types here with import statements
+
+parcelable ProcessToken;
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/util/MemInfo.aidl b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/util/MemInfo.aidl
new file mode 100644
index 000000000..653e6eb45
--- /dev/null
+++ b/matrix/matrix-android/matrix-android-lib/src/main/aidl/com/tencent/matrix/util/MemInfo.aidl
@@ -0,0 +1,6 @@
+// MemInfo.aidl
+package com.tencent.matrix.util;
+
+// Declare any non-default types here with import statements
+
+parcelable MemInfo;
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/AppActiveMatrixDelegate.java b/matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/AppActiveMatrixDelegate.java
index 2527a7b44..be9435aa2 100644
--- a/matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/AppActiveMatrixDelegate.java
+++ b/matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/AppActiveMatrixDelegate.java
@@ -2,53 +2,26 @@
import android.app.Activity;
import android.app.Application;
-import android.content.ComponentCallbacks2;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.util.ArrayMap;
import com.tencent.matrix.listeners.IAppForeground;
-import com.tencent.matrix.util.MatrixHandlerThread;
-import com.tencent.matrix.util.MatrixLog;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
+import com.tencent.matrix.lifecycle.owners.ProcessUILifecycleOwner;
+import com.tencent.matrix.util.MatrixUtil;
+/**
+ * use {@link ProcessUILifecycleOwner} instead
+ */
+@Deprecated
public enum AppActiveMatrixDelegate {
INSTANCE;
private static final String TAG = "Matrix.AppActiveDelegate";
- private final Set listeners = new HashSet();
- private boolean isAppForeground = false;
- private String visibleScene = "default";
- private Controller controller = new Controller();
- private boolean isInit = false;
- private String currentFragmentName;
- private Handler handler;
public void init(Application application) {
- if (isInit) {
- MatrixLog.e(TAG, "has inited!");
- return;
- }
- this.isInit = true;
- if (null != MatrixHandlerThread.getDefaultHandlerThread()) {
- this.handler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper());
- }
- application.registerComponentCallbacks(controller);
- application.registerActivityLifecycleCallbacks(controller);
}
public String getCurrentFragmentName() {
- return currentFragmentName;
+ return ProcessUILifecycleOwner.INSTANCE.getCurrentFragmentName();
}
/**
@@ -57,180 +30,38 @@ public String getCurrentFragmentName() {
* @param fragmentName
*/
public void setCurrentFragmentName(String fragmentName) {
- MatrixLog.i(TAG, "[setCurrentFragmentName] fragmentName:%s", fragmentName);
- this.currentFragmentName = fragmentName;
- updateScene(fragmentName);
+ ProcessUILifecycleOwner.INSTANCE.setCurrentFragmentName(fragmentName);
}
public String getVisibleScene() {
- return visibleScene;
- }
-
- private void onDispatchForeground(String visibleScene) {
- if (isAppForeground || !isInit) {
- return;
- }
-
- MatrixLog.i(TAG, "onForeground... visibleScene[%s]", visibleScene);
- handler.post(new Runnable() {
- @Override
- public void run() {
- isAppForeground = true;
- synchronized (listeners) {
- for (IAppForeground listener : listeners) {
- listener.onForeground(true);
- }
- }
- }
- });
-
- }
-
- private void onDispatchBackground(String visibleScene) {
- if (!isAppForeground || !isInit) {
- return;
- }
-
- MatrixLog.i(TAG, "onBackground... visibleScene[%s]", visibleScene);
-
- handler.post(new Runnable() {
- @Override
- public void run() {
- isAppForeground = false;
- synchronized (listeners) {
- for (IAppForeground listener : listeners) {
- listener.onForeground(false);
- }
- }
- }
- });
-
-
+ return ProcessUILifecycleOwner.INSTANCE.getVisibleScene();
}
+ @Deprecated
public boolean isAppForeground() {
- return isAppForeground;
+ return ProcessUILifecycleOwner.INSTANCE.isProcessForeground();
}
+ /**
+ * use {@link ProcessUILifecycleOwner} instead:
+ * @param listener
+ */
+ @Deprecated
public void addListener(IAppForeground listener) {
- synchronized (listeners) {
- listeners.add(listener);
- }
+ ProcessUILifecycleOwner.INSTANCE.addListener(listener);
}
+ /**
+ * use {@link ProcessUILifecycleOwner} instead:
+ * @param listener
+ */
+ @Deprecated
public void removeListener(IAppForeground listener) {
- synchronized (listeners) {
- listeners.remove(listener);
- }
- }
-
-
- private final class Controller implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
-
- @Override
- public void onActivityStarted(Activity activity) {
- updateScene(activity);
- onDispatchForeground(getVisibleScene());
- }
-
-
- @Override
- public void onActivityStopped(Activity activity) {
- if (getTopActivityName() == null) {
- onDispatchBackground(getVisibleScene());
- }
- }
-
-
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
-
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
-
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
-
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
-
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
-
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
-
- }
-
- @Override
- public void onLowMemory() {
-
- }
-
- @Override
- public void onTrimMemory(int level) {
- MatrixLog.i(TAG, "[onTrimMemory] level:%s", level);
- if (level == TRIM_MEMORY_UI_HIDDEN && isAppForeground) { // fallback
- onDispatchBackground(visibleScene);
- }
- }
- }
-
- private void updateScene(Activity activity) {
- visibleScene = activity.getClass().getName();
- }
-
- private void updateScene(String currentFragmentName) {
- StringBuilder ss = new StringBuilder();
- ss.append(TextUtils.isEmpty(currentFragmentName) ? "?" : currentFragmentName);
- visibleScene = ss.toString();
+ ProcessUILifecycleOwner.INSTANCE.removeListener(listener);
}
+ @Deprecated
public static String getTopActivityName() {
- long start = System.currentTimeMillis();
- try {
- Class activityThreadClass = Class.forName("android.app.ActivityThread");
- Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
- Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
- activitiesField.setAccessible(true);
-
- Map