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

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

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

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

Но проблема проста в том, что это перехватывает запросы any, и, кроме того, это сработает диалог выбора между моим приложением и установщиком акций. Поэтому, если пользователь выберет установщик запаса - я ничего не смогу сделать.

Моя цель - не помешать пользователю удалить мое приложение, а просто отменить изменения, сделанные моим приложением.

Изучая эти антивирусные приложения, я вижу, что такая операция возможна, поэтому, пожалуйста, помогите мне и объясните, как это возможно?

Обновление

Поскольку есть некоторые ребята, которые не считают, что это реально - я бы назвал Avast Mobile Security:

Anti-Theft защищает себя от удаления, маскируя его компоненты с различными методами самосохранения.

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

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

Ответ 1

Хорошо. Я много раз изучал эту проблему с двух дней и, наконец, нашел "дикий путь", чтобы решить эту проблему, не укореняя устройство:)

Во-первых, вот основные моменты для достижения решения:

1. Всякий раз, когда пользователь переходит в Настройки → Управление приложениями → Выбирает конкретное приложение мы получаем широковещательный файл android.intent.action.QUERY_PACKAGE_RESTART с именем пакета приложения в качестве дополнительных функций.

2.. После этого, когда мы нажимаем кнопку Удалить (с установщиком пакетов), она открывает действие с именем - com.android.packageinstaller.UninstallerActivity

Управляющий поток будет выглядеть следующим образом:

В разделе "Настройки приложения" кнопка "Нажимает кнопку на кнопку" Удалить "--- > Мы получаем контроль, чтобы показать диалог/запустить другое действие /etc --- > Закончить задачу" Предварительная деинсталляция "--- > Пользователь возвращается обратно на экран подтверждения удаления --- > Пользователь подтверждает и удаляет приложение

Используемый метод:

Мы будем использовать 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, и если оно было установлено для удаления, это будет означать, что пользователь не завершил деинсталляцию. И теперь вы можете отменить сделанные ранее изменения (реверсирование выполняемых задач перед удалением), чтобы убедиться, что приложение работает отлично!

Ответ 2

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

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

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

UPDATE

Цель 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) }

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

Не пытался, хотя

С помощью Android Jellybean снова нельзя отслеживать logcat

Ответ 3

Другой способ обнаружения удаления приложения - использовать собственный код.

Вам необходимо отслеживать для своего каталога, используя структуру inotify в forked-процессе.

При удалении Вы можете запустить некоторую системную команду, например. am, которая запускает Intent

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

Ответ 4

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

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

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

enter image description here

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