├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── modules
│ └── Daemon.iml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── daemonLibrary
│ │ └── demo
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── daemonLibrary
│ │ │ └── demo
│ │ │ ├── ActivityOne.kt
│ │ │ ├── ActivityTow.kt
│ │ │ ├── ForegroundNotifyUtils.java
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApp.kt
│ │ │ ├── OnRemoveActivity.kt
│ │ │ └── SystemBroadcastReceiver.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_homee.png
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── activity_on_remove.xml
│ │ ├── menu
│ │ └── menu_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
│ │ ├── navigation
│ │ └── nav_graph.xml
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── daemonLibrary
│ └── demo
│ └── ExampleUnitTest.kt
├── 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
│ └── daemon.c
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── daemon
│ │ │ ├── BaseService.java
│ │ │ ├── CoreService.java
│ │ │ ├── DInstrumentation.java
│ │ │ ├── Daemon.java
│ │ │ ├── DaemonJobService.java
│ │ │ ├── DaemonLog.java
│ │ │ ├── DaemonService.java
│ │ │ ├── IntentJobService.java
│ │ │ ├── onepixel
│ │ │ └── OnePixelActivity.java
│ │ │ ├── process
│ │ │ ├── Configs.java
│ │ │ ├── Daemon.java
│ │ │ ├── IProcess.java
│ │ │ ├── NativeLoader.java
│ │ │ └── ProcessImpl.java
│ │ │ ├── receiver
│ │ │ ├── DaemonStaticReceiver.java
│ │ │ └── ScreenReceiver.java
│ │ │ ├── utils
│ │ │ ├── ActivityUtils.java
│ │ │ ├── FileStorage.java
│ │ │ ├── IntentUtils.java
│ │ │ ├── ROMUtils.java
│ │ │ └── ServiceUtils.java
│ │ │ └── whitelist
│ │ │ ├── IWhiteListCallback.java
│ │ │ ├── IWhiteListProvider.java
│ │ │ ├── IntentType.java
│ │ │ ├── WhiteList.java
│ │ │ ├── WhiteListIntentWrapper.java
│ │ │ └── impl
│ │ │ ├── DefaultWhiteListCallback.java
│ │ │ └── DefaultWhiteListProvider.java
│ └── res
│ │ ├── drawable-anydpi-v24
│ │ └── ic_stat_name.xml
│ │ ├── drawable-hdpi
│ │ └── ic_stat_name.png
│ │ ├── drawable-mdpi
│ │ └── ic_stat_name.png
│ │ ├── drawable-xhdpi
│ │ └── ic_stat_name.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_stat_name.png
│ │ ├── raw
│ │ └── no_notice.mp3
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── daemon
│ └── library
│ └── ExampleUnitTest.java
├── readme.md
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle/
2 | local.properties
3 | .idea/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/modules/Daemon.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 30
8 | buildToolsVersion "30.0.2"
9 |
10 | defaultConfig {
11 | applicationId "com.daemonLibrary.demo"
12 | minSdkVersion 21
13 | targetSdkVersion 25
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | }
34 |
35 | dependencies {
36 |
37 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
38 | implementation 'androidx.core:core-ktx:1.3.2'
39 | implementation 'androidx.appcompat:appcompat:1.2.0'
40 | implementation 'com.google.android.material:material:1.2.1'
41 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
42 | implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
43 | implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
44 | implementation project(path: ':library')
45 | testImplementation 'junit:junit:4.+'
46 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
48 | implementation "org.jetbrains.anko:anko:$anko_version"
49 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/daemonLibrary/demo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.daemonLibrary.demo", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
42 |
43 |
44 |
45 |
46 |
48 |
49 |
51 |
52 |
53 |
54 |
55 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/ActivityOne.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 |
5 | class ActivityOne: AppCompatActivity() {
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/ActivityTow.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 |
5 | class ActivityTow: AppCompatActivity() {
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/ForegroundNotifyUtils.java:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo;
2 |
3 |
4 | import android.app.Notification;
5 | import android.app.NotificationChannel;
6 | import android.app.NotificationManager;
7 | import android.content.Context;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Color;
10 | import android.os.Build;
11 |
12 | public class ForegroundNotifyUtils {
13 | private static final String CHANNEL_ID = "BCD41FB3-2EE7-4781-8917-7B8D8DB355DC";
14 | public static Notification getForegroundNotification(Context context,String title,String text){
15 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
16 | NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
17 | NotificationChannel Channel = new NotificationChannel(CHANNEL_ID, "可关闭通知", NotificationManager.IMPORTANCE_DEFAULT);
18 | Channel.enableLights(true);
19 | Channel.setLightColor(Color.GREEN);
20 | Channel.setShowBadge(true);
21 | Channel.setDescription("");
22 | Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
23 | Channel.enableVibration(false);
24 | Channel.setSound(null, null);
25 | manager.createNotificationChannel(Channel);
26 |
27 | Notification notification = new Notification.Builder(context, CHANNEL_ID)
28 | .setContentTitle(title)
29 | .setContentText(text)
30 | .setWhen(System.currentTimeMillis())
31 | .setSmallIcon(R.drawable.ic_launcher_foreground)
32 | .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher_foreground))
33 | .build();
34 | return notification;
35 | } else {
36 | Notification notification = new Notification.Builder(context)
37 | .setContentTitle(title)//设置标题
38 | .setContentText(text)//设置内容
39 | .setWhen(System.currentTimeMillis())//设置创建时间
40 | .build();
41 | return notification;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.os.Build
6 | import android.os.Bundle
7 | import android.provider.Settings
8 | import androidx.annotation.RequiresApi
9 | import androidx.appcompat.app.AppCompatActivity
10 |
11 |
12 | class MainActivity : AppCompatActivity() {
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_main)
17 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
18 | if (!Settings.canDrawOverlays(this)) {
19 | val intent: Intent = Intent(
20 | Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
21 | Uri.parse("package:" + getPackageName())
22 | )
23 | startActivityForResult(intent, 10)
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/MainApp.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import android.annotation.TargetApi
4 | import android.app.Application
5 | import android.app.Notification
6 | import android.app.NotificationChannel
7 | import android.app.NotificationManager
8 | import android.content.Context
9 | import android.content.Intent
10 | import android.content.IntentFilter
11 | import android.graphics.BitmapFactory
12 | import android.os.Build
13 | import android.util.Log
14 | import androidx.core.app.NotificationCompat
15 | import androidx.core.app.NotificationManagerCompat
16 | import com.daemon.Daemon
17 |
18 | class DaemonConfigurationImplement(val context: Context): Daemon.DaemonConfiguration{
19 | @TargetApi(26)
20 | fun getIntentChannelId(context: Context): String {
21 | val from: NotificationManagerCompat = NotificationManagerCompat.from(context)
22 | val notificationChannel = NotificationChannel("com.daemon.lockscreen.brandnew", "com.daemon.lockscreen.brandnew", NotificationManager.IMPORTANCE_HIGH)
23 | notificationChannel.enableLights(false)
24 | notificationChannel.setShowBadge(false)
25 | notificationChannel.enableVibration(false)
26 | notificationChannel.setSound(null, null)
27 | from.createNotificationChannel(notificationChannel)
28 | return notificationChannel.getId()
29 | }
30 |
31 | override fun getIntentNotificationBuilder(context: Context): NotificationCompat.Builder? {
32 | val builder = if (Build.VERSION.SDK_INT >= 26) {
33 | NotificationCompat.Builder(
34 | context,
35 | getIntentChannelId(context)!!
36 | )
37 | } else {
38 | NotificationCompat.Builder(context)
39 | }
40 | builder.setContentTitle("手机优化中")
41 | builder.setContentText("正在优化您的手机")
42 | builder.setSmallIcon(R.drawable.ic_homee)
43 | builder.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_homee))
44 | builder.setAutoCancel(true)
45 | builder.setDefaults(4)
46 | builder.priority = -1
47 | return builder
48 | }
49 | override fun getForegroundNotification(): Notification? {
50 | return null;
51 | //return ForegroundNotifyUtils.getForegroundNotification(context, "", "Service is running")
52 | }
53 |
54 | override fun isMusicPlayEnabled(): Boolean {
55 | return true
56 | }
57 |
58 | override fun isOnePixelActivityEnabled(): Boolean {
59 | return true
60 | }
61 |
62 | }
63 |
64 | class DaemonCallbackImplement:Daemon.DaemonCallback{
65 | override fun onStart() {
66 | Log.d("DEOM", "onStart")
67 | }
68 |
69 | override fun onStop() {
70 | Log.d("DEOM", "onStop")
71 | }
72 | }
73 |
74 | class MainApp:Application() {
75 | override fun onCreate() {
76 | super.onCreate()
77 | if (Daemon.isMainProcess(this)){
78 | var flt = IntentFilter()
79 | flt.addAction (Intent.ACTION_PACKAGE_REMOVED)
80 | flt.addAction(Intent.ACTION_PACKAGE_ADDED)
81 | flt.addAction(Intent.ACTION_PACKAGE_CHANGED)
82 | flt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
83 | flt.addDataScheme("package")
84 | registerReceiver(SystemBroadcastReceiver(), flt)
85 | //DaemonLog.d("register Package notify")
86 | }
87 | }
88 | override fun attachBaseContext(base: Context?) {
89 | super.attachBaseContext(base)
90 | //DaemonLog.d("Application onCrearte")
91 | Daemon.startWork(this, DaemonConfigurationImplement(this), DaemonCallbackImplement())
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/OnRemoveActivity.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import android.app.ActivityManager
4 | import android.content.Context
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import com.daemon.utils.ActivityUtils
8 |
9 | class OnRemoveActivity : AppCompatActivity() {
10 |
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | setContentView(R.layout.activity_on_remove)
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/daemonLibrary/demo/SystemBroadcastReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.os.Build
7 | import com.daemon.Daemon
8 | class SystemBroadcastReceiver :BroadcastReceiver(){
9 | private fun startActivity(context: Context,intent: Intent){
10 | intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
11 | if (Build.VERSION.SDK_INT <21){
12 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
13 | context.startActivity(intent)
14 | return
15 | }
16 | intent.addCategory("android.intent.category.LAUNCHER")
17 | intent.action = "android.intent.action.MAIN"
18 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
19 | Daemon.startActivity(context,intent)
20 | }
21 | override fun onReceive(p0: Context?, p1: Intent?) {
22 | if (p1== null){
23 | return
24 | }
25 | when (p1.action){
26 | Intent.ACTION_PACKAGE_ADDED,Intent.ACTION_PACKAGE_REMOVED->{
27 | startActivity(Daemon.getApplication(), Intent(Daemon.getApplication(),OnRemoveActivity::class.java))
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_homee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/drawable/ic_homee.png
--------------------------------------------------------------------------------
/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 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_on_remove.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
23 |
24 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | My Application
3 | Settings
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 |
10 | Hello first fragment
11 | Hello second fragment. Arg: %1$s
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/test/java/com/daemonLibrary/demo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.daemonLibrary.demo
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun 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 | buildscript {
3 | ext.kotlin_version = '1.5.0-M1'
4 | ext.kotlin_version = "1.4.21"
5 | ext.anko_version='0.10.8'
6 | repositories {
7 | google()
8 | jcenter()
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.1.3'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | maven {url 'https://maven.aliyun.com/repository/public'}
22 | maven {url 'https://maven.aliyun.com/repository/google'}
23 | maven { url 'https://jitpack.io' }
24 | maven { url 'http://developer.huawei.com/repo/' }
25 | }
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
--------------------------------------------------------------------------------
/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=-Xmx2048m -Dfile.encoding=UTF-8
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 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Mar 21 15:11:17 CST 2021
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-6.5-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 | .cxx
3 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 29
6 | buildToolsVersion "29.0.2"
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 21
11 | targetSdkVersion 23
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles 'consumer-rules.pro'
17 | proguardFiles getDefaultProguardFile('proguard-android.txt')
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled true
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | externalNativeBuild {
28 | ndkBuild {
29 | path 'jni/Android.mk'
30 | }
31 | }
32 |
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
37 | implementation 'androidx.core:core:1.3.2'
38 |
39 | testImplementation 'junit:junit:4.12'
40 | androidTestImplementation 'androidx.test:runner:1.2.0'
41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
42 | implementation "androidx.core:core-ktx:+"
43 | implementation 'me.weishu:free_reflection:2.2.0'
44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
45 | implementation 'com.huawei.android.hms:push:2.6.3.306'
46 | implementation 'com.huawei.android.hms:base:2.6.3.306'
47 | implementation 'androidx.appcompat:appcompat:1.0.0'
48 | }
49 | repositories {
50 | mavenCentral()
51 | }
52 |
--------------------------------------------------------------------------------
/library/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/consumer-rules.pro
--------------------------------------------------------------------------------
/library/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 | LOCAL_MODULE := daemon
5 | LOCAL_SRC_FILES := daemon.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/daemon.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 |
31 | #define TAG "DEMO"
32 | #define LOGI(...) //__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
33 | #define LOGD(...) //__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
34 | #define LOGW(...) //__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
35 | #define LOGE(...) //__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
36 |
37 | #define DAEMON_CALLBACK_NAME "onDaemonDead"
38 |
39 | void waitfor_self_observer(char *observer_file_path) {
40 | int lockFileDescriptor = open(observer_file_path, O_RDONLY);
41 | if (lockFileDescriptor == -1) {
42 | LOGE("Watched >>>>OBSERVER<<<< has been ready before watching...");
43 | return;
44 | }
45 |
46 | void *p_buf = malloc(sizeof(struct inotify_event));
47 | if (p_buf == NULL) {
48 | LOGE("malloc failed !!!");
49 | return;
50 | }
51 | int maskStrLength = 7 + 10 + 1;
52 | char *p_maskStr = malloc(maskStrLength);
53 | if (p_maskStr == NULL) {
54 | free(p_buf);
55 | LOGE("malloc failed !!!");
56 | return;
57 | }
58 | int fileDescriptor = inotify_init();
59 | if (fileDescriptor < 0) {
60 | free(p_buf);
61 | free(p_maskStr);
62 | LOGE("inotify_init failed !!!");
63 | return;
64 | }
65 |
66 | int watchDescriptor = inotify_add_watch(fileDescriptor, observer_file_path, IN_ALL_EVENTS);
67 | if (watchDescriptor < 0) {
68 | free(p_buf);
69 | free(p_maskStr);
70 | LOGE("inotify_add_watch failed !!!");
71 | return;
72 | }
73 |
74 |
75 | while (1) {
76 | size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
77 | if (4 == ((struct inotify_event *) p_buf)->mask) {
78 | LOGE("Watched >>>>OBSERVER<<<< has been ready...");
79 | free(p_maskStr);
80 | free(p_buf);
81 | return;
82 | }
83 | }
84 | }
85 |
86 | void notify_daemon_observer(unsigned char is_persistent, char *observer_file_path) {
87 | if (!is_persistent) {
88 | int lockFileDescriptor = open(observer_file_path, O_RDONLY);
89 | while (lockFileDescriptor == -1) {
90 | lockFileDescriptor = open(observer_file_path, O_RDONLY);
91 | }
92 | }
93 | remove(observer_file_path);
94 | }
95 |
96 | void notify_and_waitfor(char *observer_self_path, char *observer_daemon_path) {
97 | int observer_self_descriptor = open(observer_self_path, O_RDONLY);
98 | if (observer_self_descriptor == -1) {
99 | observer_self_descriptor = open(observer_self_path, O_CREAT, S_IRUSR | S_IWUSR);
100 | }
101 | int observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY);
102 | while (observer_daemon_descriptor == -1) {
103 | usleep(1000);
104 | observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY);
105 | }
106 | remove(observer_daemon_path);
107 | LOGE("Watched >>>>OBSERVER<<<< has been ready...");
108 | }
109 |
110 |
111 | int lock_file(char *lock_file_path) {
112 | LOGD("start try to lock file >> %s <<", lock_file_path);
113 | int lockFileDescriptor = open(lock_file_path, O_RDONLY);
114 | if (lockFileDescriptor == -1) {
115 | lockFileDescriptor = open(lock_file_path, O_CREAT, S_IRUSR);
116 | }
117 | int lockRet = flock(lockFileDescriptor, LOCK_EX);
118 | if (lockRet == -1) {
119 | LOGE("lock file failed >> %s <<", lock_file_path);
120 | return 0;
121 | } else {
122 | LOGD("lock file success >> %s <<", lock_file_path);
123 | return 1;
124 | }
125 | }
126 |
127 | void java_callback(JNIEnv *env, jobject jobj, char *method_name) {
128 | jclass cls = (*env)->GetObjectClass(env, jobj);
129 | jmethodID cb_method = (*env)->GetMethodID(env, cls, method_name, "()V");
130 | (*env)->CallVoidMethod(env, jobj, cb_method);
131 | }
132 |
133 | void do_daemon(JNIEnv *env, jobject jobj, char *indicator_self_path, char *indicator_daemon_path,
134 | char *observer_self_path, char *observer_daemon_path) {
135 | int lock_status = 0;
136 | int try_time = 0;
137 | while (try_time < 3 && !(lock_status = lock_file(indicator_self_path))) {
138 | try_time++;
139 | LOGD("Persistent lock myself failed and try again as %d times", try_time);
140 | usleep(10000);
141 | }
142 | if (!lock_status) {
143 | LOGE("Persistent lock myself failed and exit");
144 | return;
145 | }
146 |
147 | notify_and_waitfor(observer_self_path, observer_daemon_path);
148 |
149 | lock_status = lock_file(indicator_daemon_path);
150 | if (lock_status) {
151 | LOGE("Watch >>>>DAEMON<<<<< Daed !!");
152 | remove(observer_self_path);// it`s important ! to prevent from deadlock
153 | java_callback(env, jobj, DAEMON_CALLBACK_NAME);
154 | }
155 | }
156 |
157 | void create_file_if_not_exist(char *path) {
158 | FILE *fp = fopen(path, "ab+");
159 | if (fp) {
160 | fclose(fp);
161 | }
162 | }
163 |
164 | void set_process_name(JNIEnv *env) {
165 | jclass process = (*env)->FindClass(env, "android/os/Process");
166 | jmethodID setArgV0 = (*env)->GetStaticMethodID(env, process, "setArgV0",
167 | "(Ljava/lang/String;)V");
168 | jstring name = (*env)->NewStringUTF(env, "app");
169 | (*env)->CallStaticVoidMethod(env, process, setArgV0, name);
170 | }
171 |
172 | JNIEXPORT void JNICALL
173 | Java_com_daemon_process_NativeLoader_doDaemon(JNIEnv *env, jobject jobj,
174 | jstring indicatorSelfPath,
175 | jstring indicatorDaemonPath,
176 | jstring observerSelfPath,
177 | jstring observerDaemonPath) {
178 | if (indicatorSelfPath == NULL || indicatorDaemonPath == NULL || observerSelfPath == NULL ||
179 | observerDaemonPath == NULL) {
180 | LOGE("parameters cannot be NULL !");
181 | return;
182 | }
183 |
184 | char *indicator_self_path = (char *) (*env)->GetStringUTFChars(env, indicatorSelfPath, 0);
185 | char *indicator_daemon_path = (char *) (*env)->GetStringUTFChars(env, indicatorDaemonPath, 0);
186 | char *observer_self_path = (char *) (*env)->GetStringUTFChars(env, observerSelfPath, 0);
187 | char *observer_daemon_path = (char *) (*env)->GetStringUTFChars(env, observerDaemonPath, 0);
188 |
189 |
190 | pid_t pid;
191 | if ((pid = fork()) < 0) {
192 | //printf("fork 1 error\n");
193 | exit(-1);
194 | } else if (pid == 0) { //第一个子进程
195 | if ((pid = fork()) < 0) {
196 | // printf("fork 2 error\n");
197 | exit(-1);
198 | } else if (pid > 0) {
199 | // 托孤
200 | exit(0);
201 | }
202 |
203 | LOGD("mypid: %d", getpid());
204 | const int MAX_PATH = 256;
205 | char indicator_self_path_child[MAX_PATH];
206 | char indicator_daemon_path_child[MAX_PATH];
207 | char observer_self_path_child[MAX_PATH];
208 | char observer_daemon_path_child[MAX_PATH];
209 |
210 | strcpy(indicator_self_path_child, indicator_self_path);
211 | strcat(indicator_self_path_child, "-c");
212 |
213 | strcpy(indicator_daemon_path_child, indicator_daemon_path);
214 | strcat(indicator_daemon_path_child, "-c");
215 |
216 | strcpy(observer_self_path_child, observer_self_path);
217 | strcat(observer_self_path_child, "-c");
218 |
219 | strcpy(observer_daemon_path_child, observer_daemon_path);
220 | strcat(observer_daemon_path_child, "-c");
221 |
222 | create_file_if_not_exist(indicator_self_path_child);
223 | create_file_if_not_exist(indicator_daemon_path_child);
224 |
225 | set_process_name(env);
226 |
227 | do_daemon(env, jobj, indicator_self_path_child, indicator_daemon_path_child,
228 | observer_self_path_child, observer_daemon_path_child);
229 | return;
230 | }
231 |
232 | if (waitpid(pid, NULL, 0) != pid){
233 | //printf("waitpid error\n");
234 | }
235 |
236 | do_daemon(env, jobj, indicator_self_path, indicator_daemon_path, observer_self_path,
237 | observer_daemon_path);
238 | }
239 |
--------------------------------------------------------------------------------
/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 | -keep public class com.daemon.Daemon{
23 | boolean isMainProcess(android.app.Application);
24 | void startActivity(android.content.Context ,android.content.Intent );
25 | void startWork(android.app.Application,com.daemon.Daemon$DaemonConfiguration,com.daemon.Daemon$DaemonCallback);
26 | }
27 |
28 | -keep class com.daemon.process.NativeLoader{
29 | void onDaemonDead();
30 | }
31 |
32 | -keep class com.daemon.Daemon$DaemonCallback{*;
33 | }
34 | -keep class com.daemon.Daemon$DaemonConfiguration{*;
35 | }
36 | -keepclasseswithmembernames class * { # 保持native方法不被混淆
37 | native ;
38 | }
39 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
221 |
222 |
223 |
224 |
227 |
230 |
235 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/BaseService.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.Service;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.graphics.BitmapFactory;
10 | import android.graphics.Color;
11 | import android.os.Build;
12 | import android.os.IBinder;
13 |
14 | import androidx.annotation.Nullable;
15 |
16 | public abstract class BaseService extends Service {
17 | class LoggerRunnable implements Runnable {
18 | @Override
19 | public void run() {
20 | while (true){
21 | try {
22 | Thread.sleep(3000);
23 | } catch (InterruptedException e) {
24 | e.printStackTrace();
25 | }
26 | DaemonLog.d(getName()+" thread is running");
27 | }
28 | }
29 | }
30 | public String getName() {
31 | return getClass().getSimpleName();
32 | }
33 |
34 | @Nullable
35 | public IBinder onBind(Intent intent) {
36 | return null;
37 | }
38 |
39 |
40 | private Notification defaultNotification(){
41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
42 | NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
43 | NotificationChannel Channel = new NotificationChannel("48098DDD-C1F8-4CA0-9BE9-A1466CF692B2", "可关闭通知", NotificationManager.IMPORTANCE_DEFAULT);
44 | Channel.enableLights(true);
45 | Channel.setLightColor(Color.GREEN);
46 | Channel.setShowBadge(true);
47 | Channel.setDescription("");
48 | Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
49 | Channel.enableVibration(false);
50 | Channel.setSound(null, null);
51 | manager.createNotificationChannel(Channel);
52 |
53 | Notification notification = new Notification.Builder(this, "48098DDD-C1F8-4CA0-9BE9-A1466CF692B2")
54 | .setContentTitle("App")
55 | .setContentText("App is running")
56 | .setWhen(System.currentTimeMillis())
57 | .setSmallIcon(R.drawable.ic_stat_name)
58 | .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),R.drawable.ic_stat_name))
59 | .build();
60 | return notification;
61 | } else {
62 | Notification notification = new Notification.Builder(this)
63 | .setContentTitle("App")//设置标题
64 | .setContentText("App is running")//设置内容
65 | .setWhen(System.currentTimeMillis())//设置创建时间
66 | .build();
67 | return notification;
68 | }
69 | }
70 |
71 | private Notification getNotification(){
72 | Notification notification = Daemon.getConfiguration().getForegroundNotification();
73 | if(notification != null){
74 | return notification;
75 | }
76 | return defaultNotification();
77 | }
78 |
79 | public void onCreate() {
80 | super.onCreate();
81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
82 | startForeground(1,getNotification());
83 | }
84 | DaemonLog.d(getName() + " oncreate");
85 | //Thread thread = new Thread(new LoggerRunnable());
86 | //thread.start();
87 | }
88 |
89 | public void onDestroy() {
90 | super.onDestroy();
91 | DaemonLog.d(getName() + " onDestroy");
92 | }
93 |
94 | public int onStartCommand(Intent intent, int i, int i2) {
95 | DaemonLog.d(getName() + " onStartCommand");
96 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
97 | startForeground(1,getNotification());
98 | }
99 | return START_STICKY;
100 | }
101 |
102 | public void onTaskRemoved(Intent intent) {
103 | DaemonLog.d("onTaskRemoved");
104 | }
105 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/CoreService.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.res.AssetFileDescriptor;
6 | import android.media.AudioAttributes;
7 | import android.media.MediaPlayer;
8 | import android.os.Build;
9 | import android.os.Handler;
10 | import android.os.Message;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import com.daemon.onepixel.OnePixelActivity;
15 | import com.daemon.receiver.ScreenReceiver;
16 | import com.daemon.utils.IntentUtils;
17 | import com.daemon.utils.ROMUtils;
18 | import com.daemon.utils.ServiceUtils;
19 |
20 | import java.io.IOException;
21 | import java.util.concurrent.TimeUnit;
22 |
23 | import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
24 |
25 | public class CoreService extends BaseService implements Handler.Callback, ScreenReceiver.Observer {
26 |
27 | private static final String KEY_INTENT = "intent";
28 | private static final String START_ACTIVITY = "start_activity";
29 | private static final int STOP_MUSIC_MESSAGE = 1;
30 | private static final int STAT_MUSIC_MESSAGE = 2;
31 | private static final long STOP_MUSIC_DELAY = TimeUnit.MINUTES.toMillis(1);
32 |
33 | public MediaPlayer mMediaPlayer;
34 | public Handler mHandler;
35 |
36 | public class PlayErrorListener implements MediaPlayer.OnErrorListener {
37 | public PlayErrorListener() {
38 | }
39 |
40 | public boolean onError(MediaPlayer mediaPlayer, int i, int i2) {
41 | DaemonLog.d(CoreService.this.getName() + " player onError");
42 | return false;
43 | }
44 | }
45 |
46 | public class CompletionListener implements MediaPlayer.OnCompletionListener {
47 | public CompletionListener() {
48 | }
49 |
50 | public void onCompletion(MediaPlayer mediaPlayer) {
51 | DaemonLog.d(getName()+" restart music....");
52 | mediaPlayer.start();
53 | }
54 | }
55 |
56 | public class StopRunnable implements Runnable {
57 | public StopRunnable() {
58 | }
59 |
60 | public void run() {
61 | CoreService.this.stopSelf();
62 | }
63 | }
64 |
65 | private void startPlayMusic() {
66 | DaemonLog.d(getName() + " startPlay called");
67 | stopPlayMusic();
68 | try{
69 | mMediaPlayer = MediaPlayer.create(this,R.raw.no_notice);
70 | mMediaPlayer.setWakeMode(getApplicationContext(), 1);
71 | mMediaPlayer.setOnErrorListener(new PlayErrorListener());
72 | mMediaPlayer.setOnCompletionListener(new CompletionListener());
73 | if (ROMUtils.isHuawei() && Build.VERSION.SDK_INT >= 21) {
74 | mMediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setUsage(USAGE_ASSISTANCE_ACCESSIBILITY).build());
75 | }
76 | //mMediaPlayer.prepare();
77 | mMediaPlayer.start();
78 | }catch (Throwable exp){
79 | exp.printStackTrace();
80 | return;
81 | }
82 | }
83 |
84 | private void stopPlayMusic() {
85 | if (mMediaPlayer != null){
86 | try{
87 | mMediaPlayer.stop();
88 | mMediaPlayer.release();
89 | mMediaPlayer = null;
90 | }catch (Exception exp){
91 | exp.printStackTrace();
92 | }
93 | this.mMediaPlayer = null;
94 | }
95 | }
96 |
97 | public static void start(Context context) {
98 | try {
99 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
100 | context.startForegroundService(new Intent(context, CoreService.class));
101 | }else{
102 | context.startService(new Intent(context, CoreService.class));
103 | }
104 | } catch (Exception unused) {
105 | }
106 | try{
107 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
108 | context.startForegroundService(new Intent(context, DaemonService.class));
109 | }else{
110 | context.startService(new Intent(context, DaemonService.class));
111 | }
112 | } catch (Exception unused) {
113 | }
114 | }
115 |
116 | public static void startForLockScreen(Context context, Intent intent) {
117 | IntentUtils.startActivitySafe(context, intent);
118 | Intent intent2 = new Intent(context, CoreService.class);
119 | if (intent != null) {
120 | intent2.putExtra(KEY_INTENT, intent);
121 | intent2.setAction(START_ACTIVITY);
122 | }
123 | try {
124 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
125 | context.startForegroundService(intent2);
126 | }else{
127 | context.startService(intent2);
128 | }
129 | } catch (Exception e2) {
130 | DaemonLog.d("startService onError", e2);
131 | }
132 | }
133 |
134 | @Override //
135 | public void onCreate() {
136 | super.onCreate();
137 | this.mHandler = new Handler(getMainLooper(), this);
138 | ScreenReceiver.addObserver(this);
139 | //Daemon.getCallback().onStart();
140 | if(!ROMUtils.isXiaomi()&&Daemon.getConfiguration().isMusicPlayEnabled()){
141 | startPlayMusic();
142 | }
143 | }
144 |
145 | @Override //
146 | public void onDestroy() {
147 | super.onDestroy();
148 | ScreenReceiver.removeObserver(this);
149 | MediaPlayer mediaPlayer = this.mMediaPlayer;
150 | if (mediaPlayer != null) {
151 | try {
152 | mediaPlayer.release();
153 | } catch (Exception e2) {
154 | DaemonLog.d("mPlayer release error", e2);
155 | }
156 | }
157 | //Daemon.getCallback().onStop();
158 | }
159 |
160 | public boolean handleMessage(@NonNull Message message) {
161 | if (message.what == STOP_MUSIC_MESSAGE) {
162 | DaemonLog.d(getName() + " handleMessage stopPlay");
163 | stopPlayMusic();
164 | }
165 | if (message.what == STAT_MUSIC_MESSAGE){
166 | DaemonLog.d(getName() + " handleMessage startPlay");
167 | startPlayMusic();
168 | }
169 | return true;
170 | }
171 |
172 |
173 | @Override
174 | public void screenStatusChanged(Boolean screenON) {
175 | DaemonLog.d(getName() + " screenStatusChanged->" + screenON);
176 | if (screenON) {
177 | OnePixelActivity.finishIfExist();
178 | mHandler.sendEmptyMessageDelayed(STOP_MUSIC_MESSAGE, STOP_MUSIC_DELAY);
179 | } else {
180 | if (Daemon.getConfiguration().isOnePixelActivityEnabled() && !ROMUtils.isHuawei()) {
181 | DaemonLog.d(getName()+" start onepixelactivity");
182 | IntentUtils.startActivitySafe(Daemon.getApplication(), OnePixelActivity.class);
183 | }
184 | if (Daemon.getConfiguration().isMusicPlayEnabled() && !ROMUtils.isXiaomi()) {
185 | DaemonLog.d(getName()+" start playmusic");
186 | mHandler.sendEmptyMessage(STAT_MUSIC_MESSAGE);
187 | }
188 | }
189 | }
190 |
191 | @Override //
192 | public int onStartCommand(Intent intent, int i, int i2) {
193 | super.onStartCommand(intent,i,i2);
194 | Intent intent2;
195 | if (intent == null || !START_ACTIVITY.equals(intent.getAction()) || (intent2 = (Intent) intent.getParcelableExtra(KEY_INTENT)) == null) {
196 | return START_STICKY;
197 | }
198 | boolean isOppo = ROMUtils.isOppo();
199 | if (isOppo) {
200 | startPlayMusic();
201 | }
202 | IntentUtils.startActivitySafe(getApplicationContext(), intent2);
203 | DaemonLog.d(getName() + " startActivity,targetIntent=" + intent2);
204 | if (!isOppo) {
205 | return START_STICKY;
206 | }
207 | mHandler.postDelayed(new StopRunnable(), 5000);
208 | return START_STICKY;
209 | }
210 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/DInstrumentation.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.app.Instrumentation;
4 | import android.os.Bundle;
5 |
6 | public class DInstrumentation extends Instrumentation {
7 | public void onCreate(Bundle bundle) {
8 | super.onCreate(bundle);
9 | DaemonLog.d("DInstrumentation.OnCreate");
10 | }
11 |
12 | public void onDestroy() {
13 | super.onDestroy();
14 | }
15 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/Daemon.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.app.Application;
4 | import android.app.Notification;
5 | import android.content.Context;
6 | import android.content.Intent;
7 |
8 | import androidx.core.app.NotificationCompat;
9 |
10 | import com.daemon.process.Configs;
11 | import com.daemon.receiver.ScreenReceiver;
12 | import com.daemon.utils.ActivityUtils;
13 | import com.daemon.utils.ServiceUtils;
14 |
15 | public class Daemon {
16 |
17 | public interface DaemonCallback {
18 | void onStart();
19 | void onStop();
20 | }
21 |
22 | public interface DaemonConfiguration{
23 | NotificationCompat.Builder getIntentNotificationBuilder(Context context);
24 | Notification getForegroundNotification();
25 | Boolean isMusicPlayEnabled();
26 | Boolean isOnePixelActivityEnabled();
27 | }
28 |
29 | private static DaemonConfiguration configuration =null;
30 | private static DaemonCallback callback=null;
31 | private static Application application=null;
32 |
33 | public static DaemonCallback getCallback(){return callback;}
34 |
35 | public static Application getApplication() {
36 | return application;
37 | }
38 |
39 | public static DaemonConfiguration getConfiguration(){return configuration;}
40 |
41 | public static boolean isMainProcess(Application app){
42 | return ServiceUtils.isMainProcess(app);
43 | }
44 |
45 | public static void startActivity(Context context,Intent intent){
46 | if (ActivityUtils.isAppRunningForeground(context)){
47 | context.startActivity(intent);
48 | return;
49 | }
50 | ActivityUtils.hookJumpActivity(context,intent);
51 | }
52 |
53 | public static void startWork(Application application, DaemonConfiguration configuration, DaemonCallback callback){
54 | if (Daemon.application != null){
55 | return;
56 | }
57 | Daemon.callback= callback;
58 | Daemon.configuration = configuration;
59 | Daemon.application = application;
60 | ScreenReceiver.register(application);
61 | com.daemon.process.Daemon.init(application.getBaseContext(),new com.daemon.process.Configs(new Configs.Config(
62 | "android.process.media","com.daemon.DaemonService","",""
63 | ),new Configs.Config(
64 | application.getPackageName()+":service","com.daemon.CoreService","",""
65 | )));
66 | CoreService.start(application);
67 | DaemonJobService.scheduleService(application);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/DaemonJobService.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.job.JobInfo;
5 | import android.app.job.JobParameters;
6 | import android.app.job.JobScheduler;
7 | import android.app.job.JobService;
8 | import android.content.ComponentName;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.os.Build;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | @TargetApi(21)
15 | public class DaemonJobService extends JobService {
16 | public static final int JOB_ID = 1000;
17 | public static final long JOB_INTERVAL = TimeUnit.SECONDS.toMillis(8);
18 |
19 | public static void scheduleService(Context context) {
20 | DaemonLog.d("DaemonJobService scheduleService");
21 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
22 | if (jobScheduler != null) {
23 | JobInfo.Builder persisted = new JobInfo.Builder(JOB_ID, new ComponentName(context, DaemonJobService.class)).setPersisted(true);
24 | if (Build.VERSION.SDK_INT < 24) {
25 | persisted.setPeriodic(JOB_INTERVAL);
26 | } else {
27 | persisted.setMinimumLatency(JOB_INTERVAL);
28 | }
29 | if (jobScheduler != null) {
30 | try {
31 | jobScheduler.schedule(persisted.build());
32 | } catch (Throwable unused) {
33 | }
34 | }
35 | }
36 | }
37 |
38 | public static void stopScheduleService(Context context) {
39 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
40 | if (jobScheduler != null) {
41 | jobScheduler.cancel(1000);
42 | }
43 | }
44 |
45 | public void onCreate() {
46 | super.onCreate();
47 | DaemonLog.d("DaemonJobService onCreate");
48 | }
49 |
50 | public void onDestroy() {
51 | super.onDestroy();
52 | DaemonLog.d("DaemonJobService onDestroy");
53 | }
54 |
55 | public int onStartCommand(Intent intent, int i, int i2) {
56 | super.onStartCommand(intent, i, i2);
57 | DaemonLog.d("DaemonJobService onStartCommand");
58 | return START_STICKY;
59 | }
60 |
61 | public boolean onStartJob(JobParameters jobParameters) {
62 | DaemonLog.d("DaemonJobService onStartJob");
63 | if (Build.VERSION.SDK_INT >= 24) {
64 | scheduleService(getApplicationContext());
65 | }
66 | CoreService.start(getApplicationContext());
67 | return false;
68 | }
69 |
70 | public boolean onStopJob(JobParameters jobParameters) {
71 | DaemonLog.d("DaemonJobService onStopJob");
72 | return false;
73 | }
74 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/DaemonLog.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 |
4 | import android.util.Log;
5 |
6 | public class DaemonLog {
7 | public static final String TAG = "DEMO:";
8 |
9 | public static void d(String str) {
10 | Log.d(TAG,str);
11 | }
12 |
13 | public static void d(String str, Throwable th) {
14 | Log.d(TAG,str,th);
15 | }
16 |
17 | public static void e(String str) {
18 | Log.e(TAG,str);
19 | }
20 |
21 | public static void e(String str, Throwable th) {
22 | Log.e(TAG,str,th);
23 | }
24 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/DaemonService.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Intent;
5 | import android.content.ServiceConnection;
6 | import android.os.Build;
7 | import android.os.IBinder;
8 |
9 | public class DaemonService extends BaseService {
10 | @Override
11 | public void onCreate() {
12 | super.onCreate();
13 | new Thread(){
14 | @Override
15 | public void run() {
16 | Daemon.getCallback().onStart();
17 | }
18 | }.start();
19 | }
20 |
21 | @Override
22 | public void onDestroy() {
23 | super.onDestroy();
24 | Daemon.getCallback().onStop();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/IntentJobService.java:
--------------------------------------------------------------------------------
1 | package com.daemon;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.job.JobInfo;
5 | import android.app.job.JobParameters;
6 | import android.app.job.JobScheduler;
7 | import android.app.job.JobService;
8 | import android.content.ComponentName;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 |
14 | import com.daemon.utils.IntentUtils;
15 |
16 | @TargetApi(21)
17 | public class IntentJobService extends JobService {
18 |
19 | /* renamed from: a reason: collision with root package name */
20 | public static final String IS_ACTIVITY = "is_activity";
21 |
22 | /* renamed from: b reason: collision with root package name */
23 | public static final int JOB_ID = 1001;
24 |
25 | public static void scheduleService(Context context, Intent intent, boolean z) {
26 | DaemonLog.d("IntentJobService scheduleService");
27 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
28 | if (jobScheduler != null) {
29 | JobInfo.Builder persisted = new JobInfo.Builder(JOB_ID, new ComponentName(context, IntentJobService.class)).setPersisted(false);
30 | if (Build.VERSION.SDK_INT >= 28) {
31 | persisted.setImportantWhileForeground(true);
32 | }
33 | persisted.setRequiresDeviceIdle(false);
34 | persisted.setOverrideDeadline(3000);
35 | if (Build.VERSION.SDK_INT >= 26 && intent != null) {
36 | Bundle bundle = new Bundle();
37 | bundle.putParcelable("android.intent.extra.INTENT", intent);
38 | bundle.putBoolean(IS_ACTIVITY, z);
39 | persisted.setTransientExtras(bundle);
40 | }
41 | if (jobScheduler != null) {
42 | try {
43 | jobScheduler.schedule(persisted.build());
44 | } catch (Throwable th) {
45 | DaemonLog.d("IntentJobService schedule error", th);
46 | }
47 | }
48 | }
49 | }
50 |
51 | public static void stopScheduleService(Context context) {
52 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
53 | if (jobScheduler != null) {
54 | jobScheduler.cancel(JOB_ID);
55 | }
56 | }
57 |
58 | public void onCreate() {
59 | super.onCreate();
60 | DaemonLog.d("IntentJobService onCreate");
61 | }
62 |
63 | public void onDestroy() {
64 | super.onDestroy();
65 | DaemonLog.d("IntentJobService onDestroy");
66 | }
67 |
68 | public int onStartCommand(Intent intent, int i, int i2) {
69 | super.onStartCommand(intent, i, i2);
70 | DaemonLog.d("IntentJobService onStartCommand");
71 | return START_STICKY;
72 | }
73 |
74 | public boolean onStartJob(JobParameters jobParameters) {
75 | DaemonLog.d("IntentJobService onStartJob");
76 | if (Build.VERSION.SDK_INT >= 28) {
77 | Bundle transientExtras = jobParameters.getTransientExtras();
78 | Intent intent = (Intent) transientExtras.getParcelable("android.intent.extra.INTENT");
79 | DaemonLog.d("IntentJobService onStartJob intent=" + intent);
80 | if (intent != null) {
81 | if (transientExtras.getBoolean(IS_ACTIVITY)) {
82 | IntentUtils.startActivitySafe((Context) this, intent, false);
83 | } else {
84 | try {
85 | startService(intent);
86 | } catch (Exception e) {
87 | DaemonLog.d("IntentJobService start service error", e);
88 | }
89 | }
90 | }
91 | }
92 | return false;
93 | }
94 |
95 | public boolean onStopJob(JobParameters jobParameters) {
96 | DaemonLog.d("IntentJobService onStopJob");
97 | return false;
98 | }
99 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/onepixel/OnePixelActivity.java:
--------------------------------------------------------------------------------
1 | package com.daemon.onepixel;
2 |
3 | import android.app.Activity;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.os.PowerManager;
12 | import android.util.Log;
13 | import android.view.Gravity;
14 | import android.view.MotionEvent;
15 | import android.view.Window;
16 | import android.view.WindowManager;
17 |
18 | import com.daemon.CoreService;
19 |
20 |
21 | public class OnePixelActivity extends Activity {
22 |
23 | private static final String TAG = "OnePixelActivity";
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | Window mWindow = getWindow();
29 | mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
30 | WindowManager.LayoutParams attrParams = mWindow.getAttributes();
31 | attrParams.x = 0;
32 | attrParams.y = 0;
33 | attrParams.height = 1;
34 | attrParams.width = 1;
35 | mWindow.setAttributes(attrParams);
36 | OnePixelActivity.sActivity = this;
37 | }
38 |
39 | @Override
40 | protected void onDestroy() {
41 | Log.d(TAG, "OnDestroy---- start WatchDogService");
42 | Intent intentAlive = new Intent(this, CoreService.class);
43 | startService(intentAlive);
44 | super.onDestroy();
45 | OnePixelActivity.sActivity = null;
46 | }
47 |
48 | @Override
49 | protected void onResume() {
50 | super.onResume();
51 | if (isScreenOn()) {
52 | finishSelf();
53 | }
54 | }
55 |
56 | @Override
57 | public boolean dispatchTouchEvent(MotionEvent motionEvent) {
58 | finishSelf();
59 | return super.dispatchTouchEvent(motionEvent);
60 | }
61 |
62 | @Override
63 | public boolean onTouchEvent(MotionEvent motionEvent) {
64 | finishSelf();
65 | return super.onTouchEvent(motionEvent);
66 | }
67 |
68 | public void finishSelf() {
69 | if (!isFinishing()) {
70 | finish();
71 | }
72 | }
73 |
74 |
75 | private boolean isScreenOn() {
76 | PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
78 | return powerManager.isInteractive();
79 | } else {
80 | return powerManager.isScreenOn();
81 | }
82 | }
83 |
84 | private static OnePixelActivity sActivity = null;
85 |
86 | public static void finishIfExist(){
87 | if (sActivity != null){
88 | sActivity.finish();
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/process/Configs.java:
--------------------------------------------------------------------------------
1 | package com.daemon.process;
2 |
3 | public class Configs {
4 |
5 | public final Config PERSISTENT_CONFIG;
6 | public final Config DAEMON_ASSISTANT_CONFIG;
7 |
8 | public Configs(Config persistentConfig, Config daemonAssistantConfig) {
9 | this.PERSISTENT_CONFIG = persistentConfig;
10 | this.DAEMON_ASSISTANT_CONFIG = daemonAssistantConfig;
11 | }
12 |
13 | public static class Config {
14 |
15 | final String processName;
16 | final String serviceName;
17 | final String receiverName;
18 | final String activityName;
19 |
20 | public Config(String processName, String serviceName, String receiverName, String activityName) {
21 | this.processName = processName;
22 | this.serviceName = serviceName;
23 | this.receiverName = receiverName;
24 | this.activityName = activityName;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/process/Daemon.java:
--------------------------------------------------------------------------------
1 | package com.daemon.process;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.SharedPreferences.Editor;
6 | import android.util.Log;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.File;
10 | import java.io.FileReader;
11 | import java.io.IOException;
12 |
13 | public class Daemon {
14 |
15 | private static final String TAG = "Daemon";
16 |
17 | private Configs mConfigurations;
18 |
19 | public static Context context;
20 |
21 | private Daemon(Configs configurations) {
22 | this.mConfigurations = configurations;
23 | }
24 |
25 | public static void init(Context base, Configs configurations) {
26 | //Reflection.unseal(base);
27 | Daemon.context = base;
28 | Daemon client = new Daemon(configurations);
29 | client.initDaemon(base);
30 | }
31 |
32 |
33 | private final String DAEMON_PERMITTING_SP_FILENAME = "D16EA7BB-AC54-4CDD-8C69-50D0838F4DE4";
34 | private final String DAEMON_PERMITTING_SP_KEY = "permitted";
35 |
36 |
37 | private BufferedReader mBufferedReader;
38 |
39 | private void initDaemon(Context base) {
40 | if (mConfigurations == null) {
41 | return;
42 | }
43 |
44 | String processName = getProcessName();
45 | String packageName = base.getPackageName();
46 |
47 | if (processName.startsWith(mConfigurations.PERSISTENT_CONFIG.processName)) {
48 | Log.d("DEMO","onPersistentCreate");
49 | IProcess.Fetcher.fetchStrategy().onPersistentCreate(base, mConfigurations);
50 | } else if (processName.startsWith(mConfigurations.DAEMON_ASSISTANT_CONFIG.processName)) {
51 | Log.d("DEMO","onDaemonAssistantCreate");
52 | IProcess.Fetcher.fetchStrategy().onDaemonAssistantCreate(base, mConfigurations);
53 | } else if (processName.startsWith(packageName)) {
54 | Log.d("DEMO","onInit");
55 | IProcess.Fetcher.fetchStrategy().onInit(base);
56 | }
57 |
58 | releaseIO();
59 | }
60 |
61 |
62 | private String getProcessName() {
63 | try {
64 | File file = new File("/proc/self/cmdline");
65 | mBufferedReader = new BufferedReader(new FileReader(file));
66 | return mBufferedReader.readLine().trim();
67 | } catch (Exception e) {
68 | e.printStackTrace();
69 | return null;
70 | }
71 | }
72 |
73 | private void releaseIO() {
74 | if (mBufferedReader != null) {
75 | try {
76 | mBufferedReader.close();
77 | } catch (IOException e) {
78 | e.printStackTrace();
79 | }
80 | mBufferedReader = null;
81 | }
82 | }
83 |
84 | private boolean isDaemonPermitting(Context context) {
85 | SharedPreferences sp = context.getSharedPreferences(DAEMON_PERMITTING_SP_FILENAME, Context.MODE_PRIVATE);
86 | return sp.getBoolean(DAEMON_PERMITTING_SP_KEY, true);
87 | }
88 |
89 | protected boolean setDaemonPermitting(Context context, boolean isPermitting) {
90 | SharedPreferences sp = context.getSharedPreferences(DAEMON_PERMITTING_SP_FILENAME, Context.MODE_PRIVATE);
91 | Editor editor = sp.edit();
92 | editor.putBoolean(DAEMON_PERMITTING_SP_KEY, isPermitting);
93 | return editor.commit();
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/process/IProcess.java:
--------------------------------------------------------------------------------
1 | package com.daemon.process;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 |
6 | public interface IProcess {
7 | /**
8 | * Initialization some files or other when 1st time
9 | */
10 | boolean onInit(Context context);
11 |
12 | /**
13 | * when Persistent processName create
14 | *
15 | */
16 | void onPersistentCreate(Context context, Configs configs);
17 |
18 | /**
19 | * when DaemonAssistant processName create
20 | */
21 | void onDaemonAssistantCreate(Context context, Configs configs);
22 |
23 | /**
24 | * when watches the processName dead which it watched
25 | */
26 | void onDaemonDead();
27 |
28 |
29 | class Fetcher {
30 |
31 | private static volatile IProcess mDaemonStrategy;
32 |
33 | /**
34 | * fetch the strategy for this device
35 | *
36 | * @return the daemon strategy for this device
37 | */
38 | static IProcess fetchStrategy() {
39 | if (mDaemonStrategy != null) {
40 | return mDaemonStrategy;
41 | }
42 | int sdk = Build.VERSION.SDK_INT;
43 | mDaemonStrategy = new ProcessImpl();
44 | return mDaemonStrategy;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/process/NativeLoader.java:
--------------------------------------------------------------------------------
1 | package com.daemon.process;
2 |
3 | /* package */class NativeLoader {
4 |
5 | static {
6 | try {
7 | System.loadLibrary("daemon");
8 | } catch (Exception e) {
9 | e.printStackTrace();
10 | }
11 | }
12 |
13 | public native void doDaemon(String indicatorSelfPath, String indicatorDaemonPath, String observerSelfPath, String observerDaemonPath);
14 |
15 | public void onDaemonDead() {
16 | IProcess.Fetcher.fetchStrategy().onDaemonDead();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/process/ProcessImpl.java:
--------------------------------------------------------------------------------
1 | package com.daemon.process;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.IBinder;
9 | import android.os.Parcel;
10 | import android.os.Process;
11 | import android.os.RemoteException;
12 | import android.util.Log;
13 |
14 | import com.daemon.DInstrumentation;
15 |
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.lang.reflect.Field;
19 |
20 | public class ProcessImpl implements IProcess {
21 |
22 | private static final String TAG = "DEMO";
23 |
24 | private final static String INDICATOR_DIR_NAME = "5FAD3EB1C66A40ADAB5FDEBE67B53F6F";
25 | private final static String INDICATOR_PERSISTENT_FILENAME = "file1";
26 | private final static String INDICATOR_DAEMON_ASSISTANT_FILENAME = "file2";
27 | private final static String OBSERVER_PERSISTENT_FILENAME = "file3";
28 | private final static String OBSERVER_DAEMON_ASSISTANT_FILENAME = "file4";
29 |
30 | private IBinder mRemote;
31 | private Parcel mServiceData;
32 | private ComponentName mApp;
33 | private int mPid = Process.myPid();
34 |
35 | @Override
36 | public boolean onInit(Context context) {
37 | return initIndicatorFiles(context);
38 | }
39 |
40 | @Override
41 | public void onPersistentCreate(final Context context, Configs configs) {
42 |
43 | initAmsBinder();
44 | initServiceParcel(context, configs.DAEMON_ASSISTANT_CONFIG.serviceName);
45 | startServiceByAmsBinder();
46 |
47 | Thread t = new Thread() {
48 | public void run() {
49 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
50 | new NativeLoader().doDaemon(
51 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(),
52 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
53 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath(),
54 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath());
55 | }
56 |
57 | ;
58 | };
59 | t.start();
60 |
61 | }
62 |
63 | @Override
64 | public void onDaemonAssistantCreate(final Context context, Configs configs) {
65 | initAmsBinder();
66 | initServiceParcel(context, configs.PERSISTENT_CONFIG.serviceName);
67 | startServiceByAmsBinder();
68 |
69 | Thread t = new Thread() {
70 | public void run() {
71 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
72 | new NativeLoader().doDaemon(
73 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
74 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(),
75 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(),
76 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath());
77 | }
78 |
79 | ;
80 | };
81 | t.start();
82 |
83 | }
84 |
85 |
86 | @Override
87 | public void onDaemonDead() {
88 | Log.d(TAG, "on daemon dead!");
89 | if (startServiceByAmsBinder()) {
90 |
91 | int pid = Process.myPid();
92 | Log.d(TAG, "mPid: " + mPid + " current pid: " + pid);
93 | Daemon.context.startInstrumentation(mApp,null,null);
94 | android.os.Process.killProcess(mPid);
95 | }
96 | }
97 |
98 |
99 | private void initAmsBinder() {
100 | mApp = new ComponentName(Daemon.context, DInstrumentation.class);
101 | Class> activityManagerNative;
102 | try {
103 | activityManagerNative = Class.forName("android.app.ActivityManagerNative");
104 | Object amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative);
105 | Field mRemoteField = amn.getClass().getDeclaredField("mRemote");
106 | mRemoteField.setAccessible(true);
107 | mRemote = (IBinder) mRemoteField.get(amn);
108 | } catch (Throwable e) {
109 | e.printStackTrace();
110 | }
111 | }
112 |
113 |
114 | @SuppressLint("Recycle")
115 | // when processName dead, we should save time to restart and kill self, don`t take a waste of time to recycle
116 | private void initServiceParcel(Context context, String serviceName) {
117 | Intent intent = new Intent();
118 | ComponentName component = new ComponentName(context.getPackageName(), serviceName);
119 | intent.setComponent(component);
120 |
121 | Parcel parcel = Parcel.obtain();
122 | intent.writeToParcel(parcel, 0);
123 |
124 | mServiceData = Parcel.obtain();
125 | if (Build.VERSION.SDK_INT >= 26) {
126 | // Android 8.1
127 | mServiceData.writeInterfaceToken("android.app.IActivityManager");
128 | mServiceData.writeStrongBinder(null);
129 | mServiceData.writeInt(1);
130 | intent.writeToParcel(mServiceData, 0);
131 | mServiceData.writeString(null);
132 | mServiceData.writeInt(context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O ? 1 : 0);
133 | mServiceData.writeString(context.getPackageName());
134 | mServiceData.writeInt(0);
135 | } else {
136 | // http://aospxref.com/android-7.1.2_r36/xref/frameworks/base/core/java/android/app/ActivityManagerNative.java
137 | mServiceData.writeInterfaceToken("android.app.IActivityManager");
138 | mServiceData.writeStrongBinder(null);
139 | intent.writeToParcel(mServiceData, 0);
140 | mServiceData.writeString(null);
141 | mServiceData.writeString(context.getPackageName());
142 | mServiceData.writeInt(0);
143 | }
144 |
145 | }
146 |
147 |
148 | private boolean startServiceByAmsBinder() {
149 | try {
150 | if (mRemote == null || mServiceData == null) {
151 | Log.e(TAG, "REMOTE IS NULL or PARCEL IS NULL !!!");
152 | return false;
153 | }
154 | int code;
155 | switch (Build.VERSION.SDK_INT) {
156 | case 26:
157 | case 27:
158 | code = 26;
159 | break;
160 | case 28:
161 | code = 30;
162 | break;
163 | case 29:
164 | code = 24;
165 | break;
166 | default:
167 | code = 34;
168 | break;
169 | }
170 | mRemote.transact(code, mServiceData, null, 1);//START_SERVICE_TRANSACTION = 34
171 | return true;
172 | } catch (RemoteException e) {
173 | e.printStackTrace();
174 | return false;
175 | }
176 | }
177 |
178 |
179 | private boolean initIndicatorFiles(Context context) {
180 | File dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE);
181 | if (!dirFile.exists()) {
182 | dirFile.mkdirs();
183 | }
184 | try {
185 | createNewFile(dirFile, INDICATOR_PERSISTENT_FILENAME);
186 | createNewFile(dirFile, INDICATOR_DAEMON_ASSISTANT_FILENAME);
187 | return true;
188 | } catch (IOException e) {
189 | e.printStackTrace();
190 | return false;
191 | }
192 | }
193 |
194 | private void createNewFile(File dirFile, String fileName) throws IOException {
195 | File file = new File(dirFile, fileName);
196 | if (!file.exists()) {
197 | file.createNewFile();
198 | }
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/receiver/DaemonStaticReceiver.java:
--------------------------------------------------------------------------------
1 | package com.daemon.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import com.daemon.DaemonLog;
8 |
9 | public class DaemonStaticReceiver extends BroadcastReceiver {
10 | public void onReceive(Context context, Intent intent) {
11 | String action = intent == null ? null : intent.getAction();
12 | //DaemonLog.d("DaemonStaticReceiver onReceive,action=" + action);
13 | }
14 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/receiver/ScreenReceiver.java:
--------------------------------------------------------------------------------
1 | package com.daemon.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 |
8 | import com.daemon.Daemon;
9 | import com.daemon.onepixel.OnePixelActivity;
10 | import com.daemon.utils.ActivityUtils;
11 |
12 | import java.util.LinkedList;
13 | import java.util.List;
14 | import java.util.logging.Handler;
15 |
16 | public class ScreenReceiver {
17 | public interface Observer{
18 | void screenStatusChanged(Boolean screenON);
19 | }
20 | static class ScreenBroadcastReceiver extends BroadcastReceiver {
21 | //public Context mContext;
22 | @Override
23 | public void onReceive(Context context, Intent intent) {
24 | String action = intent.getAction();
25 | if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
26 | for (Observer ob:sObservers){
27 | ob.screenStatusChanged(true);
28 | }
29 | } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
30 | for (Observer ob:sObservers){
31 | ob.screenStatusChanged(false);
32 | }
33 | } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
34 |
35 | }
36 | }
37 | }
38 |
39 |
40 | //private static OnePixelActivity sActivity = null;
41 | private static ScreenBroadcastReceiver sReceiver = null;
42 | private static LinkedList sObservers = new LinkedList<>();
43 |
44 | public static void addObserver(Observer observer){
45 | sObservers.add(observer);
46 | }
47 |
48 | public static void removeObserver(Observer observer){
49 | sObservers.remove(observer);
50 | }
51 |
52 | public static void register(Context context) {
53 | if (sReceiver == null){
54 | sReceiver = new ScreenBroadcastReceiver();
55 | }
56 | IntentFilter filter = new IntentFilter();
57 | filter.addAction(Intent.ACTION_SCREEN_ON);
58 | filter.addAction(Intent.ACTION_SCREEN_OFF);
59 | filter.addAction(Intent.ACTION_USER_PRESENT);
60 | context.registerReceiver(sReceiver,filter);
61 | }
62 |
63 | public static void deregister(Context context) {
64 | if (sReceiver != null){
65 | context.unregisterReceiver(sReceiver);
66 | sReceiver = null;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/utils/ActivityUtils.java:
--------------------------------------------------------------------------------
1 | package com.daemon.utils;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.PendingIntent;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.os.Build;
9 | import android.os.Handler;
10 | import android.os.Looper;
11 | import android.text.TextUtils;
12 | import androidx.annotation.RequiresApi;
13 | import androidx.core.app.NotificationCompat;
14 | import androidx.core.app.NotificationManagerCompat;
15 |
16 | import com.daemon.CoreService;
17 | import com.daemon.Daemon;
18 | import com.daemon.DaemonLog;
19 | import com.daemon.IntentJobService;
20 |
21 | import java.nio.Buffer;
22 | import java.util.List;
23 |
24 | public class ActivityUtils {
25 |
26 | public static final class StartActivityRunnable implements Runnable {
27 | private final Context mContext;
28 | private final Intent mIntent;
29 |
30 | public StartActivityRunnable(Context context, Intent intent) {
31 | this.mContext = context;
32 | this.mIntent = intent;
33 | }
34 |
35 | public void run() {
36 | ActivityUtils.startActivity(this.mContext, this.mIntent);
37 | }
38 | }
39 |
40 | public static final class CancelNotificationRunnable implements Runnable {
41 |
42 | /* renamed from: a reason: collision with root package name */
43 | public final /* synthetic */ NotificationManagerCompat notificationManagerCompat;
44 |
45 | public CancelNotificationRunnable(NotificationManagerCompat notificationManagerCompat) {
46 | this.notificationManagerCompat = notificationManagerCompat;
47 | }
48 |
49 | public void run() {
50 | this.notificationManagerCompat.cancel(99);
51 | }
52 | }
53 |
54 | public static void startActivity(Context context, Intent intent) {
55 | IntentUtils.startActivitySafe(context, intent);
56 | if (Build.VERSION.SDK_INT >= 26) {
57 | bringAppToForeground(context);
58 | IntentJobService.scheduleService(context, intent, true);
59 | }
60 | boolean isAppRunningForeground = isAppRunningForeground(context);
61 | DaemonLog.d("vivo,isAppRunningForeground=" + isAppRunningForeground);
62 | if (!isAppRunningForeground) {
63 | for (int i = 0; i < 10; i++) {
64 | try {
65 | bringAppToForeground(context);
66 | Thread.sleep((long) 50);
67 | if (isAppRunningForeground(context)) {
68 | IntentUtils.startActivitySafe(context, intent);
69 | //nk.getInstance().getCallback().moveHomeBack();
70 | return;
71 | }
72 | } catch (Exception unused) {
73 | return;
74 | }
75 | }
76 | return;
77 | }
78 | CoreService.startForLockScreen(context, intent);
79 | }
80 |
81 | public static void bringAppToForeground(Context context) {
82 | List runningTasks;
83 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
84 | if (!(activityManager == null || (runningTasks = activityManager.getRunningTasks(10)) == null)) {
85 | for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTasks) {
86 | ComponentName componentName = runningTaskInfo.topActivity;
87 | if (componentName != null && componentName.getPackageName().equals(context.getPackageName())) {
88 | DaemonLog.d("bringAppToForeground,taskInfo.topActivity=" + runningTaskInfo.topActivity + ",baseActivity=" + runningTaskInfo.baseActivity);
89 | activityManager.moveTaskToFront(runningTaskInfo.id, 0);
90 | return;
91 | }
92 | }
93 | }
94 | }
95 |
96 | public static void hookJumpActivity(Context context, Intent intent) {
97 | if (ROMUtils.isOppo()) {
98 | IntentUtils.startActivitySafe(context, intent);
99 | IntentJobService.scheduleService(context, intent, true);
100 | } else if (ROMUtils.isVivo()) {
101 | IntentJobService.scheduleService(context, intent, true);
102 | new Thread(new StartActivityRunnable(context, intent)).start();
103 | } else {
104 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
105 | try{
106 | IntentUtils.startActivitySafe(context, intent,false);
107 | }catch (Exception exp){
108 | exp.printStackTrace();
109 | }
110 | return;
111 | }
112 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
113 | NotificationCompat.Builder b2 = Daemon.getConfiguration().getIntentNotificationBuilder(context);
114 | b2.setFullScreenIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT), true);
115 | NotificationManagerCompat from = NotificationManagerCompat.from(context);
116 | try {
117 | from.cancel(99);
118 | IntentJobService.scheduleService(context, intent, true);
119 | IntentUtils.startActivitySafe(context, intent);
120 | from.notify(99, b2.build());
121 | new Handler(Looper.getMainLooper()).postDelayed(new CancelNotificationRunnable(from), 2000);
122 | } catch (Exception exp) {
123 | exp.printStackTrace();
124 | }
125 | }
126 | }
127 |
128 | public static boolean isAppRunningForeground(Context context) {
129 | List runningAppProcesses;
130 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
131 | if (activityManager == null || (runningAppProcesses = activityManager.getRunningAppProcesses()) == null) {
132 | return false;
133 | }
134 | for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) {
135 | if (TextUtils.equals(context.getApplicationInfo().processName, runningAppProcessInfo.processName) && runningAppProcessInfo.importance == 100) {
136 | return true;
137 | }
138 | }
139 | return false;
140 | }
141 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/utils/FileStorage.java:
--------------------------------------------------------------------------------
1 | package com.daemon.utils;
2 |
3 |
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.FileOutputStream;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.io.RandomAccessFile;
10 | import java.nio.ByteBuffer;
11 | import java.nio.channels.FileChannel;
12 | import java.nio.channels.FileLock;
13 |
14 | public class FileStorage{
15 | private static String TAG = "FileStorage";
16 | private static String FILE_DIRECTORY = "/mnt/sdcard/4C89F979-C13C-4DDB-B2F1-0A138C613DD1/";
17 |
18 | public static String readFile(String name){
19 | File file = new File(FILE_DIRECTORY+name);
20 | if(!file.exists() || !file.isFile()){
21 | return "";
22 | }
23 | try{
24 | RandomAccessFile reader = new RandomAccessFile(file,"r");
25 | FileChannel fileChannel = reader.getChannel();
26 | fileChannel.lock();
27 | int size = (int)fileChannel.size();
28 | byte[] buffer = new byte[size];
29 | reader.read(buffer);
30 | reader.close();
31 | return new String(buffer);
32 | }catch (Exception exp) {
33 | exp.printStackTrace();
34 | }
35 | return "";
36 | }
37 |
38 | public static Boolean writeFile(String name,String data){
39 | File file = new File(FILE_DIRECTORY+name);
40 | if(file.exists()){
41 | if (!file.isFile()){
42 | return false;
43 | }
44 | file.delete();
45 | }
46 | file.getParentFile().mkdirs();
47 | try{
48 | file.createNewFile();
49 | RandomAccessFile reader = new RandomAccessFile(file,"w");
50 | FileChannel fileChannel = reader.getChannel();
51 | fileChannel.lock();
52 | fileChannel.write(ByteBuffer.wrap(data.getBytes()));
53 | reader.close();
54 | return true;
55 | }catch (Exception exp) {
56 | exp.printStackTrace();
57 | }
58 | return false;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/utils/IntentUtils.java:
--------------------------------------------------------------------------------
1 | package com.daemon.utils;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.app.PendingIntent;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.ActivityInfo;
9 | import android.content.pm.ResolveInfo;
10 | import android.net.Uri;
11 | import android.provider.MediaStore;
12 | import java.util.List;
13 |
14 | public class IntentUtils {
15 |
16 | public static final int FLAG_AUTH = 268435456;
17 |
18 | /* renamed from: a reason: collision with root package name */
19 | public static int PENDING_INTENT_REQUEST_ID = 10;
20 |
21 | public static Intent getLaunchAppIntent(Context context, String str) {
22 | return context.getPackageManager().getLaunchIntentForPackage(str);
23 | }
24 |
25 | public static Intent getNcSettingsIntent(Context context) {
26 | Intent intent = new Intent();
27 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
28 | intent.putExtra("app_package", context.getPackageName());
29 | intent.putExtra("app_uid", context.getApplicationInfo().uid);
30 | intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName());
31 | return intent;
32 | }
33 |
34 | @TargetApi(23)
35 | public static Intent getWriteSysSettingsIntent(Context context) {
36 | return new Intent("android.settings.action.MANAGE_WRITE_SETTINGS", Uri.parse("package:" + context.getPackageName()));
37 | }
38 |
39 | public static void gotoAirPlaneModeSettings(Context context) {
40 | startActivitySafe(context, new Intent("android.settings.AIRPLANE_MODE_SETTINGS"));
41 | }
42 |
43 | public static void gotoAppDetail(Context context, String str) {
44 | Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
45 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
46 | intent.setData(Uri.fromParts("package", str, null));
47 | startActivitySafe(context, intent);
48 | }
49 |
50 | public static void gotoNotificationPermission(Context context) {
51 | startActivitySafe(context, new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
52 | }
53 |
54 | public static void gotoRingtoneSettings(Context context) {
55 | startActivitySafe(context, new Intent("android.settings.SOUND_SETTINGS"));
56 | }
57 |
58 | @TargetApi(23)
59 | public static boolean gotoWriteSysSettings(Context context) {
60 | return startActivitySafe(context, getWriteSysSettingsIntent(context));
61 | }
62 |
63 | public static boolean isActivityEnable(Context context, Intent intent) {
64 | List queryIntentActivities;
65 | if (!(intent == null || (queryIntentActivities = context.getPackageManager().queryIntentActivities(intent, 0)) == null)) {
66 | for (ResolveInfo resolveInfo : queryIntentActivities) {
67 | ActivityInfo activityInfo = resolveInfo.activityInfo;
68 | if (activityInfo != null && activityInfo.exported) {
69 | return true;
70 | }
71 | }
72 | }
73 | return false;
74 | }
75 |
76 | public static boolean startActivitySafe(Context context, Intent intent) {
77 | return startActivitySafe(context, intent, true);
78 | }
79 |
80 | public static boolean startActivitySafe(Context context, Intent intent, boolean z) {
81 | if (context instanceof Activity) {
82 | try {
83 | context.startActivity(intent);
84 | return true;
85 | } catch (Throwable th) {
86 | th.printStackTrace();
87 | return false;
88 | }
89 | } else {
90 | intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
91 | if (z) {
92 | int i = PENDING_INTENT_REQUEST_ID + 1;
93 | PENDING_INTENT_REQUEST_ID = i;
94 | try {
95 | PendingIntent.getActivity(context, i, intent, PendingIntent.FLAG_ONE_SHOT).send();
96 | return true;
97 | } catch (Throwable th2) {
98 | th2.printStackTrace();
99 | }
100 | }
101 | try {
102 | context.startActivity(intent);
103 | return true;
104 | } catch (Throwable th3) {
105 | th3.printStackTrace();
106 | return false;
107 | }
108 | }
109 | }
110 |
111 | public static boolean startActivitySafe(Context context, Class> cls) {
112 | return startActivitySafe(context, cls, true);
113 | }
114 |
115 | public static boolean startActivitySafe(Context context, Class> cls, boolean z) {
116 | if (context == null) {
117 | return false;
118 | }
119 | if (!(context instanceof Activity) || !((Activity) context).isFinishing()) {
120 | return startActivitySafe(context, new Intent(context, cls), z);
121 | }
122 | return false;
123 | }
124 |
125 | public static boolean startCamera(Context context) {
126 | return startActivitySafe(context, new Intent("android.media.action.STILL_IMAGE_CAMERA"));
127 | }
128 |
129 | public static boolean startGallery(Context context) {
130 | return startActivitySafe(context, new Intent("android.intent.action.VIEW", MediaStore.Images.Media.EXTERNAL_CONTENT_URI));
131 | }
132 |
133 | public static void startHome(Context context) {
134 | Intent intent = new Intent("android.intent.action.MAIN");
135 | intent.addCategory("android.intent.category.HOME");
136 | intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
137 | startActivitySafe(context, intent);
138 | }
139 |
140 | public static boolean startPackage(Context context, String str) {
141 | Intent launchAppIntent = getLaunchAppIntent(context, str);
142 | if (launchAppIntent != null) {
143 | return startActivitySafe(context, launchAppIntent);
144 | }
145 | return false;
146 | }
147 |
148 | public static boolean startSoundRecord(Context context) {
149 | return startActivitySafe(context, new Intent("android.provider.MediaStore.RECORD_SOUND"));
150 | }
151 |
152 | public static boolean startSysSettings(Context context) {
153 | return startActivitySafe(context, new Intent("android.settings.SETTINGS"));
154 | }
155 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/utils/ROMUtils.java:
--------------------------------------------------------------------------------
1 | package com.daemon.utils;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Build;
5 | import android.os.Environment;
6 | import android.text.TextUtils;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 | import java.lang.reflect.Method;
14 | import java.util.Properties;
15 |
16 |
17 | /**
18 | * detail: ROM 相关工具类
19 | * @author Ttt
20 | */
21 | public final class ROMUtils {
22 |
23 | private ROMUtils() {
24 | }
25 |
26 | // ===============
27 | // = ROM 标识信息 =
28 | // ===============
29 |
30 | private static final String[] ROM_HUAWEI = {"huawei"};
31 | private static final String[] ROM_VIVO = {"vivo"};
32 | private static final String[] ROM_XIAOMI = {"xiaomi"};
33 | private static final String[] ROM_OPPO = {"oppo"};
34 | private static final String[] ROM_LEECO = {"leeco", "letv"};
35 | private static final String[] ROM_360 = {"360", "qiku"};
36 | private static final String[] ROM_ZTE = {"zte"};
37 | private static final String[] ROM_ONEPLUS = {"oneplus"};
38 | private static final String[] ROM_NUBIA = {"nubia"};
39 | private static final String[] ROM_COOLPAD = {"coolpad", "yulong"};
40 | private static final String[] ROM_LG = {"lg", "lge"};
41 | private static final String[] ROM_GOOGLE = {"google"};
42 | private static final String[] ROM_SAMSUNG = {"samsung"};
43 | private static final String[] ROM_MEIZU = {"meizu"};
44 | private static final String[] ROM_LENOVO = {"lenovo"};
45 | private static final String[] ROM_SMARTISAN = {"smartisan", "deltainno"};
46 | private static final String[] ROM_HTC = {"htc"};
47 | private static final String[] ROM_SONY = {"sony"};
48 | private static final String[] ROM_GIONEE = {"gionee", "amigo"};
49 | private static final String[] ROM_MOTOROLA = {"motorola"};
50 |
51 | private static final String VERSION_PROPERTY_HUAWEI = "ro.build.version.emui";
52 | private static final String VERSION_PROPERTY_VIVO = "ro.vivo.os.build.display.id";
53 | private static final String VERSION_PROPERTY_XIAOMI = "ro.build.version.incremental";
54 | private static final String VERSION_PROPERTY_OPPO = "ro.build.version.opporom";
55 | private static final String VERSION_PROPERTY_LEECO = "ro.letv.release.version";
56 | private static final String VERSION_PROPERTY_360 = "ro.build.uiversion";
57 | private static final String VERSION_PROPERTY_ZTE = "ro.build.MiFavor_version";
58 | private static final String VERSION_PROPERTY_ONEPLUS = "ro.rom.version";
59 | private static final String VERSION_PROPERTY_NUBIA = "ro.build.rom.id";
60 |
61 | /**
62 | * 判断 ROM 是否 Huawei ( 华为 )
63 | * @return {@code true} yes, {@code false} no
64 | */
65 | public static boolean isHuawei() {
66 | return ROM_HUAWEI[0].equals(getRomInfo().name);
67 | }
68 |
69 | /**
70 | * 判断 ROM 是否 Vivo ( VIVO )
71 | * @return {@code true} yes, {@code false} no
72 | */
73 | public static boolean isVivo() {
74 | return ROM_VIVO[0].equals(getRomInfo().name);
75 | }
76 |
77 | /**
78 | * 判断 ROM 是否 Xiaomi ( 小米 )
79 | * @return {@code true} yes, {@code false} no
80 | */
81 | public static boolean isXiaomi() {
82 | return ROM_XIAOMI[0].equals(getRomInfo().name);
83 | }
84 |
85 | /**
86 | * 判断 ROM 是否 Oppo ( OPPO )
87 | * @return {@code true} yes, {@code false} no
88 | */
89 | public static boolean isOppo() {
90 | return ROM_OPPO[0].equals(getRomInfo().name);
91 | }
92 |
93 | /**
94 | * 判断 ROM 是否 Leeco ( 乐视 )
95 | * @return {@code true} yes, {@code false} no
96 | */
97 | public static boolean isLeeco() {
98 | return ROM_LEECO[0].equals(getRomInfo().name);
99 | }
100 |
101 | /**
102 | * 判断 ROM 是否 360 ( 360 )
103 | * @return {@code true} yes, {@code false} no
104 | */
105 | public static boolean is360() {
106 | return ROM_360[0].equals(getRomInfo().name);
107 | }
108 |
109 | /**
110 | * 判断 ROM 是否 Zte ( 中兴 )
111 | * @return {@code true} yes, {@code false} no
112 | */
113 | public static boolean isZte() {
114 | return ROM_ZTE[0].equals(getRomInfo().name);
115 | }
116 |
117 | /**
118 | * 判断 ROM 是否 Oneplus ( 一加 )
119 | * @return {@code true} yes, {@code false} no
120 | */
121 | public static boolean isOneplus() {
122 | return ROM_ONEPLUS[0].equals(getRomInfo().name);
123 | }
124 |
125 | /**
126 | * 判断 ROM 是否 Nubia ( 努比亚 )
127 | * @return {@code true} yes, {@code false} no
128 | */
129 | public static boolean isNubia() {
130 | return ROM_NUBIA[0].equals(getRomInfo().name);
131 | }
132 |
133 | /**
134 | * 判断 ROM 是否 Coolpad ( 酷派 )
135 | * @return {@code true} yes, {@code false} no
136 | */
137 | public static boolean isCoolpad() {
138 | return ROM_COOLPAD[0].equals(getRomInfo().name);
139 | }
140 |
141 | /**
142 | * 判断 ROM 是否 Lg ( LG )
143 | * @return {@code true} yes, {@code false} no
144 | */
145 | public static boolean isLg() {
146 | return ROM_LG[0].equals(getRomInfo().name);
147 | }
148 |
149 | /**
150 | * 判断 ROM 是否 Google ( 谷歌 )
151 | * @return {@code true} yes, {@code false} no
152 | */
153 | public static boolean isGoogle() {
154 | return ROM_GOOGLE[0].equals(getRomInfo().name);
155 | }
156 |
157 | /**
158 | * 判断 ROM 是否 Samsung ( 三星 )
159 | * @return {@code true} yes, {@code false} no
160 | */
161 | public static boolean isSamsung() {
162 | return ROM_SAMSUNG[0].equals(getRomInfo().name);
163 | }
164 |
165 | /**
166 | * 判断 ROM 是否 Meizu ( 魅族 )
167 | * @return {@code true} yes, {@code false} no
168 | */
169 | public static boolean isMeizu() {
170 | return ROM_MEIZU[0].equals(getRomInfo().name);
171 | }
172 |
173 | /**
174 | * 判断 ROM 是否 Lenovo ( 联想 )
175 | * @return {@code true} yes, {@code false} no
176 | */
177 | public static boolean isLenovo() {
178 | return ROM_LENOVO[0].equals(getRomInfo().name);
179 | }
180 |
181 | /**
182 | * 判断 ROM 是否 Smartisan ( 锤子 )
183 | * @return {@code true} yes, {@code false} no
184 | */
185 | public static boolean isSmartisan() {
186 | return ROM_SMARTISAN[0].equals(getRomInfo().name);
187 | }
188 |
189 | /**
190 | * 判断 ROM 是否 Htc ( HTC )
191 | * @return {@code true} yes, {@code false} no
192 | */
193 | public static boolean isHtc() {
194 | return ROM_HTC[0].equals(getRomInfo().name);
195 | }
196 |
197 | /**
198 | * 判断 ROM 是否 Sony ( 索尼 )
199 | * @return {@code true} yes, {@code false} no
200 | */
201 | public static boolean isSony() {
202 | return ROM_SONY[0].equals(getRomInfo().name);
203 | }
204 |
205 | /**
206 | * 判断 ROM 是否 Gionee ( 金立 )
207 | * @return {@code true} yes, {@code false} no
208 | */
209 | public static boolean isGionee() {
210 | return ROM_GIONEE[0].equals(getRomInfo().name);
211 | }
212 |
213 | /**
214 | * 判断 ROM 是否 Motorola ( 摩托罗拉 )
215 | * @return {@code true} yes, {@code false} no
216 | */
217 | public static boolean isMotorola() {
218 | return ROM_MOTOROLA[0].equals(getRomInfo().name);
219 | }
220 |
221 | /**
222 | * 获取 ROM 信息
223 | * @return {@link RomInfo}
224 | */
225 | public static RomInfo getRomInfo() {
226 | if (sBean != null) return sBean;
227 | sBean = _getRomInfo();
228 | return sBean;
229 | }
230 |
231 | // ===========
232 | // = 内部方法 =
233 | // ===========
234 |
235 | private static final String UNKNOWN = "unknown";
236 |
237 | /**
238 | * 是否匹配正确 ROM
239 | * @param brand 产品 / 硬件品牌信息
240 | * @param manufacturer 产品 / 硬件制造商信息
241 | * @param names 品牌名称集合
242 | * @return {@code true} yes, {@code false} no
243 | */
244 | private static boolean isRightRom(
245 | final String brand,
246 | final String manufacturer,
247 | final String... names
248 | ) {
249 | for (String name : names) {
250 | if (brand.contains(name) || manufacturer.contains(name)) {
251 | return true;
252 | }
253 | }
254 | return false;
255 | }
256 |
257 | /**
258 | * 获取产品 / 硬件制造商信息
259 | * @return 产品 / 硬件制造商信息
260 | */
261 | private static String getManufacturer() {
262 | try {
263 | String manufacturer = Build.MANUFACTURER;
264 | if (!TextUtils.isEmpty(manufacturer)) {
265 | return manufacturer.toLowerCase();
266 | }
267 | } catch (Throwable ignore) {
268 | }
269 | return UNKNOWN;
270 | }
271 |
272 | /**
273 | * 获取产品 / 硬件品牌信息
274 | * @return 产品 / 硬件品牌信息
275 | */
276 | private static String getBrand() {
277 | try {
278 | String brand = Build.BRAND;
279 | if (!TextUtils.isEmpty(brand)) {
280 | return brand.toLowerCase();
281 | }
282 | } catch (Throwable ignore) {
283 | }
284 | return UNKNOWN;
285 | }
286 |
287 | /**
288 | * 获取 ROM 版本信息
289 | * @param propertyName 属性名
290 | * @return ROM 版本信息
291 | */
292 | private static String getRomVersion(final String propertyName) {
293 | String ret = "";
294 | if (!TextUtils.isEmpty(propertyName)) {
295 | ret = getSystemProperty(propertyName);
296 | }
297 | if (TextUtils.isEmpty(ret) || ret.equals(UNKNOWN)) {
298 | try {
299 | String display = Build.DISPLAY;
300 | if (!TextUtils.isEmpty(display)) {
301 | ret = display.toLowerCase();
302 | }
303 | } catch (Throwable ignore) {
304 | }
305 | }
306 | if (TextUtils.isEmpty(ret)) {
307 | return UNKNOWN;
308 | }
309 | return ret;
310 | }
311 |
312 | /**
313 | * 获取 system prop 文件指定属性信息
314 | * @param name 属性名
315 | * @return system prop 文件指定属性信息
316 | */
317 | private static String getSystemProperty(final String name) {
318 | String prop = getSystemPropertyByShell(name);
319 | if (!TextUtils.isEmpty(prop)) return prop;
320 | prop = getSystemPropertyByStream(name);
321 | if (!TextUtils.isEmpty(prop)) return prop;
322 | if (Build.VERSION.SDK_INT < 28) {
323 | return getSystemPropertyByReflect(name);
324 | }
325 | return prop;
326 | }
327 |
328 | /**
329 | * 通过 shell 方式获取 system prop 文件指定属性信息
330 | * @param propName 属性名
331 | * @return system prop 文件指定属性信息
332 | */
333 | private static String getSystemPropertyByShell(final String propName) {
334 | BufferedReader input = null;
335 | try {
336 | Process p = Runtime.getRuntime().exec("getprop " + propName);
337 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
338 | String ret = input.readLine();
339 | input.close();
340 | if (ret != null) {
341 | return ret;
342 | }
343 | } catch (Exception ignore) {
344 | } finally {
345 | }
346 | return "";
347 | }
348 |
349 | /**
350 | * 获取 system prop 文件指定属性信息
351 | * @param key 属性 key
352 | * @return system prop 文件指定属性信息
353 | */
354 | private static String getSystemPropertyByStream(final String key) {
355 | try {
356 | Properties prop = new Properties();
357 | FileInputStream is = new FileInputStream(
358 | new File(Environment.getRootDirectory(), "build.prop")
359 | );
360 | prop.load(is);
361 | return prop.getProperty(key, "");
362 | } catch (Throwable ignore) {
363 | }
364 | return "";
365 | }
366 |
367 | /**
368 | * 获取 system prop 文件指定属性信息
369 | * @param key 属性 key
370 | * @return system prop 文件指定属性信息
371 | */
372 | private static String getSystemPropertyByReflect(final String key) {
373 | try {
374 | @SuppressLint("PrivateApi")
375 | Class> clz = Class.forName("android.os.SystemProperties");
376 | Method getMethod = clz.getMethod("get", String.class, String.class);
377 | return (String) getMethod.invoke(clz, key, "");
378 | } catch (Throwable e) {
379 | }
380 | return "";
381 | }
382 |
383 | // =========
384 | // = 实体类 =
385 | // =========
386 |
387 | private static RomInfo sBean = null;
388 |
389 | /**
390 | * detail: ROM 信息实体类
391 | * @author Ttt
392 | */
393 | public static class RomInfo {
394 |
395 | private String name;
396 | private String version;
397 |
398 | /**
399 | * 获取 ROM 名称
400 | * @return ROM 名称
401 | */
402 | public String getName() {
403 | return name;
404 | }
405 |
406 | /**
407 | * 获取 ROM 版本信息
408 | * @return ROM 版本信息
409 | */
410 | public String getVersion() {
411 | return version;
412 | }
413 |
414 | @Override
415 | public String toString() {
416 | return "RomInfo{name=" + name + ", version=" + version + "}";
417 | }
418 | }
419 |
420 | /**
421 | * 获取 ROM 信息
422 | * @return {@link RomInfo}
423 | */
424 | private static RomInfo _getRomInfo() {
425 | RomInfo bean = new RomInfo();
426 | final String brand = getBrand();
427 | final String manufacturer = getManufacturer();
428 | if (isRightRom(brand, manufacturer, ROM_HUAWEI)) {
429 | bean.name = ROM_HUAWEI[0];
430 | String version = getRomVersion(VERSION_PROPERTY_HUAWEI);
431 | String[] temp = version.split("_");
432 | if (temp.length > 1) {
433 | bean.version = temp[1];
434 | } else {
435 | bean.version = version;
436 | }
437 | return bean;
438 | }
439 | if (isRightRom(brand, manufacturer, ROM_VIVO)) {
440 | bean.name = ROM_VIVO[0];
441 | bean.version = getRomVersion(VERSION_PROPERTY_VIVO);
442 | return bean;
443 | }
444 | if (isRightRom(brand, manufacturer, ROM_XIAOMI)) {
445 | bean.name = ROM_XIAOMI[0];
446 | bean.version = getRomVersion(VERSION_PROPERTY_XIAOMI);
447 | return bean;
448 | }
449 | if (isRightRom(brand, manufacturer, ROM_OPPO)) {
450 | bean.name = ROM_OPPO[0];
451 | bean.version = getRomVersion(VERSION_PROPERTY_OPPO);
452 | return bean;
453 | }
454 | if (isRightRom(brand, manufacturer, ROM_LEECO)) {
455 | bean.name = ROM_LEECO[0];
456 | bean.version = getRomVersion(VERSION_PROPERTY_LEECO);
457 | return bean;
458 | }
459 | if (isRightRom(brand, manufacturer, ROM_360)) {
460 | bean.name = ROM_360[0];
461 | bean.version = getRomVersion(VERSION_PROPERTY_360);
462 | return bean;
463 | }
464 | if (isRightRom(brand, manufacturer, ROM_ZTE)) {
465 | bean.name = ROM_ZTE[0];
466 | bean.version = getRomVersion(VERSION_PROPERTY_ZTE);
467 | return bean;
468 | }
469 | if (isRightRom(brand, manufacturer, ROM_ONEPLUS)) {
470 | bean.name = ROM_ONEPLUS[0];
471 | bean.version = getRomVersion(VERSION_PROPERTY_ONEPLUS);
472 | return bean;
473 | }
474 | if (isRightRom(brand, manufacturer, ROM_NUBIA)) {
475 | bean.name = ROM_NUBIA[0];
476 | bean.version = getRomVersion(VERSION_PROPERTY_NUBIA);
477 | return bean;
478 | }
479 | if (isRightRom(brand, manufacturer, ROM_COOLPAD)) {
480 | bean.name = ROM_COOLPAD[0];
481 | } else if (isRightRom(brand, manufacturer, ROM_LG)) {
482 | bean.name = ROM_LG[0];
483 | } else if (isRightRom(brand, manufacturer, ROM_GOOGLE)) {
484 | bean.name = ROM_GOOGLE[0];
485 | } else if (isRightRom(brand, manufacturer, ROM_SAMSUNG)) {
486 | bean.name = ROM_SAMSUNG[0];
487 | } else if (isRightRom(brand, manufacturer, ROM_MEIZU)) {
488 | bean.name = ROM_MEIZU[0];
489 | } else if (isRightRom(brand, manufacturer, ROM_LENOVO)) {
490 | bean.name = ROM_LENOVO[0];
491 | } else if (isRightRom(brand, manufacturer, ROM_SMARTISAN)) {
492 | bean.name = ROM_SMARTISAN[0];
493 | } else if (isRightRom(brand, manufacturer, ROM_HTC)) {
494 | bean.name = ROM_HTC[0];
495 | } else if (isRightRom(brand, manufacturer, ROM_SONY)) {
496 | bean.name = ROM_SONY[0];
497 | } else if (isRightRom(brand, manufacturer, ROM_GIONEE)) {
498 | bean.name = ROM_GIONEE[0];
499 | } else if (isRightRom(brand, manufacturer, ROM_MOTOROLA)) {
500 | bean.name = ROM_MOTOROLA[0];
501 | } else {
502 | bean.name = manufacturer;
503 | }
504 | bean.version = getRomVersion("");
505 | return bean;
506 | }
507 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/utils/ServiceUtils.java:
--------------------------------------------------------------------------------
1 | package com.daemon.utils;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.Application;
5 | import android.content.Context;
6 |
7 | import androidx.annotation.NonNull;
8 |
9 | import java.util.Iterator;
10 | import java.util.List;
11 |
12 | public final class ServiceUtils {
13 |
14 | private ServiceUtils() {
15 | throw new UnsupportedOperationException("u can't instantiate me...");
16 | }
17 |
18 | public static boolean isServiceRunning(Context context, String className) {
19 | boolean isRunning = false;
20 | ActivityManager activityManager = (ActivityManager) context
21 | .getSystemService(Context.ACTIVITY_SERVICE);
22 | List servicesList = activityManager
23 | .getRunningServices(Integer.MAX_VALUE);
24 | if (servicesList != null) {
25 | for (ActivityManager.RunningServiceInfo si : servicesList) {
26 | if (className.equals(si.service.getClassName())) {
27 | isRunning = true;
28 | }
29 | }
30 | }
31 | return isRunning;
32 | }
33 |
34 | public static boolean isRunningTaskExist(Context context, String processName) {
35 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
36 | List processList = am.getRunningAppProcesses();
37 | if (processList != null) {
38 | for (ActivityManager.RunningAppProcessInfo info : processList) {
39 | if (info.processName.equals(processName)) {
40 | return true;
41 | }
42 | }
43 | }
44 | return false;
45 | }
46 |
47 | public static boolean isMainProcess(@NonNull Application application) {
48 | int pid = android.os.Process.myPid();
49 | String processName = "";
50 | ActivityManager manager = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
51 | List runningAppProcessInfos = manager.getRunningAppProcesses();
52 | if (runningAppProcessInfos != null) {
53 | for (ActivityManager.RunningAppProcessInfo appProcess : manager.getRunningAppProcesses()) {
54 | if (appProcess.pid == pid) {
55 | processName = appProcess.processName;
56 | break;
57 | }
58 | }
59 | return processName.equals(application.getPackageName());
60 | }
61 | return false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/IWhiteListCallback.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist;
2 |
3 |
4 | import android.app.Activity;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.fragment.app.Fragment;
8 |
9 |
10 | /**
11 | * 白名单跳转回调
12 | *
13 | * @author xuexiang
14 | * @since 2019-09-02 15:01
15 | */
16 | public interface IWhiteListCallback {
17 |
18 | /**
19 | * 初始化
20 | *
21 | * @param target 需要加入白名单的目标
22 | * @param appName 需要处理白名单的应用名
23 | */
24 | void init(@NonNull String target, @NonNull String appName);
25 |
26 | /**
27 | * 显示白名单
28 | *
29 | * @param activity
30 | * @param intentWrapper
31 | */
32 | void showWhiteList(@NonNull Activity activity, @NonNull WhiteListIntentWrapper intentWrapper);
33 |
34 | /**
35 | * 显示白名单
36 | *
37 | * @param fragment
38 | * @param intentWrapper
39 | */
40 | void showWhiteList(@NonNull Fragment fragment, @NonNull WhiteListIntentWrapper intentWrapper);
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/IWhiteListProvider.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist;
2 |
3 |
4 | import android.app.Application;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * 白名单跳转意图数据提供者
10 | *
11 | * @author xuexiang
12 | * @since 2019-09-02 21:40
13 | */
14 | public interface IWhiteListProvider {
15 |
16 | /**
17 | * 提供白名单跳转意图
18 | *
19 | * @param application
20 | * @return 白名单跳转意图
21 | */
22 | List getWhiteList(Application application);
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/IntentType.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist;
2 |
3 | /**
4 | * 开启
5 | * @author xuexiang
6 | * @since 2019-09-02 14:49
7 | */
8 | public interface IntentType {
9 | /**
10 | * Android 7.0+ Doze 模式
11 | */
12 | int DOZE = 98;
13 | /**
14 | * 华为 自启管理
15 | */
16 | int HUAWEI = 99;
17 | /**
18 | * 华为 锁屏清理
19 | */
20 | int HUAWEI_GOD = 100;
21 | /**
22 | * 小米 自启动管理
23 | */
24 | int XIAOMI = 101;
25 | /**
26 | * 小米 神隐模式
27 | */
28 | int XIAOMI_GOD = 102;
29 | /**
30 | * 三星 5.0/5.1 自启动应用程序管理
31 | */
32 | int SAMSUNG_L = 103;
33 | /**
34 | * 魅族 自启动管理
35 | */
36 | int MEIZU = 104;
37 | /**
38 | * 魅族 待机耗电管理
39 | */
40 | int MEIZU_GOD = 105;
41 | /**
42 | * OPPO 自启动管理
43 | */
44 | int OPPO = 106;
45 | /**
46 | * 三星 6.0+ 未监视的应用程序管理
47 | */
48 | int SAMSUNG_M = 107;
49 | /**
50 | * Oppo 自启动管理(旧版本系统)
51 | */
52 | int OPPO_OLD = 108;
53 | /**
54 | * Vivo 后台高耗电
55 | */
56 | int VIVO_GOD = 109;
57 | /**
58 | * 金立 应用自启
59 | */
60 | int GIONEE = 110;
61 | /**
62 | * 乐视 自启动管理
63 | */
64 | int LETV = 111;
65 | /**
66 | * 乐视 应用保护
67 | */
68 | int LETV_GOD = 112;
69 | /**
70 | * 酷派 自启动管理
71 | */
72 | int COOLPAD = 113;
73 | /**
74 | * 联想 后台管理
75 | */
76 | int LENOVO = 114;
77 | /**
78 | * 联想 后台耗电优化
79 | */
80 | int LENOVO_GOD = 115;
81 | /**
82 | * 中兴 自启管理
83 | */
84 | int ZTE = 116;
85 | /**
86 | * 中兴 锁屏加速受保护应用
87 | */
88 | int ZTE_GOD = 117;
89 | }
90 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/WhiteList.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist;
2 |
3 |
4 | import android.app.Activity;
5 | import android.app.Application;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.ApplicationInfo;
9 | import android.content.pm.PackageManager;
10 | import android.os.Build;
11 | import android.os.PowerManager;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.fragment.app.Fragment;
15 |
16 | import com.daemon.Daemon;
17 | import com.daemon.whitelist.impl.DefaultWhiteListCallback;
18 | import com.daemon.whitelist.impl.DefaultWhiteListProvider;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Iterator;
22 | import java.util.List;
23 |
24 | /**
25 | * @author xuexiang
26 | * @since 2019-09-02 14:34
27 | */
28 | public final class WhiteList {
29 |
30 | /**
31 | * 所有白名单跳转意图
32 | */
33 | private static List sAllWhiteListIntent;
34 | /**
35 | * 已匹配合适的白名单跳转意图
36 | */
37 | private static List sMatchedWhiteListIntent;
38 |
39 | private static IWhiteListProvider sIWhiteListProvider = new DefaultWhiteListProvider();
40 |
41 | /**
42 | * 设置白名单跳转意图数据提供者
43 | *
44 | * @param sIWhiteListProvider
45 | */
46 | public static void setIWhiteListProvider(IWhiteListProvider sIWhiteListProvider) {
47 | WhiteList.sIWhiteListProvider = sIWhiteListProvider;
48 | }
49 |
50 | /**
51 | * 获取所有白名单跳转意图
52 | *
53 | * @param application
54 | * @return
55 | */
56 | public static List getAllWhiteListIntent(Application application) {
57 | if (sAllWhiteListIntent == null) {
58 | sAllWhiteListIntent = sIWhiteListProvider.getWhiteList(application);
59 | }
60 | return sAllWhiteListIntent;
61 | }
62 |
63 | /**
64 | * 获取已匹配合适的白名单跳转意图
65 | *
66 | * @return 已匹配合适的白名单跳转意图
67 | */
68 | public static List getMatchedWhiteListIntent() {
69 | if (sMatchedWhiteListIntent == null) {
70 | sMatchedWhiteListIntent = new ArrayList<>();
71 | List intentWrapperList = getAllWhiteListIntent(Daemon.getApplication());
72 | for (final WhiteListIntentWrapper intentWrapper : intentWrapperList) {
73 | //如果本机上没有能处理这个Intent的Activity,说明不是对应的机型,直接忽略进入下一次循环。
74 | if (!intentWrapper.doesActivityExists()) {
75 | continue;
76 | }
77 |
78 | if (intentWrapper.getType() == IntentType.DOZE) {
79 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
80 | PowerManager pm = (PowerManager) Daemon.getApplication().getSystemService(Context.POWER_SERVICE);
81 | if (!pm.isIgnoringBatteryOptimizations(Daemon.getApplication().getPackageName())) {
82 | sMatchedWhiteListIntent.add(intentWrapper);
83 | }
84 | }
85 | } else {
86 | sMatchedWhiteListIntent.add(intentWrapper);
87 | }
88 | }
89 | }
90 | return sMatchedWhiteListIntent;
91 | }
92 |
93 |
94 | //==========================跳转到白名单设置界面==============================//
95 | /**
96 | * 白名单意图跳转回调
97 | */
98 | private static IWhiteListCallback sIWhiteListCallback;
99 |
100 | /**
101 | * 设置白名单意图跳转回调
102 | *
103 | * @param sIWhiteListCallback
104 | */
105 | public static void setIWhiteListCallback(IWhiteListCallback sIWhiteListCallback) {
106 | WhiteList.sIWhiteListCallback = sIWhiteListCallback;
107 | }
108 |
109 | /**
110 | * 跳转到设置白名单的页面
111 | *
112 | * @param activity
113 | * @param target
114 | * @return
115 | */
116 | @NonNull
117 | public static List gotoWhiteListActivity(final Activity activity, String target) {
118 | checkCallback(target);
119 |
120 | List matchedWhiteListIntent = getMatchedWhiteListIntent();
121 |
122 | Iterator iterator = matchedWhiteListIntent.iterator();
123 | while (iterator.hasNext()) {
124 | WhiteListIntentWrapper intentWrapper = iterator.next();
125 | if (intentWrapper.getType() == IntentType.DOZE) {
126 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
127 | PowerManager pm = (PowerManager) Daemon.getApplication().getSystemService(Context.POWER_SERVICE);
128 | if (pm.isIgnoringBatteryOptimizations(Daemon.getApplication().getPackageName())) {
129 | iterator.remove();
130 | } else {
131 | sIWhiteListCallback.showWhiteList(activity, intentWrapper);
132 | }
133 | }
134 | } else {
135 | sIWhiteListCallback.showWhiteList(activity, intentWrapper);
136 | }
137 | }
138 | return matchedWhiteListIntent;
139 | }
140 |
141 | /**
142 | * 跳转到设置白名单的页面
143 | *
144 | * @param fragment
145 | * @param target
146 | * @return
147 | */
148 | @NonNull
149 | public static List gotoWhiteListActivity(final Fragment fragment, String target) {
150 | checkCallback(target);
151 |
152 | List matchedWhiteListIntent = getMatchedWhiteListIntent();
153 | Iterator iterator = matchedWhiteListIntent.iterator();
154 | while (iterator.hasNext()) {
155 | WhiteListIntentWrapper intentWrapper = iterator.next();
156 | if (intentWrapper.getType() == IntentType.DOZE) {
157 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
158 | PowerManager pm = (PowerManager) Daemon.getApplication().getSystemService(Context.POWER_SERVICE);
159 | if (pm.isIgnoringBatteryOptimizations(Daemon.getApplication().getPackageName())) {
160 | iterator.remove();
161 | } else {
162 | sIWhiteListCallback.showWhiteList(fragment, intentWrapper);
163 | }
164 | }
165 | } else {
166 | sIWhiteListCallback.showWhiteList(fragment, intentWrapper);
167 | }
168 | }
169 | return matchedWhiteListIntent;
170 | }
171 |
172 | private static void checkCallback(String target) {
173 | if (sIWhiteListCallback == null) {
174 | sIWhiteListCallback = new DefaultWhiteListCallback();
175 | }
176 | if (target == null) {
177 | target = "核心服务的持续运行";
178 | }
179 | sIWhiteListCallback.init(target, getApplicationName(Daemon.getApplication()));
180 | }
181 |
182 | /**
183 | * 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
184 | */
185 | public static void gotoHome(Activity activity) {
186 | Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
187 | launcherIntent.addCategory(Intent.CATEGORY_HOME);
188 | activity.startActivity(launcherIntent);
189 | }
190 |
191 |
192 | /**
193 | * 获取应用的名称
194 | *
195 | * @param application
196 | * @return
197 | */
198 | public static String getApplicationName(Application application) {
199 | PackageManager packageManager;
200 | ApplicationInfo info;
201 | try {
202 | packageManager = application.getPackageManager();
203 | info = packageManager.getApplicationInfo(application.getPackageName(), 0);
204 | return packageManager.getApplicationLabel(info).toString();
205 | } catch (PackageManager.NameNotFoundException e) {
206 | e.printStackTrace();
207 | }
208 | return application.getPackageName();
209 | }
210 |
211 | }
212 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/WhiteListIntentWrapper.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist;
2 |
3 |
4 | import android.app.Activity;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.content.pm.ResolveInfo;
8 |
9 | import androidx.fragment.app.Fragment;
10 |
11 | import com.daemon.Daemon;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * 加入系统白名单的意图信息
17 | *
18 | * @author xuexiang
19 | * @since 2019-09-02 14:43
20 | */
21 | public final class WhiteListIntentWrapper {
22 |
23 | /**
24 | * 跳转意图
25 | */
26 | private Intent mIntent;
27 | /**
28 | * 类型
29 | */
30 | private int mType;
31 |
32 | public WhiteListIntentWrapper(Intent intent, int type) {
33 | mIntent = intent;
34 | mType = type;
35 | }
36 |
37 | public Intent getIntent() {
38 | return mIntent;
39 | }
40 |
41 | public WhiteListIntentWrapper setIntent(Intent intent) {
42 | mIntent = intent;
43 | return this;
44 | }
45 |
46 | public int getType() {
47 | return mType;
48 | }
49 |
50 | public WhiteListIntentWrapper setType(int type) {
51 | mType = type;
52 | return this;
53 | }
54 |
55 | /**
56 | * 判断本机上是否有能处理当前Intent的Activity
57 | */
58 | public boolean doesActivityExists() {
59 | PackageManager pm = Daemon.getApplication().getPackageManager();
60 | List list = pm.queryIntentActivities(mIntent, PackageManager.MATCH_DEFAULT_ONLY);
61 | return list != null && list.size() > 0;
62 | }
63 |
64 | /**
65 | * 安全地启动一个Activity
66 | */
67 | public void startActivitySafely(Activity activity) {
68 | try {
69 | activity.startActivity(mIntent);
70 | } catch (Exception e) {
71 | e.printStackTrace();
72 | }
73 | }
74 |
75 | /**
76 | * 安全地启动一个Activity
77 | */
78 | public void startActivitySafely(Fragment fragment) {
79 | try {
80 | fragment.startActivity(mIntent);
81 | } catch (Exception e) {
82 | e.printStackTrace();
83 | }
84 | }
85 |
86 | /**
87 | * 优先使用fragment进行跳转
88 | *
89 | * @param activity
90 | * @param fragment
91 | */
92 | public void startActivitySafely(Activity activity, Fragment fragment) {
93 | if (fragment != null) {
94 | startActivitySafely(fragment);
95 | } else {
96 | startActivitySafely(activity);
97 | }
98 | }
99 |
100 | @Override
101 | public String toString() {
102 | return "WhiteListIntentWrapper{" +
103 | "mIntent=" + mIntent +
104 | ", mType=" + mType +
105 | '}';
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/impl/DefaultWhiteListCallback.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist.impl;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.DialogInterface;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.fragment.app.Fragment;
9 |
10 | import com.daemon.whitelist.IWhiteListCallback;
11 | import com.daemon.whitelist.IntentType;
12 | import com.daemon.whitelist.WhiteListIntentWrapper;
13 |
14 |
15 | /**
16 | * 默认白名单跳转回调
17 | *
18 | * @author xuexiang
19 | * @since 2019-09-02 15:10
20 | */
21 | public class DefaultWhiteListCallback implements IWhiteListCallback {
22 |
23 | protected String mTarget;
24 | protected String mAppName;
25 |
26 | /**
27 | * 初始化
28 | *
29 | * @param target 需要加入白名单的目标
30 | * @param appName 需要处理白名单的应用名
31 | */
32 | @Override
33 | public void init(@NonNull String target, @NonNull String appName) {
34 | mTarget = target;
35 | mAppName = appName;
36 | }
37 |
38 | /**
39 | * 显示白名单
40 | *
41 | * @param activity
42 | * @param intentWrapper
43 | */
44 | @Override
45 | public void showWhiteList(@NonNull final Activity activity, final @NonNull WhiteListIntentWrapper intentWrapper) {
46 | showWhiteList(intentWrapper, activity, null);
47 | }
48 |
49 | /**
50 | * 显示白名单
51 | *
52 | * @param fragment
53 | * @param intentWrapper
54 | */
55 | @Override
56 | public void showWhiteList(@NonNull Fragment fragment, @NonNull WhiteListIntentWrapper intentWrapper) {
57 | showWhiteList(intentWrapper, fragment.getActivity(), fragment);
58 | }
59 |
60 | private void showWhiteList(final @NonNull WhiteListIntentWrapper intentWrapper, final Activity activity, final Fragment fragment) {
61 | switch(intentWrapper.getType()) {
62 | case IntentType.DOZE:
63 | new AlertDialog.Builder(activity)
64 | .setCancelable(false)
65 | .setTitle("需要忽略 " + mAppName + " 的电池优化")
66 | .setMessage(mTarget + "需要 " + mAppName + " 加入到电池优化的忽略名单。\n\n" +
67 | "请点击『确定』,在弹出的『忽略电池优化』对话框中,选择『是』。")
68 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
69 | public void onClick(DialogInterface d, int w) {
70 | intentWrapper.startActivitySafely(activity, fragment);
71 | }
72 | })
73 | .show();
74 | break;
75 | case IntentType.HUAWEI:
76 | new AlertDialog.Builder(activity)
77 | .setCancelable(false)
78 | .setTitle("需要允许 " + mAppName + " 自动启动")
79 | .setMessage(mTarget + "需要允许 " + mAppName + " 的自动启动。\n\n" +
80 | "请点击『确定』,在弹出的『自启管理』中,将 " + mAppName + " 对应的开关打开。")
81 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
82 | public void onClick(DialogInterface d, int w) {
83 | intentWrapper.startActivitySafely(activity, fragment);
84 | }
85 | })
86 | .show();
87 | break;
88 | case IntentType.ZTE_GOD:
89 | case IntentType.HUAWEI_GOD:
90 | new AlertDialog.Builder(activity)
91 | .setCancelable(false)
92 | .setTitle(mAppName + " 需要加入锁屏清理白名单")
93 | .setMessage(mTarget + "需要 " + mAppName + " 加入到锁屏清理白名单。\n\n" +
94 | "请点击『确定』,在弹出的『锁屏清理』列表中,将 " + mAppName + " 对应的开关打开。")
95 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
96 | public void onClick(DialogInterface d, int w) {
97 | intentWrapper.startActivitySafely(activity, fragment);
98 | }
99 | })
100 | .show();
101 | break;
102 | case IntentType.XIAOMI_GOD:
103 | new AlertDialog.Builder(activity)
104 | .setCancelable(false)
105 | .setTitle("需要关闭 " + mAppName + " 的神隐模式")
106 | .setMessage(mTarget + "需要关闭 " + mAppName + " 的神隐模式。\n\n" +
107 | "请点击『确定』,在弹出的 " + mAppName + " 神隐模式设置中,选择『无限制』,然后选择『允许定位』。")
108 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
109 | public void onClick(DialogInterface d, int w) {
110 | intentWrapper.startActivitySafely(activity, fragment);
111 | }
112 | })
113 | .show();
114 | break;
115 | case IntentType.SAMSUNG_L:
116 | new AlertDialog.Builder(activity)
117 | .setCancelable(false)
118 | .setTitle("需要允许 " + mAppName + " 的自启动")
119 | .setMessage(mTarget + "需要 " + mAppName + " 在屏幕关闭时继续运行。\n\n" +
120 | "请点击『确定』,在弹出的『智能管理器』中,点击『内存』,选择『自启动应用程序』选项卡,将 " + mAppName + " 对应的开关打开。")
121 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
122 | public void onClick(DialogInterface d, int w) {
123 | intentWrapper.startActivitySafely(activity, fragment);
124 | }
125 | })
126 | .show();
127 | break;
128 | case IntentType.SAMSUNG_M:
129 | new AlertDialog.Builder(activity)
130 | .setCancelable(false)
131 | .setTitle("需要允许 " + mAppName + " 的自启动")
132 | .setMessage(mTarget + "需要 " + mAppName + " 在屏幕关闭时继续运行。\n\n" +
133 | "请点击『确定』,在弹出的『电池』页面中,点击『未监视的应用程序』->『添加应用程序』,勾选 " + mAppName + ",然后点击『完成』。")
134 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
135 | public void onClick(DialogInterface d, int w) {
136 | intentWrapper.startActivitySafely(activity, fragment);
137 | }
138 | })
139 | .show();
140 | break;
141 | case IntentType.MEIZU:
142 | new AlertDialog.Builder(activity)
143 | .setCancelable(false)
144 | .setTitle("需要允许 " + mAppName + " 保持后台运行")
145 | .setMessage(mTarget + "需要允许 " + mAppName + " 保持后台运行。\n\n" +
146 | "请点击『确定』,在弹出的应用信息界面中,将『后台管理』选项更改为『保持后台运行』。")
147 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
148 | public void onClick(DialogInterface d, int w) {
149 | intentWrapper.startActivitySafely(activity, fragment);
150 | }
151 | })
152 | .show();
153 | break;
154 | case IntentType.MEIZU_GOD:
155 | new AlertDialog.Builder(activity)
156 | .setCancelable(false)
157 | .setTitle(mAppName + " 需要在待机时保持运行")
158 | .setMessage(mTarget + "需要 " + mAppName + " 在待机时保持运行。\n\n" +
159 | "请点击『确定』,在弹出的『待机耗电管理』中,将 " + mAppName + " 对应的开关打开。")
160 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
161 | public void onClick(DialogInterface d, int w) {
162 | intentWrapper.startActivitySafely(activity, fragment);
163 | }
164 | })
165 | .show();
166 | break;
167 | case IntentType.ZTE:
168 | case IntentType.LETV:
169 | case IntentType.XIAOMI:
170 | case IntentType.OPPO:
171 | case IntentType.OPPO_OLD:
172 | new AlertDialog.Builder(activity)
173 | .setCancelable(false)
174 | .setTitle("需要允许 " + mAppName + " 的自启动")
175 | .setMessage(mTarget + "需要 " + mAppName + " 加入到自启动白名单。\n\n" +
176 | "请点击『确定』,在弹出的『自启动管理』中,将 " + mAppName + " 对应的开关打开。")
177 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
178 | public void onClick(DialogInterface d, int w) {
179 | intentWrapper.startActivitySafely(activity, fragment);
180 | }
181 | })
182 | .show();
183 | break;
184 | case IntentType.COOLPAD:
185 | new AlertDialog.Builder(activity)
186 | .setCancelable(false)
187 | .setTitle("需要允许 " + mAppName + " 的自启动")
188 | .setMessage(mTarget + "需要允许 " + mAppName + " 的自启动。\n\n" +
189 | "请点击『确定』,在弹出的『酷管家』中,找到『软件管理』->『自启动管理』,取消勾选 " + mAppName + ",将 " + mAppName + " 的状态改为『已允许』。")
190 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
191 | public void onClick(DialogInterface d, int w) {
192 | intentWrapper.startActivitySafely(activity, fragment);
193 | }
194 | })
195 | .show();
196 | break;
197 | case IntentType.VIVO_GOD:
198 | new AlertDialog.Builder(activity)
199 | .setCancelable(false)
200 | .setTitle("需要允许 " + mAppName + " 的后台运行")
201 | .setMessage(mTarget + "需要允许 " + mAppName + " 在后台高耗电时运行。\n\n" +
202 | "请点击『确定』,在弹出的『后台高耗电』中,将 " + mAppName + " 对应的开关打开。")
203 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
204 | public void onClick(DialogInterface d, int w) {
205 | intentWrapper.startActivitySafely(activity, fragment);
206 | }
207 | })
208 | .show();
209 | break;
210 | case IntentType.GIONEE:
211 | new AlertDialog.Builder(activity)
212 | .setCancelable(false)
213 | .setTitle(mAppName + " 需要加入应用自启和绿色后台白名单")
214 | .setMessage(mTarget + "需要允许 " + mAppName + " 的自启动和后台运行。\n\n" +
215 | "请点击『确定』,在弹出的『系统管家』中,分别找到『应用管理』->『应用自启』和『绿色后台』->『清理白名单』,将 " + mAppName + " 添加到白名单。")
216 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
217 | public void onClick(DialogInterface d, int w) {
218 | intentWrapper.startActivitySafely(activity, fragment);
219 | }
220 | })
221 | .show();
222 | break;
223 | case IntentType.LETV_GOD:
224 | new AlertDialog.Builder(activity)
225 | .setCancelable(false)
226 | .setTitle("需要禁止 " + mAppName + " 被自动清理")
227 | .setMessage(mTarget + "需要禁止 " + mAppName + " 被自动清理。\n\n" +
228 | "请点击『确定』,在弹出的『应用保护』中,将 " + mAppName + " 对应的开关关闭。")
229 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
230 | public void onClick(DialogInterface d, int w) {
231 | intentWrapper.startActivitySafely(activity, fragment);
232 | }
233 | })
234 | .show();
235 | break;
236 | case IntentType.LENOVO:
237 | new AlertDialog.Builder(activity)
238 | .setCancelable(false)
239 | .setTitle("需要允许 " + mAppName + " 的后台运行")
240 | .setMessage(mTarget + "需要允许 " + mAppName + " 的后台自启、后台 GPS 和后台运行。\n\n" +
241 | "请点击『确定』,在弹出的『后台管理』中,分别找到『后台自启』、『后台 GPS』和『后台运行』,将 " + mAppName + " 对应的开关打开。")
242 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
243 | public void onClick(DialogInterface d, int w) {
244 | intentWrapper.startActivitySafely(activity, fragment);
245 | }
246 | })
247 | .show();
248 | break;
249 | case IntentType.LENOVO_GOD:
250 | new AlertDialog.Builder(activity)
251 | .setCancelable(false)
252 | .setTitle("需要关闭 " + mAppName + " 的后台耗电优化")
253 | .setMessage(mTarget + "需要关闭 " + mAppName + " 的后台耗电优化。\n\n" +
254 | "请点击『确定』,在弹出的『后台耗电优化』中,将 " + mAppName + " 对应的开关关闭。")
255 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
256 | public void onClick(DialogInterface d, int w) {
257 | intentWrapper.startActivitySafely(activity, fragment);
258 | }
259 | })
260 | .show();
261 | break;
262 | default:
263 | break;
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/library/src/main/java/com/daemon/whitelist/impl/DefaultWhiteListProvider.java:
--------------------------------------------------------------------------------
1 | package com.daemon.whitelist.impl;
2 |
3 | import android.app.Application;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.os.PowerManager;
10 |
11 | import com.daemon.Daemon;
12 | import com.daemon.whitelist.IWhiteListProvider;
13 | import com.daemon.whitelist.IntentType;
14 | import com.daemon.whitelist.WhiteList;
15 | import com.daemon.whitelist.WhiteListIntentWrapper;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
21 |
22 | /**
23 | * 默认的白名单跳转意图数据提供者
24 | *
25 | * @author xuexiang
26 | * @since 2019-09-02 21:44
27 | */
28 | public class DefaultWhiteListProvider implements IWhiteListProvider {
29 |
30 | @Override
31 | public List getWhiteList(Application application) {
32 | List intentWrappers = new ArrayList<>();
33 |
34 | //Android 7.0+ Doze 模式
35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
36 | PowerManager pm = (PowerManager) application.getSystemService(Context.POWER_SERVICE);
37 | boolean ignoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(application.getPackageName());
38 | if (!ignoringBatteryOptimizations) {
39 | Intent dozeIntent = new Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
40 | dozeIntent.setData(Uri.parse("package:" + application.getPackageName()));
41 | intentWrappers.add(new WhiteListIntentWrapper(dozeIntent, IntentType.DOZE));
42 | }
43 | }
44 |
45 | //华为 自启管理
46 | Intent huaweiIntent = new Intent();
47 | huaweiIntent.setAction("huawei.intent.action.HSM_BOOTAPP_MANAGER");
48 | intentWrappers.add(new WhiteListIntentWrapper(huaweiIntent, IntentType.HUAWEI));
49 |
50 | //华为 锁屏清理
51 | Intent huaweiGodIntent = new Intent();
52 | huaweiGodIntent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
53 | intentWrappers.add(new WhiteListIntentWrapper(huaweiGodIntent, IntentType.HUAWEI_GOD));
54 |
55 | //小米 自启动管理
56 | Intent xiaomiIntent = new Intent();
57 | xiaomiIntent.setAction("miui.intent.action.OP_AUTO_START");
58 | xiaomiIntent.addCategory(Intent.CATEGORY_DEFAULT);
59 | intentWrappers.add(new WhiteListIntentWrapper(xiaomiIntent, IntentType.XIAOMI));
60 |
61 | //小米 神隐模式
62 | Intent xiaomiGodIntent = new Intent();
63 | xiaomiGodIntent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity"));
64 | xiaomiGodIntent.putExtra("package_name", application.getPackageName());
65 | xiaomiGodIntent.putExtra("package_label", WhiteList.getApplicationName(Daemon.getApplication()));
66 | intentWrappers.add(new WhiteListIntentWrapper(xiaomiGodIntent, IntentType.XIAOMI_GOD));
67 |
68 | //三星 5.0/5.1 自启动应用程序管理
69 | Intent samsungLIntent = application.getPackageManager().getLaunchIntentForPackage("com.samsung.android.sm");
70 | if (samsungLIntent != null) {
71 | intentWrappers.add(new WhiteListIntentWrapper(samsungLIntent, IntentType.SAMSUNG_L));
72 | }
73 |
74 | //三星 6.0+ 未监视的应用程序管理
75 | Intent samsungMIntent = new Intent();
76 | samsungMIntent.setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.battery.BatteryActivity"));
77 | intentWrappers.add(new WhiteListIntentWrapper(samsungMIntent, IntentType.SAMSUNG_M));
78 |
79 | //魅族 自启动管理
80 | Intent meizuIntent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
81 | meizuIntent.addCategory(Intent.CATEGORY_DEFAULT);
82 | meizuIntent.putExtra("packageName", application.getPackageName());
83 | intentWrappers.add(new WhiteListIntentWrapper(meizuIntent, IntentType.MEIZU));
84 |
85 | //魅族 待机耗电管理
86 | Intent meizuGodIntent = new Intent();
87 | meizuGodIntent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.powerui.PowerAppPermissionActivity"));
88 | intentWrappers.add(new WhiteListIntentWrapper(meizuGodIntent, IntentType.MEIZU_GOD));
89 |
90 | //Oppo 自启动管理
91 | Intent oppoIntent = new Intent();
92 | oppoIntent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
93 | intentWrappers.add(new WhiteListIntentWrapper(oppoIntent, IntentType.OPPO));
94 |
95 | //Oppo 自启动管理(旧版本系统)
96 | Intent oppoOldIntent = new Intent();
97 | oppoOldIntent.setComponent(new ComponentName("com.color.safecenter", "com.color.safecenter.permission.startup.StartupAppListActivity"));
98 | intentWrappers.add(new WhiteListIntentWrapper(oppoOldIntent, IntentType.OPPO_OLD));
99 |
100 | //Vivo 后台高耗电
101 | Intent vivoGodIntent = new Intent();
102 | vivoGodIntent.setComponent(new ComponentName("com.vivo.abe", "com.vivo.applicationbehaviorengine.ui.ExcessivePowerManagerActivity"));
103 | intentWrappers.add(new WhiteListIntentWrapper(vivoGodIntent, IntentType.VIVO_GOD));
104 |
105 | //金立 应用自启
106 | Intent gioneeIntent = new Intent();
107 | gioneeIntent.setComponent(new ComponentName("com.gionee.softmanager", "com.gionee.softmanager.MainActivity"));
108 | intentWrappers.add(new WhiteListIntentWrapper(gioneeIntent, IntentType.GIONEE));
109 |
110 | //乐视 自启动管理
111 | Intent letvIntent = new Intent();
112 | letvIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
113 | intentWrappers.add(new WhiteListIntentWrapper(letvIntent, IntentType.LETV));
114 |
115 | //乐视 应用保护
116 | Intent letvGodIntent = new Intent();
117 | letvGodIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.BackgroundAppManageActivity"));
118 | intentWrappers.add(new WhiteListIntentWrapper(letvGodIntent, IntentType.LETV_GOD));
119 |
120 | //酷派 自启动管理
121 | Intent coolpadIntent = new Intent();
122 | coolpadIntent.setComponent(new ComponentName("com.yulong.android.security", "com.yulong.android.seccenter.tabbarmain"));
123 | intentWrappers.add(new WhiteListIntentWrapper(coolpadIntent, IntentType.COOLPAD));
124 |
125 | //联想 后台管理
126 | Intent lenovoIntent = new Intent();
127 | lenovoIntent.setComponent(new ComponentName("com.lenovo.security", "com.lenovo.security.purebackground.PureBackgroundActivity"));
128 | intentWrappers.add(new WhiteListIntentWrapper(lenovoIntent, IntentType.LENOVO));
129 |
130 | //联想 后台耗电优化
131 | Intent lenovoGodIntent = new Intent();
132 | lenovoGodIntent.setComponent(new ComponentName("com.lenovo.powersetting", "com.lenovo.powersetting.ui.Settings$HighPowerApplicationsActivity"));
133 | intentWrappers.add(new WhiteListIntentWrapper(lenovoGodIntent, IntentType.LENOVO_GOD));
134 |
135 | //中兴 自启管理
136 | Intent zteIntent = new Intent();
137 | zteIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.autorun.AppAutoRunManager"));
138 | intentWrappers.add(new WhiteListIntentWrapper(zteIntent, IntentType.ZTE));
139 |
140 | //中兴 锁屏加速受保护应用
141 | Intent zteGodIntent = new Intent();
142 | zteGodIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.setting.ClearAppSettingsActivity"));
143 | intentWrappers.add(new WhiteListIntentWrapper(zteGodIntent, IntentType.ZTE_GOD));
144 | return intentWrappers;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable-anydpi-v24/ic_stat_name.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
14 |
15 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/src/main/res/drawable-hdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-mdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/src/main/res/drawable-mdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/src/main/res/drawable-xhdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xxhdpi/ic_stat_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/src/main/res/drawable-xxhdpi/ic_stat_name.png
--------------------------------------------------------------------------------
/library/src/main/res/raw/no_notice.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clivebi/Daemon/00c4d19625606bd85a8077b1550f89daf794e532/library/src/main/res/raw/no_notice.mp3
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | library
3 |
4 |
--------------------------------------------------------------------------------
/library/src/test/java/com/daemon/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 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## 安卓后台保活2021新姿势
2 | 适配华为大部分系列手机,vivo,OPPO 部分机型,小米的不支持,可见小米在对抗后台自保上做得最好
3 | 本项目原本是给某个公司合作开发的,最后给了对方SDK之后由于付款问题闹得很郁闷,想着这个代码拿在自己手上也没用,就发出来给大家参考参考。目前分析的结果来看,这个是全网目前还能使用的保活方案,曝光之后很有可能活不到明年,如果你的公司恰好使用了这种方案,那么是时候开始研究新的方案了。最后说一下这种想白拿的公司,说实话我不是靠Android技术谋生的,最多就少了点零花钱,但是这个方案被封之后,你还有多少个机会白拿?
4 | # 原理
5 |
6 | 安卓后台保活前前后后网上出了好多公开的方案,但是到目前为止(2021年5月),还能广泛使用的并没有,我通过了研究了一下网上几个大厂的APP(APP名字就不点名了),整理实现了这个方案
7 | 虽然本方案看似集成了好几个保活的方案,比如一像素,JOB,等等,但是对于新版本android真正起作用的还是双进程守护。双进程守护的代码网上一搜一大堆,但是自己试过就知道了,现在的很多ROM已经封杀了这个方案,那些代码只能在原生系统上玩玩。
8 | 这里大概说一下双进程守护的逻辑,同时启动A进程和B进程,相互守护,检测到对方退出就再次启动对方,大部分公开的方案都是使用startservice启动,网上有好几个改进版,甚至有的都在native层自己实现和service manger的通信逻辑来启动服务,为的就是能在被杀时候第一时间再次启动。但是,改到native层也没有用,现在大部分rom已经封杀了startservice,我大概研究了下样本,发现样本使用的是startInstrumentation来启动进程,对于Instrumentation不了解的同学可以自行百度。 所以只需要在MarsDaemon基础上做一下小改动即可:
9 | ```
10 | @Override
11 | public void onDaemonDead() {
12 | Log.d(TAG, "on daemon dead!");
13 | if (startServiceByAmsBinder()) {
14 |
15 | int pid = Process.myPid();
16 | Log.d(TAG, "mPid: " + mPid + " current pid: " + pid);
17 | Daemon.context.startInstrumentation(mApp,null,null);
18 | android.os.Process.killProcess(mPid);
19 | }
20 | }
21 | ```
22 |
23 | # 2步集成使用
24 | 1、 打开library的AndroidManifest.xml找 如下位置:
25 | ```
29 |
30 | ```
31 | 将包名替换成自己的包名
32 |
33 | 2、在app的Application中添加启动代码,并实现配置接口和回调接口
34 | ```
35 | override fun attachBaseContext(base: Context?) {
36 | super.attachBaseContext(base)
37 | //DaemonLog.d("Application onCrearte")
38 | Daemon.startWork(this, DaemonConfigurationImplement(this), DaemonCallbackImplement())
39 | }
40 | ```
41 |
42 |
43 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 | include ':app'
3 | rootProject.name = "Daemon"
--------------------------------------------------------------------------------