web-dev-qa-db-ja.com

アプリは、アンインストールされることをどのように検出できますか?

アンインストール前の通常の(実際には)アンチウイルスアプリケーションが、「アプリをアンインストールするのですか?」のような単純なダイアログを起動するために使用されたことを知っています。 - "はい・いいえ"。

はい、次のような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>

しかし、問題はこれがインターセプトany削除リクエストをインターセプトし、さらにこれがアプリとストックインストーラーの間で選択ダイアログを起動するという事実です。そのため、ユーザーが在庫インストーラーを選択した場合、何もできません。

私の目標は、ユーザーがアプリをアンインストールできないようにすることではなく、アプリによって加えられた変更をロールバックすることです。

これらのウイルス対策アプリから学ぶと、この種の操作は可能だと思うので、私を助けてどのようにできるのか説明してください。

更新

それが本当だと信じていない人がいるので、私は Avast Mobile Security を参照します:

盗難防止機能は、さまざまな自己保存技術を使用してコンポーネントを偽装することにより、アンインストールから保護します。

別の例:Kaspersky Internet Security for Android- これをアンインストールするための特別な手順 )。秘密コードの入力が必要です。

とにかく、アンインストールを防止するか、何らかのファイナライズジョブを実行するために、アンインストール手順を傍受する方法があることを意味します。

65
barmaley

はい。私は2日からこの問題について多くのことを調査しており、デバイスをルート化せずにそれを解決する「野生の方法」を最終的に見つけました:)

まず、ソリューションを実現するためのハイライトを以下に示します。

1。ユーザーが行くたびに設定->アプリの管理->特定のアプリケーションを選択ブロードキャストを受信Android.intent.action.QUERY_PACKAGE_RESTART withエキストラとしてのアプリケーションのパッケージの名前。

2。その後、アンインストールボタン(パッケージインストーラーを使用)をクリックすると、com.Android.packageinstaller.UninstallerActivityという名前のアクティビティが開きます。 =

制御フローは次のようになります:

ユーザーが[アンインストール]ボタンをクリックするアプリ設定の下で--->ダイアログを表示したり、別のアクティビティを開始したりするなどの制御を取得--->アンインストール前のタスクを終了--->ユーザーが戻るアンインストール確認画面--->ユーザーがアプリを確認してアンインストールする

使用方法:

アクション "Android.intent.action.QUERY_PACKAGE_RESTART"をリッスンするためにアプリケーションにBroadcastReceiverを実装し、onReceive()メソッド内でパッケージ名と一致させます。目的のアプリケーションパッケージの選択のためにブロードキャストが受信された場合、ActivityManagerを使用してフォアグラウンドで実行中のアクティビティを監視し続けるバックグラウンドスレッドを開始します。

フォアグラウンドアクティビティが「com.Android.packageinstaller.UninstallerActivity」であることがわかると、ユーザーがアプリケーションをアンインストールすることを確認します。この時点で、アンインストールの前に実行する必要なタスク(ダイアログを表示するか、アンインストールウィンドウに重なる別のアクティビティを開始するなど)を実行します。タスクを実行した後、ユーザーがアンインストールプロセスの確認を続行できるようにします。

実装/ソースコード:

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

ninstallIntentReceiver.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-フォアグラウンドアクティビティの監視用

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

既知の制限:

ユーザーが[アプリの設定の管理]の下の[アンインストール]ボタンをクリックすると、アンインストール前のタスクが実行され、アンインストールの確認または確認ができる確認ウィンドウが表示されますキャンセル操作。

上記のアプローチは、タスクを実行した後、ユーザーがCancelボタンをクリックした場合に対応しません。しかし、これはいくつかの修正によって簡単に取り組むことができます。

例:ブロードキャスト「Android.intent.action.PACKAGE_REMOVED」が最後に受信されなかった場合、行った変更を元に戻すロジックを実装できます。

このアプローチがあなたに役立つことを願っています:)これは私の意見では唯一の方法であるため、デバイスをルート化せずに問題を解決できます!

[Update 1]:アンインストールタスクがあったかどうかを確認するための推奨アプローチCanceled

ちょっと変わっていて、以前はまったく異なっていて非常に複雑なアイデア(放送、ActivityManagerなどを含む)がありましたが、ここに書いている間に、もう1つのアイデアが思いつきました。

ユーザーが[アプリの管理]設定の[アンインストール]ボタンをクリックし、アンインストール前のタスクを実行した後、アンインストール前のタスクを実行し、アンインストールの準備ができたSharedPreferenceをアプリに設定します。この後、何も気にする必要はありません。

ユーザーがアンインストールを続行する場合->必要なタスクを既に実行しているため、その状態は良好です。

一方、ユーザーが最終的にCancelボタンをクリックして消えた場合->気にしないでください。ユーザーがアプリケーションを再度実行するまで。これで、アプリケーションのメインアクティビティの「onStart()」/「onResume()」内で、SharedPreferenceの値を確認でき、アンインストール用に設定されていた場合は、ユーザーが最終的にアンインストールを続行しなかったことを意味します。また、以前に行った変更を元に戻して(実行されるアンインストール前のタスクを元に戻す)、アプリケーションが完全に実行されるようにすることができます!

135
AnniJais

それは単にAndroidでは不可能です

アプリケーションが(カーネルを変更せずに)アンインストールされていることを知る方法はありません。 data/data/your.app.packageで作成されたすべてのファイルは、インストール時に自動的に削除されます。

別のアプローチとして、このアプリケーションがインストールされているかどうかを確認する別のアプリケーションを使用することもできます。そうでない場合は、クリーンアップ作業を実行できます。

[〜#〜] update [〜#〜]

ACTION_PACKAGE_REMOVEDインテントは、自分以外のすべての受信者に送信されます。これは確認済みです [〜#〜] here [〜#〜]

更新2

ちょうど別の考え。

これを検索したときに、アプリケーションのlogcatを監視することでこれを行うことができることがわかりました here はサンプルのlogcatモニターです

良いことは、同じアプリケーションのlogcatを監視するために、ルート化されたデバイスが必要ないことです

logcatの各エントリを読み取ると、次の文字列を検索できます

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

このイベントが受信されると、アプリがアンインストールされることがわかります

試しませんでした

繰り返しますが、logcatの監視はAndroid Jellybean

7
Android Fanatic

最大Android 5.0では、アプリのアンインストールがネイティブコードを使用していることを検出するオプションがありました。

分岐プロセスでinotifyフレームワークを使用して、ディレクトリを監視する必要があります。削除されたときいくつかのシステムコマンドを実行できます。 amを開始するIntentコマンド

そのようなソリューションのPoC: https://github.com/pelotasplus/ActionAfterUninstall/blob/master/app/src/main/jni/hello-jni.c

7
pixel

アプリを永続化するには、ルート化されたデバイスが必要で、システムパーティションにインストールできる必要があります。更新プログラムがそこにあると、システム以外のアプリと一緒に保存されるので、更新プログラムをアンインストールできますが、システムからアンインストールするのはそれほど乾燥していません。

デバイスの一部が工場出荷時の状態にリセットされた場合に備えて、それらの一部はシステムパーティションに少しのデータも保存することを知っていますが、パッケージマネージャーがアンインストールされた場合に保存されたデータを残す方法もあります。

別のオプションは、デバイス管理者として登録することです。それを行うと、管理者ステータスを手動で削除しない限り、アンインストールできなくなります。

enter image description here

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

ここでは、他の方法と同様にルートを使用しているように見えます。彼らが持っているかもしれないと思われるクレイジーで精巧なサービスを作る以外には、これを他の方法で行う正当な方法はありません。

ルートの利点を活用することは、このような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>
1
Jon