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

我们所知道的是,在卸载之前,通常(实际上是)任何防病毒应用程序都会使用如下简单对话框:“您将卸载应用程序,您确定吗? – “是/否”。

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

<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> 

但问题是简单的事实,这拦截任何删除请求,而且这将触发我的应用程序和股票安装程序之间的选择器对话框。 所以如果用户选择股票安装程序 – 我将无法做任何事情。

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

从这些防病毒应用程序中学习,我发现这种操作是可能的,所以请帮助我解释它是如何可能的?

更新

由于有些人不相信这是真的 – 我会参考Avast移动安全 :

通过用各种自我保护技术来掩饰其组件,防盗可以防止卸载。

另一个例子:卡巴斯基互联网安全的Android – 这里是卸载它的特殊程序 ,这需要输入密码。

无论如何,这意味着有拦截卸载程序,以防止卸载或做一些定稿工作的方法。

好的。 自从2天以来,我一直在调查这个问题,终于找到了一个“疯狂的方式”来解决这个问题,而没有扎根设备:)

首先,这里是实现解决方案的亮点:

1.每当用户转到设置 – >管理应用程序 – >选择一个特定的应用程序,我们收到广播android.intent.action.QUERY_PACKAGE_RESTART与应用程序的包的名称作为额外的。

2.之后,当我们点击卸载按钮(使用软件包安装程序),它会打开一个名为 – com.android.packageinstaller.UninstallerActivity

控制流程将如下所示:

在应用程序设置下,用户点击卸载按钮—>我们得到控制来显示对话/启动另一个活动/等—>我们完成我们的预卸载任务—>用户返回到卸载确认屏幕 – – >用户确认并卸载应用程序

使用方法:

我们将在我们的应用程序中实现一个BroadcastReceiver来监听动作“ android.intent.action.QUERY_PACKAGE_RESTART ”,并在onReceive()方法中匹配我们的包名称。 如果接收到广播以选择我们想要的应用程序包,那么我们将启动后台线程,使用ActivityManager继续监视前台运行活动。

一旦我们发现前台的活动是“ com.android.packageinstaller.UninstallerActivity ”,就会确认用户想要卸载我们的应用程序。 此时,我们将执行所需的任务(显示对话框,或启动与卸载窗口重叠的另一个活动等),这些任务将在卸载之前执行。 执行完任务后,我们将允许用户继续确认卸载过程。

实现/源代码:

在manifest.xml中

添加权限:

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

和广播接收机:

 <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 (广播接收器类)

 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 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(); } } 

已知限制:

当用户单击管理应用程序设置下的卸载按钮时,我们将执行我们的卸载前任务,然后提示用户进入确认窗口,用户可以确认卸载或取消操作。

如果用户在执行任务后点击取消按钮,上述方法目前还不能涵盖这种情况。 但是这可以通过一些修改轻松解决。

例如:如果广播“ android.intent.action.PACKAGE_REMOVED ”最终没有收到,我们可以实现一个逻辑来恢复我们所做的更改。

我希望这种方法对你有帮助:)因为这是我认为唯一的方法,我们可以解决你的问题,而不需要设备的根!

[更新1] :建议的方法来检查卸载任务是否已取消

它的一种有趣的,我有早些时候完全不同和复杂的想法(涉及广播,ActivityManager等..等),但在这里写它只是另一个想法,我的脑海里,这是相当简单:)

当用户单击“管理应用程序”设置下的“卸载”按钮并执行卸载前任务后,只需在应用程序中设置一些SharedPreference,即已执行卸载前任务并准备好进行卸载。 在此之后,你不需要关心任何事情。

如果用户继续卸载 – >它已经完成了所需的任务。

而如果用户最后点击取消按钮,并消失 – >不要打扰。 直到用户再次运行应用程序。 现在在应用程序主要活动的“onStart()”/“onResume()”中,您可以检查SharedPreference的值,如果它被设置为卸载,这意味着用户没有最终进行卸载。 现在,您可以恢复之前所做的更改(反转执行的卸载前任务),以确保您的应用程序完美运行!

在Android中根本不可能

您的应用程序无法知道它正在被卸载(无需修改内核)。 在data / data / your.app.package中创建的所有文件都会在安装后自动删除。

另一种方法可能是让另一个应用程序检查这个应用程序是否安装。 如果没有,可以做清理工作。

UPDATE

ACTION_PACKAGE_REMOVED意图将被发送到除您自己以外的所有接收者。 这在这里得到证实。

更新2

只是另一个想法。

当我搜索这个我发现,这可以通过监视logcat为您的应用程序在这里是一个示例logcat监视器

好的是,为了监视相同的应用程序的logcat,我们不需要一个根设备

当我们读取logcat中的每个条目时,我们可以搜索以下字符串

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

当收到这个事件,我们知道我们的应用程序现在将被卸载

虽然没有尝试

Android Jellybean不允许监控logcat

检测应用程序卸载的另一个选择是使用本机代码。

您需要在分叉进程中使用inotify框架监视您的目录。

当它被删除你可以运行一些系统命令,例如。 am启动Intent命令

这种解决方案的PoC: https : //github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c

为了让您的应用程序坚持下去,您需要拥有一个有根的设备,并能够将其安装到系统分区。 一旦它在那里,您可以卸载更新,因为它们是保存在非系统应用程序,但它不是从系统中卸载它的切割和干燥。

我知道其中的一些还会在系统分区上保存一些数据,以防设备出厂重置,但是也有一些方法可以让软件包管理器在刚刚卸载的情况下保留已保存的数据。

另一种选择是将其注册为设备管理员。 一旦你这样做,他们将无法卸载它,除非他们手动删除它的管理状态。

在这里输入图像描述

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

这里看起来他们正在使用root以及其他方法。 他们似乎可能有一些疯狂的精心制作的服务,但没有任何其他方法可以做到这一点。

利用root对于像这样的AV /安全应用程序几乎是标准的做法,没有它对任何其他应用程序没有任何真正的权力,所以他们是非常有限的。 我想超级用户的权限是不会显示,除非你已经安装,许多人仍然不知道这是一个选项。

 <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>