├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keeplive ├── .gitignore ├── build.gradle ├── jni │ ├── Android.mk │ ├── Application.mk │ ├── com_marswin89_marsdaemon_nativ_NativeDaemonAPI20.h │ ├── com_marswin89_marsdaemon_nativ_NativeDaemonAPI21.h │ ├── common.c │ ├── constant.h │ ├── daemon.c │ ├── daemon_api20.c │ ├── daemon_api21.c │ └── log.h ├── libs │ ├── armeabi-v7a │ │ ├── libdaemon_api20.so │ │ └── libdaemon_api21.so │ ├── armeabi │ │ ├── libdaemon_api20.so │ │ └── libdaemon_api21.so │ └── x86 │ │ ├── libdaemon_api20.so │ │ └── libdaemon_api21.so ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── armeabi-v7a │ │ └── daemon │ ├── armeabi │ │ └── daemon │ └── x86 │ │ └── daemon │ ├── java │ └── com │ │ ├── henrik │ │ └── keeplive │ │ │ ├── MainActivity.java │ │ │ ├── ProcApplication.java │ │ │ ├── accountsync │ │ │ ├── AccountAuthenticatorService.java │ │ │ ├── EmptyProviderForSync.java │ │ │ └── SyncService.java │ │ │ ├── onepx │ │ │ ├── KeepLiveActivity.java │ │ │ └── OnepxReceiver.java │ │ │ ├── receiver │ │ │ └── BootReceiver.java │ │ │ └── schedulerjob │ │ │ └── DaemonJobService.java │ │ └── marswin89 │ │ └── marsdaemon │ │ ├── DaemonClient.java │ │ ├── DaemonConfigurations.java │ │ ├── IDaemonClient.java │ │ ├── IDaemonStrategy.java │ │ ├── NativeDaemonBase.java │ │ ├── PackageUtils.java │ │ ├── nativ │ │ ├── NativeDaemonAPI20.java │ │ └── NativeDaemonAPI21.java │ │ ├── proc │ │ ├── GuardService.java │ │ ├── Receiver1.java │ │ ├── Receiver2.java │ │ └── Service2.java │ │ └── strategy │ │ ├── DaemonStrategy21.java │ │ ├── DaemonStrategy22.java │ │ ├── DaemonStrategy23.java │ │ ├── DaemonStrategyUnder21.java │ │ └── DaemonStrategyXiaomi.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── authenticator.xml │ └── sync.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KeepProcLive 2 | Android进程保活主流方案集锦 3 | 4 | 这里面集成的方案包括: 5 | 6 | 1. Service指定为START_STICKY 被系统回收的进程会被系统重新拉起 7 | 8 | 2. Service设置为前台进程 将后台进程设置为前台进程,提高进程优先级 9 | 10 | 11 | 12 | 3. 1像素Activity方案 关屏后加载1个像素的Activity到Window,提高锁屏 后的进程优先级 13 | 14 | 4. 静态广播自启 利用监听开机启动广播、网络变化广播、应用安装删 除等广播,接收到广播后实现自启 15 | 16 | 5. JobSchedule (5.0以上)和AlarmManager 利用Android的API某些机制去实现自启 17 | 18 | 6. 账号同步拉活 利用Android自身的账号同步机制周期拉活 19 | 20 | 7. 守护进程 : 这块为了解决5.0以上机器拉活的问题,采用了文件锁监听进程死亡的机制,具体参考:http://blog.csdn.net/marswin89/article/details/50916631 21 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | android.useDeprecatedNdk=true 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /keeplive/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /keeplive/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | defaultConfig { 7 | applicationId "com.henrik.keeplive" 8 | minSdkVersion 14 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | sourceSets { 21 | main { 22 | jniLibs.srcDirs =['libs'] 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:23.1.1' 33 | testCompile 'junit:junit:4.12' 34 | } 35 | -------------------------------------------------------------------------------- /keeplive/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | LOCAL_MODULE := daemon_api20 4 | LOCAL_SRC_FILES := daemon_api20.c \ 5 | common.c 6 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include 7 | LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lm -lz 8 | include $(BUILD_SHARED_LIBRARY) 9 | 10 | include $(CLEAR_VARS) 11 | LOCAL_MODULE := daemon_api21 12 | LOCAL_SRC_FILES := daemon_api21.c \ 13 | common.c 14 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include 15 | LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lm -lz 16 | include $(BUILD_SHARED_LIBRARY) 17 | 18 | include $(CLEAR_VARS) 19 | LOCAL_MODULE := daemon 20 | LOCAL_SRC_FILES := daemon.c 21 | LOCAL_CFLAGS += -pie -fPIE 22 | LOCAL_LDFLAGS += -pie -fPIE 23 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include 24 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog -lm -lz 25 | include $(BUILD_EXECUTABLE) 26 | 27 | -------------------------------------------------------------------------------- /keeplive/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a x86 2 | APP_PLATFORM := android-15 -------------------------------------------------------------------------------- /keeplive/jni/com_marswin89_marsdaemon_nativ_NativeDaemonAPI20.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_marswin89_marsdaemon_nativ_NativeDaemonAPI20 */ 4 | 5 | #ifndef _Included_com_marswin89_marsdaemon_nativ_NativeDaemonAPI20 6 | #define _Included_com_marswin89_marsdaemon_nativ_NativeDaemonAPI20 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_marswin89_marsdaemon_nativ_NativeDaemonAPI20 12 | * Method: doDaemon 13 | * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V 14 | */ 15 | JNIEXPORT void JNICALL Java_com_marswin89_marsdaemon_nativ_NativeDaemonAPI20_doDaemon 16 | (JNIEnv *, jobject, jstring, jstring, jstring); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /keeplive/jni/com_marswin89_marsdaemon_nativ_NativeDaemonAPI21.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_marswin89_marsdaemon_nativ_NativeDaemonAPI21 */ 4 | 5 | #ifndef _Included_com_marswin89_marsdaemon_nativ_NativeDaemonAPI21 6 | #define _Included_com_marswin89_marsdaemon_nativ_NativeDaemonAPI21 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_marswin89_marsdaemon_nativ_NativeDaemonAPI21 12 | * Method: doDaemon 13 | * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V 14 | */ 15 | JNIEXPORT void JNICALL Java_com_marswin89_marsdaemon_nativ_NativeDaemonAPI21_doDaemon 16 | (JNIEnv *, jobject, jstring, jstring, jstring, jstring); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /keeplive/jni/common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : daemon_api21.c 3 | * Author : Mars Kwok 4 | * Date : Jul. 21, 2015 5 | * Description : common method here 6 | * 7 | * Copyright (C) Mars Kwok 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "log.h" 17 | 18 | /** 19 | * get the android version code 20 | */ 21 | int get_version(){ 22 | char value[8] = ""; 23 | __system_property_get("ro.build.version.sdk", value); 24 | return atoi(value); 25 | } 26 | 27 | /** 28 | * stitch three string to one 29 | */ 30 | char *str_stitching(const char *str1, const char *str2, const char *str3){ 31 | char *result; 32 | result = (char*) malloc(strlen(str1) + strlen(str2) + strlen(str3) + 1); 33 | if (!result){ 34 | return NULL; 35 | } 36 | strcpy(result, str1); 37 | strcat(result, str2); 38 | strcat(result, str3); 39 | return result; 40 | } 41 | 42 | /** 43 | * get android context 44 | */ 45 | jobject get_context(JNIEnv* env, jobject jobj){ 46 | jclass thiz_cls = (*env)->GetObjectClass(env, jobj); 47 | jfieldID context_field = (*env)->GetFieldID(env, thiz_cls, "mContext", "Landroid/content/Context;"); 48 | return (*env)->GetObjectField(env, jobj, context_field); 49 | } 50 | 51 | 52 | char* get_package_name(JNIEnv* env, jobject jobj){ 53 | jobject context_obj = get_context(env, jobj); 54 | jclass context_cls = (*env)->GetObjectClass(env, context_obj); 55 | jmethodID getpackagename_method = (*env)->GetMethodID(jobj, context_cls, "getPackageName", "()Ljava/lang/String;"); 56 | jstring package_name = (jstring)(*env)->CallObjectMethod(env, context_obj, getpackagename_method); 57 | return (char*)(*env)->GetStringUTFChars(env, package_name, 0); 58 | } 59 | 60 | 61 | /** 62 | * call java callback 63 | */ 64 | void java_callback(JNIEnv* env, jobject jobj, char* method_name){ 65 | jclass cls = (*env)->GetObjectClass(env, jobj); 66 | jmethodID cb_method = (*env)->GetMethodID(env, cls, method_name, "()V"); 67 | (*env)->CallVoidMethod(env, jobj, cb_method); 68 | } 69 | 70 | /** 71 | * start a android service 72 | */ 73 | void start_service(char* package_name, char* service_name){ 74 | pid_t pid = fork(); 75 | if(pid < 0){ 76 | //error, do nothing... 77 | }else if(pid == 0){ 78 | if(package_name == NULL || service_name == NULL){ 79 | exit(EXIT_SUCCESS); 80 | } 81 | int version = get_version(); 82 | char* pkg_svc_name = str_stitching(package_name, "/", service_name); 83 | if (version >= 17 || version == 0) { 84 | execlp("am", "am", "startservice", "--user", "0", "-n", pkg_svc_name, (char *) NULL); 85 | } else { 86 | execlp("am", "am", "startservice", "-n", pkg_svc_name, (char *) NULL); 87 | } 88 | exit(EXIT_SUCCESS); 89 | }else{ 90 | waitpid(pid, NULL, 0); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /keeplive/jni/constant.h: -------------------------------------------------------------------------------- 1 | #define NATIVE_DAEMON_NAME "mars_d" 2 | #define BUFFER_SIZE 2048 3 | #define DAEMON_CALLBACK_NAME "onDaemonDead" 4 | #define PARAM_PIPE_1_READ "-p1r" 5 | #define PARAM_PIPE_1_WRITE "-p1w" 6 | #define PARAM_PIPE_2_READ "-p2r" 7 | #define PARAM_PIPE_2_WRITE "-p2w" 8 | #define PARAM_PKG_NAME "-p" 9 | #define PARAM_SVC_NAME "-s" 10 | 11 | 12 | -------------------------------------------------------------------------------- /keeplive/jni/daemon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : daemon_api21.c 3 | * Author : Mars Kwok 4 | * Date : Jul. 21, 2015 5 | * Description : This is native process to watch parent process. 6 | * 7 | * Copyright (C) Mars Kwok 8 | * 9 | */ 10 | #include 11 | #include 12 | 13 | #include "log.h" 14 | #include "constant.h" 15 | 16 | 17 | /** 18 | * get the android version code 19 | */ 20 | int get_version(){ 21 | char value[8] = ""; 22 | __system_property_get("ro.build.version.sdk", value); 23 | return atoi(value); 24 | } 25 | 26 | char *str_stitching(const char *str1, const char *str2, const char *str3){ 27 | char *result; 28 | result = (char*) malloc(strlen(str1) + strlen(str2) + strlen(str3) + 1); 29 | if (!result){ 30 | return NULL; 31 | } 32 | strcpy(result, str1); 33 | strcat(result, str2); 34 | strcat(result, str3); 35 | return result; 36 | } 37 | 38 | /** 39 | * start a android service 40 | */ 41 | 42 | 43 | 44 | void start_service(char* package_name, char* service_name){ 45 | pid_t pid = fork(); 46 | if(pid < 0){ 47 | //error, do nothing... 48 | }else if(pid == 0){ 49 | if(package_name == NULL || service_name == NULL){ 50 | exit(EXIT_SUCCESS); 51 | } 52 | int version = get_version(); 53 | char* pkg_svc_name = str_stitching(package_name, "/", service_name); 54 | if (version >= 17 || version == 0) { 55 | execlp("am", "am", "startservice", "--user", "0", "-n", pkg_svc_name,"--include-stopped-packages", (char *) NULL); 56 | } else { 57 | execlp("am", "am", "startservice", "-n", pkg_svc_name,"--include-stopped-packages", (char *) NULL); 58 | } 59 | exit(EXIT_SUCCESS); 60 | }else{ 61 | waitpid(pid, NULL, 0); 62 | } 63 | } 64 | 65 | 66 | 67 | int main(int argc, char *argv[]){ 68 | pid_t pid = fork(); 69 | if(pid == 0){ 70 | setsid(); 71 | int pipe_fd1[2]; 72 | int pipe_fd2[2]; 73 | char* pkg_name; 74 | char* svc_name; 75 | if(argc < 13){ 76 | LOGE("daemon parameters error"); 77 | return -1; 78 | } 79 | int i; 80 | for (i = 0; i < argc; i ++){ 81 | if(argv[i] == NULL){ 82 | continue; 83 | } 84 | if (!strcmp(PARAM_PKG_NAME, argv[i])){ 85 | pkg_name = argv[i + 1]; 86 | }else if (!strcmp(PARAM_SVC_NAME, argv[i])) { 87 | svc_name = argv[i + 1]; 88 | }else if (!strcmp(PARAM_PIPE_1_READ, argv[i])){ 89 | char* p1r = argv[i + 1]; 90 | pipe_fd1[0] = atoi(p1r); 91 | }else if (!strcmp(PARAM_PIPE_1_WRITE, argv[i])) { 92 | char* p1w = argv[i + 1]; 93 | pipe_fd1[1] = atoi(p1w); 94 | }else if (!strcmp(PARAM_PIPE_2_READ, argv[i])) { 95 | char* p2r = argv[i + 1]; 96 | pipe_fd2[0] = atoi(p2r); 97 | }else if (!strcmp(PARAM_PIPE_2_WRITE, argv[i])) { 98 | char* p2w = argv[i + 1]; 99 | pipe_fd2[1] = atoi(p2w); 100 | } 101 | } 102 | 103 | close(pipe_fd1[0]); 104 | close(pipe_fd2[1]); 105 | 106 | char r_buf[100]; 107 | int r_num; 108 | memset(r_buf,0, sizeof(r_buf)); 109 | 110 | r_num=read(pipe_fd2[0], r_buf, 100); 111 | LOGE("Watch >>>>PARENT<<<< Dead !!"); 112 | int count = 0; 113 | while(count < 50){ 114 | start_service(pkg_name, svc_name); 115 | usleep(100000); 116 | count++; 117 | } 118 | }else{ 119 | exit(EXIT_SUCCESS); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /keeplive/jni/daemon_api20.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : daemon_api21.c 3 | * Author : Mars Kwok 4 | * Date : Jul. 21, 2015 5 | * Description : This is used to watch process dead under api 20 6 | * 7 | * Copyright (C) Mars Kwok 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | 14 | #include "log.h" 15 | #include "constant.h" 16 | #include "com_marswin89_marsdaemon_nativ_NativeDaemonAPI20.h" 17 | 18 | 19 | 20 | /** 21 | * get the process pid by process name 22 | */ 23 | int find_pid_by_name(char *pid_name, int *pid_list){ 24 | DIR *dir; 25 | struct dirent *next; 26 | int i = 0; 27 | pid_list[0] = 0; 28 | dir = opendir("/proc"); 29 | if (!dir){ 30 | return 0; 31 | } 32 | while ((next = readdir(dir)) != NULL){ 33 | FILE *status; 34 | char proc_file_name[BUFFER_SIZE]; 35 | char buffer[BUFFER_SIZE]; 36 | char process_name[BUFFER_SIZE]; 37 | 38 | if (strcmp(next->d_name, "..") == 0){ 39 | continue; 40 | } 41 | if (!isdigit(*next->d_name)){ 42 | continue; 43 | } 44 | sprintf(proc_file_name, "/proc/%s/cmdline", next->d_name); 45 | if (!(status = fopen(proc_file_name, "r"))){ 46 | continue; 47 | } 48 | if (fgets(buffer, BUFFER_SIZE - 1, status) == NULL){ 49 | fclose(status); 50 | continue; 51 | } 52 | fclose(status); 53 | sscanf(buffer, "%[^-]", process_name); 54 | if (strcmp(process_name, pid_name) == 0){ 55 | pid_list[i ++] = atoi(next->d_name); 56 | } 57 | } 58 | if (pid_list){ 59 | pid_list[i] = 0; 60 | } 61 | closedir(dir); 62 | return i; 63 | } 64 | 65 | /** 66 | * kill all process by name 67 | */ 68 | void kill_zombie_process(char* zombie_name){ 69 | int pid_list[200]; 70 | int total_num = find_pid_by_name(zombie_name, pid_list); 71 | LOGD("zombie process name is %s, and number is %d, killing...", zombie_name, total_num); 72 | int i; 73 | for (i = 0; i < total_num; i ++) { 74 | int retval = 0; 75 | int daemon_pid = pid_list[i]; 76 | if (daemon_pid > 1 && daemon_pid != getpid() && daemon_pid != getppid()){ 77 | retval = kill(daemon_pid, SIGTERM); 78 | if (!retval){ 79 | LOGD("kill zombie successfully, zombie`s pid = %d", daemon_pid); 80 | }else{ 81 | LOGE("kill zombie failed, zombie`s pid = %d", daemon_pid); 82 | } 83 | } 84 | } 85 | } 86 | 87 | JNIEXPORT void JNICALL Java_com_marswin89_marsdaemon_nativ_NativeDaemonAPI20_doDaemon(JNIEnv *env, jobject jobj, jstring pkgName, jstring svcName, jstring daemonPath){ 88 | if(pkgName == NULL || svcName == NULL || daemonPath == NULL){ 89 | LOGE("native doDaemon parameters cannot be NULL !"); 90 | return ; 91 | } 92 | 93 | char *pkg_name = (char*)(*env)->GetStringUTFChars(env, pkgName, 0); 94 | char *svc_name = (char*)(*env)->GetStringUTFChars(env, svcName, 0); 95 | char *daemon_path = (char*)(*env)->GetStringUTFChars(env, daemonPath, 0); 96 | 97 | kill_zombie_process(NATIVE_DAEMON_NAME); 98 | 99 | int pipe_fd1[2];//order to watch child 100 | int pipe_fd2[2];//order to watch parent 101 | 102 | pid_t pid; 103 | char r_buf[100]; 104 | int r_num; 105 | memset(r_buf, 0, sizeof(r_buf)); 106 | if(pipe(pipe_fd1)<0){ 107 | LOGE("pipe1 create error"); 108 | return ; 109 | } 110 | if(pipe(pipe_fd2)<0){ 111 | LOGE("pipe2 create error"); 112 | return ; 113 | } 114 | 115 | char str_p1r[10]; 116 | char str_p1w[10]; 117 | char str_p2r[10]; 118 | char str_p2w[10]; 119 | 120 | sprintf(str_p1r,"%d",pipe_fd1[0]); 121 | sprintf(str_p1w,"%d",pipe_fd1[1]); 122 | sprintf(str_p2r,"%d",pipe_fd2[0]); 123 | sprintf(str_p2w,"%d",pipe_fd2[1]); 124 | 125 | 126 | if((pid=fork())==0){ 127 | execlp(daemon_path, 128 | NATIVE_DAEMON_NAME, 129 | PARAM_PKG_NAME, pkg_name, 130 | PARAM_SVC_NAME, svc_name, 131 | PARAM_PIPE_1_READ, str_p1r, 132 | PARAM_PIPE_1_WRITE, str_p1w, 133 | PARAM_PIPE_2_READ, str_p2r, 134 | PARAM_PIPE_2_WRITE, str_p2w, 135 | (char *) NULL); 136 | }else if(pid>0){ 137 | close(pipe_fd1[1]); 138 | close(pipe_fd2[0]); 139 | //wait for child 140 | r_num=read(pipe_fd1[0], r_buf, 100); 141 | LOGE("Watch >>>>CHILD<<<< Dead !!!"); 142 | java_callback(env, jobj, DAEMON_CALLBACK_NAME); 143 | } 144 | } 145 | 146 | -------------------------------------------------------------------------------- /keeplive/jni/daemon_api21.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : daemon_api21.c 3 | * Author : Mars Kwok 4 | * Date : Jul. 21, 2015 5 | * Description : This is used to watch process dead over api 21 6 | * 7 | * Copyright (C) Mars Kwok 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | 14 | #include "com_marswin89_marsdaemon_nativ_NativeDaemonAPI21.h" 15 | #include "log.h" 16 | #include "constant.h" 17 | 18 | void waitfor_self_observer(char* observer_file_path){ 19 | int lockFileDescriptor = open(observer_file_path, O_RDONLY); 20 | if (lockFileDescriptor == -1){ 21 | LOGE("Watched >>>>OBSERVER<<<< has been ready before watching..."); 22 | return ; 23 | } 24 | 25 | void *p_buf = malloc(sizeof(struct inotify_event)); 26 | if (p_buf == NULL){ 27 | LOGE("malloc failed !!!"); 28 | return; 29 | } 30 | int maskStrLength = 7 + 10 + 1; 31 | char *p_maskStr = malloc(maskStrLength); 32 | if (p_maskStr == NULL){ 33 | free(p_buf); 34 | LOGE("malloc failed !!!"); 35 | return; 36 | } 37 | int fileDescriptor = inotify_init(); 38 | if (fileDescriptor < 0){ 39 | free(p_buf); 40 | free(p_maskStr); 41 | LOGE("inotify_init failed !!!"); 42 | return; 43 | } 44 | 45 | int watchDescriptor = inotify_add_watch(fileDescriptor, observer_file_path, IN_ALL_EVENTS); 46 | if (watchDescriptor < 0){ 47 | free(p_buf); 48 | free(p_maskStr); 49 | LOGE("inotify_add_watch failed !!!"); 50 | return; 51 | } 52 | 53 | 54 | while(1){ 55 | size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event)); 56 | if (4 == ((struct inotify_event *) p_buf)->mask){ 57 | LOGE("Watched >>>>OBSERVER<<<< has been ready..."); 58 | free(p_maskStr); 59 | free(p_buf); 60 | return; 61 | } 62 | } 63 | } 64 | 65 | void notify_daemon_observer(unsigned char is_persistent, char* observer_file_path){ 66 | if(!is_persistent){ 67 | int lockFileDescriptor = open(observer_file_path, O_RDONLY); 68 | while(lockFileDescriptor == -1){ 69 | lockFileDescriptor = open(observer_file_path, O_RDONLY); 70 | } 71 | } 72 | remove(observer_file_path); 73 | } 74 | 75 | notify_and_waitfor(char *observer_self_path, char *observer_daemon_path){ 76 | int observer_self_descriptor = open(observer_self_path, O_RDONLY); 77 | if (observer_self_descriptor == -1){ 78 | observer_self_descriptor = open(observer_self_path, O_CREAT, S_IRUSR | S_IWUSR); 79 | } 80 | int observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY); 81 | while (observer_daemon_descriptor == -1){ 82 | usleep(1000); 83 | observer_daemon_descriptor = open(observer_daemon_path, O_RDONLY); 84 | } 85 | remove(observer_daemon_path); 86 | LOGE("Watched >>>>OBSERVER<<<< has been ready..."); 87 | } 88 | 89 | 90 | /** 91 | * Lock the file, this is block method. 92 | */ 93 | int lock_file(char* lock_file_path){ 94 | LOGD("start try to lock file >> %s <<", lock_file_path); 95 | int lockFileDescriptor = open(lock_file_path, O_RDONLY); 96 | if (lockFileDescriptor == -1){ 97 | lockFileDescriptor = open(lock_file_path, O_CREAT, S_IRUSR); 98 | } 99 | int lockRet = flock(lockFileDescriptor, LOCK_EX); 100 | if (lockRet == -1){ 101 | LOGE("lock file failed >> %s <<", lock_file_path); 102 | return 0; 103 | }else{ 104 | LOGD("lock file success >> %s <<", lock_file_path); 105 | return 1; 106 | } 107 | } 108 | 109 | 110 | JNIEXPORT void JNICALL Java_com_marswin89_marsdaemon_nativ_NativeDaemonAPI21_doDaemon(JNIEnv *env, jobject jobj, jstring indicatorSelfPath, jstring indicatorDaemonPath, jstring observerSelfPath, jstring observerDaemonPath){ 111 | if(indicatorSelfPath == NULL || indicatorDaemonPath == NULL || observerSelfPath == NULL || observerDaemonPath == NULL){ 112 | LOGE("parameters cannot be NULL !"); 113 | return ; 114 | } 115 | 116 | char* indicator_self_path = (char*)(*env)->GetStringUTFChars(env, indicatorSelfPath, 0); 117 | char* indicator_daemon_path = (char*)(*env)->GetStringUTFChars(env, indicatorDaemonPath, 0); 118 | char* observer_self_path = (char*)(*env)->GetStringUTFChars(env, observerSelfPath, 0); 119 | char* observer_daemon_path = (char*)(*env)->GetStringUTFChars(env, observerDaemonPath, 0); 120 | 121 | int lock_status = 0; 122 | int try_time = 0; 123 | while(try_time < 3 && !(lock_status = lock_file(indicator_self_path))){ 124 | try_time++; 125 | LOGD("Persistent lock myself failed and try again as %d times", try_time); 126 | usleep(10000); 127 | } 128 | if(!lock_status){ 129 | LOGE("Persistent lock myself failed and exit"); 130 | return ; 131 | } 132 | 133 | // notify_daemon_observer(observer_daemon_path); 134 | // waitfor_self_observer(observer_self_path); 135 | notify_and_waitfor(observer_self_path, observer_daemon_path); 136 | 137 | lock_status = lock_file(indicator_daemon_path); 138 | if(lock_status){ 139 | LOGE("Watch >>>>DAEMON<<<<< Daed !!"); 140 | remove(observer_self_path);// it`s important ! to prevent from deadlock 141 | java_callback(env, jobj, DAEMON_CALLBACK_NAME); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /keeplive/jni/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File : daemon_below20.c 3 | * Author : Guoyang3 4 | * Date : Aug. 14, 2015 5 | * Description : for easy log. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define TAG "Daemon" 12 | 13 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) 14 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 15 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) 16 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) -------------------------------------------------------------------------------- /keeplive/libs/armeabi-v7a/libdaemon_api20.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/armeabi-v7a/libdaemon_api20.so -------------------------------------------------------------------------------- /keeplive/libs/armeabi-v7a/libdaemon_api21.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/armeabi-v7a/libdaemon_api21.so -------------------------------------------------------------------------------- /keeplive/libs/armeabi/libdaemon_api20.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/armeabi/libdaemon_api20.so -------------------------------------------------------------------------------- /keeplive/libs/armeabi/libdaemon_api21.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/armeabi/libdaemon_api21.so -------------------------------------------------------------------------------- /keeplive/libs/x86/libdaemon_api20.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/x86/libdaemon_api20.so -------------------------------------------------------------------------------- /keeplive/libs/x86/libdaemon_api21.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/libs/x86/libdaemon_api21.so -------------------------------------------------------------------------------- /keeplive/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /keeplive/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 80 | 81 | 84 | 85 | 86 | 87 | 90 | 91 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /keeplive/src/main/assets/armeabi-v7a/daemon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/assets/armeabi-v7a/daemon -------------------------------------------------------------------------------- /keeplive/src/main/assets/armeabi/daemon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/assets/armeabi/daemon -------------------------------------------------------------------------------- /keeplive/src/main/assets/x86/daemon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/assets/x86/daemon -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.marswin89.marsdaemon.proc.GuardService; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | 15 | GuardService.start(getApplicationContext()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/ProcApplication.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.marswin89.marsdaemon.DaemonClient; 7 | 8 | public class ProcApplication extends Application { 9 | 10 | @Override 11 | protected void attachBaseContext(Context base) { 12 | super.attachBaseContext(base); 13 | DaemonClient.startDaemon(base); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/accountsync/AccountAuthenticatorService.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.accountsync; 2 | 3 | import android.accounts.AbstractAccountAuthenticator; 4 | import android.accounts.Account; 5 | import android.accounts.AccountAuthenticatorResponse; 6 | import android.accounts.NetworkErrorException; 7 | import android.app.Service; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Bundle; 11 | import android.os.IBinder; 12 | import android.util.Log; 13 | 14 | /** 15 | * Created by henrikwu on 2017/5/4. 16 | */ 17 | 18 | public class AccountAuthenticatorService extends Service { 19 | 20 | private AccountAuthenticator accountAuthenticator; 21 | 22 | 23 | @Override 24 | public void onCreate() { 25 | accountAuthenticator = new AccountAuthenticator(this); 26 | accountAuthenticator.toString(); 27 | try { 28 | Thread.sleep(5000); 29 | } catch (InterruptedException e) { 30 | e.printStackTrace(); 31 | } 32 | Log.e("AccountAuthenticator", accountAuthenticator.toString()); 33 | } 34 | 35 | @Override 36 | public IBinder onBind(Intent intent) { 37 | if (intent.getAction().equals( 38 | android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) 39 | return accountAuthenticator.getIBinder(); 40 | return null; 41 | } 42 | 43 | private static class AccountAuthenticator extends 44 | AbstractAccountAuthenticator { 45 | final Context context; 46 | 47 | public AccountAuthenticator(Context context) { 48 | super(context); 49 | this.context = context; 50 | } 51 | 52 | @Override 53 | public Bundle addAccount(AccountAuthenticatorResponse response, 54 | String accountType, String authTokenType, 55 | String[] requiredFeatures, Bundle options) 56 | throws NetworkErrorException { 57 | // Intent intent = new Intent(context, LoginActivity.class); 58 | // intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, 59 | // response); 60 | // Bundle bundle = new Bundle(); 61 | // bundle.putParcelable(AccountManager.KEY_INTENT, intent); 62 | return null; 63 | } 64 | 65 | @Override 66 | public Bundle confirmCredentials(AccountAuthenticatorResponse response, 67 | Account account, Bundle options) throws NetworkErrorException { 68 | return null; 69 | } 70 | 71 | @Override 72 | public Bundle editProperties(AccountAuthenticatorResponse response, 73 | String accountType) { 74 | return null; 75 | } 76 | 77 | @Override 78 | public Bundle getAuthToken(AccountAuthenticatorResponse response, 79 | Account account, String authTokenType, Bundle options) 80 | throws NetworkErrorException { 81 | return null; 82 | } 83 | 84 | @Override 85 | public String getAuthTokenLabel(String authTokenType) { 86 | return null; 87 | } 88 | 89 | @Override 90 | public Bundle hasFeatures(AccountAuthenticatorResponse response, 91 | Account account, String[] features) 92 | throws NetworkErrorException { 93 | return null; 94 | } 95 | 96 | @Override 97 | public Bundle updateCredentials(AccountAuthenticatorResponse response, 98 | Account account, String authTokenType, Bundle options) 99 | throws NetworkErrorException { 100 | return null; 101 | } 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/accountsync/EmptyProviderForSync.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.accountsync; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.support.annotation.Nullable; 8 | 9 | /** 10 | * Created by henrikwu on 2017/5/4. 11 | */ 12 | 13 | /** 14 | * 空的provider 为了帐号同步 保活 15 | */ 16 | public class EmptyProviderForSync extends ContentProvider { 17 | @Override 18 | public boolean onCreate() { 19 | return false; 20 | } 21 | 22 | @Nullable 23 | @Override 24 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 25 | return null; 26 | } 27 | 28 | @Nullable 29 | @Override 30 | public String getType(Uri uri) { 31 | return null; 32 | } 33 | 34 | @Nullable 35 | @Override 36 | public Uri insert(Uri uri, ContentValues values) { 37 | return null; 38 | } 39 | 40 | @Override 41 | public int delete(Uri uri, String selection, String[] selectionArgs) { 42 | return 0; 43 | } 44 | 45 | @Override 46 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 47 | return 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/accountsync/SyncService.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.accountsync; 2 | 3 | import android.accounts.Account; 4 | import android.accounts.AccountManager; 5 | import android.app.Service; 6 | import android.content.AbstractThreadedSyncAdapter; 7 | import android.content.ContentProviderClient; 8 | import android.content.ContentResolver; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.SyncResult; 12 | import android.os.Bundle; 13 | import android.os.IBinder; 14 | import android.util.Log; 15 | 16 | /** 17 | * Created by henrikwu on 2017/5/4. 18 | */ 19 | public class SyncService extends Service{ 20 | 21 | private static final Object syncLock = new Object(); 22 | private static SyncAdapter syncAdapter = null; 23 | 24 | @Override 25 | public void onCreate() { 26 | super.onCreate(); 27 | synchronized (syncLock) { 28 | if (syncAdapter == null) { 29 | syncAdapter = new SyncAdapter(getApplicationContext(), true); 30 | } 31 | } 32 | } 33 | 34 | @Override 35 | public IBinder onBind(Intent intent) { 36 | return syncAdapter.getSyncAdapterBinder(); 37 | } 38 | 39 | 40 | class SyncAdapter extends AbstractThreadedSyncAdapter { 41 | 42 | public SyncAdapter(Context context, boolean autoInitialize) { 43 | super(context, autoInitialize); 44 | } 45 | 46 | @Override 47 | public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { 48 | //do sync here 49 | Log.v(getClass().getName(),"onPerformSync"); 50 | } 51 | } 52 | 53 | /** 54 | * 利用帐号同步机制拉活 55 | * @param context 56 | */ 57 | public static void startAccountSync(Context context){ 58 | String accountType = "com.henrik.keeplive"; 59 | AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 60 | Account account = null; 61 | Account[] accounts = accountManager.getAccountsByType(accountType); 62 | if(accounts.length>0){ 63 | account = accounts[0]; 64 | }else { 65 | account = new Account("test", accountType); 66 | } 67 | 68 | if(accountManager.addAccountExplicitly(account,null,null)){ 69 | String authority = accountType; 70 | long sync_interval = 15*60; 71 | ContentResolver.setIsSyncable(account,authority,1); 72 | ContentResolver.setSyncAutomatically(account, authority, true); //自动同步 73 | ContentResolver.addPeriodicSync(account, authority, new Bundle(), sync_interval);//设置同步时间间隔 74 | } 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/onepx/KeepLiveActivity.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.onepx; 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.Bundle; 9 | import android.os.PowerManager; 10 | import android.util.Log; 11 | import android.view.Gravity; 12 | import android.view.Window; 13 | import android.view.WindowManager; 14 | 15 | /** 16 | * Created by henrikwu on 2017/5/4. 17 | */ 18 | 19 | public class KeepLiveActivity extends Activity { 20 | private BroadcastReceiver br; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | Window window = getWindow(); 26 | window.setGravity(Gravity.LEFT | Gravity.TOP); 27 | WindowManager.LayoutParams params = window.getAttributes(); 28 | params.x = 0; 29 | params.y = 0; 30 | params.height = 1; 31 | params.width = 1; 32 | window.setAttributes(params); 33 | br = new BroadcastReceiver() { 34 | @Override 35 | public void onReceive(Context context, Intent intent) { 36 | // LogUtils.i(intent.getAction()); 37 | // LogUtils.i(context.toString()); 38 | // Intent intent1 = new Intent(Intent.ACTION_MAIN); 39 | // intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 40 | // intent1.addCategory(Intent.CATEGORY_HOME); 41 | // startActivity(intent1); 42 | Log.d(getClass().getName(), "OnepxActivity finish ================"); 43 | finish(); 44 | } 45 | }; 46 | registerReceiver(br, new IntentFilter("finish activity")); 47 | checkScreenOn("onCreate"); 48 | Log.d(getClass().getName(), "===onCreate==="); 49 | } 50 | 51 | private void checkScreenOn(String methodName) { 52 | Log.d(getClass().getName(), "from call method: " + methodName); 53 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 54 | boolean isScreenOn = pm.isScreenOn(); 55 | Log.d(getClass().getName(), "isScreenOn: " + isScreenOn); 56 | if (isScreenOn) { 57 | finish(); 58 | } 59 | 60 | } 61 | 62 | @Override 63 | protected void onDestroy() { 64 | Log.d(getClass().getName(), "===onDestroy==="); 65 | try { 66 | unregisterReceiver(br); 67 | } catch (IllegalArgumentException e) { 68 | Log.e(getClass().getName(), "receiver is not resisted: " + e); 69 | } 70 | super.onDestroy(); 71 | } 72 | 73 | @Override 74 | protected void onResume() { 75 | super.onResume(); 76 | checkScreenOn("onResume"); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/onepx/OnepxReceiver.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.onepx; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.util.Log; 8 | 9 | /** 10 | * Created by henrikwu on 2017/5/4. 11 | */ 12 | 13 | public class OnepxReceiver extends BroadcastReceiver { 14 | private static OnepxReceiver receiver; 15 | 16 | @Override 17 | public void onReceive(Context context, Intent intent) { 18 | if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 19 | Intent it = new Intent(context, KeepLiveActivity.class); 20 | it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 21 | context.startActivity(it); 22 | Log.d(getClass().getName(), "1px--screen off-"); 23 | } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { 24 | context.sendBroadcast(new Intent("finish activity")); 25 | Log.d(getClass().getName(), "1px--screen on-"); 26 | } 27 | } 28 | 29 | public static void register1pxReceiver(Context context) { 30 | if (receiver == null) { 31 | receiver = new OnepxReceiver(); 32 | } 33 | context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 34 | context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON)); 35 | } 36 | 37 | public static void unregister1pxReceiver(Context context) { 38 | context.unregisterReceiver(receiver); 39 | } 40 | } -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/receiver/BootReceiver.java: -------------------------------------------------------------------------------- 1 | package com.henrik.keeplive.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | import com.marswin89.marsdaemon.PackageUtils; 9 | import com.marswin89.marsdaemon.proc.GuardService; 10 | 11 | /** 12 | * 这个是app狗带或者手机重启之后把app拉起的receiver, 13 | * 一般监听 android.intent.action.BOOT_COMPLETED 和 android.intent.action.MEDIA_MOUNTED 就能应付手机重启, 14 | * Created by pelli on 2015/12/4. 15 | * add by henrikwu. 监听一下系统广播 拉活 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 | */ 42 | public class BootReceiver extends BroadcastReceiver { 43 | 44 | 45 | @Override 46 | public void onReceive(Context context, Intent intent) { 47 | Log.i("BootReceiver", 48 | "onReceive, action=" + (intent == null ? "" : intent.getAction())); 49 | 50 | GuardService.start(context); 51 | if(!PackageUtils.isComponentDefault(context,BootReceiver.class.getName())){ 52 | PackageUtils.setComponentDefault(context,BootReceiver.class.getName()); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/henrik/keeplive/schedulerjob/DaemonJobService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * create by henrikwu 3 | * 利用JobScheduler拉活进程,在5.0以上设备,这个方法很好用(除了小米) 4 | */ 5 | package com.henrik.keeplive.schedulerjob; 6 | 7 | import android.annotation.TargetApi; 8 | import android.app.job.JobInfo; 9 | import android.app.job.JobParameters; 10 | import android.app.job.JobScheduler; 11 | import android.app.job.JobService; 12 | import android.content.ComponentName; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.os.Build; 16 | import android.util.Log; 17 | 18 | 19 | /** 20 | * android5.0以上用JobScheduler 21 | */ 22 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 23 | public class DaemonJobService extends JobService { 24 | private static final String TAG = "DaemonJobService";; 25 | 26 | @Override 27 | public void onCreate() { 28 | super.onCreate(); 29 | Log.i(TAG, "Service created"); 30 | } 31 | 32 | @Override 33 | public void onDestroy() { 34 | super.onDestroy(); 35 | Log.i(TAG, "Service destroyed"); 36 | } 37 | 38 | /** 39 | * When the app's MainActivity is created, it starts this service. This is so that the 40 | * activity and this service can communicate back and forth. See "setUiCalback()" 41 | */ 42 | @Override 43 | public int onStartCommand(Intent intent, int flags, int startId) { 44 | return START_NOT_STICKY; 45 | } 46 | 47 | @Override 48 | public boolean onStartJob(JobParameters params) { 49 | Log.i(TAG, "on start job: " + params.getJobId()); 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean onStopJob(JobParameters params) { 55 | Log.i(TAG, "on stop job: " + params.getJobId()); 56 | return true; 57 | } 58 | 59 | /** 60 | * Android5.0以上可以使用JobScheduler来拉活进程 61 | */ 62 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 63 | public static void startScheduleDaemonJob(Context context) { 64 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){ 65 | return; 66 | } 67 | try { 68 | int jobId = 1; 69 | JobInfo.Builder builder = new JobInfo.Builder(jobId,new ComponentName(context, DaemonJobService.class)); 70 | builder.setPeriodic(15*60*1000); 71 | builder.setPersisted(true); 72 | JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); 73 | jobScheduler.schedule(builder.build()); 74 | }catch (Exception e){ 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/DaemonClient.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import android.content.Context; 4 | 5 | import com.marswin89.marsdaemon.proc.GuardService; 6 | import com.marswin89.marsdaemon.proc.Receiver1; 7 | import com.marswin89.marsdaemon.proc.Receiver2; 8 | import com.marswin89.marsdaemon.proc.Service2; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.File; 12 | import java.io.FileReader; 13 | import java.io.IOException; 14 | 15 | /** 16 | * 在{@link com.henrik.keeplive.ProcApplication #attachBaseContext(Context base)}启动 17 | */ 18 | public class DaemonClient implements IDaemonClient{ 19 | private DaemonConfigurations mConfigurations; 20 | public DaemonClient(DaemonConfigurations configurations) { 21 | this.mConfigurations = configurations; 22 | } 23 | @Override 24 | public void onAttachBaseContext(Context base) { 25 | initDaemon(base); 26 | } 27 | 28 | 29 | 30 | private BufferedReader mBufferedReader;//release later to save time 31 | 32 | 33 | /** 34 | * do some thing about daemon 35 | * @param base 36 | */ 37 | private void initDaemon(Context base) { 38 | if(mConfigurations == null){ 39 | return ; 40 | } 41 | String processName = getProcessName(); 42 | String packageName = base.getPackageName(); 43 | 44 | if(processName.startsWith(mConfigurations.PERSISTENT_CONFIG.PROCESS_NAME)){ 45 | IDaemonStrategy.Fetcher.fetchStrategy().onPersistentCreate(base, mConfigurations); 46 | }else if(processName.startsWith(mConfigurations.DAEMON_ASSISTANT_CONFIG.PROCESS_NAME)){ 47 | IDaemonStrategy.Fetcher.fetchStrategy().onDaemonAssistantCreate(base, mConfigurations); 48 | }else if(processName.startsWith(packageName)){ 49 | IDaemonStrategy.Fetcher.fetchStrategy().onInitialization(base); 50 | } 51 | 52 | releaseIO(); 53 | } 54 | 55 | 56 | /* spend too much time !! 60+ms 57 | private String getProcessName(){ 58 | ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); 59 | int pid = android.os.Process.myPid(); 60 | List infos = am.getRunningAppProcesses(); 61 | for (int i = 0; i < infos.size(); i++) { 62 | RunningAppProcessInfo info = infos.get(i); 63 | if(pid == info.pid){ 64 | return info.processName; 65 | } 66 | } 67 | return null; 68 | } 69 | */ 70 | 71 | private String getProcessName() { 72 | try { 73 | File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline"); 74 | mBufferedReader = new BufferedReader(new FileReader(file)); 75 | return mBufferedReader.readLine(); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | return null; 79 | } 80 | } 81 | 82 | /** 83 | * release reader IO 84 | */ 85 | private void releaseIO(){ 86 | if(mBufferedReader != null){ 87 | try { 88 | mBufferedReader.close(); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | mBufferedReader = null; 93 | } 94 | } 95 | 96 | 97 | private static DaemonConfigurations createDaemonConfigurations(){ 98 | DaemonConfigurations.DaemonConfiguration configuration1 = new DaemonConfigurations.DaemonConfiguration( 99 | "com.henrik.keeplive:service", 100 | GuardService.class.getCanonicalName(), 101 | Receiver1.class.getCanonicalName()); 102 | DaemonConfigurations.DaemonConfiguration configuration2 = new DaemonConfigurations.DaemonConfiguration( 103 | "com.henrik.keeplive:deamonService", 104 | Service2.class.getCanonicalName(), 105 | Receiver2.class.getCanonicalName()); 106 | DaemonConfigurations.DaemonListener listener = new MyDaemonListener(); 107 | //return new DaemonConfigurations(configuration1, configuration2);//listener can be null 108 | return new DaemonConfigurations(configuration1, configuration2, listener); 109 | } 110 | 111 | 112 | static class MyDaemonListener implements DaemonConfigurations.DaemonListener{ 113 | @Override 114 | public void onPersistentStart(Context context) { 115 | } 116 | 117 | @Override 118 | public void onDaemonAssistantStart(Context context) { 119 | } 120 | 121 | @Override 122 | public void onWatchDaemonDaed() { 123 | } 124 | } 125 | 126 | /** 127 | * 新的进程保活方案:文件锁方案兼容5.0以上机型 128 | * 技术参考:http://blog.csdn.net/marswin89/article/details/50917098 129 | */ 130 | public static void startDaemon(Context base){ 131 | DaemonClient daemonClient = new DaemonClient(createDaemonConfigurations()); 132 | daemonClient.onAttachBaseContext(base); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/DaemonConfigurations.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * the configurations of Daemon SDK, contains two process configuration. 7 | * @author Mars 8 | * 9 | */ 10 | public class DaemonConfigurations { 11 | 12 | public final DaemonConfiguration PERSISTENT_CONFIG; 13 | public final DaemonConfiguration DAEMON_ASSISTANT_CONFIG; 14 | public final DaemonListener LISTENER; 15 | 16 | public DaemonConfigurations(DaemonConfiguration persistentConfig, DaemonConfiguration daemonAssistantConfig){ 17 | this.PERSISTENT_CONFIG = persistentConfig; 18 | this.DAEMON_ASSISTANT_CONFIG = daemonAssistantConfig; 19 | this.LISTENER = null; 20 | } 21 | 22 | public DaemonConfigurations(DaemonConfiguration persistentConfig, DaemonConfiguration daemonAssistantConfig, DaemonListener listener){ 23 | this.PERSISTENT_CONFIG = persistentConfig; 24 | this.DAEMON_ASSISTANT_CONFIG = daemonAssistantConfig; 25 | this.LISTENER = listener; 26 | } 27 | 28 | 29 | 30 | /** 31 | * the configuration of a daemon process, contains process name, service name and receiver name if Android 6.0 32 | * @author guoyang 33 | * 34 | */ 35 | public static class DaemonConfiguration{ 36 | 37 | public final String PROCESS_NAME; 38 | public final String SERVICE_NAME; 39 | public final String RECEIVER_NAME; 40 | 41 | public DaemonConfiguration(String processName, String serviceName, String receiverName){ 42 | this.PROCESS_NAME = processName; 43 | this.SERVICE_NAME = serviceName; 44 | this.RECEIVER_NAME = receiverName; 45 | } 46 | } 47 | 48 | /** 49 | * listener of daemon for external 50 | * 51 | * @author Mars 52 | * 53 | */ 54 | public interface DaemonListener { 55 | void onPersistentStart(Context context); 56 | void onDaemonAssistantStart(Context context); 57 | void onWatchDaemonDaed(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/IDaemonClient.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * 7 | * @author Mars 8 | * 9 | */ 10 | public interface IDaemonClient { 11 | /** 12 | * override this method by {@link android.app.Application}

13 | * ****************************************************************
14 | * DO super.attchBaseContext() first !
15 | * ****************************************************************
16 | * 17 | * @param base 18 | */ 19 | void onAttachBaseContext(Context base); 20 | } 21 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/IDaemonStrategy.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import com.marswin89.marsdaemon.strategy.DaemonStrategy21; 4 | import com.marswin89.marsdaemon.strategy.DaemonStrategy22; 5 | import com.marswin89.marsdaemon.strategy.DaemonStrategy23; 6 | import com.marswin89.marsdaemon.strategy.DaemonStrategyUnder21; 7 | import com.marswin89.marsdaemon.strategy.DaemonStrategyXiaomi; 8 | 9 | import android.content.Context; 10 | import android.os.Build; 11 | 12 | /** 13 | * define strategy method 14 | * 15 | * @author Mars 16 | * 17 | */ 18 | public interface IDaemonStrategy { 19 | /** 20 | * Initialization some files or other when 1st time 21 | * 22 | * @param context 23 | * @return 24 | */ 25 | boolean onInitialization(Context context); 26 | 27 | /** 28 | * when Persistent process create 29 | * 30 | * @param context 31 | * @param configs 32 | */ 33 | void onPersistentCreate(Context context, DaemonConfigurations configs); 34 | 35 | /** 36 | * when DaemonAssistant process create 37 | * @param context 38 | * @param configs 39 | */ 40 | void onDaemonAssistantCreate(Context context, DaemonConfigurations configs); 41 | 42 | /** 43 | * when watches the process dead which it watched 44 | */ 45 | void onDaemonDead(); 46 | 47 | 48 | 49 | /** 50 | * all about strategy on different device here 51 | * 52 | * @author Mars 53 | * 54 | */ 55 | public static class Fetcher { 56 | 57 | private static IDaemonStrategy mDaemonStrategy; 58 | 59 | /** 60 | * fetch the strategy for this device 61 | * 62 | * @return the daemon strategy for this device 63 | */ 64 | static IDaemonStrategy fetchStrategy() { 65 | if (mDaemonStrategy != null) { 66 | return mDaemonStrategy; 67 | } 68 | int sdk = Build.VERSION.SDK_INT; 69 | switch (sdk) { 70 | case 23: 71 | mDaemonStrategy = new DaemonStrategy23(); 72 | break; 73 | 74 | case 22: 75 | mDaemonStrategy = new DaemonStrategy22(); 76 | break; 77 | 78 | case 21: 79 | if("MX4 Pro".equalsIgnoreCase(Build.MODEL)){ 80 | mDaemonStrategy = new DaemonStrategyUnder21(); 81 | }else{ 82 | mDaemonStrategy = new DaemonStrategy21(); 83 | } 84 | break; 85 | 86 | default: 87 | if(Build.MODEL != null && Build.MODEL.toLowerCase().startsWith("mi")){ 88 | mDaemonStrategy = new DaemonStrategyXiaomi(); 89 | }else if(Build.MODEL != null && Build.MODEL.toLowerCase().startsWith("a31")){ 90 | mDaemonStrategy = new DaemonStrategy21(); 91 | }else{ 92 | mDaemonStrategy = new DaemonStrategyUnder21(); 93 | } 94 | break; 95 | } 96 | return mDaemonStrategy; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/NativeDaemonBase.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * native base class 7 | * 8 | * @author Mars 9 | * 10 | */ 11 | public class NativeDaemonBase { 12 | /** 13 | * used for native 14 | */ 15 | protected Context mContext; 16 | 17 | public NativeDaemonBase(Context context){ 18 | this.mContext = context; 19 | } 20 | 21 | /** 22 | * native call back 23 | */ 24 | protected void onDaemonDead(){ 25 | IDaemonStrategy.Fetcher.fetchStrategy().onDaemonDead(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/PackageUtils.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.pm.PackageManager; 6 | 7 | /** 8 | * Utils to prevent component from third-party app forbidding 9 | * 10 | * @author Mars 11 | * 12 | */ 13 | public class PackageUtils { 14 | /** 15 | * set the component in our package default 16 | * @param context 17 | * @param componentClassName 18 | */ 19 | public static void setComponentDefault(Context context, String componentClassName){ 20 | PackageManager pm = context.getPackageManager(); 21 | ComponentName componentName = new ComponentName(context.getPackageName(), componentClassName); 22 | pm.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP); 23 | } 24 | 25 | /** 26 | * get the component in our package default 27 | * @param context 28 | * @param componentClassName 29 | */ 30 | public static boolean isComponentDefault(Context context, String componentClassName){ 31 | PackageManager pm = context.getPackageManager(); 32 | ComponentName componentName = new ComponentName(context.getPackageName(), componentClassName); 33 | return pm.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/nativ/NativeDaemonAPI20.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.nativ; 2 | 3 | import com.marswin89.marsdaemon.NativeDaemonBase; 4 | 5 | import android.content.Context; 6 | 7 | /** 8 | * native code to watch each other when api under 20 (contains 20) 9 | * @author Mars 10 | * 11 | */ 12 | public class NativeDaemonAPI20 extends NativeDaemonBase { 13 | 14 | public NativeDaemonAPI20(Context context) { 15 | super(context); 16 | } 17 | 18 | static{ 19 | try { 20 | System.loadLibrary("daemon_api20"); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | public native void doDaemon(String pkgName, String svcName, String daemonPath); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/nativ/NativeDaemonAPI21.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.nativ; 2 | 3 | import com.marswin89.marsdaemon.NativeDaemonBase; 4 | 5 | import android.content.Context; 6 | 7 | /** 8 | * native code to watch each other when api over 21 (contains 21) 9 | * @author Mars 10 | * 11 | */ 12 | public class NativeDaemonAPI21 extends NativeDaemonBase{ 13 | 14 | public NativeDaemonAPI21(Context context) { 15 | super(context); 16 | } 17 | 18 | static{ 19 | try { 20 | System.loadLibrary("daemon_api21"); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | public native void doDaemon(String indicatorSelfPath, String indicatorDaemonPath, String observerSelfPath, String observerDaemonPath); 27 | } 28 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/proc/GuardService.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.proc; 2 | 3 | import android.app.Notification; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.Build; 8 | import android.os.IBinder; 9 | import android.util.Log; 10 | 11 | import com.henrik.keeplive.accountsync.SyncService; 12 | import com.henrik.keeplive.onepx.OnepxReceiver; 13 | import com.henrik.keeplive.schedulerjob.DaemonJobService; 14 | import com.marswin89.marsdaemon.DaemonConfigurations; 15 | 16 | 17 | /** 18 | * GuardService运行在":service"进程中,被{@link com.marswin89.marsdaemon.IDaemonStrategy#onDaemonAssistantCreate(Context, DaemonConfigurations)}启动 19 | */ 20 | public class GuardService extends Service { 21 | 22 | private static String TAG = "GuardService"; 23 | private static String KEY_INNER = "key_inner"; 24 | 25 | static final int NOTIFICATION_ID = 99999; 26 | static Service sCore = null; 27 | 28 | public static void start(Context context) { 29 | Log.i(TAG, "start GuardService"); 30 | try { 31 | Intent service = new Intent(context, GuardService.class); 32 | service.putExtra(KEY_INNER, true); 33 | context.startService(service); 34 | 35 | } catch (Throwable t) { 36 | Log.e(TAG, t.getMessage(), t); 37 | } 38 | 39 | 40 | } 41 | 42 | @Override 43 | public int onStartCommand(Intent intent, int flags, int startId) { 44 | // try { 45 | // boolean isStartFromDeamonProcess = true; 46 | // if (intent == null || intent.getBooleanExtra(KEY_INNER, false)) { 47 | // isStartFromDeamonProcess = false; 48 | // } 49 | // if (isStartFromDeamonProcess) { 50 | // Log.i(TAG, "start service from deamon process"); 51 | // 52 | // } 53 | // } catch (Exception e) { 54 | // Log.w(TAG, e); 55 | // } 56 | return super.onStartCommand(intent, flags, startId); 57 | } 58 | 59 | @Override 60 | public IBinder onBind(Intent intent) { 61 | return null; 62 | } 63 | 64 | @Override 65 | public void onCreate() { 66 | super.onCreate(); 67 | Log.i(TAG, "service onCreate"); 68 | sCore = this; 69 | if (Build.VERSION.SDK_INT < 18) { // 旧版本直接设一个空的通知就能变成前台服务 70 | startForeground(NOTIFICATION_ID, new Notification()); 71 | } else { // API18以后会展示空白通知, 需要hack一下, 在另一个服务把这个通知移除 72 | stopSubService(); // 先结束一下, 保证后面service的onStartCommand执行到 73 | startSubService(); 74 | } 75 | 76 | //开启一像素activity 拉活 77 | OnepxReceiver.register1pxReceiver(getApplicationContext()); 78 | //利用帐号同步机制 拉活 79 | SyncService.startAccountSync(getApplicationContext()); 80 | //利用JobScheduler机制 拉活 81 | DaemonJobService.startScheduleDaemonJob(getApplicationContext()); 82 | } 83 | 84 | @Override 85 | public void onDestroy() { 86 | Log.i(TAG, "service onDestroy"); 87 | if (Build.VERSION.SDK_INT < 18) { 88 | stopForeground(true); 89 | } else { 90 | stopSubService(); 91 | } 92 | sCore = null; 93 | 94 | OnepxReceiver.unregister1pxReceiver(this); 95 | super.onDestroy(); 96 | } 97 | 98 | private void startSubService() { 99 | try { 100 | startService(new Intent(getApplicationContext(), subService.class)); 101 | } catch (Throwable t) { 102 | Log.e(TAG, t.getMessage(), t); 103 | } 104 | } 105 | 106 | private void stopSubService() { 107 | try { 108 | stopService(new Intent(getApplicationContext(), subService.class)); 109 | } catch (Throwable t) { 110 | Log.e(TAG, t.getMessage(), t); 111 | } 112 | } 113 | 114 | public static class subService extends Service { 115 | 116 | @Override 117 | public IBinder onBind(Intent intent) { 118 | return null; 119 | } 120 | 121 | @Override 122 | public void onCreate() { 123 | super.onCreate(); 124 | Log.i(TAG, "subService onCreate"); 125 | } 126 | 127 | @Override 128 | public void onDestroy() { 129 | Log.i(TAG, "subService onDestroy"); 130 | try { 131 | stopForeground(true); 132 | } catch (Exception ignored) { 133 | } 134 | super.onDestroy(); 135 | } 136 | 137 | @Override 138 | public int onStartCommand(Intent intent, int flags, int startId) { 139 | Service core = sCore; 140 | if (null != intent && null != core) { 141 | Log.i(TAG, "onStartCommand"); 142 | try { 143 | core.startForeground(NOTIFICATION_ID, new Notification()); 144 | startForeground(NOTIFICATION_ID, new Notification()); 145 | core.stopForeground(true); 146 | } catch (Exception e) { 147 | Log.e(TAG, e.getMessage(), e); 148 | } 149 | } 150 | return Service.START_NOT_STICKY; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/proc/Receiver1.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.proc; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * DO NOT do anything in this Receiver!
9 | * 10 | * Created by Mars on 12/24/15. 11 | */ 12 | public class Receiver1 extends BroadcastReceiver { 13 | @Override 14 | public void onReceive(Context context, Intent intent) { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/proc/Receiver2.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.proc; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * DO NOT do anything in this Receiver!
9 | * 10 | * Created by Mars on 12/24/15. 11 | */ 12 | public class Receiver2 extends BroadcastReceiver { 13 | @Override 14 | public void onReceive(Context context, Intent intent) { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/proc/Service2.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.proc; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | /** 8 | * DO NOT do anything in this Service!
9 | * 10 | * Created by Mars on 12/24/15. 11 | */ 12 | public class Service2 extends Service{ 13 | 14 | @Override 15 | public IBinder onBind(Intent intent) { 16 | return null; 17 | } 18 | 19 | @Override 20 | public int onStartCommand(Intent intent, int flags, int startId) { 21 | return Service.START_NOT_STICKY; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategy21.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.strategy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import android.app.AlarmManager; 7 | import android.app.PendingIntent; 8 | import android.content.ComponentName; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.os.SystemClock; 12 | 13 | import com.marswin89.marsdaemon.DaemonConfigurations; 14 | import com.marswin89.marsdaemon.IDaemonStrategy; 15 | import com.marswin89.marsdaemon.nativ.NativeDaemonAPI21; 16 | /** 17 | * the strategy in android API 21. 18 | * 19 | * @author Mars 20 | * 21 | */ 22 | public class DaemonStrategy21 implements IDaemonStrategy{ 23 | private final static String INDICATOR_DIR_NAME = "indicators"; 24 | private final static String INDICATOR_PERSISTENT_FILENAME = "indicator_p"; 25 | private final static String INDICATOR_DAEMON_ASSISTANT_FILENAME = "indicator_d"; 26 | private final static String OBSERVER_PERSISTENT_FILENAME = "observer_p"; 27 | private final static String OBSERVER_DAEMON_ASSISTANT_FILENAME = "observer_d"; 28 | 29 | private AlarmManager mAlarmManager; 30 | private PendingIntent mPendingIntent; 31 | private DaemonConfigurations mConfigs; 32 | 33 | @Override 34 | public boolean onInitialization(Context context) { 35 | return initIndicators(context); 36 | } 37 | 38 | @Override 39 | public void onPersistentCreate(final Context context, DaemonConfigurations configs) { 40 | Intent intent = new Intent(); 41 | ComponentName componentName = new ComponentName(context.getPackageName(), configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME); 42 | intent.setComponent(componentName); 43 | context.startService(intent); 44 | 45 | initAlarm(context, configs.PERSISTENT_CONFIG.SERVICE_NAME); 46 | 47 | Thread t = new Thread(){ 48 | @Override 49 | public void run() { 50 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 51 | new NativeDaemonAPI21(context).doDaemon( 52 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 53 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 54 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath(), 55 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath()); 56 | } 57 | }; 58 | t.setPriority(Thread.MAX_PRIORITY); 59 | t.start(); 60 | 61 | if(configs != null && configs.LISTENER != null){ 62 | this.mConfigs = configs; 63 | configs.LISTENER.onPersistentStart(context); 64 | } 65 | } 66 | 67 | @Override 68 | public void onDaemonAssistantCreate(final Context context, DaemonConfigurations configs) { 69 | Intent intent = new Intent(); 70 | ComponentName componentName = new ComponentName(context.getPackageName(), configs.PERSISTENT_CONFIG.SERVICE_NAME); 71 | intent.setComponent(componentName); 72 | context.startService(intent); 73 | 74 | initAlarm(context, configs.PERSISTENT_CONFIG.SERVICE_NAME); 75 | 76 | Thread t = new Thread(){ 77 | public void run() { 78 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 79 | new NativeDaemonAPI21(context).doDaemon( 80 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 81 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 82 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 83 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath()); 84 | }; 85 | }; 86 | t.setPriority(Thread.MAX_PRIORITY); 87 | t.start(); 88 | 89 | if(configs != null && configs.LISTENER != null){ 90 | this.mConfigs = configs; 91 | configs.LISTENER.onDaemonAssistantStart(context); 92 | } 93 | } 94 | 95 | @Override 96 | public void onDaemonDead() { 97 | mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 100, mPendingIntent); 98 | 99 | if(mConfigs != null && mConfigs.LISTENER != null){ 100 | mConfigs.LISTENER.onWatchDaemonDaed(); 101 | } 102 | android.os.Process.killProcess(android.os.Process.myPid()); 103 | } 104 | 105 | 106 | private void initAlarm(Context context, String serviceName){ 107 | if(mAlarmManager == null){ 108 | mAlarmManager = ((AlarmManager)context.getSystemService(Context.ALARM_SERVICE)); 109 | } 110 | if(mPendingIntent == null){ 111 | Intent intent = new Intent(); 112 | ComponentName component = new ComponentName(context.getPackageName(), serviceName); 113 | intent.setComponent(component); 114 | intent.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 115 | mPendingIntent = PendingIntent.getService(context, 0, intent, 0); 116 | } 117 | mAlarmManager.cancel(mPendingIntent); 118 | } 119 | 120 | 121 | private boolean initIndicators(Context context){ 122 | File dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 123 | if(!dirFile.exists()){ 124 | dirFile.mkdirs(); 125 | } 126 | try { 127 | createNewFile(dirFile, INDICATOR_PERSISTENT_FILENAME); 128 | createNewFile(dirFile, INDICATOR_DAEMON_ASSISTANT_FILENAME); 129 | return true; 130 | } catch (IOException e) { 131 | e.printStackTrace(); 132 | return false; 133 | } 134 | } 135 | 136 | 137 | private void createNewFile(File dirFile, String fileName) throws IOException{ 138 | File file = new File(dirFile, fileName); 139 | if(!file.exists()){ 140 | file.createNewFile(); 141 | } 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategy22.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.strategy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.content.ComponentName; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.os.IBinder; 13 | import android.os.Parcel; 14 | import android.os.RemoteException; 15 | import android.util.Log; 16 | 17 | import com.marswin89.marsdaemon.DaemonConfigurations; 18 | import com.marswin89.marsdaemon.IDaemonStrategy; 19 | import com.marswin89.marsdaemon.nativ.NativeDaemonAPI21; 20 | /** 21 | * the strategy in android API 22. 22 | * 23 | * @author Mars 24 | * 25 | */ 26 | public class DaemonStrategy22 implements IDaemonStrategy{ 27 | private final static String INDICATOR_DIR_NAME = "indicators"; 28 | private final static String INDICATOR_PERSISTENT_FILENAME = "indicator_p"; 29 | private final static String INDICATOR_DAEMON_ASSISTANT_FILENAME = "indicator_d"; 30 | private final static String OBSERVER_PERSISTENT_FILENAME = "observer_p"; 31 | private final static String OBSERVER_DAEMON_ASSISTANT_FILENAME = "observer_d"; 32 | 33 | private IBinder mRemote; 34 | private Parcel mServiceData; 35 | private DaemonConfigurations mConfigs; 36 | 37 | @Override 38 | public boolean onInitialization(Context context) { 39 | return initIndicatorFiles(context); 40 | } 41 | 42 | @Override 43 | public void onPersistentCreate(final Context context, DaemonConfigurations configs) { 44 | initAmsBinder(); 45 | initServiceParcel(context, configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME); 46 | startServiceByAmsBinder(); 47 | 48 | Thread t = new Thread(){ 49 | public void run() { 50 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 51 | new NativeDaemonAPI21(context).doDaemon( 52 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 53 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 54 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath(), 55 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath()); 56 | }; 57 | }; 58 | t.start(); 59 | 60 | if(configs != null && configs.LISTENER != null){ 61 | this.mConfigs = configs; 62 | configs.LISTENER.onPersistentStart(context); 63 | } 64 | } 65 | 66 | @Override 67 | public void onDaemonAssistantCreate(final Context context, DaemonConfigurations configs) { 68 | initAmsBinder(); 69 | initServiceParcel(context, configs.PERSISTENT_CONFIG.SERVICE_NAME); 70 | startServiceByAmsBinder(); 71 | 72 | Thread t = new Thread(){ 73 | public void run() { 74 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 75 | new NativeDaemonAPI21(context).doDaemon( 76 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 77 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 78 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 79 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath()); 80 | }; 81 | }; 82 | t.start(); 83 | 84 | if(configs != null && configs.LISTENER != null){ 85 | this.mConfigs = configs; 86 | configs.LISTENER.onDaemonAssistantStart(context); 87 | } 88 | 89 | } 90 | 91 | 92 | @Override 93 | public void onDaemonDead() { 94 | if(startServiceByAmsBinder()){ 95 | 96 | if(mConfigs != null && mConfigs.LISTENER != null){ 97 | mConfigs.LISTENER.onWatchDaemonDaed(); 98 | } 99 | 100 | android.os.Process.killProcess(android.os.Process.myPid()); 101 | } 102 | } 103 | 104 | 105 | private void initAmsBinder(){ 106 | Class activityManagerNative; 107 | try { 108 | activityManagerNative = Class.forName("android.app.ActivityManagerNative"); 109 | Object amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative); 110 | Field mRemoteField = amn.getClass().getDeclaredField("mRemote"); 111 | mRemoteField.setAccessible(true); 112 | mRemote = (IBinder) mRemoteField.get(amn); 113 | } catch (ClassNotFoundException e) { 114 | e.printStackTrace(); 115 | } catch (IllegalAccessException e) { 116 | e.printStackTrace(); 117 | } catch (IllegalArgumentException e) { 118 | e.printStackTrace(); 119 | } catch (InvocationTargetException e) { 120 | e.printStackTrace(); 121 | } catch (NoSuchMethodException e) { 122 | e.printStackTrace(); 123 | } catch (NoSuchFieldException e) { 124 | e.printStackTrace(); 125 | } 126 | } 127 | 128 | 129 | @SuppressLint("Recycle")// when process dead, we should save time to restart and kill self, don`t take a waste of time to recycle 130 | private void initServiceParcel(Context context, String serviceName){ 131 | Intent intent = new Intent(); 132 | ComponentName component = new ComponentName(context.getPackageName(), serviceName); 133 | intent.setComponent(component); 134 | 135 | /* 136 | //get ContextImpl instance 137 | // Object contextImpl = ((Application)context.getApplicationContext()).getBaseContext(); 138 | //this context is ContextImpl, get MainThread instance immediately 139 | Field mainThreadField = context.getClass().getDeclaredField("mMainThread"); 140 | mainThreadField.setAccessible(true); 141 | Object mainThread = mainThreadField.get(context); 142 | //get ApplicationThread instance 143 | Object applicationThread = mainThread.getClass().getMethod("getApplicationThread").invoke(mainThread); 144 | //get Binder 145 | Binder callerBinder = (Binder) (applicationThread.getClass().getMethod("asBinder").invoke(applicationThread)); 146 | */ 147 | 148 | //get handle 149 | // UserHandle userHandle = android.os.Process.myUserHandle(); 150 | // int handle = (Integer) userHandle.getClass().getMethod("getIdentifier").invoke(userHandle); 151 | 152 | //write pacel 153 | mServiceData = Parcel.obtain(); 154 | mServiceData.writeInterfaceToken("android.app.IActivityManager"); 155 | mServiceData.writeStrongBinder(null); 156 | // mServiceData.writeStrongBinder(callerBinder); 157 | intent.writeToParcel(mServiceData, 0); 158 | mServiceData.writeString(null); 159 | // mServiceData.writeString(intent.resolveTypeIfNeeded(context.getContentResolver())); 160 | mServiceData.writeInt(0); 161 | // mServiceData.writeInt(handle); 162 | 163 | } 164 | 165 | 166 | private boolean startServiceByAmsBinder(){ 167 | try { 168 | if(mRemote == null || mServiceData == null){ 169 | Log.e("Daemon", "REMOTE IS NULL or PARCEL IS NULL !!!"); 170 | return false; 171 | } 172 | mRemote.transact(34, mServiceData, null, 0);//START_SERVICE_TRANSACTION = 34 173 | return true; 174 | } catch (RemoteException e) { 175 | e.printStackTrace(); 176 | return false; 177 | } 178 | } 179 | 180 | 181 | 182 | 183 | private boolean initIndicatorFiles(Context context){ 184 | File dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 185 | if(!dirFile.exists()){ 186 | dirFile.mkdirs(); 187 | } 188 | try { 189 | createNewFile(dirFile, INDICATOR_PERSISTENT_FILENAME); 190 | createNewFile(dirFile, INDICATOR_DAEMON_ASSISTANT_FILENAME); 191 | return true; 192 | } catch (IOException e) { 193 | e.printStackTrace(); 194 | return false; 195 | } 196 | } 197 | 198 | private void createNewFile(File dirFile, String fileName) throws IOException{ 199 | File file = new File(dirFile, fileName); 200 | if(!file.exists()){ 201 | file.createNewFile(); 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategy23.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.strategy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.app.Activity; 10 | import android.content.ComponentName; 11 | import android.content.Context; 12 | import android.content.Intent; 13 | import android.os.IBinder; 14 | import android.os.Parcel; 15 | import android.os.RemoteException; 16 | import android.util.Log; 17 | 18 | import com.marswin89.marsdaemon.DaemonConfigurations; 19 | import com.marswin89.marsdaemon.IDaemonStrategy; 20 | import com.marswin89.marsdaemon.nativ.NativeDaemonAPI21; 21 | /** 22 | * the strategy in android API 23. 23 | * 24 | * @author Mars 25 | * 26 | */ 27 | public class DaemonStrategy23 implements IDaemonStrategy{ 28 | private final static String INDICATOR_DIR_NAME = "indicators"; 29 | private final static String INDICATOR_PERSISTENT_FILENAME = "indicator_p"; 30 | private final static String INDICATOR_DAEMON_ASSISTANT_FILENAME = "indicator_d"; 31 | private final static String OBSERVER_PERSISTENT_FILENAME = "observer_p"; 32 | private final static String OBSERVER_DAEMON_ASSISTANT_FILENAME = "observer_d"; 33 | 34 | private IBinder mRemote; 35 | private Parcel mBroadcastData; 36 | private DaemonConfigurations mConfigs; 37 | 38 | @Override 39 | public boolean onInitialization(Context context) { 40 | return initIndicatorFiles(context); 41 | } 42 | 43 | @Override 44 | public void onPersistentCreate(final Context context, DaemonConfigurations configs) { 45 | initAmsBinder(); 46 | initBroadcastParcel(context, configs.DAEMON_ASSISTANT_CONFIG.RECEIVER_NAME); 47 | sendBroadcastByAmsBinder(); 48 | 49 | Thread t = new Thread(){ 50 | public void run() { 51 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 52 | new NativeDaemonAPI21(context).doDaemon( 53 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 54 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 55 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath(), 56 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath()); 57 | }; 58 | }; 59 | t.start(); 60 | 61 | ComponentName componentName = new ComponentName(context.getPackageName(), configs.PERSISTENT_CONFIG.SERVICE_NAME); 62 | Intent intent = new Intent(); 63 | intent.setComponent(componentName); 64 | context.startService(intent); 65 | 66 | if(configs != null && configs.LISTENER != null){ 67 | this.mConfigs = configs; 68 | configs.LISTENER.onPersistentStart(context); 69 | } 70 | } 71 | 72 | @Override 73 | public void onDaemonAssistantCreate(final Context context, DaemonConfigurations configs) { 74 | initAmsBinder(); 75 | initBroadcastParcel(context, configs.PERSISTENT_CONFIG.RECEIVER_NAME); 76 | sendBroadcastByAmsBinder(); 77 | 78 | Thread t = new Thread(){ 79 | public void run() { 80 | File indicatorDir = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 81 | new NativeDaemonAPI21(context).doDaemon( 82 | new File(indicatorDir, INDICATOR_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 83 | new File(indicatorDir, INDICATOR_PERSISTENT_FILENAME).getAbsolutePath(), 84 | new File(indicatorDir, OBSERVER_DAEMON_ASSISTANT_FILENAME).getAbsolutePath(), 85 | new File(indicatorDir, OBSERVER_PERSISTENT_FILENAME).getAbsolutePath()); 86 | }; 87 | }; 88 | t.start(); 89 | 90 | ComponentName componentName = new ComponentName(context.getPackageName(), configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME); 91 | Intent intent = new Intent(); 92 | intent.setComponent(componentName); 93 | context.startService(intent); 94 | 95 | if(configs != null && configs.LISTENER != null){ 96 | this.mConfigs = configs; 97 | configs.LISTENER.onDaemonAssistantStart(context); 98 | } 99 | } 100 | 101 | 102 | @Override 103 | public void onDaemonDead() { 104 | if(sendBroadcastByAmsBinder()){ 105 | 106 | if(mConfigs != null && mConfigs.LISTENER != null){ 107 | mConfigs.LISTENER.onWatchDaemonDaed(); 108 | } 109 | 110 | android.os.Process.killProcess(android.os.Process.myPid()); 111 | } 112 | } 113 | 114 | 115 | private void initAmsBinder(){ 116 | Class activityManagerNative; 117 | try { 118 | activityManagerNative = Class.forName("android.app.ActivityManagerNative"); 119 | Object amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative); 120 | Field mRemoteField = amn.getClass().getDeclaredField("mRemote"); 121 | mRemoteField.setAccessible(true); 122 | mRemote = (IBinder) mRemoteField.get(amn); 123 | } catch (ClassNotFoundException e) { 124 | e.printStackTrace(); 125 | } catch (IllegalAccessException e) { 126 | e.printStackTrace(); 127 | } catch (IllegalArgumentException e) { 128 | e.printStackTrace(); 129 | } catch (InvocationTargetException e) { 130 | e.printStackTrace(); 131 | } catch (NoSuchMethodException e) { 132 | e.printStackTrace(); 133 | } catch (NoSuchFieldException e) { 134 | e.printStackTrace(); 135 | } 136 | } 137 | 138 | 139 | @SuppressLint("Recycle")// when process dead, we should save time to restart and kill self, don`t take a waste of time to recycle 140 | private void initBroadcastParcel(Context context, String broadcastName){ 141 | Intent intent = new Intent(); 142 | ComponentName componentName = new ComponentName(context.getPackageName(), broadcastName); 143 | intent.setComponent(componentName); 144 | intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 145 | 146 | /* 147 | // Object contextImpl = ((Application)context.getApplicationContext()).getBaseContext(); 148 | //this context is ContextImpl, get MainThread instance immediately 149 | Field mainThreadField = context.getClass().getDeclaredField("mMainThread"); 150 | mainThreadField.setAccessible(true); 151 | Object mainThread = mainThreadField.get(context); 152 | //get ApplicationThread instance 153 | Object applicationThread = mainThread.getClass().getMethod("getApplicationThread").invoke(mainThread); 154 | //get Binder 155 | Binder callerBinder = (Binder) (applicationThread.getClass().getMethod("asBinder").invoke(applicationThread)); 156 | */ 157 | 158 | // UserHandle userHandle = android.os.Process.myUserHandle(); 159 | // int handle = (Integer) userHandle.getClass().getMethod("getIdentifier").invoke(userHandle); 160 | 161 | mBroadcastData = Parcel.obtain(); 162 | mBroadcastData.writeInterfaceToken("android.app.IActivityManager"); 163 | // mBroadcastData.writeStrongBinder(callerBinder); 164 | mBroadcastData.writeStrongBinder(null); 165 | intent.writeToParcel(mBroadcastData, 0); 166 | mBroadcastData.writeString(intent.resolveTypeIfNeeded(context.getContentResolver())); 167 | mBroadcastData.writeStrongBinder(null); 168 | mBroadcastData.writeInt(Activity.RESULT_OK); 169 | mBroadcastData.writeString(null); 170 | mBroadcastData.writeBundle(null); 171 | mBroadcastData.writeString(null); 172 | mBroadcastData.writeInt(-1); 173 | mBroadcastData.writeInt(0); 174 | mBroadcastData.writeInt(0); 175 | // mBroadcastData.writeInt(handle); 176 | mBroadcastData.writeInt(0); 177 | } 178 | 179 | 180 | private boolean sendBroadcastByAmsBinder(){ 181 | 182 | try { 183 | if(mRemote == null || mBroadcastData == null){ 184 | Log.e("Daemon", "REMOTE IS NULL or PARCEL IS NULL !!!"); 185 | return false; 186 | } 187 | mRemote.transact(14, mBroadcastData, null, 0);//BROADCAST_INTENT_TRANSACTION = 0x00000001 + 13 188 | return true; 189 | } catch (RemoteException e) { 190 | e.printStackTrace(); 191 | return false; 192 | } 193 | } 194 | 195 | 196 | 197 | private boolean initIndicatorFiles(Context context){ 198 | File dirFile = context.getDir(INDICATOR_DIR_NAME, Context.MODE_PRIVATE); 199 | if(!dirFile.exists()){ 200 | dirFile.mkdirs(); 201 | } 202 | try { 203 | createNewFile(dirFile, INDICATOR_PERSISTENT_FILENAME); 204 | createNewFile(dirFile, INDICATOR_DAEMON_ASSISTANT_FILENAME); 205 | return true; 206 | } catch (IOException e) { 207 | e.printStackTrace(); 208 | return false; 209 | } 210 | } 211 | 212 | private void createNewFile(File dirFile, String fileName) throws IOException{ 213 | File file = new File(dirFile, fileName); 214 | if(!file.exists()){ 215 | file.createNewFile(); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategyUnder21.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.strategy; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | import android.app.AlarmManager; 9 | import android.app.PendingIntent; 10 | import android.content.ComponentName; 11 | import android.content.Context; 12 | import android.content.Intent; 13 | import android.content.res.AssetManager; 14 | import android.os.Build; 15 | import android.os.SystemClock; 16 | import android.text.TextUtils; 17 | 18 | import com.marswin89.marsdaemon.DaemonConfigurations; 19 | import com.marswin89.marsdaemon.IDaemonStrategy; 20 | import com.marswin89.marsdaemon.nativ.NativeDaemonAPI20; 21 | 22 | /** 23 | * the strategy in android API below 21. 24 | * 25 | * @author Mars 26 | * 27 | */ 28 | public class DaemonStrategyUnder21 implements IDaemonStrategy{ 29 | private final String BINARY_DEST_DIR_NAME = "bin"; 30 | private final String BINARY_FILE_NAME = "daemon"; 31 | 32 | private AlarmManager mAlarmManager; 33 | private PendingIntent mPendingIntent; 34 | 35 | @Override 36 | public boolean onInitialization(Context context) { 37 | return installBinary(context); 38 | } 39 | 40 | @Override 41 | public void onPersistentCreate(final Context context, final DaemonConfigurations configs) { 42 | initAlarm(context, configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME); 43 | Thread t = new Thread(){ 44 | public void run() { 45 | File binaryFile = new File(context.getDir(BINARY_DEST_DIR_NAME, Context.MODE_PRIVATE), BINARY_FILE_NAME); 46 | new NativeDaemonAPI20(context).doDaemon( 47 | context.getPackageName(), 48 | configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME, 49 | binaryFile.getAbsolutePath()); 50 | }; 51 | }; 52 | t.setPriority(Thread.MAX_PRIORITY); 53 | t.start(); 54 | 55 | if(configs != null && configs.LISTENER != null){ 56 | configs.LISTENER.onPersistentStart(context); 57 | } 58 | } 59 | 60 | @Override 61 | public void onDaemonAssistantCreate(Context context, DaemonConfigurations configs) { 62 | Intent intent = new Intent(); 63 | ComponentName component = new ComponentName(context.getPackageName(), configs.PERSISTENT_CONFIG.SERVICE_NAME); 64 | intent.setComponent(component); 65 | context.startService(intent); 66 | if(configs != null && configs.LISTENER != null){ 67 | configs.LISTENER.onWatchDaemonDaed(); 68 | } 69 | android.os.Process.killProcess(android.os.Process.myPid()); 70 | } 71 | 72 | 73 | @Override 74 | public void onDaemonDead() { 75 | mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 100, mPendingIntent); 76 | android.os.Process.killProcess(android.os.Process.myPid()); 77 | } 78 | 79 | 80 | private void initAlarm(Context context, String serviceName){ 81 | if(mAlarmManager == null){ 82 | mAlarmManager = ((AlarmManager)context.getSystemService(Context.ALARM_SERVICE)); 83 | } 84 | if(mPendingIntent == null){ 85 | Intent intent = new Intent(); 86 | ComponentName component = new ComponentName(context.getPackageName(), serviceName); 87 | intent.setComponent(component); 88 | intent.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 89 | mPendingIntent = PendingIntent.getService(context, 0, intent, 0); 90 | } 91 | mAlarmManager.cancel(mPendingIntent); 92 | } 93 | 94 | 95 | private boolean installBinary(Context context){ 96 | String binaryDirName = null; 97 | String abi = Build.CPU_ABI; 98 | if (abi.startsWith("armeabi-v7a")) { 99 | binaryDirName = "armeabi-v7a"; 100 | }else if(abi.startsWith("x86")) { 101 | binaryDirName = "x86"; 102 | }else{ 103 | binaryDirName = "armeabi"; 104 | } 105 | return install(context, BINARY_DEST_DIR_NAME, binaryDirName, BINARY_FILE_NAME); 106 | } 107 | 108 | 109 | private boolean install(Context context, String destDirName, String assetsDirName, String filename) { 110 | File file = new File(context.getDir(destDirName, Context.MODE_PRIVATE), filename); 111 | if (file.exists()) { 112 | return true; 113 | } 114 | try { 115 | copyAssets(context, (TextUtils.isEmpty(assetsDirName) ? "" : (assetsDirName + File.separator)) + filename, file, "700"); 116 | return true; 117 | } catch (Exception e) { 118 | return false; 119 | } 120 | } 121 | 122 | private void copyAssets(Context context, String assetsFilename, File file, String mode) throws IOException, InterruptedException { 123 | AssetManager manager = context.getAssets(); 124 | final InputStream is = manager.open(assetsFilename); 125 | copyFile(file, is, mode); 126 | } 127 | 128 | private void copyFile(File file, InputStream is, String mode) throws IOException, InterruptedException { 129 | if(!file.getParentFile().exists()){ 130 | file.getParentFile().mkdirs(); 131 | } 132 | final String abspath = file.getAbsolutePath(); 133 | final FileOutputStream out = new FileOutputStream(file); 134 | byte buf[] = new byte[1024]; 135 | int len; 136 | while ((len = is.read(buf)) > 0) { 137 | out.write(buf, 0, len); 138 | } 139 | out.close(); 140 | is.close(); 141 | Runtime.getRuntime().exec("chmod " + mode + " " + abspath).waitFor(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /keeplive/src/main/java/com/marswin89/marsdaemon/strategy/DaemonStrategyXiaomi.java: -------------------------------------------------------------------------------- 1 | package com.marswin89.marsdaemon.strategy; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.InvocationTargetException; 9 | 10 | import android.annotation.SuppressLint; 11 | import android.content.ComponentName; 12 | import android.content.Context; 13 | import android.content.Intent; 14 | import android.content.res.AssetManager; 15 | import android.os.IBinder; 16 | import android.os.Parcel; 17 | import android.os.RemoteException; 18 | import android.text.TextUtils; 19 | import android.util.Log; 20 | 21 | import com.marswin89.marsdaemon.DaemonConfigurations; 22 | import com.marswin89.marsdaemon.IDaemonStrategy; 23 | import com.marswin89.marsdaemon.nativ.NativeDaemonAPI20; 24 | 25 | /** 26 | * the strategy in Mi. 27 | * 28 | * @author Mars 29 | * 30 | */ 31 | public class DaemonStrategyXiaomi implements IDaemonStrategy{ 32 | private final String BINARY_DEST_DIR_NAME = "bin"; 33 | private final String BINARY_FILE_NAME = "daemon"; 34 | 35 | private IBinder mRemote; 36 | private Parcel mServiceData; 37 | private DaemonConfigurations mConfigs; 38 | 39 | @Override 40 | public boolean onInitialization(Context context) { 41 | return installBinary(context); 42 | } 43 | 44 | @Override 45 | public void onPersistentCreate(final Context context, final DaemonConfigurations configs) { 46 | initAmsBinder(); 47 | initServiceParcel(context, configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME); 48 | Thread t = new Thread(){ 49 | public void run() { 50 | File binaryFile = new File(context.getDir(BINARY_DEST_DIR_NAME, Context.MODE_PRIVATE), BINARY_FILE_NAME); 51 | new NativeDaemonAPI20(context).doDaemon( 52 | context.getPackageName(), 53 | configs.DAEMON_ASSISTANT_CONFIG.SERVICE_NAME, 54 | binaryFile.getAbsolutePath()); 55 | }; 56 | }; 57 | t.setPriority(Thread.MAX_PRIORITY); 58 | t.start(); 59 | 60 | if(configs != null && configs.LISTENER != null){ 61 | this.mConfigs = configs; 62 | configs.LISTENER.onPersistentStart(context); 63 | } 64 | 65 | } 66 | 67 | 68 | @Override 69 | public void onDaemonAssistantCreate(Context context, DaemonConfigurations configs) { 70 | Intent intent = new Intent(); 71 | ComponentName component = new ComponentName(context.getPackageName(), configs.PERSISTENT_CONFIG.SERVICE_NAME); 72 | intent.setComponent(component); 73 | context.startService(intent); 74 | if(configs != null && configs.LISTENER != null){ 75 | configs.LISTENER.onWatchDaemonDaed(); 76 | } 77 | android.os.Process.killProcess(android.os.Process.myPid()); 78 | } 79 | 80 | @Override 81 | public void onDaemonDead() { 82 | if(startServiceByAmsBinder()){ 83 | 84 | if(mConfigs != null && mConfigs.LISTENER != null){ 85 | mConfigs.LISTENER.onWatchDaemonDaed(); 86 | } 87 | 88 | android.os.Process.killProcess(android.os.Process.myPid()); 89 | } 90 | } 91 | 92 | private void initAmsBinder(){ 93 | Class activityManagerNative; 94 | try { 95 | activityManagerNative = Class.forName("android.app.ActivityManagerNative"); 96 | Object amn = activityManagerNative.getMethod("getDefault").invoke(activityManagerNative); 97 | Field mRemoteField = amn.getClass().getDeclaredField("mRemote"); 98 | mRemoteField.setAccessible(true); 99 | mRemote = (IBinder) mRemoteField.get(amn); 100 | } catch (ClassNotFoundException e) { 101 | e.printStackTrace(); 102 | } catch (IllegalAccessException e) { 103 | e.printStackTrace(); 104 | } catch (IllegalArgumentException e) { 105 | e.printStackTrace(); 106 | } catch (InvocationTargetException e) { 107 | e.printStackTrace(); 108 | } catch (NoSuchMethodException e) { 109 | e.printStackTrace(); 110 | } catch (NoSuchFieldException e) { 111 | e.printStackTrace(); 112 | } 113 | } 114 | 115 | 116 | @SuppressLint("Recycle")// when process dead, we should save time to restart and kill self, don`t take a waste of time to recycle 117 | private void initServiceParcel(Context context, String serviceName){ 118 | Intent intent = new Intent(); 119 | ComponentName component = new ComponentName(context.getPackageName(), serviceName); 120 | intent.setComponent(component); 121 | 122 | /* 123 | //get ContextImpl instance 124 | // Object contextImpl = ((Application)context.getApplicationContext()).getBaseContext(); 125 | //this context is ContextImpl, get MainThread instance immediately 126 | Field mainThreadField = context.getClass().getDeclaredField("mMainThread"); 127 | mainThreadField.setAccessible(true); 128 | Object mainThread = mainThreadField.get(context); 129 | //get ApplicationThread instance 130 | Object applicationThread = mainThread.getClass().getMethod("getApplicationThread").invoke(mainThread); 131 | //get Binder 132 | Binder callerBinder = (Binder) (applicationThread.getClass().getMethod("asBinder").invoke(applicationThread)); 133 | */ 134 | 135 | //get handle 136 | // UserHandle userHandle = android.os.Process.myUserHandle(); 137 | // int handle = (Integer) userHandle.getClass().getMethod("getIdentifier").invoke(userHandle); 138 | 139 | //write pacel 140 | mServiceData = Parcel.obtain(); 141 | mServiceData.writeInterfaceToken("android.app.IActivityManager"); 142 | mServiceData.writeStrongBinder(null); 143 | // mServiceData.writeStrongBinder(callerBinder); 144 | intent.writeToParcel(mServiceData, 0); 145 | mServiceData.writeString(null); 146 | // mServiceData.writeString(intent.resolveTypeIfNeeded(context.getContentResolver())); 147 | mServiceData.writeInt(0); 148 | // mServiceData.writeInt(handle); 149 | 150 | } 151 | 152 | private boolean startServiceByAmsBinder(){ 153 | try { 154 | if(mRemote == null || mServiceData == null){ 155 | Log.e("Daemon", "REMOTE IS NULL or PARCEL IS NULL !!!"); 156 | return false; 157 | } 158 | mRemote.transact(34, mServiceData, null, 0);//START_SERVICE_TRANSACTION = 34 159 | return true; 160 | } catch (RemoteException e) { 161 | e.printStackTrace(); 162 | return false; 163 | } 164 | } 165 | 166 | private boolean installBinary(Context context){ 167 | String binaryDirName = null; 168 | // String abi = Build.CPU_ABI; 169 | // if (abi.startsWith("armeabi-v7a")) { 170 | // binaryDirName = "armeabi-v7a"; 171 | // }else if(abi.startsWith("x86")) { 172 | // binaryDirName = "x86"; 173 | // }else{ 174 | // binaryDirName = "armeabi"; 175 | // } 176 | return install(context, BINARY_DEST_DIR_NAME, binaryDirName, BINARY_FILE_NAME); 177 | } 178 | 179 | 180 | private boolean install(Context context, String destDirName, String assetsDirName, String filename) { 181 | File file = new File(context.getDir(destDirName, Context.MODE_PRIVATE), filename); 182 | if (file.exists()) { 183 | return true; 184 | } 185 | try { 186 | copyAssets(context, (TextUtils.isEmpty(assetsDirName) ? "" : (assetsDirName + File.separator)) + filename, file, "700"); 187 | return true; 188 | } catch (Exception e) { 189 | return false; 190 | } 191 | } 192 | 193 | private void copyAssets(Context context, String assetsFilename, File file, String mode) throws IOException, InterruptedException { 194 | AssetManager manager = context.getAssets(); 195 | final InputStream is = manager.open(assetsFilename); 196 | copyFile(file, is, mode); 197 | } 198 | 199 | private void copyFile(File file, InputStream is, String mode) throws IOException, InterruptedException { 200 | if(!file.getParentFile().exists()){ 201 | file.getParentFile().mkdirs(); 202 | } 203 | final String abspath = file.getAbsolutePath(); 204 | final FileOutputStream out = new FileOutputStream(file); 205 | byte buf[] = new byte[1024]; 206 | int len; 207 | while ((len = is.read(buf)) > 0) { 208 | out.write(buf, 0, len); 209 | } 210 | out.close(); 211 | is.close(); 212 | Runtime.getRuntime().exec("chmod " + mode + " " + abspath).waitFor(); 213 | } 214 | 215 | } 216 | -------------------------------------------------------------------------------- /keeplive/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /keeplive/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /keeplive/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /keeplive/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /keeplive/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /keeplive/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephen8341/KeepProcLive/588f21f10a0b95ddaf70f1d1cd2b5a0cf9526dbe/keeplive/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /keeplive/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /keeplive/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /keeplive/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /keeplive/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | KeepProcLive 3 | 4 | -------------------------------------------------------------------------------- /keeplive/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /keeplive/src/main/res/xml/authenticator.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /keeplive/src/main/res/xml/sync.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':keeplive' 2 | --------------------------------------------------------------------------------