1. 程式人生 > >Android APP監聽自己被解除安裝以及解除安裝後的邏輯處理

Android APP監聽自己被解除安裝以及解除安裝後的邏輯處理

兩個月前發了兩篇有關監聽自己是否被解除安裝和解除安裝反饋功能實現的部落格,第二版的地址如下:http://www.cnblogs.com/zealotrouge/p/3159772.html,感謝@whiletrue_童鞋發現了我的程式碼在4.2.2系統上無法實現解除安裝反饋,經過除錯,有了問題的解決方案,但是由於發完部落格後即處於閉關開發階段,沒時間打理部落格,所以解決方案遲遲沒有與大家見面,最近空閒下來,將解決思路及方案發出來給大家看看還有沒有問題。

  除錯發現,監聽依然沒有問題,畢竟是Linux Kernel中的介面,Framework層再怎麼改也改不到那兒去,那麼問題出在哪呢?阻塞結束後,通過呼叫exec函式發出am命令調起瀏覽器訪問網頁,在API16(Android 4.1.x)的裝置上尚可正常訪問網頁,而API17(Android 4.2.x)的裝置上連瀏覽器也不能調起。

  通過分析log,發現了一條線索,如下面的log的所示:

W/ActivityManager( 387): Permission Denial: startActivity asks to run as user -2 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL

  log中直接給出提示,需要加一個許可權INTERACT_ACROSS_USERS_FULL,這個許可權時API17新引入的,目的在於允許不同使用者的應用之間可以產生互動。可是加上去之後發現,還不是無法調起瀏覽器,而且log依然提示需要許可權INTERACT_ACROSS_USERS_FULL,很是奇怪,於是繼續分析。

  首先說明一下Linux中的pid和uid,以及android擴充套件的userSerialNumber。pid是Process的標識,用於系統對程序的控制,從API層面看就是用於Process.killProcess()和Process.sendSignal();uid在Linux系統中是用來標識使用者的,而在android將uid視為app的標識id,用於"sandbox"安全模型,即用於app許可權控制;而對於API17引入的多使用者支援(目前只支援平板),uid已經被佔用,只好新引入userSerialNumber來標識使用者。

  回到剛才的問題,log中告知startActivity時執行使用者標識為-2,而呼叫卻是由使用者標識0發起,導致拒絕執行。用這句話搜尋,發現在Google開發者網站中有相關的issue,連結如下:

https://code.google.com/p/android/issues/detail?id=39801(打不開可以把https改為http)。結合官方的回答,問題原因如下:由於被解除安裝,C端程序監聽到目錄被刪除,立即執行am命令,此時將會預設以USER_CURRENT的身份執行,由於API17中ActivityManagerService.handleIncomingUser()會校驗userSerialNumber,發現使用者標識不匹配,導致許可權校驗失敗——這也說明了許可權的影響範圍僅限於Java端的程序,對於fork()出來的C端程序來說,並不繼承父程序在Android中宣告的許可權。

  解決方案:增加處理分支,若API>=17,將userSerialNumber傳遞給C端程序,然後在am命令中帶上引數--user userSerialNumber即可。

Java端程式碼如下:

複製程式碼
  1 package main.activity;
  2 
  3 import java.lang.reflect.InvocationTargetException;
  4 import java.lang.reflect.Method;
  5 
  6 import pym.test.uninstalledobserver.R;
  7 import android.app.Activity;
  8 import android.os.Build;
  9 import android.os.Bundle;
 10 import android.util.Log;
 11 
 12 /**
 13  * @author pengyiming
 14  * @note 監聽此應用是否被解除安裝,若被解除安裝則彈出解除安裝反饋
 15  * @note 由於API17加入多使用者支援,原有命令在4.2及更高版本上執行時缺少userSerial引數,特此修改
 16  *
 17  */
 18 
 19 public class UninstalledObserverActivity extends Activity
 20 {
 21     /* 資料段begin */
 22     private static final String TAG = "UninstalledObserverActivity";
 23     
 24     // 監聽程序pid
 25     private int mObserverProcessPid = -1;
 26     /* 資料段end */
 27     
 28     /* static */
 29     // 初始化監聽程序
 30     private native int init(String userSerial);
 31     static
 32     {
 33         Log.d(TAG, "load lib --> uninstalled_observer");
 34         System.loadLibrary("uninstalled_observer");
 35     }
 36     /* static */
 37     
 38     /* 函式段begin */
 39     @Override
 40     public void onCreate(Bundle savedInstanceState)
 41     {
 42         super.onCreate(savedInstanceState);
 43         
 44         setContentView(R.layout.uninstalled_observer_layout);
 45         
 46         // API level小於17,不需要獲取userSerialNumber
 47         if (Build.VERSION.SDK_INT < 17)
 48         {
 49             mObserverProcessPid = init(null);
 50         }
 51         // 否則,需要獲取userSerialNumber
 52         else
 53         {
 54             mObserverProcessPid = init(getUserSerial());
 55         }
 56     }
 57     
 58     @Override
 59     protected void onDestroy()
 60     {
 61         super.onDestroy();
 62         
 63         // 示例程式碼,用於結束監聽程序
 64 //        if (mObserverProcessPid > 0)
 65 //        {
 66 //            android.os.Process.killProcess(mObserverProcessPid);
 67 //        }
 68     }
 69     
 70     // 由於targetSdkVersion低於17,只能通過反射獲取
 71     private String getUserSerial()
 72     {
 73         Object userManager = getSystemService("user");
 74         if (userManager == null)
 75         {
 76             Log.e(TAG, "userManager not exsit !!!");
 77             return null;
 78         }
 79         
 80         try
 81         {
 82             Method myUserHandleMethod = android.os.Process.class.getMethod("myUserHandle", (Class<?>[]) null);
 83             Object myUserHandle = myUserHandleMethod.invoke(android.os.Process.class, (Object[]) null);
 84             
 85             Method getSerialNumberForUser = userManager.getClass().getMethod("getSerialNumberForUser", myUserHandle.getClass());
 86             long userSerial = (Long) getSerialNumberForUser.invoke(userManager, myUserHandle);
 87             return String.valueOf(userSerial);
 88         }
 89         catch (NoSuchMethodException e)
 90         {
 91             Log.e(TAG, "", e);
 92         }
 93         catch (IllegalArgumentException e)
 94         {
 95             Log.e(TAG, "", e);
 96         }
 97         catch (IllegalAccessException e)
 98         {
 99             Log.e(TAG, "", e);
100         }
101         catch (InvocationTargetException e)
102         {
103             Log.e(TAG, "", e);
104         }
105         
106         return null;
107     }
108     /* 函式段end */
109 }
複製程式碼

核心——native方法標頭檔案:

複製程式碼
 1 /* 標頭檔案begin */
 2 #include <jni.h>
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <string.h>
 6 #include <unistd.h>
 7 #include <fcntl.h>
 8 #include <sys/inotify.h>
 9 #include <sys/stat.h>
10 
11 #include <android/log.h>
12 /* 標頭檔案end */
13 
14 /* 巨集定義begin */
15 //清0巨集
16 #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
17 
18 //LOG巨集定義
19 #define LOG_INFO(tag, msg) __android_log_write(ANDROID_LOG_INFO, tag, msg)
20 #define LOG_DEBUG(tag, msg) __android_log_write(ANDROID_LOG_DEBUG, tag, msg)
21 #define LOG_WARN(tag, msg) __android_log_write(ANDROID_LOG_WARN, tag, msg)
22 #define LOG_ERROR(tag, msg) __android_log_write(ANDROID_LOG_ERROR, tag, msg)
23 /* 巨集定義end */
24 
25 #ifndef _Included_main_activity_UninstalledObserverActivity
26 #define _Included_main_activity_UninstalledObserverActivity
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
31 #undef main_activity_UninstalledObserverActivity_MODE_PRIVATE
32 #define main_activity_UninstalledObserverActivity_MODE_PRIVATE 0L
33 #undef main_activity_UninstalledObserverActivity_MODE_WORLD_READABLE
34 #define main_activity_UninstalledObserverActivity_MODE_WORLD_READABLE 1L
35 #undef main_activity_UninstalledObserverActivity_MODE_WORLD_WRITEABLE
36 #define main_activity_UninstalledObserverActivity_MODE_WORLD_WRITEABLE 2L
37 #undef main_activity_UninstalledObserverActivity_MODE_APPEND
38 #define main_activity_UninstalledObserverActivity_MODE_APPEND 32768L
39 #undef main_activity_UninstalledObserverActivity_MODE_MULTI_PROCESS
40 #define main_activity_UninstalledObserverActivity_MODE_MULTI_PROCESS 4L
41 #undef main_activity_UninstalledObserverActivity_BIND_AUTO_CREATE
42 #define main_activity_UninstalledObserverActivity_BIND_AUTO_CREATE 1L
43 #undef main_activity_UninstalledObserverActivity_BIND_DEBUG_UNBIND
44 #define main_activity_UninstalledObserverActivity_BIND_DEBUG_UNBIND 2L
45 #undef main_activity_UninstalledObserverActivity_BIND_NOT_FOREGROUND
46 #define main_activity_UninstalledObserverActivity_BIND_NOT_FOREGROUND 4L
47 #undef main_activity_UninstalledObserverActivity_BIND_ABOVE_CLIENT
48 #define main_activity_UninstalledObserverActivity_BIND_ABOVE_CLIENT 8L
49 #undef main_activity_UninstalledObserverActivity_BIND_ALLOW_OOM_MANAGEMENT
50 #define main_activity_UninstalledObserverActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
51 #undef main_activity_UninstalledObserverActivity_BIND_WAIVE_PRIORITY
52 #define main_activity_UninstalledObserverActivity_BIND_WAIVE_PRIORITY 32L
53 #undef main_activity_UninstalledObserverActivity_BIND_IMPORTANT
54 #define main_activity_UninstalledObserverActivity_BIND_IMPORTANT 64L
55 #undef main_activity_UninstalledObserverActivity_BIND_ADJUST_WITH_ACTIVITY
56 #define main_activity_UninstalledObserverActivity_BIND_ADJUST_WITH_ACTIVITY 128L
57 #undef main_activity_UninstalledObserverActivity_CONTEXT_INCLUDE_CODE
58 #define main_activity_UninstalledObserverActivity_CONTEXT_INCLUDE_CODE 1L
59 #undef main_activity_UninstalledObserverActivity_CONTEXT_IGNORE_SECURITY
60 #define main_activity_UninstalledObserverActivity_CONTEXT_IGNORE_SECURITY 2L
61 #undef main_activity_UninstalledObserverActivity_CONTEXT_RESTRICTED
62 #define main_activity_UninstalledObserverActivity_CONTEXT_RESTRICTED 4L
63 #undef main_activity_UninstalledObserverActivity_RESULT_CANCELED
64 #define main_activity_UninstalledObserverActivity_RESULT_CANCELED 0L
65 #undef main_activity_UninstalledObserverActivity_RESULT_OK
66 #define main_activity_UninstalledObserverActivity_RESULT_OK -1L
67 #undef main_activity_UninstalledObserverActivity_RESULT_FIRST_USER
68 #define main_activity_UninstalledObserverActivity_RESULT_FIRST_USER 1L
69 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DISABLE
70 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DISABLE 0L
71 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DIALER
72 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_DIALER 1L
73 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SHORTCUT
74 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SHORTCUT 2L
75 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_LOCAL
76 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
77 #undef main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_GLOBAL
78 #define main_activity_UninstalledObserverActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
79 
80 /*
81  * Class:     main_activity_UninstalledObserverActivity
82  * Method:    init
83  * Signature: (Ljava/lang/String;)V
84  */
85 JNIEXPORT int JNICALL Java_main_activity_UninstalledObserverActivity_init(JNIEnv *, jobject, jstring);
86 
87 #ifdef __cplusplus
88 }
89 #endif
90 #endif
複製程式碼

核心——native方法實現:

複製程式碼
  1 /* 標頭檔案begin */
  2 #include "main_activity_UninstalledObserverActivity.h"
  3 /* 標頭檔案end */
  4 
  5 #ifdef __cplusplus
  6 extern "C"
  7 {
  8 #endif
  9 
 10 /* 內全域性變數begin */
 11 static char TAG[] = "UninstalledObserverActivity.init";
 12 static jboolean isCopy = JNI_TRUE;
 13 
 14 static const char APP_DIR[] = "/data/data/pym.test.uninstalledobserver";
 15 static const char APP_FILES_DIR[] = "/data/data/pym.test.uninstalledobserver/files";
 16 static const char APP_OBSERVED_FILE[] = "/data/data/pym.test.uninstalledobserver/files/observedFile";
 17 static const char APP_LOCK_FILE[] = "/data/data/pym.test.uninstalledobserver/files/lockFile";
 18 /* 內全域性變數 */
 19 
 20 /*
 21  * Class:     main_activity_UninstalledObserverActivity
 22  * Method:    init
 23  * Signature: ()V
 24  * return: 子程序pid
 25  */
 26 JNIEXPORT int JNICALL Java_main_activity_UninstalledObserverActivity_init(JNIEnv *env, jobject obj, jstring userSerial)
 27 {
 28     jstring tag = (*env)->NewStringUTF(env, TAG);
 29 
 30     LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &isCopy)
 31             , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "init observer"), &isCopy));
 32 
 33     // fork子程序,以執行輪詢任務
 34     pid_t pid = fork();
 35     if (pid < 0)
 36     {
 37         LOG_ERROR((*env)->GetStringUTFChars(env, tag, &isCopy)
 38                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork failed !!!"), &isCopy));
 39 
 40         exit(1);
 41     }
 42     
            
           

相關推薦

Android APP自己解除安裝以及解除安裝邏輯處理

兩個月前發了兩篇有關監聽自己是否被解除安裝和解除安裝反饋功能實現的部落格,第二版的地址如下:http://www.cnblogs.com/zealotrouge/p/3159772.html,感謝@whiletrue_童鞋發現了我的程式碼在4.2.2系統上無法實現解除安裝反饋,經過除錯,有了問題的解決方案

Android App軟鍵盤按鍵的三種方式

前言: 我們在android手機上面有時候會遇到監聽手機軟鍵盤按鍵的時候,例如:我們在瀏覽器輸入url完畢後可以點選軟鍵盤右下角的“GO”按鍵載入url頁面;在點選搜尋框的時候,點選右下角的search符號鍵可以進行搜尋;或者在全部資料輸入完畢後,點選右下角的"done"

Android App軟鍵盤按鍵的三種方式與改變軟鍵盤右下角確定鍵樣式

actionNone : 回車鍵,按下後游標到下一行actionGo : Go,actionSearch : 放大鏡actionSend : SendactionNext : Nextacti

Android應用如何自己是否解除安裝解除安裝反饋功能的實現(第二版)

原文地址為: Android應用如何監聽自己是否被解除安裝及解除安裝反饋功能的實現(第二版)   昨天發了一篇有關監聽自己是否被解除安裝和解除安裝反饋功能實現的部落格,地址如下:http://www.cnblogs.com/zealotrouge/p/3157126.html,發出去後收

Android應用如何自己是否解除安裝解除安裝反饋功能的實現

  一個應用被使用者解除安裝肯定是有理由的,而開發者卻未必能得知這一重要的理由,畢竟使用者很少會主動反饋建議,多半就是用得不爽就卸,如果能在被解除安裝後獲取到使用者的一些反饋,那對開發者進一步改進應用是非常有利的。目前據我所知,國內的Android應用中實現這一功能的只有360手機衛士、360平板衛士,那麼如

Android應用如何自己是否解除安裝解除安裝反饋功能的實現(第三版)

1 /* 標頭檔案begin */ 2 #include "main_activity_UninstalledObserverActivity.h" 3 /* 標頭檔案end */ 4 5 #ifdef __cplusplus 6 extern "C" 7 { 8 #en

android 如何自身應用解除安裝

   前段時間有個同事問我android應用在解除安裝以後,如何能夠通知一下伺服器,讓使用者填寫一下解除安裝的原因,以求為將來的應用修改積累資料。當時他是有段原始碼的,但是有點小問題,我只是幫他定位一下了程式碼的問題,具體細節沒有研究。又加上最近工作比較繁忙,所以就放下來了,今天稍微有點空,就自己

Android 如何App的輸入了那些內容

監聽手機上任意一個App都輸入了哪類內容,比如像QQ聊天等。其實Android給我們提供了一個輔助類AccessibilityService,這個類能幹很多事情,模擬點選(比如自動搶紅包例項,感謝CSDN_SXL:http://blog.csdn.net/csdn_sxl/article

android 通過edittext實現button的點擊事件

監聽 edittext 如果你沒有接第三方的輸入設備,那麽點擊按鈕只需找到你的button然後:button.performClick();就可以了那麽如果你用到第三方輸入法,有些時候監聽就沒有這麽好使了:以下場景為:外接輸入服務,直接給我丟一串字符,我拿到字符後執行按鈕事件代碼如下: editTe

android 真正軟鍵盤得彈起和收

 在開發中我們有時需要監聽鍵盤得變化情況  Softkeyboardlistener.class 工具類   public class Softkeyboardlistener { private View rootView;//activity的根

Android如何藍芽耳機的按鍵事件

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android 手勢demo

package com.shengfang.demo.testdemo; import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import and

AndroidScrollView滑動停止和滑動到底部

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

如何實現android來電並生成懸浮窗體提示

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android事件

案例一 事件監聽(三種寫法) 1.1 標籤上直接繫結監聽方法 public void xxx(View view) //第一種 public void xxx(View view) 1.2 建立監聽器物件,元件再繫結監聽器物件 1.2.1 匿名內部類 1.2

Android實時軟鍵盤顯示或隱藏

搞了一個實時監聽軟鍵盤顯示或隱藏,當軟鍵盤隱藏了,做邏輯;軟鍵盤顯示出來了,做邏輯; 直接上程式碼了,下面是我寫的工具類,名字為 SoftKeyBoardListener : import android.app.Activity; import android.graphics.

Android動態網路變化

廣播接收器可以自由地對自己感興趣的廣播進行註冊,這樣當有相應的廣播接收時,廣播接收器就能收到該廣播,並在內部處理其相應的邏輯。廣播註冊的方法有兩種,在程式碼中註冊與在AndridManifest.xml中註冊,前者稱為動態註冊,後者為靜態註冊,接下來我們就說說關於動態註冊

AndroidView的兩個指頭是放大和縮小

我們有時需要對view進行手勢監聽,通過兩個手指開始距離和結束距離,來判斷放大縮小,下面是實現程式碼,所有View通用。 private double nLenStart = 0;//監聽 WebView所用手勢 @Override public boolean on

android 實現簡訊接收並將當前位置傳送出去

1、獲取定位地址,這裡使用高德定位,從官網下載定位的jar包AMap_Location_V3.50_20170731.jar包然後在build.gradlecompile files('libs/AMap_Location_V3.5.0_20170731.jar')然後在許

xamarin android如何單擊事件

在xamarin android單擊事件是最基礎的事情,看過菜鳥上的android教程時,java寫的都是監聽事件,為一個按鈕,單選按鈕、多選按鈕的單擊事件有三種,前面兩種用的非常普遍,也很簡易,我這裡主要就是寫一下xamarin android中的監聽事件。 1.使用委託