From 8ca71723fa4a0c1e605697236a6e8ae4a853716a Mon Sep 17 00:00:00 2001 From: shihu wang <451082005@qq.com> Date: Thu, 13 Dec 2018 11:06:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=A0=B8=E5=BF=83=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 4 +- .../daemon/shihoo/com/daemonlibrary/App.java | 22 + build.gradle | 3 + deamonlibrary/build.gradle | 5 +- .../com/deamon/ExampleInstrumentedTest.java | 26 - deamonlibrary/src/main/AndroidManifest.xml | 65 ++- .../com/shihoo/daemon/AbsWorkService.java | 161 +++++++ .../java/com/shihoo/daemon/DaemonEnv.java | 74 +++ .../java/com/shihoo/daemon/IntentWrapper.java | 444 ++++++++++++++++++ .../shihoo/daemon/JobSchedulerService.java | 27 ++ .../com/shihoo/daemon/WakeUpReceiver.java | 40 ++ .../com/shihoo/daemon/WatchDogService.java | 189 ++++++++ .../daemon/singlepixel/ScreenManager.java | 60 +++ .../singlepixel/ScreenReceiverUtil.java | 65 +++ .../singlepixel/SinglePixelActivity.java | 45 ++ deamonlibrary/src/main/res/values/styles.xml | 16 + .../shihoo/com/deamon/ExampleUnitTest.java | 17 - 17 files changed, 1213 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java delete mode 100644 deamonlibrary/src/androidTest/java/shihoo/com/deamon/ExampleInstrumentedTest.java create mode 100644 deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java create mode 100755 deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java create mode 100755 deamonlibrary/src/main/java/com/shihoo/daemon/IntentWrapper.java create mode 100755 deamonlibrary/src/main/java/com/shihoo/daemon/JobSchedulerService.java create mode 100755 deamonlibrary/src/main/java/com/shihoo/daemon/WakeUpReceiver.java create mode 100755 deamonlibrary/src/main/java/com/shihoo/daemon/WatchDogService.java create mode 100644 deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenManager.java create mode 100644 deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenReceiverUtil.java create mode 100644 deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/SinglePixelActivity.java create mode 100644 deamonlibrary/src/main/res/values/styles.xml delete mode 100644 deamonlibrary/src/test/java/shihoo/com/deamon/ExampleUnitTest.java diff --git a/app/build.gradle b/app/build.gradle index ff59331..63030bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,7 +22,5 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation 'com.github.wangshihu123:DaemonLibrary:v0.0.0' } diff --git a/app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java b/app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java new file mode 100644 index 0000000..64957de --- /dev/null +++ b/app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java @@ -0,0 +1,22 @@ +package daemon.shihoo.com.daemonlibrary; + +import android.app.Application; + +/** + * Created by shihoo ON 2018/12/13. + * Email shihu.wang@bodyplus.cc 451082005@qq.com + */ +public class App extends Application { + + @Override + public void onCreate() { + super.onCreate(); + //需要在 Application 的 onCreate() 中调用一次 DaemonEnv.initialize() + // 每一次创建进程的时候都需要对Daemon环境进行初始化,所以这里没有判断进程 + DaemonEnv.initialize(this, TraceServiceImpl.class, DaemonEnv.DEFAULT_WAKE_UP_INTERVAL); + TraceServiceImpl.sShouldStopService = false; + DaemonEnv.startServiceMayBind(TraceServiceImpl.class); + + + } +} diff --git a/build.gradle b/build.gradle index 2aad21d..7dc72bf 100644 --- a/build.gradle +++ b/build.gradle @@ -19,9 +19,12 @@ allprojects { repositories { google() jcenter() + maven { url 'https://jitpack.io' } } + } + task clean(type: Delete) { delete rootProject.buildDir } diff --git a/deamonlibrary/build.gradle b/deamonlibrary/build.gradle index 06e2580..8b2eb2e 100644 --- a/deamonlibrary/build.gradle +++ b/deamonlibrary/build.gradle @@ -26,8 +26,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' - testImplementation 'junit:junit:4.12' + api 'io.reactivex.rxjava2:rxjava:2.+' + api 'com.android.support:support-annotations:+' } diff --git a/deamonlibrary/src/androidTest/java/shihoo/com/deamon/ExampleInstrumentedTest.java b/deamonlibrary/src/androidTest/java/shihoo/com/deamon/ExampleInstrumentedTest.java deleted file mode 100644 index f2972f4..0000000 --- a/deamonlibrary/src/androidTest/java/shihoo/com/deamon/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package shihoo.com.deamon; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("shihoo.com.deamon.test", appContext.getPackageName()); - } -} diff --git a/deamonlibrary/src/main/AndroidManifest.xml b/deamonlibrary/src/main/AndroidManifest.xml index 6591aea..a7475ca 100644 --- a/deamonlibrary/src/main/AndroidManifest.xml +++ b/deamonlibrary/src/main/AndroidManifest.xml @@ -1,2 +1,65 @@ + package="com.shihoo.daemon" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java b/deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java new file mode 100644 index 0000000..5129651 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java @@ -0,0 +1,161 @@ +package com.shihoo.daemon; + +import android.app.Notification; +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.IBinder; +import android.support.annotation.Nullable; + +/** + * Created by shihoo ON 2018/12/12. + * Email shihu.wang@bodyplus.cc 451082005@qq.com + * + * 主要Service 用来处理业务逻辑 + */ +public abstract class AbsWorkService extends Service { + protected static final int HASH_CODE = 1; + + protected boolean mFirstStarted = true; + + /** + * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription. + */ + public static void cancelJobAlarmSub() { + if (!DaemonEnv.sInitialized) return; + DaemonEnv.sApp.sendBroadcast(new Intent(WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB)); + } + + /** + * 是否 任务完成, 不再需要服务运行? + * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null. + */ + public abstract Boolean shouldStopService(Intent intent, int flags, int startId); + public abstract void startWork(Intent intent, int flags, int startId); + public abstract void stopWork(Intent intent, int flags, int startId); + /** + * 任务是否正在运行? + * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null. + */ + public abstract Boolean isWorkRunning(Intent intent, int flags, int startId); + @Nullable + public abstract IBinder onBind(Intent intent, Void alwaysNull); + public abstract void onServiceKilled(Intent rootIntent); + + /** + * 1.防止重复启动,可以任意调用 DaemonEnv.startServiceMayBind(Class serviceClass); + * 2.利用漏洞启动前台服务而不显示通知; + * 3.在子线程中运行定时任务,处理了运行前检查和销毁时保存的问题; + * 4.启动守护服务; + * 5.守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用. + */ + protected int onStart(Intent intent, int flags, int startId) { + + //启动守护服务,运行在:watch子进程中 + DaemonEnv.startServiceMayBind(WatchDogService.class); + + //业务逻辑: 实际使用时,根据需求,将这里更改为自定义的条件,判定服务应当启动还是停止 (任务是否需要运行) + Boolean shouldStopService = shouldStopService(intent, flags, startId); + if (shouldStopService != null) { + if (shouldStopService) stopService(intent, flags, startId); else startService(intent, flags, startId); + } + + if (mFirstStarted) { + mFirstStarted = false; + //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心! + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { + //利用漏洞在 API Level 17 及以下的 Android 系统中,启动前台服务而不显示通知 + startForeground(HASH_CODE, new Notification()); + //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + DaemonEnv.startServiceSafely(new Intent(getApplication(), WorkNotificationService.class)); + } + getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchDogService.class.getName()), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + } + + return START_STICKY; + } + + void startService(Intent intent, int flags, int startId) { + //检查服务是否不需要运行 + Boolean shouldStopService = shouldStopService(intent, flags, startId); + if (shouldStopService != null && shouldStopService) return; + //若还没有取消订阅,说明任务仍在运行,为防止重复启动,直接 return + Boolean workRunning = isWorkRunning(intent, flags, startId); + if (workRunning != null && workRunning) return; + //业务逻辑 + startWork(intent, flags, startId); + } + + /** + * 停止服务并取消定时唤醒 + * + * 停止服务使用取消订阅的方式实现,而不是调用 Context.stopService(Intent name)。因为: + * 1.stopService 会调用 Service.onDestroy(),而 AbsWorkService 做了保活处理,会把 Service 再拉起来; + * 2.我们希望 AbsWorkService 起到一个类似于控制台的角色,即 AbsWorkService 始终运行 (无论任务是否需要运行), + * 而是通过 onStart() 里自定义的条件,来决定服务是否应当启动或停止。 + */ + void stopService(Intent intent, int flags, int startId) { + //取消对任务的订阅 + stopWork(intent, flags, startId); + //取消 Job / Alarm / Subscription + cancelJobAlarmSub(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return onStart(intent, flags, startId); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + onStart(intent, 0, 0); + return onBind(intent, null); + } + + protected void onEnd(Intent rootIntent) { + onServiceKilled(rootIntent); + if (!DaemonEnv.sInitialized) return; + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + DaemonEnv.startServiceMayBind(WatchDogService.class); + } + + /** + * 最近任务列表中划掉卡片时回调 + */ + @Override + public void onTaskRemoved(Intent rootIntent) { + onEnd(rootIntent); + } + + /** + * 设置-正在运行中停止服务时回调 + */ + @Override + public void onDestroy() { + onEnd(null); + } + + public static class WorkNotificationService extends Service { + + /** + * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(AbsWorkService.HASH_CODE, new Notification()); + stopSelf(); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + } + +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java b/deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java new file mode 100755 index 0000000..6401251 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java @@ -0,0 +1,74 @@ +package com.shihoo.daemon; + +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public final class DaemonEnv { + + private DaemonEnv() {} + + public static final int DEFAULT_WAKE_UP_INTERVAL = 6 * 60 * 1000; + private static final int MINIMAL_WAKE_UP_INTERVAL = 3 * 60 * 1000; + + static Context sApp; + static Class sServiceClass; + private static int sWakeUpInterval = DEFAULT_WAKE_UP_INTERVAL; + public static boolean sInitialized; + + static final Map, ServiceConnection> BIND_STATE_MAP = new HashMap<>(); + + /** + * @param app Application Context. + * @param wakeUpInterval 定时唤醒的时间间隔(ms). + */ + public static void initialize(@NonNull Context app, @NonNull Class serviceClass, @Nullable Integer wakeUpInterval) { + sApp = app; + sServiceClass = serviceClass; + if (wakeUpInterval != null) sWakeUpInterval = wakeUpInterval; + sInitialized = true; + } + + public static void startServiceMayBind(@NonNull final Class serviceClass) { + if (!sInitialized) return; + final Intent i = new Intent(sApp, serviceClass); + startServiceSafely(i); + ServiceConnection bound = BIND_STATE_MAP.get(serviceClass); + if (bound == null) sApp.bindService(i, new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + BIND_STATE_MAP.put(serviceClass, this); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + BIND_STATE_MAP.remove(serviceClass); + startServiceSafely(i); + if (!sInitialized) return; + sApp.bindService(i, this, Context.BIND_AUTO_CREATE); + } + + @Override + public void onBindingDied(ComponentName name) { + onServiceDisconnected(name); + } + }, Context.BIND_AUTO_CREATE); + } + + static void startServiceSafely(Intent i) { + if (!sInitialized) return; + try { sApp.startService(i); } catch (Exception ignored) {} + } + + static int getWakeUpInterval() { + return Math.max(sWakeUpInterval, MINIMAL_WAKE_UP_INTERVAL); + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/IntentWrapper.java b/deamonlibrary/src/main/java/com/shihoo/daemon/IntentWrapper.java new file mode 100755 index 0000000..e2f4352 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/IntentWrapper.java @@ -0,0 +1,444 @@ +package com.shihoo.daemon; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; +import android.os.PowerManager; +import android.provider.Settings; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +public class IntentWrapper { + + //Android 7.0+ Doze 模式 + protected static final int DOZE = 98; + //华为 自启管理 + protected static final int HUAWEI = 99; + //华为 锁屏清理 + protected static final int HUAWEI_GOD = 100; + //小米 自启动管理 + protected static final int XIAOMI = 101; + //小米 神隐模式 + protected static final int XIAOMI_GOD = 102; + //三星 5.0/5.1 自启动应用程序管理 + protected static final int SAMSUNG_L = 103; + //魅族 自启动管理 + protected static final int MEIZU = 104; + //魅族 待机耗电管理 + protected static final int MEIZU_GOD = 105; + //Oppo 自启动管理 + protected static final int OPPO = 106; + //三星 6.0+ 未监视的应用程序管理 + protected static final int SAMSUNG_M = 107; + //Oppo 自启动管理(旧版本系统) + protected static final int OPPO_OLD = 108; + //Vivo 后台高耗电 + protected static final int VIVO_GOD = 109; + //金立 应用自启 + protected static final int GIONEE = 110; + //乐视 自启动管理 + protected static final int LETV = 111; + //乐视 应用保护 + protected static final int LETV_GOD = 112; + //酷派 自启动管理 + protected static final int COOLPAD = 113; + //联想 后台管理 + protected static final int LENOVO = 114; + //联想 后台耗电优化 + protected static final int LENOVO_GOD = 115; + //中兴 自启管理 + protected static final int ZTE = 116; + //中兴 锁屏加速受保护应用 + protected static final int ZTE_GOD = 117; + + protected static List sIntentWrapperList; + + public static List getIntentWrapperList() { + if (sIntentWrapperList == null) { + + if (!DaemonEnv.sInitialized) return new ArrayList<>(); + + sIntentWrapperList = new ArrayList<>(); + + //Android 7.0+ Doze 模式 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + PowerManager pm = (PowerManager) DaemonEnv.sApp.getSystemService(Context.POWER_SERVICE); + boolean ignoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(DaemonEnv.sApp.getPackageName()); + if (!ignoringBatteryOptimizations) { + Intent dozeIntent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + dozeIntent.setData(Uri.parse("package:" + DaemonEnv.sApp.getPackageName())); + sIntentWrapperList.add(new IntentWrapper(dozeIntent, DOZE)); + } + } + + //华为 自启管理 + Intent huaweiIntent = new Intent(); + huaweiIntent.setAction("huawei.intent.action.HSM_BOOTAPP_MANAGER"); + sIntentWrapperList.add(new IntentWrapper(huaweiIntent, HUAWEI)); + + //华为 锁屏清理 + Intent huaweiGodIntent = new Intent(); + huaweiGodIntent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")); + sIntentWrapperList.add(new IntentWrapper(huaweiGodIntent, HUAWEI_GOD)); + + //小米 自启动管理 + Intent xiaomiIntent = new Intent(); + xiaomiIntent.setAction("miui.intent.action.OP_AUTO_START"); + xiaomiIntent.addCategory(Intent.CATEGORY_DEFAULT); + sIntentWrapperList.add(new IntentWrapper(xiaomiIntent, XIAOMI)); + + //小米 神隐模式 + Intent xiaomiGodIntent = new Intent(); + xiaomiGodIntent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity")); + xiaomiGodIntent.putExtra("package_name", DaemonEnv.sApp.getPackageName()); + xiaomiGodIntent.putExtra("package_label", getApplicationName()); + sIntentWrapperList.add(new IntentWrapper(xiaomiGodIntent, XIAOMI_GOD)); + + //三星 5.0/5.1 自启动应用程序管理 + Intent samsungLIntent = DaemonEnv.sApp.getPackageManager().getLaunchIntentForPackage("com.samsung.android.sm"); + if (samsungLIntent != null) sIntentWrapperList.add(new IntentWrapper(samsungLIntent, SAMSUNG_L)); + + //三星 6.0+ 未监视的应用程序管理 + Intent samsungMIntent = new Intent(); + samsungMIntent.setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.battery.BatteryActivity")); + sIntentWrapperList.add(new IntentWrapper(samsungMIntent, SAMSUNG_M)); + + //魅族 自启动管理 + Intent meizuIntent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); + meizuIntent.addCategory(Intent.CATEGORY_DEFAULT); + meizuIntent.putExtra("packageName", DaemonEnv.sApp.getPackageName()); + sIntentWrapperList.add(new IntentWrapper(meizuIntent, MEIZU)); + + //魅族 待机耗电管理 + Intent meizuGodIntent = new Intent(); + meizuGodIntent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.powerui.PowerAppPermissionActivity")); + sIntentWrapperList.add(new IntentWrapper(meizuGodIntent, MEIZU_GOD)); + + //Oppo 自启动管理 + Intent oppoIntent = new Intent(); + oppoIntent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")); + sIntentWrapperList.add(new IntentWrapper(oppoIntent, OPPO)); + + //Oppo 自启动管理(旧版本系统) + Intent oppoOldIntent = new Intent(); + oppoOldIntent.setComponent(new ComponentName("com.color.safecenter", "com.color.safecenter.permission.startup.StartupAppListActivity")); + sIntentWrapperList.add(new IntentWrapper(oppoOldIntent, OPPO_OLD)); + + //Vivo 后台高耗电 + Intent vivoGodIntent = new Intent(); + vivoGodIntent.setComponent(new ComponentName("com.vivo.abe", "com.vivo.applicationbehaviorengine.ui.ExcessivePowerManagerActivity")); + sIntentWrapperList.add(new IntentWrapper(vivoGodIntent, VIVO_GOD)); + + //金立 应用自启 + Intent gioneeIntent = new Intent(); + gioneeIntent.setComponent(new ComponentName("com.gionee.softmanager", "com.gionee.softmanager.MainActivity")); + sIntentWrapperList.add(new IntentWrapper(gioneeIntent, GIONEE)); + + //乐视 自启动管理 + Intent letvIntent = new Intent(); + letvIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")); + sIntentWrapperList.add(new IntentWrapper(letvIntent, LETV)); + + //乐视 应用保护 + Intent letvGodIntent = new Intent(); + letvGodIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.BackgroundAppManageActivity")); + sIntentWrapperList.add(new IntentWrapper(letvGodIntent, LETV_GOD)); + + //酷派 自启动管理 + Intent coolpadIntent = new Intent(); + coolpadIntent.setComponent(new ComponentName("com.yulong.android.security", "com.yulong.android.seccenter.tabbarmain")); + sIntentWrapperList.add(new IntentWrapper(coolpadIntent, COOLPAD)); + + //联想 后台管理 + Intent lenovoIntent = new Intent(); + lenovoIntent.setComponent(new ComponentName("com.lenovo.security", "com.lenovo.security.purebackground.PureBackgroundActivity")); + sIntentWrapperList.add(new IntentWrapper(lenovoIntent, LENOVO)); + + //联想 后台耗电优化 + Intent lenovoGodIntent = new Intent(); + lenovoGodIntent.setComponent(new ComponentName("com.lenovo.powersetting", "com.lenovo.powersetting.ui.Settings$HighPowerApplicationsActivity")); + sIntentWrapperList.add(new IntentWrapper(lenovoGodIntent, LENOVO_GOD)); + + //中兴 自启管理 + Intent zteIntent = new Intent(); + zteIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.autorun.AppAutoRunManager")); + sIntentWrapperList.add(new IntentWrapper(zteIntent, ZTE)); + + //中兴 锁屏加速受保护应用 + Intent zteGodIntent = new Intent(); + zteGodIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.setting.ClearAppSettingsActivity")); + sIntentWrapperList.add(new IntentWrapper(zteGodIntent, ZTE_GOD)); + } + return sIntentWrapperList; + } + + protected static String sApplicationName; + + public static String getApplicationName() { + if (sApplicationName == null) { + if (!DaemonEnv.sInitialized) return ""; + PackageManager pm; + ApplicationInfo ai; + try { + pm = DaemonEnv.sApp.getPackageManager(); + ai = pm.getApplicationInfo(DaemonEnv.sApp.getPackageName(), 0); + sApplicationName = pm.getApplicationLabel(ai).toString(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + sApplicationName = DaemonEnv.sApp.getPackageName(); + } + } + return sApplicationName; + } + + /** + * 处理白名单. + * @return 弹过框的 IntentWrapper. + */ + @NonNull + public static List whiteListMatters(final Activity a, String reason) { + List showed = new ArrayList<>(); + if (reason == null) reason = "核心服务的持续运行"; + List intentWrapperList = getIntentWrapperList(); + for (final IntentWrapper iw : intentWrapperList) { + //如果本机上没有能处理这个Intent的Activity,说明不是对应的机型,直接忽略进入下一次循环。 + if (!iw.doesActivityExists()) continue; + switch (iw.type) { + case DOZE: + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + PowerManager pm = (PowerManager) a.getSystemService(Context.POWER_SERVICE); + if (pm.isIgnoringBatteryOptimizations(a.getPackageName())) break; + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要忽略 " + getApplicationName() + " 的电池优化") + .setMessage(reason + "需要 " + getApplicationName() + " 加入到电池优化的忽略名单。\n\n" + + "请点击『确定』,在弹出的『忽略电池优化』对话框中,选择『是』。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + } + break; + case HUAWEI: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 自动启动") + .setMessage(reason + "需要允许 " + getApplicationName() + " 的自动启动。\n\n" + + "请点击『确定』,在弹出的『自启管理』中,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case ZTE_GOD: + case HUAWEI_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle(getApplicationName() + " 需要加入锁屏清理白名单") + .setMessage(reason + "需要 " + getApplicationName() + " 加入到锁屏清理白名单。\n\n" + + "请点击『确定』,在弹出的『锁屏清理』列表中,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case XIAOMI_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要关闭 " + getApplicationName() + " 的神隐模式") + .setMessage(reason + "需要关闭 " + getApplicationName() + " 的神隐模式。\n\n" + + "请点击『确定』,在弹出的 " + getApplicationName() + " 神隐模式设置中,选择『无限制』,然后选择『允许定位』。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case SAMSUNG_L: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的自启动") + .setMessage(reason + "需要 " + getApplicationName() + " 在屏幕关闭时继续运行。\n\n" + + "请点击『确定』,在弹出的『智能管理器』中,点击『内存』,选择『自启动应用程序』选项卡,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case SAMSUNG_M: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的自启动") + .setMessage(reason + "需要 " + getApplicationName() + " 在屏幕关闭时继续运行。\n\n" + + "请点击『确定』,在弹出的『电池』页面中,点击『未监视的应用程序』->『添加应用程序』,勾选 " + getApplicationName() + ",然后点击『完成』。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case MEIZU: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 保持后台运行") + .setMessage(reason + "需要允许 " + getApplicationName() + " 保持后台运行。\n\n" + + "请点击『确定』,在弹出的应用信息界面中,将『后台管理』选项更改为『保持后台运行』。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case MEIZU_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle(getApplicationName() + " 需要在待机时保持运行") + .setMessage(reason + "需要 " + getApplicationName() + " 在待机时保持运行。\n\n" + + "请点击『确定』,在弹出的『待机耗电管理』中,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case ZTE: + case LETV: + case XIAOMI: + case OPPO: + case OPPO_OLD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的自启动") + .setMessage(reason + "需要 " + getApplicationName() + " 加入到自启动白名单。\n\n" + + "请点击『确定』,在弹出的『自启动管理』中,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case COOLPAD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的自启动") + .setMessage(reason + "需要允许 " + getApplicationName() + " 的自启动。\n\n" + + "请点击『确定』,在弹出的『酷管家』中,找到『软件管理』->『自启动管理』,取消勾选 " + getApplicationName() + ",将 " + getApplicationName() + " 的状态改为『已允许』。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case VIVO_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的后台运行") + .setMessage(reason + "需要允许 " + getApplicationName() + " 在后台高耗电时运行。\n\n" + + "请点击『确定』,在弹出的『后台高耗电』中,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case GIONEE: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle(getApplicationName() + " 需要加入应用自启和绿色后台白名单") + .setMessage(reason + "需要允许 " + getApplicationName() + " 的自启动和后台运行。\n\n" + + "请点击『确定』,在弹出的『系统管家』中,分别找到『应用管理』->『应用自启』和『绿色后台』->『清理白名单』,将 " + getApplicationName() + " 添加到白名单。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case LETV_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要禁止 " + getApplicationName() + " 被自动清理") + .setMessage(reason + "需要禁止 " + getApplicationName() + " 被自动清理。\n\n" + + "请点击『确定』,在弹出的『应用保护』中,将 " + getApplicationName() + " 对应的开关关闭。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case LENOVO: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要允许 " + getApplicationName() + " 的后台运行") + .setMessage(reason + "需要允许 " + getApplicationName() + " 的后台自启、后台 GPS 和后台运行。\n\n" + + "请点击『确定』,在弹出的『后台管理』中,分别找到『后台自启』、『后台 GPS』和『后台运行』,将 " + getApplicationName() + " 对应的开关打开。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + case LENOVO_GOD: + new AlertDialog.Builder(a) + .setCancelable(false) + .setTitle("需要关闭 " + getApplicationName() + " 的后台耗电优化") + .setMessage(reason + "需要关闭 " + getApplicationName() + " 的后台耗电优化。\n\n" + + "请点击『确定』,在弹出的『后台耗电优化』中,将 " + getApplicationName() + " 对应的开关关闭。") + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);} + }) + .show(); + showed.add(iw); + break; + } + } + return showed; + } + + /** + * 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀 + */ + public static void onBackPressed(Activity a) { + Intent launcherIntent = new Intent(Intent.ACTION_MAIN); + launcherIntent.addCategory(Intent.CATEGORY_HOME); + a.startActivity(launcherIntent); + } + + protected Intent intent; + protected int type; + + protected IntentWrapper(Intent intent, int type) { + this.intent = intent; + this.type = type; + } + + /** + * 判断本机上是否有能处理当前Intent的Activity + */ + protected boolean doesActivityExists() { + if (!DaemonEnv.sInitialized) return false; + PackageManager pm = DaemonEnv.sApp.getPackageManager(); + List list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + return list != null && list.size() > 0; + } + + /** + * 安全地启动一个Activity + */ + protected void startActivitySafely(Activity activityContext) { + try { activityContext.startActivity(intent); } catch (Exception e) { e.printStackTrace(); } + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/JobSchedulerService.java b/deamonlibrary/src/main/java/com/shihoo/daemon/JobSchedulerService.java new file mode 100755 index 0000000..6878dc2 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/JobSchedulerService.java @@ -0,0 +1,27 @@ +package com.shihoo.daemon; + +import android.annotation.TargetApi; +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.os.Build; + +/** + * Android 5.0+ 使用的 JobScheduler. + * 运行在 :watch 子进程中. + * + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class JobSchedulerService extends JobService { + + @Override + public boolean onStartJob(JobParameters params) { + if (!DaemonEnv.sInitialized) return false; + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + return false; + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/WakeUpReceiver.java b/deamonlibrary/src/main/java/com/shihoo/daemon/WakeUpReceiver.java new file mode 100755 index 0000000..b7653e6 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/WakeUpReceiver.java @@ -0,0 +1,40 @@ +package com.shihoo.daemon; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class WakeUpReceiver extends BroadcastReceiver { + + /** + * 向 WakeUpReceiver 发送带有此 Action 的广播, 即可在不需要服务运行的时候取消 Job / Alarm / Subscription. + */ + protected static final String ACTION_CANCEL_JOB_ALARM_SUB = "com.shihoo.CANCEL_JOB_ALARM_SUB"; + + /** + * 监听 8 种系统广播 : + * CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED, + * BOOT\_COMPLETED, MEDIA\_MOUNTED, PACKAGE\_ADDED, PACKAGE\_REMOVED. + * 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 挂载 SD 卡, 安装 / 卸载软件包时拉起 Service. + * Service 内部做了判断,若 Service 已在运行,不会重复启动. + * 运行在:watch子进程中. + */ + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && ACTION_CANCEL_JOB_ALARM_SUB.equals(intent.getAction())) { + WatchDogService.cancelJobAlarmSub(); + return; + } + if (!DaemonEnv.sInitialized) return; + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + } + + public static class WakeUpAutoStartReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (!DaemonEnv.sInitialized) return; + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + } + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/WatchDogService.java b/deamonlibrary/src/main/java/com/shihoo/daemon/WatchDogService.java new file mode 100755 index 0000000..72f7207 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/WatchDogService.java @@ -0,0 +1,189 @@ +package com.shihoo.daemon; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.IBinder; +import android.util.Log; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + +import com.shihoo.daemon.singlepixel.ScreenManager; +import com.shihoo.daemon.singlepixel.ScreenReceiverUtil; + +import static android.content.ContentValues.TAG; + +public class WatchDogService extends Service { + + protected static final int HASH_CODE = 2; + + protected static Disposable sDisposable; + protected static PendingIntent sPendingIntent; + + /** + * 守护服务,运行在:watch子进程中 + */ + protected final int onStart(Intent intent, int flags, int startId) { + + if (!DaemonEnv.sInitialized) return START_STICKY; + + if (sDisposable != null && !sDisposable.isDisposed()) return START_STICKY; + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { + startForeground(HASH_CODE, new Notification()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + DaemonEnv.startServiceSafely(new Intent(DaemonEnv.sApp, WatchDogNotificationService.class)); + } + + //定时检查 AbsWorkService 是否在运行,如果不在运行就把它拉起来 + //Android 5.0+ 使用 JobScheduler,效果比 AlarmManager 好 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + JobInfo.Builder builder = new JobInfo.Builder(HASH_CODE, new ComponentName(DaemonEnv.sApp, JobSchedulerService.class)); + builder.setPeriodic(DaemonEnv.getWakeUpInterval()); + //Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis()); + builder.setPersisted(true); + JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); + scheduler.schedule(builder.build()); + } else { + //Android 4.4- 使用 AlarmManager + AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); + Intent i = new Intent(DaemonEnv.sApp, DaemonEnv.sServiceClass); + sPendingIntent = PendingIntent.getService(DaemonEnv.sApp, HASH_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT); + am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + DaemonEnv.getWakeUpInterval(), DaemonEnv.getWakeUpInterval(), sPendingIntent); + } + + //使用定时 Observable,避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况 + sDisposable = Observable + .interval(DaemonEnv.getWakeUpInterval(), TimeUnit.MILLISECONDS) + .subscribe(new Consumer() { + @Override + public void accept(Long aLong) throws Exception { + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + throwable.printStackTrace(); + } + }); + + //守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用 + getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), DaemonEnv.sServiceClass.getName()), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + + return START_STICKY; + } + + @Override + public final int onStartCommand(Intent intent, int flags, int startId) { + return onStart(intent, flags, startId); + } + + @Override + public final IBinder onBind(Intent intent) { + onStart(intent, 0, 0); + return null; + } + + protected void onEnd(Intent rootIntent) { + if (!DaemonEnv.sInitialized) return; + DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass); + DaemonEnv.startServiceMayBind(WatchDogService.class); + } + + /** + * 最近任务列表中划掉卡片时回调 + */ + @Override + public void onTaskRemoved(Intent rootIntent) { + onEnd(rootIntent); + } + + /** + * 设置-正在运行中停止服务时回调 + */ + @Override + public void onDestroy() { + onEnd(null); + } + + /** + * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription. + * + * 因 WatchDogService 运行在 :watch 子进程, 请勿在主进程中直接调用此方法. + * 而是向 WakeUpReceiver 发送一个 Action 为 WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB 的广播. + */ + public static void cancelJobAlarmSub() { + if (!DaemonEnv.sInitialized) return; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + JobScheduler scheduler = (JobScheduler) DaemonEnv.sApp.getSystemService(JOB_SCHEDULER_SERVICE); + scheduler.cancel(HASH_CODE); + } else { + AlarmManager am = (AlarmManager) DaemonEnv.sApp.getSystemService(ALARM_SERVICE); + if (sPendingIntent != null) am.cancel(sPendingIntent); + } + if (sDisposable != null) sDisposable.dispose(); + } + + public static class WatchDogNotificationService extends Service { + + /** + * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知 + * 运行在:watch子进程中 + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(WatchDogService.HASH_CODE, new Notification()); + stopSelf(); + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + } + + private ScreenReceiverUtil mScreenListener; + private ScreenManager mScreenManager; + + private void createScreenListener(){ + // 注册锁屏广播监听器 + mScreenListener = new ScreenReceiverUtil(this); + mScreenManager = ScreenManager.getInstance(this); + mScreenListener.setScreenReceiverListener(mScreenListenerer); + } + + + private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() { + @Override + public void onSreenOn() { + mScreenManager.finishActivity(); + Log.d(TAG, "关闭了1像素Activity"); + + } + + @Override + public void onSreenOff() { + mScreenManager.startActivity(); + Log.d(TAG, "打开了1像素Activity"); + } + + @Override + public void onUserPresent() { + } + }; + +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenManager.java b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenManager.java new file mode 100644 index 0000000..948913b --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenManager.java @@ -0,0 +1,60 @@ +package com.shihoo.daemon.singlepixel; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; + +import java.lang.ref.WeakReference; + +/** + * Created by shihoo ON 2018/12/12. + * Email shihu.wang@bodyplus.cc 451082005@qq.com + * + * 对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类 + */ +public class ScreenManager { + + private static final String TAG = ScreenManager.class.getSimpleName(); + private static ScreenManager sInstance; + private Context mContext; + private WeakReference mActivity; + + private ScreenManager(Context mContext) { + this.mContext = mContext; + } + + public static ScreenManager getInstance(Context context) { + if (sInstance == null) { + sInstance = new ScreenManager(context); + } + return sInstance; + } + + /** 获得SinglePixelActivity的引用 + * @param activity + */ + public void setSingleActivity(Activity activity) { + mActivity = new WeakReference<>(activity); + } + + /** + * 启动SinglePixelActivity + */ + public void startActivity() { + Intent intent = new Intent(mContext, SinglePixelActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + /** + * 结束SinglePixelActivity + */ + public void finishActivity() { + if (mActivity != null) { + Activity activity = mActivity.get(); + if (activity != null) { + activity.finish(); + } + } + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenReceiverUtil.java b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenReceiverUtil.java new file mode 100644 index 0000000..7cea978 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenReceiverUtil.java @@ -0,0 +1,65 @@ +package com.shihoo.daemon.singlepixel; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +/** + * Created by shihoo ON 2018/12/12. + * Email shihu.wang@bodyplus.cc 451082005@qq.com + * + * 对广播进行监听,封装为一个ScreenReceiverUtil类,进行锁屏解锁的广播动态注册监听 + */ +public class ScreenReceiverUtil { + private Context mContext; + private SreenBroadcastReceiver mScreenReceiver; + private SreenStateListener mStateReceiverListener; + + public ScreenReceiverUtil(Context mContext) { + this.mContext = mContext; + } + + public void setScreenReceiverListener(SreenStateListener mStateReceiverListener) { + this.mStateReceiverListener = mStateReceiverListener; + // 动态启动广播接收器 + this.mScreenReceiver = new SreenBroadcastReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_USER_PRESENT); + mContext.registerReceiver(mScreenReceiver, filter); + } + + public void stopScreenReceiverListener() { + mContext.unregisterReceiver(mScreenReceiver); + } + + /** + * 监听sreen状态对外回调接口 + */ + public interface SreenStateListener { + void onSreenOn(); + + void onSreenOff(); + + void onUserPresent(); + } + + public class SreenBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (mStateReceiverListener == null) { + return; + } + if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏 + mStateReceiverListener.onSreenOn(); + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏 + mStateReceiverListener.onSreenOff(); + } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁 + mStateReceiverListener.onUserPresent(); + } + } + } +} diff --git a/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/SinglePixelActivity.java b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/SinglePixelActivity.java new file mode 100644 index 0000000..d09fc20 --- /dev/null +++ b/deamonlibrary/src/main/java/com/shihoo/daemon/singlepixel/SinglePixelActivity.java @@ -0,0 +1,45 @@ +package com.shihoo.daemon.singlepixel; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.Window; +import android.view.WindowManager; + +import com.shihoo.daemon.WatchDogService; + +/** + * Created by shihoo ON 2018/12/12. + * Email shihu.wang@bodyplus.cc 451082005@qq.com + * + * 该Activity的View只要设置为1像素然后设置在Window对象上即可。在Activity的onDestroy周期中进行保活服务的存活判断从而唤醒服务。 + */ +public class SinglePixelActivity extends AppCompatActivity { + + private static final String TAG = SinglePixelActivity.class.getSimpleName(); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Window mWindow = getWindow(); + mWindow.setGravity(Gravity.LEFT | Gravity.TOP); + WindowManager.LayoutParams attrParams = mWindow.getAttributes(); + attrParams.x = 0; + attrParams.y = 0; + attrParams.height = 1; + attrParams.width = 1; + mWindow.setAttributes(attrParams); + ScreenManager.getInstance(this).setSingleActivity(this); + } + + @Override + protected void onDestroy() { + if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) { + Intent intentAlive = new Intent(this, WatchDogService.class); + startService(intentAlive); + } + super.onDestroy(); + } +} diff --git a/deamonlibrary/src/main/res/values/styles.xml b/deamonlibrary/src/main/res/values/styles.xml new file mode 100644 index 0000000..8fb2a24 --- /dev/null +++ b/deamonlibrary/src/main/res/values/styles.xml @@ -0,0 +1,16 @@ + + + + diff --git a/deamonlibrary/src/test/java/shihoo/com/deamon/ExampleUnitTest.java b/deamonlibrary/src/test/java/shihoo/com/deamon/ExampleUnitTest.java deleted file mode 100644 index 0f2c90b..0000000 --- a/deamonlibrary/src/test/java/shihoo/com/deamon/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package shihoo.com.deamon; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file