web-dev-qa-db-fra.com

Comment une application peut-elle détecter qu'elle va être désinstallée?

Nous savons tous que les applications antivirus habituelles (en pratique) avant la désinstallation servent à déclencher un dialogue simple du type "Vous allez désinstaller l'application, êtes-vous sûr?" - "Oui Non".

Oui, je sais que je peux intercepter l'intention de supprimer un paquet en utilisant un filtre d'intention comme:

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

Mais le problème est simplement que cela intercepte toutes les demandes de suppression et que, de plus, cela ouvrira un dialogue de sélection entre mon application et l'installateur. Donc, si l’utilisateur choisit le programme d’installation, je ne pourrai rien faire.

Mon objectif n'est pas d'empêcher l'utilisateur de désinstaller mon application, mais simplement d'annuler les modifications apportées par mon application.

En apprenant de ces applications antivirus, je vois que ce type d'opération est possible, alors aidez-moi et expliquez comment c'est possible.

Mettre à jour

Puisqu'il y a des gars qui ne croient pas que c'est réel - je me référerais à Avast Mobile Security :

Anti-Theft se protège de la désinstallation en masquant ses composants à l'aide de diverses techniques d'auto-préservation.

Autre exemple: Kaspersky Internet Security pour Android - voici la procédure spéciale pour le désinstaller , qui nécessite la saisie d'un code secret.

Quoi qu'il en soit, cela signifie qu'il existe un moyen d'intercepter la procédure de désinstallation afin d'empêcher la désinstallation ou d'effectuer un travail de finalisation.

65
barmaley

D'accord. J'ai enquêté beaucoup sur ce problème depuis 2 jours et j'ai finalement trouvé un "moyen sauvage" de le résoudre sans enraciner l'appareil :)

Premièrement, voici les points saillants pour réaliser la solution:

1. Chaque fois que l'utilisateur accède à Paramètres -> Gérer les applications -> Sélectionne une application particulière nous recevons une diffusion Android.intent.action.QUERY_PACKAGE_RESTART avec le nom du paquet de l'application comme extras.

2. Ensuite, lorsque nous cliquons sur le bouton Désinstaller (avec le programme d'installation du paquet), une activité intitulée - com.Android.packageinstaller s'ouvre. .UninstallerActivity

Le flux de contrôle ressemblera à:

Sous Paramètres de l'application, l'utilisateur clique sur le bouton Désinstaller ---> Nous obtenons le contrôle pour afficher un dialogue/démarrer une autre activité/etc ---> Nous terminons notre tâche de pré-désinstallation ---> L'utilisateur est renvoyé à Écran de confirmation de la désinstallation ---> L’utilisateur confirme et désinstalle l’application

Méthode utilisée:

Nous allons implémenter un BroadcastReceiver dans notre application pour écouter l'action " Android.intent.action.QUERY_PACKAGE_RESTART" et correspondre au nom de notre package dans la méthode onReceive (). Si la diffusion a été reçue pour la sélection du package d'application souhaité, nous allons alors initier un thread d'arrière-plan qui continuera à surveiller les activités en cours de premier plan à l'aide de l'ActivityManager.

Une fois que nous avons trouvé l'activité de premier plan: " com.Android.packageinstaller.UninstallerActivity", nous confirmons que l'utilisateur souhaite désinstaller notre application. À ce stade, nous allons effectuer les tâches souhaitées (afficher une boîte de dialogue ou démarrer une autre activité chevauchant la fenêtre de désinstallation, etc.) à exécuter avant la désinstallation. Après avoir exécuté notre tâche, nous autoriserons l’utilisateur à confirmer le processus de désinstallation.

Code d'implémentation/source:

Dans manifest.xml

ajouter la permission:

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

et récepteur de radiodiffusion:

<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 (classe de récepteur de diffusion)

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

                }
            }
        }
    }

}

Classe ListenActivities - pour surveiller les activités de premier plan

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

Limitations Connues:

Lorsque l'utilisateur clique sur le bouton Désinstaller sous Gérer les paramètres des applications, nous effectuons nos tâches de pré-désinstallation, puis incitons l'utilisateur à la fenêtre de confirmation dans laquelle l'utilisateur peut confirmer la désinstallation ou peut = Annuler l'opération.

L’approche décrite ci-dessus ne couvre pas le cas à présent si l’utilisateur clique sur le bouton Annuler après que nous ayons exécuté notre tâche. Mais cela pourrait être résolu facilement avec des amendements.

Exemple: nous pouvons implémenter une logique pour annuler les modifications apportées si la diffusion " Android.intent.action.PACKAGE_REMOVED" n'a pas été reçue à la fin.

J'espère que cette approche vous sera utile :) Comme c'est la seule façon, à mon avis, de résoudre votre problème sans enraciner l'appareil!

[Update 1]: Approche suggérée pour vérifier si la tâche de désinstallation était annulé:

C’est un peu drôle que j’ai eu une idée tout à fait différente et complexe à l’avance (concernant les émissions, ActivityManager, etc. etc.), mais lors de l’écriture, c’est une autre idée qui m’a frappé, qui est comparativement très simple :)

Lorsque l'utilisateur clique sur le bouton Désinstaller dans les paramètres de gestion des applications et après avoir effectué les tâches de pré-désinstallation, il vous suffit de définir une référence partagée dans votre application: vous avez effectué les tâches de pré-désinstallation et vous êtes prêt pour la désinstallation. Après cela, vous ne devez plus vous soucier de rien.

Si l'utilisateur continue à désinstaller -> tout va bien, vous avez déjà effectué les tâches requises.

Alors que si l'utilisateur clique enfin sur le bouton Annuler et s'en va -> ne vous embêtez pas. Jusqu'à ce que l'utilisateur aille exécuter votre application à nouveau. À présent, dans "onStart ()"/"onResume ()" de l'activité principale de votre application, vous pouvez vérifier la valeur de SharedPreference. Si cette option était configurée pour la désinstallation, cela signifierait que l'utilisateur n'a finalement pas procédé à la désinstallation. Et vous pouvez maintenant annuler les modifications apportées précédemment (en inversant les tâches de pré-désinstallation effectuées) pour vous assurer que votre application fonctionne parfaitement!

135
AnniJais

Ce n'est tout simplement pas possible dans Android

Votre application ne dispose d'aucun moyen de savoir qu'elle est en cours de désinstallation (sans modifier le noyau). Tous les fichiers créés dans data/data/your.app.package sont automatiquement supprimés à l’installation.

Une autre approche pourrait consister à avoir une autre application qui vérifie si cette application est installée ou non. Sinon, il peut faire le travail de nettoyage.

[~ # ~] met à jour [~ # ~]

L'intention ACTION_PACKAGE_REMOVED sera envoyée à tous les destinataires, à l'exception du vôtre. Ceci est confirmé ICI .

UPDATE 2

Juste une autre pensée.

comme j'ai cherché cela sur j'ai trouvé que, ceci peut être fait en surveillant logcat pour votre application ici est un exemple de moniteur de logcat

La bonne chose est que pour surveiller logcat pour la même application, nous n'avons pas besoin d'un périphérique enraciné.

et comme nous lisons chaque entrée de logcat, nous pouvons rechercher la chaîne suivante

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

comme cet événement est reçu, nous savons que notre application va maintenant être non installée

Je n'ai pas essayé

Encore une fois, la surveillance de logcat n’est pas autorisée à partir de Android Jellybean

7
Android Fanatic

Jusqu'à Android 5.0, l'option permettant de détecter la désinstallation d'une application était d'utiliser le code natif:

Vous devez surveiller votre répertoire à l'aide de inotify framework dans un processus forké. Quand il est supprimé Vous pouvez exécuter une commande système, par exemple. am commande qui démarre Intent

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

7
pixel

Pour que votre application persiste, vous devez disposer d'un appareil enraciné et pouvoir l'installer sur la partition système. Une fois que vous y êtes, vous pouvez désinstaller les mises à jour, car elles sont enregistrées avec des applications non-système, mais il n'est pas aussi simple de les désinstaller du système.

Je sais que certains d'entre eux sauvegarderont également un peu de données sur la partition système au cas où les périphériques seraient réinitialisés, mais il existe également des moyens de faire en sorte que le gestionnaire de paquets laisse vos données sauvegardées s'il venait à être désinstallé .

Une autre option serait de l'enregistrer en tant qu'administrateur de périphérique. Une fois que vous aurez fait cela, ils ne pourront plus le désinstaller à moins de le supprimer manuellement.

enter image description here

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

Ici, on dirait qu'ils utilisent root ainsi que d'autres méthodes. Hormis la possibilité de créer un service élaboré fou, ce qui semble être le cas, il n’existe aucun moyen légitime de le faire autrement.

Tirer parti de la racine est une pratique quasi standard pour les applications de sécurité/antivirus telles que celle-ci. Sans cela, elles n’ont aucune autorité réelle sur les autres applications, elles sont donc très limitées. Je pense que l'autorisation SuperUser n'est pas affichée à moins que vous ne l'ayez installée non plus, de sorte que beaucoup de personnes ignorent encore que c'est une 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>
1
Jon