一 360手機助手之卸載監聽
Android系統下列監聽方式
- 監聽系統卸載廣播:只能監聽到其他應用的卸載廣播,無法監聽到自己是否被卸載。
ACTION_PACKAGE_REMOVED
ACTION_PACKAGE_REMOVED
皮之不存毛將焉附
內存 --》
監聽 別人
-
卸載 系統log (正在被安裝的包程序不能接收到這個廣播), 監聽自身
-
Java線程 輪訓 監聽 監聽/data/data/{package-name}目錄是否存在
-
C進程 監聽/data/data/{package-name}目錄是否存在 跳轉到網頁
上述4種方式均不能監聽自身的卸載,可以用下面的方式實現
- 靜默安裝另外的apk 監聽自己是否被卸載 可以 (root)
監聽 /data/data/{package-name}目錄是否存在
從前四種方案可以看到,單純的Java層代碼是無法監聽自身卸載的。既然Java層無法實現,我們試着使用C語言在底層實現。
實現方式
藉助Linux進程fork出來的C進程在應用被卸載後不會被銷燬,監聽/data/data/{package-name}目錄是否存在,如果不存在,就證明應用被卸載了。
- fork()子進程
- 創建監聽文件
- 初始化inotify實例
- 註冊監聽事件
- 調用read函數開始監聽
- 卸載反饋統計
加載
init native
android-6.0.0_r1\frameworks\base\core\jni
//初始化監聽
private native int init();
private native int startWatching(int fd, String path, int mask);
fork(); 開兩個進程
一個函數 1
一個進程調用fork()函數後,系統先給新的進程分配資源,例如存儲數據和代碼的空間。
然後把原來的進程的所有值都複製到新的新進程中,只有少數值與原來的進程的值不同。相當於克隆了一個自己。
它是一個內核用於通知用戶空間程序文件系統變化的機制。
結構中的 wd 爲被監視目標的 watch 描述符,
mask 爲事件類型
len 爲 name字符串的長度
name 爲被監視目標的路徑名,該結構的 name 字段爲一個樁
它只是爲了用戶方面引用文件名,文件名是變長的,
buf 是一個 inotify_event 結構的數組指針,
BUF_LEN 指定要讀取的總長度,
buf 大小至少要不小於 BUF_LEN,
該調用返回的事件數取決於 BUF_LEN 以及事件中文件名的長度
。Len 爲實際讀去的字節數,即獲得的事件的總長度。
IN_ACCESS,即文件被訪問
IN_MODIFY,文件被 write
IN_ATTRIB,文件屬性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可寫文件被 close
IN_CLOSE_NOWRITE,不可寫文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移來,如 mv、cp
IN_CREATE,創建新文件
IN_DELETE,文件被刪除,如 rm
IN_DELETE_SELF,自刪除,即一個可執行文件在執行時刪除自己
IN_MOVE_SELF,自移動,即一個可執行文件在執行時移動自己
IN_UNMOUNT,宿主文件系統被 umount
IN_CLOSE,文件被關閉,等同於(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移動,等同於(IN_MOVED_FROM | IN_MOVED_TO)
注:上面所說的文件也包括目錄。
作業
#include <stdio.h>
int main(int argc, char* argv[]) {
fork();
fork() && fork() || fork();
fork();
} 求 進程 數量
Intent
am start -n com.yuanhh.app/.MainActivity
am 不需要彈選擇框
主要代碼
#include <jni.h>
#include <string>
#include <sys/inotify.h>
#include <fcntl.h>
#include <android/log.h>
#include <malloc.h>
#include <unistd.h>
#define LOG_TAG "tuch"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C"
{
JNIEXPORT void JNICALL
Java_com_dongnao_ls19uninstall_MainActivity_stringFromJNI(
JNIEnv *env,
jobject obg,jint sdk,jstring path) {
// pid_t pid=fork();
// if (pid < 0) {
// LOGD("fork失敗");
//// 系統fork()
// } else if (pid > 0) {
// LOGD("父進程");
// } else{
// LOGD("子進程");
// // 初始化 inotify
//
// int observer = open(observerFile, O_RDONLY);
// if (observer == -1) {
// //不存在就創建
// observer = open(observerFile, O_CREAT);
// }
// int fileDescriple = inotify_init();
// int watch=inotify_add_watch(fileDescriple, observerFile, IN_DELETE_SELF);
// if (watch < 0) {
// LOGD("監聽失敗");
// }
// void *p_buf=malloc(sizeof(struct inotify_event));
// // 阻塞式函數 anr
// size_t readBytes=read(fileDescriple,p_buf, sizeof(struct inotify_event));
//// 主進程 1
//// 執行到下面來了 文件發生了改變 用戶卸載了 app
// LOGD("監聽類型 %d ,%d",((struct inotify_event *) p_buf)->mask,IN_DELETE_SELF);
// if (((struct inotify_event *) p_buf)->mask == IN_DELETE_SELF) {
////// 覆蓋安裝
//// FILE *app_file = fopen("/data/data/com.dongnao.ls19uninstall", "r");
//// if (app_file == NULL) {
//// // 刪除app 移除監聽
//// inotify_rm_watch(fileDescriple, watch);
//// } else{
//// // 覆蓋安裝 重新進行監聽
//// fclose(app_file);
//// FILE *p_observelFile = fopen(observerFile, "w");
//// int watchDescip=inotify_add_watch(fileDescriple, observerFile, IN_DELETE_SELF);
//// }
// }
// LOGD("跳轉網頁");
//// 鐵了心要刪除app sdk 17 am 設置到環境變量 多用戶的操作
// if (sdk < 17) {
// execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d",
// "http://www.baidu.com",NULL);
//
// } else{
// execlp("am", "am", "start","--user","0","-a", "android.intent.action.VIEW", "-d",
// "http://www.baidu.com",NULL);
// }
//
// }
}
JNIEXPORT void JNICALL
Java_com_dongnao_ls19uninstall_MainActivity_load__ILjava_lang_String_2(JNIEnv *env,
jobject instance, jint sdk,
jstring path_) {
const char *path = env->GetStringUTFChars(path_, 0);
pid_t pid=fork();
if (pid < 0) {
LOGD("fork失敗");
// 系統fork()
} else if (pid > 0) {
LOGD("父進程");
} else {
LOGD("子進程");
// 初始化 inotify
int fileDescriple = inotify_init();
int watch=inotify_add_watch(fileDescriple, path, IN_DELETE_SELF);
void *p_buf=malloc(sizeof(struct inotify_event));
// 阻塞式函數 anr
size_t readBytes=read(fileDescriple,p_buf, sizeof(struct inotify_event));
inotify_rm_watch(fileDescriple, watch);
LOGD("跳轉網頁");
// 鐵了心要刪除app sdk 17 am 設置到環境變量 多用戶的操作
if (sdk < 17) {
execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d",
"http://www.baidu.com",NULL);
} else{
execlp("am", "am", "start","--user","0","-a", "android.intent.action.VIEW", "-d",
"http://www.baidu.com",NULL);
}
}
// TODO
env->ReleaseStringUTFChars(path_, path);
}
}
二 AM
● -a : 指定Intent action, 實現原理Intent.setAction();
● -n : 指定組件名,格式爲{包名}/.{主Activity名},實現原理Intent.setComponent();
● -d <DATA_URI>: 指定Intent data URI
● -t <MIME_TYPE>: 指定Intent MIME Type
● -c [-c ] …]:指定Intent category,實現原理Intent.addCategory()
● -p : 指定包名,實現原理Intent.setPackage();
● -f : 添加flags,實現原理Intent.setFlags(int ),緊接着的參數必須是int型;
基本類型
參數-e/-es-esn-ez-ei-el-ef-eu-ecn類型String(String)nullbooleanintlongfloaturicomponent
比如參數es是Extra String首字母簡稱,實例:
intent.putExtra(“website”,“website gityuan.com”)
am start -n com.dongnao.app/.MainActivity -es website gityuan.com
intent.put
比如參數efal,是Extra float Array List首字母簡稱,多個value值之間以逗號隔開,實例:
am start -n comdongnao.app/.MainActivity -efal nums 1.2,2.2
此處-es website gityuan.com,等價於Intent.putExtra(“website”, “gityuan.com”);
AM四大組件 所有跳轉問題
AM 命令
跳轉 模塊