应用程序如何检测它将被卸载?

我们所知道的通常(在实践中任何)卸载前防病毒应用程序用于触发简单的对话框,如: “你要卸载应用程序,你确定吗?”“是/否”。

是的,我知道我可以拦截包删除意图使用意图过滤器,如:

<activity
android:name=".UninstallIntentActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DELETE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package"  />
</intent-filter>
</activity>

但问题是,这在简单的事实,这拦截 任何删除请求,此外,这将激发我的应用程序和股票安装程序之间的选择对话。因此,如果用户将选择股票安装程序-我将无法做任何事情。

我的目标不是阻止用户卸载我的应用程序,而只是回滚我的应用程序所做的更改。

从这些反病毒应用程序的学习,我看到这种操作是可能的,所以请帮助我,并解释它是如何可能的?

更新

因为有些人不相信这是真的——我会参考 停止移动安全:

反盗窃保护自己从卸载伪装其组件 用各种自我保存技术。

另一个例子是 Android-abc0的卡巴斯基安全部队,它需要输入密码。

无论如何,这意味着有办法拦截卸载过程,以防止卸载或做一些终结工作。

90288 次浏览

Possible if you modify kernel . All files created in the data/data/your.app.package is deleted automatically upon install.

Another approach could be to have another application that checks whether this application is installed or not. If not, it can do the clean-up work.

UPDATE

The ACTION_PACKAGE_REMOVED intent will be sent out to all receivers except for your own. This is confirmed HERE.

UPDATE 2

Just Another thought.

as I searched for this on I found that, this can be done by monitoring logcat for your application here is a sample logcat monitor

Good thing is that to monitor logcat for same application we need not have a rooted device

and as we read each entry in logcat we can search for following string

Received broadcast Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.package.name flg=0x8000010 (has extras) }

as this event is received we know that our app is now going to be un installed

Did not try though

Again monitoring logcat is not allowed from Android Jellybean

In order to make your app persist you'll need to have a rooted device and be able to install it to the system partition. Once it's on there you can uninstall the updates, since they are saved along side non-system apps, but it's not as cut and dry to uninstall it from the system.

I know some of them will also save a little bit of data on the system partition just in case the devices is factory reset, but there are also ways to get the package manager to leave behind your saved data in the event that it is just uninstalled.

Another option would be to register it as a device administrator. Once you do that they will be unable to uninstall it unless they manually remove it's admin status.

enter image description here

<item name="android.permission.ACCESS_SUPERUSER" />

Here it looks like they're using root as well as other methods. Short of making some crazy elaborate service, which it appears they may have, there is no legitimate way to do this any other way.

Taking advantage of root is almost standard practice for AV/security apps like this, without it they don't have any real authority over any other apps so they're very limited. I think the SuperUser permission isn't shown unless you have it installed either, so many people are still unaware it's an option.

<perms>
<item name="android.permission.READ_EXTERNAL_STORAGE" />
<item name="android.permission.GET_TASKS" />
<item name="android.permission.PROCESS_OUTGOING_CALLS" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" />
<item name="android.permission.WRITE_CALL_LOG" />
<item name="com.avast.android.generic.CENTRAL_SERVICE_PERMISSION" />
<item name="android.permission.WRITE_SMS" />
<item name="android.permission.ACCESS_WIFI_STATE" />
<item name="android.permission.RECEIVE_SMS" />
<item name="android.permission.GET_ACCOUNTS" />
<item name="android.permission.READ_CONTACTS" />
<item name="android.permission.CALL_PHONE" />
<item name="android.permission.WRITE_CONTACTS" />
<item name="android.permission.READ_PHONE_STATE" />
<item name="android.permission.READ_SMS" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" />
<item name="android.permission.ACCESS_SUPERUSER" />
<item name="com.avast.android.mobilesecurity.permission.C2D_MESSAGE" />
<item name="android.permission.GET_PACKAGE_SIZE" />
<item name="android.permission.WAKE_LOCK" />
<item name="android.permission.ACCESS_NETWORK_STATE" />
<item name="android.permission.USE_CREDENTIALS" />
<item name="android.permission.SEND_SMS" />
<item name="android.permission.RECEIVE_MMS" />
<item name="com.google.android.c2dm.permission.RECEIVE" />
<item name="android.permission.KILL_BACKGROUND_PROCESSES" />
<item name="com.android.vending.BILLING" />
<item name="android.permission.WRITE_SETTINGS" />
<item name="android.permission.INTERNET" />
<item name="android.permission.VIBRATE" />
<item name="android.permission.READ_CALL_LOG" />
<item name="com.avast.android.generic.COMM_PERMISSION" />
<item name="com.dolphin.browser.permission.ACCESS_PROVIDER" />
<item name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
</perms>

Okay. I have been investigating a lot on this problem since 2 days and finally found a "wild way" to solve it without rooting the device :)

First, here are the highlights to achieve the solution:

1. Whenever user goes to Settings -> Manage Apps -> Selects a particular application we receive a broadcast android.intent.action.QUERY_PACKAGE_RESTART with name of the application's package as extras.

2. After that when we click on the Uninstall button (with package installer), it opens an activity named - com.android.packageinstaller.UninstallerActivity

Control flow will be like:

Under App Settings the User Clicks on Uninstall button ---> We get control to show a dialogue / start another activity / etc ---> We finish our Pre-Uninstallation task ---> User is Returned back to Uninstallation confirmation screen ---> User confirms and uninstalls the app

Used Method:

We will implement a BroadcastReceiver in our application for listening the action "android.intent.action.QUERY_PACKAGE_RESTART" and match our package name inside onReceive() method. If the broadcast was received for selection of our desired application package, then we'll initiate a background thread that will keep monitoring the foreground running activities using the ActivityManager.

Once we find the foreground activity to be "com.android.packageinstaller.UninstallerActivity", it'll be confirm that user wants to uninstall our application. At this point we'll perform the desired tasks (either display a dialogue, or start another activity overlapping the uninstallation window, etc..) that are to be performed before uninstallation. After performing our task, we'll allow the user to continue with confirming the uninstallation process.

Implementation / Source Code:

In manifest.xml

add permission:

<uses-permission android:name="android.permission.GET_TASKS"/>

and broadcast receiver:

<receiver android:name=".UninstallIntentReceiver">
<intent-filter android:priority="0">
<action android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<data android:scheme="package" />
</intent-filter>
</receiver>

UninstallIntentReceiver.java (broadcast receiver class)

public class UninstallIntentReceiver extends BroadcastReceiver{


@Override
public void onReceive(Context context, Intent intent) {
// fetching package names from extras
String[] packageNames = intent.getStringArrayExtra("android.intent.extra.PACKAGES");


if(packageNames!=null){
for(String packageName: packageNames){
if(packageName!=null && packageName.equals("YOUR_APPLICATION_PACKAGE_NAME")){
// User has selected our application under the Manage Apps settings
// now initiating background thread to watch for activity
new ListenActivities(context).start();


}
}
}
}


}

ListenActivities class - for monitoring the foreground activities

class ListenActivities extends Thread{
boolean exit = false;
ActivityManager am = null;
Context context = null;


public ListenActivities(Context con){
context = con;
am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}


public void run(){


Looper.prepare();


while(!exit){


// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(MAX_PRIORITY);


String activityName = taskInfo.get(0).topActivity.getClassName();




Log.d("topActivity", "CURRENT Activity ::"
+ activityName);


if (activityName.equals("com.android.packageinstaller.UninstallerActivity")) {
// User has clicked on the Uninstall button under the Manage Apps settings


//do whatever pre-uninstallation task you want to perform here
// show dialogue or start another activity or database operations etc..etc..


// context.startActivity(new Intent(context, MyPreUninstallationMsgActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
exit = true;
Toast.makeText(context, "Done with preuninstallation tasks... Exiting Now", Toast.LENGTH_SHORT).show();
} else if(activityName.equals("com.android.settings.ManageApplications")) {
// back button was pressed and the user has been taken back to Manage Applications window
// we should close the activity monitoring now
exit=true;
}
}
Looper.loop();
}
}

Known Limitations:

When the user clicks on the Uninstall button under Manage Apps settings, we'll perform our pre-uninstallation tasks and then promt the user to the Confirmation window where user can either confirm to uninstall or can Cancel the operation.

The approach described above is as of now not covering the case if user clicks on Cancel button after we have performed our task. But this could be tackled easily with some ammendments.

E.g.: We can implement a logic to revert the changes we made if the broadcast "android.intent.action.PACKAGE_REMOVED" was not received in the end.

I hope this approach will be helpful to you :) As this is the only way in my opinion we can solve your problem without rooting the device!

[Update 1]: Suggested Approach to check if the Uninstallation task was Canceled:

Its kind of funny that I had entirely different and much complex idea earlier(involving broadcasts, ActivityManager, etc.. etc..), but while writing it here just another idea struck into my mind which is comparatively very simple :)

When the User clicks on Uninstall button under Manage Apps settings and after you have performed your pre-uninstallation tasks, you just set some SharedPreference in your app that you have performed the pre-uninstall tasks and are ready for uninstallation. After this you need not to care about anything.

If the user continues to uninstall -> its well and good as you have already performed required tasks.

While if user finally clicks on Cancel button and goes away -> don't bother. Until the user goes and run your application again. Now inside "onStart()" / "onResume()" of your application's main activity, you can check the SharedPreference's value and if it was set for uninstallation, that will mean that user didn't finally proceeded with the uninstallation. And now you could revert the changes made earlier(reversing the pre-uninstall tasks performed) to ensure that your application runs perfectly!

Up to Android 5.0 there was option for detecting app uninstall is using native code:

You need to monitor for Your directory using inotify framework in forked process. When it's deleted You can run some system command eg. am command that starts Intent

PoC of such solution: https://github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c