我们都知道,在卸载之前通常(实际上是任何)防病毒应用程序用于触发简单的对话框,例如:“你要卸载app,你确定吗?” - “是/否”。
是的,我知道我可以使用intent-filter拦截程序包删除意图,如:
<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 Mobile Security:
Anti-Theft通过伪装其组件来保护自己免遭卸载 有各种自我保护技术。
另一个例子:适用于Android的卡巴斯基互联网安全 - here's special procedure for uninstalling it,需要输入密码。
无论如何,这意味着有办法拦截卸载程序,以防止卸载或完成一些最终工作。
答案 0 :(得分:129)
好。我从2天开始就这个问题进行了很多调查,最后找到了一个“疯狂的方法”来解决它而不需要使用设备:)
1。每当用户转到设置 - &gt;管理应用 - &gt;选择特定应用程序 我们收到广播 android.intent.action.QUERY_PACKAGE_RESTART ,并将应用程序包的名称作为附加内容。
2。之后,当我们点击卸载按钮(使用软件包安装程序)时,它会打开一个名为 - com.android.packageinstaller.UninstallerActivity的活动强>
控制流程如下:
在“应用程序设置”下,“用户单击卸载”按钮---&gt;我们得到控制以显示对话/开始另一个活动/ etc ---&gt;我们完成了预卸载任务---&gt;用户返回卸载确认屏幕---&gt;用户确认并卸载应用
使用过的方法:
我们将在应用程序中实现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。在此之后,您无需关心任何事情。
如果用户继续卸载 - &gt;因为你已经完成了必要的任务,所以它很好。
如果用户最终点击取消按钮并离开 - >&gt;不要打扰。直到用户再次运行您的应用程序。现在在应用程序主要活动的“onStart()”/“onResume()”内,您可以检查SharedPreference的值以及是否设置为卸载,这意味着用户最终没有继续卸载。现在,您可以恢复之前所做的更改(撤消执行的卸载前任务),以确保您的应用程序运行正常!
答案 1 :(得分:6)
在Android
中根本不可能您的应用程序无法知道它正在被卸载(不修改内核)。安装时会自动删除在data / data / your.app.package中创建的所有文件。
另一种方法可能是让另一个应用程序检查是否安装了此应用程序。如果没有,它可以进行清理工作。
<强>更新强>
ACTION_PACKAGE_REMOVED意图将发送给除您自己以外的所有接收者。这已得到确认HERE。
更新2
另一个想法。
当我搜索到这个时,我发现,这可以通过监视应用程序的logcat来完成 here是示例logcat监视器
好的是,要监控同一应用程序的logcat,我们不需要有root设备
当我们读取logcat中的每个条目时,我们可以搜索以下字符串
Received broadcast Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.package.name flg=0x8000010 (has extras) }
收到此事件后,我们知道我们的应用程序现在将无法安装
没试过
Android Jellybean
不允许再次监控logcat答案 2 :(得分:6)
直到Android 5.0,检测应用程序卸载的选项是使用本机代码:
您需要在分叉进程中使用inotify
框架监视您的目录。
当它被删除你可以运行一些系统命令,例如。启动am
Intent
命令
此类解决方案的PoC:https://github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c
答案 3 :(得分:0)
为了使您的应用程序保持不变,您需要拥有一个root设备并能够将其安装到系统分区。一旦它在那里你可以卸载更新,因为它们是在非系统应用程序旁边保存的,但是从系统中卸载它并不是那么简单。
我知道其中一些还会在系统分区上保存一些数据,以防设备出厂重置,但也有办法让软件包管理器在保存数据的情况下留下它们刚刚卸载。
另一种选择是将其注册为设备管理员。一旦你这样做,他们将无法卸载它,除非他们手动删除它的管理员状态。
<item name="android.permission.ACCESS_SUPERUSER" />
这里看起来他们正在使用root以及其他方法。如果没有做出一些疯狂的精心服务,看起来他们可能有这种服务,没有合法的方法可以用其他任何方式来做这件事。
利用root几乎是像这样的AV /安全应用程序的标准做法,没有它,它们对任何其他应用程序没有任何真正的权限,所以它们非常有限。我认为SuperUser权限不会显示,除非您已安装它,因此很多人仍然不知道它是一个选项。
<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>