Skip to content

Commit

Permalink
提交核心代码
Browse files Browse the repository at this point in the history
  • Loading branch information
ShihooWang committed Dec 13, 2018
1 parent 8c11e6c commit 8ca7172
Show file tree
Hide file tree
Showing 17 changed files with 1,213 additions and 50 deletions.
4 changes: 1 addition & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
22 changes: 22 additions & 0 deletions app/src/main/java/daemon/shihoo/com/daemonlibrary/App.java
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);


}
}
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}

}


task clean(type: Delete) {
delete rootProject.buildDir
}
5 changes: 2 additions & 3 deletions deamonlibrary/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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:+'
}

This file was deleted.

65 changes: 64 additions & 1 deletion deamonlibrary/src/main/AndroidManifest.xml
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 deamonlibrary/src/main/java/com/shihoo/daemon/AbsWorkService.java
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 deamonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java
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);
}
}
Loading

0 comments on commit 8ca7172

Please sign in to comment.