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

Bar*_*ica 70 android uninstall android-intent

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

是的,我知道我可以使用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>
Run Code Online (Sandbox Code Playgroud)

但问题很简单,这会拦截任何删除请求,而且这会触发我的应用程序和库存安装程序之间的选择器对话框.因此,如果用户将选择库存安装程序 - 我将无法做任何事情.

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

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

更新

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

Anti-Theft通过各种自我保护技术伪装其组件来保护自己免于卸载.

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

无论如何,这意味着有办法拦截卸载程序,以防止卸载或完成一些最终工作.

Ann*_*ais 137

好的.我从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"/>
Run Code Online (Sandbox Code Playgroud)

和广播接收者:

<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>
Run Code Online (Sandbox Code Playgroud)

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

                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

已知限制:

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

如果用户在执行完任务后单击" 取消"按钮,则上述方法现在还没有涵盖这种情况.但是这可以通过一些修正案轻松解决.

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

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

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

它有点滑稽,我之前有完全不同和更复杂的想法(涉及广播,ActivityManager等等等),但在这里写它只是另一个想法进入我的脑海中,相对来说非常简单:)

当用户单击"管理应用程序"设置下的"卸载"按钮并执行卸载前任务后,您只需在应用程序中设置一些已执行卸载前任务并准备卸载的SharedPreference.在此之后,您无需关心任何事情.

如果用户继续卸载 - >它已经很好,因为您已经执行了所需的任务.

如果用户最终点击取消按钮并消失 - >不要打扰.直到用户再次运行您的应用程序.现在在应用程序主要活动的"onStart()"/"onResume()"内,您可以检查SharedPreference的值以及是否设置为卸载,这意味着用户最终没有继续卸载.现在,您可以恢复之前所做的更改(撤消执行的卸载前任务),以确保您的应用程序完美运行!

  • `&lt;uses-permission android:name =“ android.permission.GET_TASKS” /&gt;`在* API级别21 *下已被**弃用。 (2认同)
  • 只是想感谢Update 1中的精彩创意! (2认同)
  • 这个程序还有效吗?有没有人在最新的 API 级别上尝试过? (2认同)

Sou*_*ldi 7

在Android中根本不可能

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

另一种方法可能是让另一个应用程序检查是否安装了此应用程序.如果没有,它可以进行清理工作.

UPDATE

ACTION_PACKAGE_REMOVED意图将发送给除您自己以外的所有接收者.这在此处得到确认.

更新2

只是另一个想法.

当我搜索到这个时,我发现,这可以通过监视你的应用程序的logcat来完成 这里是一个示例logcat监视器

好的是,为了监控相同应用程序的logcat,我们不需要有root设备

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

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

收到此事件后,我们知道我们的应用程序现在将无法安装

没试过

Android Jellybean不允许再次监视logcat

  • 请执行以下操作:1.在设备上安装Avast Antivirus应用程序2.尝试卸载它3.您将看到Avast将触发对话框询问"您确定吗?" PS它实际上发生在任何设备中(无根据我的意思).之后你说这是不可能的?! (6认同)
  • @ChrisStratton您应该阅读评论主题.作者给出了一个市场上的应用程序(Avast)的例子,该应用程序可以让它工作,所以现实可能比你想象的要复杂得多. (4认同)

pix*_*xel 7

高达Android 5.0有检测应用程序卸载的选项是使用本机代码:

您需要inotify在分叉进程中使用框架监视您的目录.当它被删除你可以运行一些系统命令,例如.am启动的命令Intent

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