¿Cómo puede una aplicación detectar que se va a desinstalar?

Todos sabemos que la aplicación antivirus habitual (en la práctica, cualquier) antes de la desinstalación solía generar un diálogo simple como: “Vas a desinstalar la aplicación, ¿estás seguro?” – “si no”.

Sí, sé que puedo interceptar el bash de eliminación del paquete usando intent-filter como:

        

Pero el problema está en el hecho simple de que esto intercepta cualquier solicitud de eliminación y además esto disparará el diálogo del selector entre mi aplicación y el instalador de valores. Entonces, si el usuario selecciona el instalador de stock, no podré hacer nada.

Mi objective no es evitar que el usuario desinstale mi aplicación, sino solo deshacer los cambios realizados por mi aplicación.

Aprendiendo de esas aplicaciones de antivirus veo que este tipo de operación es posible, así que por favor ayúdenme y explique cómo es posible.

Actualizar

Dado que hay algunos tipos que no creen que sea real, me referiría a Avast Mobile Security :

Anti-Theft se protege de la desinstalación al disfrazar sus componentes con diversas técnicas de autopreservación.

Otro ejemplo: Kaspersky Internet Security para Android: este es un procedimiento especial para desinstalarlo , que requiere la introducción de un código secreto.

De todos modos, significa que hay forma de interceptar el procedimiento de desinstalación para evitar la desinstalación o realizar algún trabajo de finalización.

Bueno. He estado investigando mucho sobre este problema desde hace 2 días y finalmente encontré una “manera salvaje” de resolverlo sin rootear el dispositivo 🙂

Primero, aquí están los aspectos más destacados para lograr la solución:

1. Cada vez que el usuario va a Configuración -> Administrar aplicaciones -> Selecciona una aplicación en particular , recibimos una transmisión android.intent.action.QUERY_PACKAGE_RESTART con el nombre del paquete de la aplicación como extras.

2. Después de eso, cuando hacemos clic en el botón Desinstalar (con el instalador del paquete), se abre una actividad llamada – com.android.packageinstaller.UninstallerActivity

El flujo de control será como:

En Configuración de la aplicación, el usuario hace clic en el botón Desinstalar —> Obtenemos control para mostrar un diálogo / iniciar otra actividad / etc —> Terminamos nuestra tarea previa a la desinstalación —> El usuario vuelve a la pantalla de confirmación de desinstalación – -> El usuario confirma y desinstala la aplicación

Método utilizado:

Implementaremos un BroadcastReceiver en nuestra aplicación para escuchar la acción ” android.intent.action.QUERY_PACKAGE_RESTART ” y unir nuestro nombre de paquete dentro del método onReceive (). Si la transmisión se recibió para la selección de nuestro paquete de aplicaciones deseado, iniciaremos un hilo de fondo que seguirá monitoreando las actividades de ejecución en primer plano usando el ActivityManager.

Una vez que encontremos que la actividad de primer plano sea ” com.android.packageinstaller.UninstallerActivity “, se confirmará que el usuario desea desinstalar nuestra aplicación. En este punto realizaremos las tareas deseadas (ya sea mostrar un diálogo o iniciar otra actividad que se superponga a la ventana de desinstalación, etc.) que se realizarán antes de la desinstalación. Después de realizar nuestra tarea, le permitiremos al usuario continuar confirmando el proceso de desinstalación.

Implementación / Código fuente:

En manifest.xml

agregar permiso:

  

y receptor de difusión:

       

UninstallIntentReceiver.java (clase de receptor de difusión)

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

Clase ListenActivities : para supervisar las actividades en primer plano

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

Limitaciones conocidas:

Cuando el usuario haga clic en el botón Desinstalar en la configuración de Administrar aplicaciones, realizaremos nuestras tareas previas a la desinstalación y luego enviaremos al usuario a la ventana de Confirmación donde el usuario puede confirmar la desinstalación o puede cancelar la operación.

El enfoque descrito anteriormente no cubre ahora el caso si el usuario hace clic en el botón Cancelar después de haber realizado nuestra tarea. Pero esto podría abordarse fácilmente con algunas enmiendas.

Por ejemplo: podemos implementar una lógica para revertir los cambios que hicimos si la transmisión ” android.intent.action.PACKAGE_REMOVED ” no se recibiera al final.

Espero que este enfoque sea útil para ti 🙂 ¡Como esta es la única forma en mi opinión, podemos resolver tu problema sin rootear el dispositivo!

[Actualización 1] : Enfoque sugerido para verificar si la tarea de desinstalación fue cancelada :

Es curioso que tuve una idea completamente diferente y muy compleja antes (involucrando transmisiones, ActivityManager, etc. …), pero mientras la escribía aquí me vino a la cabeza otra idea que es comparativamente muy simple 🙂

Cuando el usuario hace clic en el botón Desinstalar debajo de la configuración de Administrar aplicaciones y después de haber realizado las tareas previas a la desinstalación, simplemente configura SharedPreference en su aplicación que ya realizó las tareas previas a la desinstalación y está listo para la desinstalación. Después de esto, no necesita preocuparse por nada.

Si el usuario continúa desinstalando -> está bien y bien, ya que ya ha realizado las tareas requeridas.

Mientras que si el usuario finalmente hace clic en el botón Cancelar y se va -> no te molestes. Hasta que el usuario vaya y vuelva a ejecutar su aplicación. Ahora dentro de “onStart ()” / “onResume ()” de la actividad principal de su aplicación, puede verificar el valor de SharedPreference y si se configuró para la desinstalación, eso significará que el usuario finalmente no procedió con la desinstalación. ¡Y ahora podría revertir los cambios realizados anteriormente (invirtiendo las tareas previas a la desinstalación realizadas) para garantizar que su aplicación se ejecute perfectamente!

Otra opción para detectar la desinstalación de aplicaciones es usar código nativo.

Necesita supervisar su directorio utilizando inotify framework en el proceso bifurcado.

Cuando se elimina Puede ejecutar algún comando del sistema, por ej. am comando que comienza Intent

PoC de dicha solución: https://github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c

Simplemente no es posible en Android

No hay forma de que su aplicación sepa que se está desinstalando (sin modificar el kernel). Todos los archivos creados en data / data / your.app.package se eliminan automáticamente durante la instalación.

Otro enfoque podría ser tener otra aplicación que verifique si esta aplicación está instalada o no. Si no, puede hacer el trabajo de limpieza.

ACTUALIZAR

El bash ACTION_PACKAGE_REMOVED se enviará a todos los receptores, excepto el tuyo. Esto se confirma AQUÍ .

ACTUALIZACIÓN 2

Solo otro pensamiento.

mientras buscaba esto descubrí que esto se puede hacer monitoreando Logcat para su aplicación. Aquí hay un monitor logcat de muestra

Lo bueno es que para monitorear Logcat para la misma aplicación no necesitamos tener un dispositivo rooteado

y mientras leemos cada entrada en logcat podemos buscar la siguiente cadena

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

cuando se recibe este evento, sabemos que nuestra aplicación ahora se va a instalar

No intenté sin embargo

Nuevamente, el monitoreo de logcat no está permitido desde Android Jellybean

Para que su aplicación persista, deberá tener un dispositivo rooteado y poder instalarlo en la partición del sistema. Una vez allí, puede desinstalar las actualizaciones, ya que se guardan junto con las aplicaciones que no son del sistema, pero no es tan sencillo como desinstalarlas del sistema.

Sé que algunos de ellos también guardarán un poco de datos en la partición del sistema en caso de que los dispositivos se restablezcan a los valores de fábrica, pero también hay formas de que el administrador de paquetes deje sus datos guardados en caso de que solo se desinstalen. .

Otra opción sería registrarlo como administrador del dispositivo. Una vez que lo hagas, no podrán desinstalarlo a menos que eliminen manualmente su estado de administrador.

enter image description here

Aquí parece que están usando root y otros métodos. A menos que hagan un servicio complicado y loco, que parece que pueden tener, no hay una forma legítima de hacerlo de otra manera.

Aprovechar Root es una práctica casi estándar para AV / aplicaciones de seguridad como esta, sin esto no tienen ninguna autoridad real sobre ninguna otra aplicación, por lo que son muy limitadas. Creo que el permiso SuperUser no se muestra a menos que lo tengas instalado tampoco, por lo que muchas personas aún no saben que es una opción.