-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8c11e6c
commit 8ca7172
Showing
17 changed files
with
1,213 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package daemon.shihoo.com.daemonlibrary; | ||
|
||
import android.app.Application; | ||
|
||
/** | ||
* Created by shihoo ON 2018/12/13. | ||
* Email [email protected] [email protected] | ||
*/ | ||
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); | ||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 0 additions & 26 deletions
26
deamonlibrary/src/androidTest/java/shihoo/com/deamon/ExampleInstrumentedTest.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,65 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="shihoo.com.deamon" /> | ||
package="com.shihoo.daemon" > | ||
|
||
|
||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> | ||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> | ||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> | ||
|
||
|
||
<application | ||
android:label="@string/app_name" | ||
> | ||
<receiver | ||
android:name="com.shihoo.daemon.WakeUpReceiver" | ||
android:process=":watch"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.USER_PRESENT"/> | ||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> | ||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> | ||
<action android:name="com.shihoo.CANCEL_JOB_ALARM_SUB"/> | ||
</intent-filter> | ||
</receiver> | ||
|
||
<receiver | ||
android:name="com.shihoo.daemon.WakeUpReceiver$WakeUpAutoStartReceiver" | ||
android:process=":watch"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.BOOT_COMPLETED"/> | ||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> | ||
</intent-filter> | ||
<intent-filter> | ||
<action android:name="android.intent.action.PACKAGE_ADDED"/> | ||
<action android:name="android.intent.action.PACKAGE_REMOVED"/> | ||
<data android:scheme="package"/> | ||
</intent-filter> | ||
</receiver> | ||
|
||
<service android:name="com.shihoo.daemon.AbsWorkService$WorkNotificationService"/> | ||
|
||
<service | ||
android:name="com.shihoo.daemon.JobSchedulerService" | ||
android:permission="android.permission.BIND_JOB_SERVICE" | ||
android:enabled="true" | ||
android:exported="true" | ||
android:process=":watch"/> | ||
|
||
<service | ||
android:name="com.shihoo.daemon.WatchDogService" | ||
android:process=":watch"/> | ||
|
||
<service | ||
android:name="com.shihoo.daemon.WatchDogService$WatchDogNotificationService" | ||
android:process=":watch"/> | ||
|
||
<activity | ||
android:name="com.shihoo.daemon.singlepixel.SinglePixelActivity" | ||
android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard" | ||
android:excludeFromRecents="true" | ||
android:finishOnTaskLaunch="false" | ||
android:launchMode="singleInstance" | ||
android:theme="@style/SingleActivityStyle" /> | ||
|
||
</application> | ||
|
||
</manifest> |
161 changes: 161 additions & 0 deletions
161
deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] [email protected] | ||
* | ||
* 主要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; | ||
} | ||
} | ||
|
||
} |
74 changes: 74 additions & 0 deletions
74
deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<? extends AbsWorkService> sServiceClass; | ||
private static int sWakeUpInterval = DEFAULT_WAKE_UP_INTERVAL; | ||
public static boolean sInitialized; | ||
|
||
static final Map<Class<? extends Service>, ServiceConnection> BIND_STATE_MAP = new HashMap<>(); | ||
|
||
/** | ||
* @param app Application Context. | ||
* @param wakeUpInterval 定时唤醒的时间间隔(ms). | ||
*/ | ||
public static void initialize(@NonNull Context app, @NonNull Class<? extends AbsWorkService> serviceClass, @Nullable Integer wakeUpInterval) { | ||
sApp = app; | ||
sServiceClass = serviceClass; | ||
if (wakeUpInterval != null) sWakeUpInterval = wakeUpInterval; | ||
sInitialized = true; | ||
} | ||
|
||
public static void startServiceMayBind(@NonNull final Class<? extends Service> 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); | ||
} | ||
} |
Oops, something went wrong.