Как приложение может определить, что оно будет удалено?

Все, что мы знаем, это обычное (практически любое) антивирусное приложение перед удалением используется для запуска простого диалога, например: "Вы собираетесь удалить приложение, вы уверены?" - "да нет".

Да, я знаю, что могу перехватить намерение удалить пакет, используя фильтр намерений, например:

<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 защищает себя от деинсталляции, маскируя его компоненты различными способами самосохранения.

Другой пример: Kaspersky Internet Security для Android - вот специальная процедура для его удаления, которая требует ввода секретного кода.

В любом случае это означает, что есть способ перехватить процедуру удаления, чтобы либо предотвратить удаление, либо выполнить какую-либо завершающую работу.

4 ответа

Решение

Хорошо. Я много раз изучал эту проблему с 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 5.0 была опция для обнаружения удаления приложения с использованием собственного кода:

Вы должны следить за Вашим каталогом, используя inotify рамки в раздвоенном процессе. Когда он удален, вы можете запустить некоторую системную команду, например. am команда, которая начинается Intent

PoC такого решения: https://github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c

Это просто невозможно в Android

Ваше приложение не может знать, что оно удаляется (без изменения ядра). Все файлы, созданные в data / data / your.app.package, автоматически удаляются при установке.

Другой подход может состоять в том, чтобы иметь другое приложение, которое проверяет, установлено ли это приложение или нет. Если нет, он может сделать уборку.

ОБНОВИТЬ

Намерение 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) }

поскольку это событие получено, мы знаем, что наше приложение будет не установлено

Не пробовал хоть

Снова мониторинг logcat от Android Jellybean запрещен

Чтобы приложение сохранялось, вам нужно иметь рутированное устройство и установить его в системный раздел. Как только они появятся, вы можете удалить обновления, поскольку они сохраняются вместе с несистемными приложениями, но удалить их из системы не так просто.

Я знаю, что некоторые из них также сохранят немного данных в системном разделе на тот случай, если устройства будут сброшены на заводские настройки, но есть также способы заставить менеджер пакетов оставить после себя сохраненные данные в случае, если они просто удалены.,

Другой вариант - зарегистрировать его в качестве администратора устройства. Как только вы это сделаете, они не смогут удалить его, если они не удалят его статус администратора вручную.

<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>
Другие вопросы по тегам