├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── weishu
│ │ └── leoric
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── me
│ │ │ └── weishu
│ │ │ └── leoric
│ │ │ └── demo
│ │ │ ├── Activity1.java
│ │ │ ├── Activity2.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MainApplication.java
│ │ │ ├── Receiver1.java
│ │ │ ├── Receiver2.java
│ │ │ ├── Service1.java
│ │ │ └── Service2.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── me
│ └── weishu
│ └── leoric
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── jni
│ ├── Android.mk
│ ├── Application.mk
│ └── leoric.c
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── me
│ │ └── weishu
│ │ └── library
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── me
│ │ │ └── weishu
│ │ │ └── leoric
│ │ │ ├── ILeoricProcess.java
│ │ │ ├── Leoric.java
│ │ │ ├── LeoricConfigs.java
│ │ │ ├── LeoricProcessImpl.java
│ │ │ └── NativeLeoric.java
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── me
│ └── weishu
│ └── library
│ └── ExampleUnitTest.java
├── longlive.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Leoric
2 |
3 | Leoric 是一个 Android 上的黑科技保活方法的 PoC,它可以对抗在任意 Android 版本上的 force-stop 杀进程。
4 |
5 | > Leoric 是 Dota 中的英雄"骷髅王",本项目名取自骷髅王的大招「重生」。
6 |
7 | 
8 |
9 | ## 编译环境
10 | JDK 11 + NDK 19.2.5345600
11 |
12 |
13 | ## 重现方法
14 |
15 | 虽然理论上这个方法可以支持任意的 Android 版本,但本 PoC 在 Android 9.0 及 Android 10 上测试过。具体使用方式参见 demo 项目。
16 |
17 | ## 实现原理
18 |
19 | - [Android 黑科技保活实现原理揭秘](http://weishu.me/2020/01/16/a-keep-alive-method-on-android/)
20 | - [深度剖析App保活案例](http://gityuan.com/2018/02/24/process-keep-forever/)
21 |
22 | ## 应对方法
23 |
24 | 下面是一种简单的方法杀死 Leoric:
25 |
26 | ```
27 | ps -A | grep `ps -A | grep me.weishu.leoric | awk '{print $1}' | head -1` | awk '{print $2}' | xargs kill -19 && am force-stop me.weishu.leoric
28 | ```
29 |
30 | 对于系统有两种思路可以选择:
31 |
32 | 1. 加入在 force-stop 期间不允许启动新的进程的逻辑
33 | 2. 修改 force-stop 的杀进程逻辑为:预先收集好所有进程再进行 kill(如有必要还可以先发送 SIGSTOP)
34 |
35 | 在 Android 14 中,系统加入了[在强行停止前冻结应用全部 cgroup 的逻辑](https://android.googlesource.com/platform/frameworks/base/+/79825c6f2f8b46808e4b431fe52b3be78f1e8ac8),本方案失效。
36 |
37 | ## Contact me
38 | Email: twsxtd@gmail.com
39 |
40 | ## 郑重声明
41 |
42 | 本项目仅做学习研究使用,出现任何问题概不负责。另外,本 PoC 离实际投入使用还有很长的路要走,请不要妄想直接接入代码就能得到永生。
43 |
44 | 之所以公开代码,是为了推动厂商对系统做出改进,提供应对之法;请不要滥用!!
45 |
46 | ## License
47 |
48 | Copyright (C) 2015, Mars Kwok
49 |
50 | Copyright (C) 2020, weishu
51 |
52 | Licensed under the Apache License, Version 2.0 (the "License");
53 | you may not use this file except in compliance with the License.
54 | You may obtain a copy of the License at
55 |
56 | http://www.apache.org/licenses/LICENSE-2.0
57 |
58 | Unless required by applicable law or agreed to in writing, software
59 | distributed under the License is distributed on an "AS IS" BASIS,
60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
61 | See the License for the specific language governing permissions and
62 | limitations under the License.
63 |
64 | ## Credits
65 |
66 | [MarsDaemon](https://github.com/Marswin/MarsDaemon)
67 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 31
5 | buildToolsVersion "31.0.0"
6 | defaultConfig {
7 | applicationId "me.weishu.leoric"
8 | minSdkVersion 21
9 | targetSdkVersion 31
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation project(":library")
25 | implementation 'androidx.core:core:1.7.0'
26 | testImplementation 'junit:junit:4.12'
27 | androidTestImplementation 'androidx.test:runner:1.2.0'
28 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
29 | }
30 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/me/weishu/leoric/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package me.weishu.leoric;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("me.weishu.leoric", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
32 |
36 |
37 |
41 |
45 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Activity1.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Activity;
21 |
22 | /**
23 | * @author weishu
24 | * @date 2020-01-16.
25 | */
26 | public class Activity1 extends Activity {
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Activity2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Activity;
21 |
22 | /**
23 | * @author weishu
24 | * @date 2020-01-16.
25 | */
26 | public class Activity2 extends Activity {
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Activity;
21 | import android.content.Intent;
22 | import android.os.Bundle;
23 |
24 | public class MainActivity extends Activity {
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_main);
30 |
31 | startService(new Intent(MainActivity.this, Service1.class));
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/MainApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Application;
21 | import android.content.Context;
22 |
23 | import me.weishu.leoric.Leoric;
24 | import me.weishu.leoric.LeoricConfigs;
25 |
26 | public class MainApplication extends Application {
27 |
28 | @Override
29 | protected void attachBaseContext(Context base) {
30 | super.attachBaseContext(base);
31 | Leoric.init(base, new LeoricConfigs(
32 | new LeoricConfigs.LeoricConfig(
33 | getPackageName() + ":resident",
34 | Service1.class.getCanonicalName(),
35 | Receiver1.class.getCanonicalName(),
36 | Activity1.class.getCanonicalName()),
37 | new LeoricConfigs.LeoricConfig(
38 | "android.media",
39 | Service2.class.getCanonicalName(),
40 | Receiver2.class.getCanonicalName(),
41 | Activity2.class.getCanonicalName())
42 | ));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Receiver1.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.content.BroadcastReceiver;
21 | import android.content.Context;
22 | import android.content.Intent;
23 |
24 | public class Receiver1 extends BroadcastReceiver {
25 | @Override
26 | public void onReceive(Context context, Intent intent) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Receiver2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.content.BroadcastReceiver;
21 | import android.content.Context;
22 | import android.content.Intent;
23 |
24 | public class Receiver2 extends BroadcastReceiver {
25 | @Override
26 | public void onReceive(Context context, Intent intent) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Service1.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Notification;
21 | import android.app.NotificationChannel;
22 | import android.app.NotificationManager;
23 | import android.app.PendingIntent;
24 | import android.app.Service;
25 | import android.content.Intent;
26 | import android.os.Build;
27 | import android.os.IBinder;
28 |
29 | import androidx.core.app.NotificationCompat;
30 |
31 |
32 | public class Service1 extends Service {
33 | private final static int NOTIFY_ID = 101;
34 | Notification notification;
35 | NotificationManager manager;
36 |
37 | @Override
38 | public void onCreate() {
39 | super.onCreate();
40 | }
41 |
42 | @Override
43 | public IBinder onBind(Intent intent) {
44 | return null;
45 | }
46 |
47 | @Override
48 | public int onStartCommand(Intent intent, int flags, int startId) {
49 | showNotification();
50 | startForeground(NOTIFY_ID, notification);
51 | return super.onStartCommand(intent, flags, startId);
52 | }
53 |
54 | private void showNotification() {
55 | manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
56 | Intent hangIntent = new Intent(this, MainActivity.class);
57 |
58 | PendingIntent hangPendingIntent = PendingIntent.getActivity(this, 1002, hangIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
59 |
60 | String CHANNEL_ID = "your_custom_id";//应用频道Id唯一值, 长度若太长可能会被截断,
61 | String CHANNEL_NAME = "your_custom_name";//最长40个字符,太长会被截断
62 | notification = new NotificationCompat.Builder(this, CHANNEL_ID)
63 | .setContentTitle("通知Title")
64 | .setContentText("通知Content")
65 | .setSmallIcon(R.mipmap.ic_launcher)
66 | .setContentIntent(hangPendingIntent)
67 | .setAutoCancel(true)
68 | .build();
69 |
70 | //Android 8.0 以上需包添加渠道
71 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
72 | NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,
73 | CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
74 | manager.createNotificationChannel(notificationChannel);
75 | }
76 |
77 | manager.notify(NOTIFY_ID, notification);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/leoric/demo/Service2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric.demo;
19 |
20 | import android.app.Notification;
21 | import android.app.NotificationChannel;
22 | import android.app.NotificationManager;
23 | import android.app.PendingIntent;
24 | import android.app.Service;
25 | import android.content.Intent;
26 | import android.os.Build;
27 | import android.os.IBinder;
28 |
29 | import androidx.core.app.NotificationCompat;
30 |
31 | public class Service2 extends Service {
32 | private final static int NOTIFY_ID = 100;
33 | Notification notification;
34 | NotificationManager manager;
35 |
36 | @Override
37 | public IBinder onBind(Intent intent) {
38 | return null;
39 | }
40 |
41 | @Override
42 | public int onStartCommand(Intent intent, int flags, int startId) {
43 | showNotification();
44 | startForeground(NOTIFY_ID, notification);
45 | return Service.START_NOT_STICKY;
46 | }
47 |
48 | private void showNotification() {
49 | manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
50 | Intent hangIntent = new Intent(this, MainActivity.class);
51 |
52 | PendingIntent hangPendingIntent = PendingIntent.getActivity(this, 1001, hangIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
53 |
54 | String CHANNEL_ID = "your_custom_id";//应用频道Id唯一值, 长度若太长可能会被截断,
55 | String CHANNEL_NAME = "your_custom_name";//最长40个字符,太长会被截断
56 | notification = new NotificationCompat.Builder(this, CHANNEL_ID)
57 | .setContentTitle("通知Title")
58 | .setContentText("通知Content")
59 | .setSmallIcon(R.mipmap.ic_launcher)
60 | .setContentIntent(hangPendingIntent)
61 | .setAutoCancel(true)
62 | .build();
63 |
64 | //Android 8.0 以上需包添加渠道
65 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
66 | NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,
67 | CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
68 | manager.createNotificationChannel(notificationChannel);
69 | }
70 |
71 | manager.notify(NOTIFY_ID, notification);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Leoric
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/test/java/me/weishu/leoric/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.weishu.leoric;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.0'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | maven { url 'https://jitpack.io' }
22 |
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jan 16 12:31:39 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 31
5 | buildToolsVersion "31.0.0"
6 |
7 |
8 | defaultConfig {
9 | minSdkVersion 21
10 | targetSdkVersion 31
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles 'consumer-rules.pro'
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | externalNativeBuild {
26 | ndkBuild {
27 | path 'jni/Android.mk'
28 | }
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | implementation 'com.github.tiann:FreeReflection:3.2.0'
36 |
37 | testImplementation 'junit:junit:4.12'
38 | androidTestImplementation 'androidx.test:runner:1.2.0'
39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
40 | }
41 |
--------------------------------------------------------------------------------
/library/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/library/consumer-rules.pro
--------------------------------------------------------------------------------
/library/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 | LOCAL_MODULE := leoric
5 | LOCAL_SRC_FILES := leoric.c
6 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
7 | LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
8 | include $(BUILD_SHARED_LIBRARY)
9 |
10 |
11 |
--------------------------------------------------------------------------------
/library/jni/Application.mk:
--------------------------------------------------------------------------------
1 | APP_ABI := armeabi armeabi-v7a x86
2 | APP_PLATFORM := android-21
--------------------------------------------------------------------------------
/library/jni/leoric.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include
30 | #include
31 |
32 | #define TAG "Leoric"
33 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
34 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
35 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
36 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
37 |
38 | #define DAEMON_CALLBACK_NAME "onDaemonDead"
39 |
40 | void waitfor_self_observer(char *observer_file_path) {
41 | int lockFileDescriptor = open(observer_file_path, O_RDONLY);
42 | if (lockFileDescriptor == -1) {
43 | LOGE("Watched >>>>OBSERVER<<<< has been ready before watching...");
44 | return;
45 | }
46 |
47 | void *p_buf = malloc(sizeof(struct inotify_event));
48 | if (p_buf == NULL) {
49 | LOGE("malloc failed !!!");
50 | return;
51 | }
52 | int maskStrLength = 7 + 10 + 1;
53 | char *p_maskStr = malloc(maskStrLength);
54 | if (p_maskStr == NULL) {
55 | free(p_buf);
56 | LOGE("malloc failed !!!");
57 | return;
58 | }
59 | int fileDescriptor = inotify_init();
60 | if (fileDescriptor < 0) {
61 | free(p_buf);
62 | free(p_maskStr);
63 | LOGE("inotify_init failed !!!");
64 | return;
65 | }
66 |
67 | int watchDescriptor = inotify_add_watch(fileDescriptor, observer_file_path, IN_ALL_EVENTS);
68 | if (watchDescriptor < 0) {
69 | free(p_buf);
70 | free(p_maskStr);
71 | LOGE("inotify_add_watch failed !!!");
72 | return;
73 | }
74 |
75 |
76 | while (1) {
77 | size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
78 | if (4 == ((struct inotify_event *) p_buf)->mask) {
79 | LOGE("Watched >>>>OBSERVER<<<< has been ready...");
80 | free(p_maskStr);
81 | free(p_buf);
82 | return;
83 | }
84 | }
85 | }
86 |
87 | void notify_daemon_observer(unsigned char is_persistent, char *observer_file_path) {
88 | if (!is_persistent) {
89 | int lockFileDescriptor = open(observer_file_path, O_RDONLY);
90 | while (lockFileDescriptor == -1) {
91 | lockFileDescriptor = open(observer_file_path, O_RDONLY);
92 | }
93 | }
94 | remove(observer_file_path);
95 | }
96 |
97 | void notify_and_waitfor(char *observer_self_path, char *observer_daemon_path) {
98 | int observer_self_descriptor = open(observer_self_path, O_RDONLY);
99 | if (observer_self_descriptor == -1) {
100 | observer_self_descriptor = open(observer_self_path, O_CREAT, S_IRUSR | S_IWUSR);
101 | }
102 | int observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY);
103 | while (observer_daemon_descriptor == -1) {
104 | usleep(1000);
105 | observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY);
106 | }
107 | remove(observer_daemon_path);
108 | LOGE("Watched >>>>OBSERVER<<<< has been ready...");
109 | }
110 |
111 |
112 | int lock_file(char *lock_file_path) {
113 | LOGD("start try to lock file >> %s <<", lock_file_path);
114 | int lockFileDescriptor = open(lock_file_path, O_RDONLY);
115 | if (lockFileDescriptor == -1) {
116 | lockFileDescriptor = open(lock_file_path, O_CREAT, S_IRUSR);
117 | }
118 | int lockRet = flock(lockFileDescriptor, LOCK_EX);
119 | if (lockRet == -1) {
120 | LOGE("lock file failed >> %s <<", lock_file_path);
121 | return 0;
122 | } else {
123 | LOGD("lock file success >> %s <<", lock_file_path);
124 | return 1;
125 | }
126 | }
127 |
128 | void java_callback(JNIEnv *env, jobject jobj, char *method_name) {
129 | jclass cls = (*env)->GetObjectClass(env, jobj);
130 | jmethodID cb_method = (*env)->GetMethodID(env, cls, method_name, "()V");
131 | (*env)->CallVoidMethod(env, jobj, cb_method);
132 | }
133 |
134 | void do_daemon(JNIEnv *env, jobject jobj, char *indicator_self_path, char *indicator_daemon_path,
135 | char *observer_self_path, char *observer_daemon_path) {
136 | int lock_status = 0;
137 | int try_time = 0;
138 | while (try_time < 3 && !(lock_status = lock_file(indicator_self_path))) {
139 | try_time++;
140 | LOGD("Persistent lock myself failed and try again as %d times", try_time);
141 | usleep(10000);
142 | }
143 | if (!lock_status) {
144 | LOGE("Persistent lock myself failed and exit");
145 | return;
146 | }
147 |
148 | notify_and_waitfor(observer_self_path, observer_daemon_path);
149 |
150 | lock_status = lock_file(indicator_daemon_path);
151 | if (lock_status) {
152 | LOGE("Watch >>>>DAEMON<<<<< Daed !!");
153 | remove(observer_self_path);// it`s important ! to prevent from deadlock
154 | java_callback(env, jobj, DAEMON_CALLBACK_NAME);
155 | }
156 | }
157 |
158 | void create_file_if_not_exist(char *path) {
159 | FILE *fp = fopen(path, "ab+");
160 | if (fp) {
161 | fclose(fp);
162 | }
163 | }
164 |
165 | void set_process_name(JNIEnv *env) {
166 | jclass process = (*env)->FindClass(env, "android/os/Process");
167 | jmethodID setArgV0 = (*env)->GetStaticMethodID(env, process, "setArgV0",
168 | "(Ljava/lang/String;)V");
169 | jstring name = (*env)->NewStringUTF(env, "app_d");
170 | (*env)->CallStaticVoidMethod(env, process, setArgV0, name);
171 | }
172 |
173 | JNIEXPORT void JNICALL
174 | Java_me_weishu_leoric_NativeLeoric_doDaemon(JNIEnv *env, jobject jobj,
175 | jstring indicatorSelfPath,
176 | jstring indicatorDaemonPath,
177 | jstring observerSelfPath,
178 | jstring observerDaemonPath) {
179 | if (indicatorSelfPath == NULL || indicatorDaemonPath == NULL || observerSelfPath == NULL ||
180 | observerDaemonPath == NULL) {
181 | LOGE("parameters cannot be NULL !");
182 | return;
183 | }
184 |
185 | char *indicator_self_path = (char *) (*env)->GetStringUTFChars(env, indicatorSelfPath, 0);
186 | char *indicator_daemon_path = (char *) (*env)->GetStringUTFChars(env, indicatorDaemonPath, 0);
187 | char *observer_self_path = (char *) (*env)->GetStringUTFChars(env, observerSelfPath, 0);
188 | char *observer_daemon_path = (char *) (*env)->GetStringUTFChars(env, observerDaemonPath, 0);
189 |
190 |
191 | pid_t pid;
192 | if ((pid = fork()) < 0) {
193 | printf("fork 1 error\n");
194 | exit(-1);
195 | } else if (pid == 0) { //第一个子进程
196 | if ((pid = fork()) < 0) {
197 | printf("fork 2 error\n");
198 | exit(-1);
199 | } else if (pid > 0) {
200 | // 托孤
201 | exit(0);
202 | }
203 |
204 | LOGD("mypid: %d", getpid());
205 | const int MAX_PATH = 256;
206 | char indicator_self_path_child[MAX_PATH];
207 | char indicator_daemon_path_child[MAX_PATH];
208 | char observer_self_path_child[MAX_PATH];
209 | char observer_daemon_path_child[MAX_PATH];
210 |
211 | strcpy(indicator_self_path_child, indicator_self_path);
212 | strcat(indicator_self_path_child, "-c");
213 |
214 | strcpy(indicator_daemon_path_child, indicator_daemon_path);
215 | strcat(indicator_daemon_path_child, "-c");
216 |
217 | strcpy(observer_self_path_child, observer_self_path);
218 | strcat(observer_self_path_child, "-c");
219 |
220 | strcpy(observer_daemon_path_child, observer_daemon_path);
221 | strcat(observer_daemon_path_child, "-c");
222 |
223 | create_file_if_not_exist(indicator_self_path_child);
224 | create_file_if_not_exist(indicator_daemon_path_child);
225 |
226 | set_process_name(env);
227 |
228 | do_daemon(env, jobj, indicator_self_path_child, indicator_daemon_path_child,
229 | observer_self_path_child, observer_daemon_path_child);
230 | return;
231 | }
232 |
233 | if (waitpid(pid, NULL, 0) != pid)
234 | printf("waitpid error\n");
235 |
236 |
237 | do_daemon(env, jobj, indicator_self_path, indicator_daemon_path, observer_self_path,
238 | observer_daemon_path);
239 | }
240 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/library/src/androidTest/java/me/weishu/library/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package me.weishu.library;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("me.weishu.library.test", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/leoric/ILeoricProcess.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package me.weishu.leoric;
18 |
19 | import android.content.Context;
20 | import android.os.Build;
21 |
22 | public interface ILeoricProcess {
23 | /**
24 | * Initialization some files or other when 1st time
25 | */
26 | boolean onInit(Context context);
27 |
28 | /**
29 | * when Persistent processName create
30 | *
31 | */
32 | void onPersistentCreate(Context context, LeoricConfigs configs);
33 |
34 | /**
35 | * when DaemonAssistant processName create
36 | */
37 | void onDaemonAssistantCreate(Context context, LeoricConfigs configs);
38 |
39 | /**
40 | * when watches the processName dead which it watched
41 | */
42 | void onDaemonDead();
43 |
44 |
45 | class Fetcher {
46 |
47 | private static volatile ILeoricProcess mDaemonStrategy;
48 |
49 | /**
50 | * fetch the strategy for this device
51 | *
52 | * @return the daemon strategy for this device
53 | */
54 | static ILeoricProcess fetchStrategy() {
55 | if (mDaemonStrategy != null) {
56 | return mDaemonStrategy;
57 | }
58 | int sdk = Build.VERSION.SDK_INT;
59 | mDaemonStrategy = new LeoricProcessImpl();
60 | return mDaemonStrategy;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/leoric/Leoric.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric;
19 |
20 | import android.content.Context;
21 | import android.content.SharedPreferences;
22 | import android.content.SharedPreferences.Editor;
23 |
24 | import java.io.BufferedReader;
25 | import java.io.File;
26 | import java.io.FileReader;
27 | import java.io.IOException;
28 |
29 | import me.weishu.reflection.Reflection;
30 |
31 | public class Leoric {
32 |
33 | private static final String TAG = "Leoric";
34 |
35 | private LeoricConfigs mConfigurations;
36 |
37 | private Leoric(LeoricConfigs configurations) {
38 | this.mConfigurations = configurations;
39 | }
40 |
41 | public static void init(Context base, LeoricConfigs configurations) {
42 | Reflection.unseal(base);
43 | Leoric client = new Leoric(configurations);
44 | client.initDaemon(base);
45 | }
46 |
47 |
48 | private final String DAEMON_PERMITTING_SP_FILENAME = "d_permit";
49 | private final String DAEMON_PERMITTING_SP_KEY = "permitted";
50 |
51 |
52 | private BufferedReader mBufferedReader;
53 |
54 | private void initDaemon(Context base) {
55 | if (!isDaemonPermitting(base) || mConfigurations == null) {
56 | return;
57 | }
58 |
59 | String processName = getProcessName();
60 | String packageName = base.getPackageName();
61 |
62 | if (processName.startsWith(mConfigurations.PERSISTENT_CONFIG.processName)) {
63 | ILeoricProcess.Fetcher.fetchStrategy().onPersistentCreate(base, mConfigurations);
64 | } else if (processName.startsWith(mConfigurations.DAEMON_ASSISTANT_CONFIG.processName)) {
65 | ILeoricProcess.Fetcher.fetchStrategy().onDaemonAssistantCreate(base, mConfigurations);
66 | } else if (processName.startsWith(packageName)) {
67 | ILeoricProcess.Fetcher.fetchStrategy().onInit(base);
68 | }
69 |
70 | releaseIO();
71 | }
72 |
73 |
74 | private String getProcessName() {
75 | try {
76 | File file = new File("/proc/self/cmdline");
77 | mBufferedReader = new BufferedReader(new FileReader(file));
78 | return mBufferedReader.readLine().trim();
79 | } catch (Exception e) {
80 | e.printStackTrace();
81 | return null;
82 | }
83 | }
84 |
85 | private void releaseIO() {
86 | if (mBufferedReader != null) {
87 | try {
88 | mBufferedReader.close();
89 | } catch (IOException e) {
90 | e.printStackTrace();
91 | }
92 | mBufferedReader = null;
93 | }
94 | }
95 |
96 | private boolean isDaemonPermitting(Context context) {
97 | SharedPreferences sp = context.getSharedPreferences(DAEMON_PERMITTING_SP_FILENAME, Context.MODE_PRIVATE);
98 | return sp.getBoolean(DAEMON_PERMITTING_SP_KEY, true);
99 | }
100 |
101 | protected boolean setDaemonPermiiting(Context context, boolean isPermitting) {
102 | SharedPreferences sp = context.getSharedPreferences(DAEMON_PERMITTING_SP_FILENAME, Context.MODE_PRIVATE);
103 | Editor editor = sp.edit();
104 | editor.putBoolean(DAEMON_PERMITTING_SP_KEY, isPermitting);
105 | return editor.commit();
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/leoric/LeoricConfigs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric;
19 |
20 | public class LeoricConfigs {
21 |
22 | public final LeoricConfig PERSISTENT_CONFIG;
23 | public final LeoricConfig DAEMON_ASSISTANT_CONFIG;
24 |
25 | public LeoricConfigs(LeoricConfig persistentConfig, LeoricConfig daemonAssistantConfig) {
26 | this.PERSISTENT_CONFIG = persistentConfig;
27 | this.DAEMON_ASSISTANT_CONFIG = daemonAssistantConfig;
28 | }
29 |
30 | public static class LeoricConfig {
31 |
32 | final String processName;
33 | final String serviceName;
34 | final String receiverName;
35 | final String activityName;
36 |
37 | public LeoricConfig(String processName, String serviceName, String receiverName, String activityName) {
38 | this.processName = processName;
39 | this.serviceName = serviceName;
40 | this.receiverName = receiverName;
41 | this.activityName = activityName;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/leoric/LeoricProcessImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric;
19 |
20 | import android.annotation.SuppressLint;
21 | import android.content.ComponentName;
22 | import android.content.Context;
23 | import android.content.Intent;
24 | import android.os.Build;
25 | import android.os.IBinder;
26 | import android.os.Parcel;
27 | import android.os.Process;
28 | import android.os.RemoteException;
29 | import android.util.Log;
30 |
31 | import java.io.File;
32 | import java.io.IOException;
33 | import java.lang.reflect.Field;
34 |
35 | public class LeoricProcessImpl implements ILeoricProcess {
36 |
37 | private static final String TAG = "LeoricProcessImpl";
38 |
39 | private final static String INDICATOR_DIR_NAME = "indicators";
40 | private final static String INDICATOR_PERSISTENT_FILENAME = "indicator_p";
41 | private final static String INDICATOR_DAEMON_ASSISTANT_FILENAME = "indicator_d";
42 | private final static String OBSERVER_PERSISTENT_FILENAME = "observer_p";
43 | private final static String OBSERVER_DAEMON_ASSISTANT_FILENAME = "observer_d";
44 |
45 | private IBinder mRemote;
46 | private Parcel mServiceData;
47 |
48 | private int mPid = Process.myPid();
49 |
50 | @Override
51 | public boolean onInit(Context context) {
52 | return initIndicatorFiles(context);
53 | }
54 |
55 | @Override
56 | public void onPersistentCreate(final Context context, LeoricConfigs configs) {
57 |
58 | initAmsBinder();
59 | initServiceParcel(context, configs.DAEMON_ASSISTANT_CONFIG.serviceName);
60 | startServiceByAmsBinder();
61 |
62 | Thread t = new Thread() {
63 | public void run() {
64 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
65 | new NativeLeoric().doDaemon(
66 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(),
67 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
68 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath(),
69 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath());
70 | }
71 | };
72 | t.start();
73 |
74 | }
75 |
76 | @Override
77 | public void onDaemonAssistantCreate(final Context context, LeoricConfigs configs) {
78 | initAmsBinder();
79 | initServiceParcel(context, configs.PERSISTENT_CONFIG.serviceName);
80 | startServiceByAmsBinder();
81 |
82 | Thread t = new Thread() {
83 | public void run() {
84 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
85 | new NativeLeoric().doDaemon(
86 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
87 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(),
88 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
89 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath());
90 | }
91 |
92 | ;
93 | };
94 | t.start();
95 |
96 | }
97 |
98 |
99 | @Override
100 | public void onDaemonDead() {
101 | Log.i(TAG, "on daemon dead!");
102 | if (startServiceByAmsBinder()) {
103 |
104 | int pid = Process.myPid();
105 | Log.i(TAG, "mPid: " + mPid + " current pid: " + pid);
106 | android.os.Process.killProcess(mPid);
107 | }
108 | }
109 |
110 |
111 | private void initAmsBinder() {
112 | Class> activityManagerNative;
113 | try {
114 | activityManagerNative = Class.forName("android.app.ActivityManagerNative");
115 | Object amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative);
116 | Field mRemoteField = amn.getClass().getDeclaredField("mRemote");
117 | mRemoteField.setAccessible(true);
118 | mRemote = (IBinder) mRemoteField.get(amn);
119 | } catch (Throwable e) {
120 | e.printStackTrace();
121 | }
122 | }
123 |
124 |
125 | @SuppressLint("Recycle")
126 | // when processName dead, we should save time to restart and kill self, don`t take a waste of time to recycle
127 | private void initServiceParcel(Context context, String serviceName) {
128 | Intent intent = new Intent();
129 | ComponentName component = new ComponentName(context.getPackageName(), serviceName);
130 | intent.setComponent(component);
131 |
132 | Parcel parcel = Parcel.obtain();
133 | intent.writeToParcel(parcel, 0);
134 |
135 | mServiceData = Parcel.obtain();
136 | if (Build.VERSION.SDK_INT >= 26) {
137 | // Android 8.1
138 | mServiceData.writeInterfaceToken("android.app.IActivityManager");
139 | mServiceData.writeStrongBinder(null);
140 | mServiceData.writeInt(1);
141 | intent.writeToParcel(mServiceData, 0);
142 | mServiceData.writeString(null);
143 | mServiceData.writeInt(context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O ? 1 : 0);
144 | mServiceData.writeString(context.getPackageName());
145 | mServiceData.writeInt(0);
146 | } else {
147 | // http://aospxref.com/android-7.1.2_r36/xref/frameworks/base/core/java/android/app/ActivityManagerNative.java
148 | mServiceData.writeInterfaceToken("android.app.IActivityManager");
149 | mServiceData.writeStrongBinder(null);
150 | intent.writeToParcel(mServiceData, 0);
151 | mServiceData.writeString(null);
152 | if (Build.VERSION.SDK_INT > 22) {//适配5.1(22)
153 | mServiceData.writeString(context.getPackageName());
154 | }
155 | mServiceData.writeInt(0);
156 | }
157 |
158 | }
159 |
160 |
161 | private boolean startServiceByAmsBinder() {
162 | try {
163 | if (mRemote == null || mServiceData == null) {
164 | Log.e("Daemon", "REMOTE IS NULL or PARCEL IS NULL !!!");
165 | return false;
166 | }
167 | int code;
168 | switch (Build.VERSION.SDK_INT) {
169 | case 26:
170 | case 27:
171 | code = 26;
172 | break;
173 | case 28:
174 | code = 30;
175 | break;
176 | case 29:
177 | code = 24;
178 | break;
179 | case 30:
180 | code = 26;
181 | break;
182 | case 31:
183 | code = 27;
184 | break;
185 | default:
186 | code = 34;
187 | break;
188 | }
189 | //mRemote.transact(34, mServiceData, null, 0);//START_SERVICE_TRANSACTION = 34
190 | // mRemote.transact(26, mServiceData, null, 1);//START_SERVICE_TRANSACTION = 34
191 | mRemote.transact(code, mServiceData, null, 1);//START_SERVICE_TRANSACTION = 34
192 | return true;
193 | } catch (RemoteException e) {
194 | e.printStackTrace();
195 | return false;
196 | }
197 | }
198 |
199 |
200 | private boolean initIndicatorFiles(Context context) {
201 | File dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
202 | if (!dirFile.exists()) {
203 | dirFile.mkdirs();
204 | }
205 | try {
206 | createNewFile(dirFile, INDICATOR_PERSISTENT_FILENAME);
207 | createNewFile(dirFile, INDICATOR_DAEMON_ASSISTANT_FILENAME);
208 | return true;
209 | } catch (IOException e) {
210 | e.printStackTrace();
211 | return false;
212 | }
213 | }
214 |
215 | private void createNewFile(File dirFile, String fileName) throws IOException {
216 | File file = new File(dirFile, fileName);
217 | if (!file.exists()) {
218 | file.createNewFile();
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/leoric/NativeLeoric.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original Copyright 2015 Mars Kwok
3 | * Modified work Copyright (c) 2020, weishu
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package me.weishu.leoric;
19 |
20 | /* package */class NativeLeoric {
21 |
22 | static {
23 | try {
24 | System.loadLibrary("leoric");
25 | } catch (Exception e) {
26 | e.printStackTrace();
27 | }
28 | }
29 |
30 | public native void doDaemon(String indicatorSelfPath, String indicatorDaemonPath, String observerSelfPath, String observerDaemonPath);
31 |
32 | public void onDaemonDead() {
33 | ILeoricProcess.Fetcher.fetchStrategy().onDaemonDead();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | library
3 |
4 |
--------------------------------------------------------------------------------
/library/src/test/java/me/weishu/library/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.weishu.library;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/longlive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/Leoric/1c6f1b749c30219e674b80cabf23b21dbd1acf25/longlive.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 | rootProject.name='Leoric'
3 |
--------------------------------------------------------------------------------