プログラムによるAPKのインストール/アンインストール(PackageManagerとIntents)


141

私のアプリケーションは他のアプリケーションをインストールし、インストールしたアプリケーションを追跡する必要があります。もちろん、これは、インストールされているアプリケーションのリストを保持するだけで実現できます。しかし、これは必要ないはずです!installedManager(a、b)関係を維持するのはPackageManagerの責任です。実際、APIによると、

public abstract String getInstallerPackageName(String packageName)- パッケージをインストールしたアプリケーションのパッケージ名を取得します。これは、パッケージがどの市場から来たかを識別します。

現在のアプローチ

インテントを使用してAPKをインストールする

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);

インテントを使用してAPKをアンインストールします。

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);

これは明らかにAndroid Marketがパッケージをインストール/アンインストールする方法ではありません。それらは、PackageManagerのより豊富なバージョンを使用します。これは、AndroidソースコードをAndroid Gitリポジトリからダウンロードすることで確認できます。以下は、インテントアプローチに対応する2つの非表示メソッドです。残念ながら、外部の開発者はそれらを利用できません。しかし、おそらくそれらは将来的になるでしょうか?

より良いアプローチ

PackageManagerを使用したAPKのインストール

/**
 * @hide
 * 
 * Install a package. Since this may take a little while, the result will
 * be posted back to the given observer.  An installation will fail if the calling context
 * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
 * package named in the package file's manifest is already installed, or if there's no space
 * available on the device.
 *
 * @param packageURI The location of the package file to install.  This can be a 'file:' or a
 * 'content:' URI.
 * @param observer An observer callback to get notified when the package installation is
 * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
 * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
 * @param installerPackageName Optional package name of the application that is performing the
 * installation. This identifies which market the package came from.
 */
public abstract void installPackage(
        Uri packageURI, IPackageInstallObserver observer, int flags,
        String installerPackageName);

PackageManagerを使用したAPKのアンインストール

/**
 * Attempts to delete a package.  Since this may take a little while, the result will
 * be posted back to the given observer.  A deletion will fail if the calling context
 * lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
 * named package cannot be found, or if the named package is a "system package".
 * (TODO: include pointer to documentation on "system packages")
 *
 * @param packageName The name of the package to delete
 * @param observer An observer callback to get notified when the package deletion is
 * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #DONT_DELETE_DATA}
 *
 * @hide
 */
public abstract void deletePackage(
        String packageName, IPackageDeleteObserver observer, int flags);

違い

  • インテントを使用する場合、ローカルパッケージマネージャーは、インストール元のアプリケーションを認識しません。具体的には、getInstallerPackageName(...)はnullを返します。

  • 非表示のメソッドinstallPackage(...)は、パラメーターとしてインストーラーパッケージ名を受け取り、ほとんどの場合、この値を設定できます。

質問

インテントを使用してパッケージインストーラー名を指定することは可能ですか? (おそらく、インストーラーパッケージの名前をインストールインテントの追加として追加できますか?)

ヒント:Androidソースコードをダウンロードしたい場合は、ここで説明されている手順に従ってください:ソースツリーのダウンロード。* .javaファイルを抽出し、パッケージ階層に従ってフォルダーに配置するには、次のきちんとしたスクリプトを確認できます。EclipseでAndroidソースコードを表示する


一部のURIがテキストにありません。許可され次第、追加します(新しいユーザーにはスパムを防ぐためにいくつかの制限があります)。
HåvardGeithus

1
アンインストール機能を無効にする方法は?

2
@ user938893:「アンインストール機能を無効にする方法」-アンインストールが難しいマルウェアに取り組んでいますか?
ダニエル

回答:


66

これは現在、サードパーティのアプリケーションでは利用できません。リフレクションやその他のトリックを使用してinstallPackage()にアクセスしても、システムアプリケーションだけが使用できるため、役に立ちません。(これは、権限がユーザーによって承認された後の低レベルのインストールメカニズムであるため、通常のアプリケーションがアクセスすることは安全ではありません。)

また、installPackage()関数の引数はプラットフォームのリリース間で頻繁に変更されているため、アクセスしようとする他のさまざまなバージョンのプラットフォームでは失敗します。

編集:

また、このinstallerPackageはごく最近プラットフォーム(2.2?)に追加されただけで、もともと実際にアプリをインストールしたユーザーを追跡するために使用されなかったことを指摘する価値があります。 Androidフィードバックを実装するためのアプリ。(これは、APIメソッドの引数が変更されたときの1つでもありました。)導入されてから少なくとも長い間、Marketはそれを使用して、インストールしたアプリを追跡していませんでした(そして、使用しない可能性が非常に高いです) )が、代わりにこれを使用して、Androidフィードバックアプリ(マーケットとは別のもの)を「所有者」として設定してフィードバックを処理しました。


「リフレクションやその他のトリックを使用してinstallPackage()にアクセスしても、システムアプリケーションだけが使用できるため、役に立ちません。」ネイティブのAndroid自体ではなく、特定のプラットフォーム用のパッケージインストール/削除/管理アプリを作成しているとします。インストール/削除にアクセスするにはどうすればよいですか?
dascandy

適切に形成されたインテントを持つstartActivity()。(私はこれがStackOverflowの他の場所で回答されていると確信しているので、何かが間違ってしまう危険を冒して、ここで正確な答えを出そうとはしません。)
hackbod

mmmkay、標準のAndroidインストール/削除ダイアログが表示されます。これらの詳細はすでに処理されています。「****このパッケージをインストールするだけ」と「****このパッケージを削除するだけ」の機能を探しています。文字通り質問はありません。
dascandy

2
すでに述べたように、これらはサードパーティのアプリケーションでは使用できません。独自のシステムイメージを作成している場合は、プラットフォームの実装があり、そこに関数を見つけることができますが、それらは通常のサードパーティアプリで使用できるAPIの一部ではありません。
ハックボード、

インストール、削除、バックアップ機能を備えたapkファイルエクスプローラーを作っているので、Googleはアプリケーションをgoogle playで公開し続けることを許可していますか?そして、どの方針を破るのか?
Rahul Mandaliya 2014年

85

Android P +では、AndroidManifest.xmlでこの権限が必要です

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

次に:

Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:com.example.mypackage"));
startActivity(intent);

アンインストールします。簡単に思える...


これはコードを実行しているアプリですか?onDestroy()メソッドのように?
Mahdi-Malv 2017年

ACTION_INSTALL_PACKAGEはどうですか?Playストアから最新バージョンのアプリをダウンロードしてインストールできますか?
MAS。ジョン

3
あなたは「ACTION_DELETE」または「ACTION_UNINSTALL_PACKAGE」を使用する場合のAndroid P、アプリを削除すると、マニフェスト許可「android.permission.REQUEST_DELETE_PACKAGES」に関係なく必要とするのでdeveloper.android.com/reference/android/content/...
Darklord5

Android Pの許可について言及していただきありがとうございます。私は行き詰まり、以前何が起こっていたのかわかりませんでした。
Avi

43

APIレベル14では、ACTION_INSTALL_PACKAGEおよびACTION_UNINSTALL_PACKAGEという 2つの新しいアクションが導入されました。これらのアクションにより、EXTRA_RETURN_RESULTブール値エクストラを渡して、(アン)インストール結果の通知を取得できます。

アンインストールダイアログを呼び出すコードの例:

String app_pkg_name = "com.example.app";
int UNINSTALL_REQUEST_CODE = 1;

Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);  
intent.setData(Uri.parse("package:" + app_pkg_name));  
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, UNINSTALL_REQUEST_CODE);

そして、Activity#onActivityResultメソッドで通知を受け取ります。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == UNINSTALL_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            Log.d("TAG", "onActivityResult: user accepted the (un)install");
        } else if (resultCode == RESULT_CANCELED) {
            Log.d("TAG", "onActivityResult: user canceled the (un)install");
        } else if (resultCode == RESULT_FIRST_USER) {
            Log.d("TAG", "onActivityResult: failed to (un)install");
        }
    }
}

このアクションダイアログボックスから、ユーザーが[ok]または[キャンセル]を押したことを確認して、これに基づいて決定できるようにする方法
Erum

2
@Erum私はあなたが尋ねたものの例を追加しました
Alex Lipov

インストール時に、キャンセルボタンがonActivityResultメソッドに結果を返さなかった
diyoda_

2
API 25以降、呼び出しACTION_INSTALL_PACKAGEには署名レベルのREQUEST_INSTALL_PACKAGES権限が必要です。同様に、API 28(Android P)以降でACTION_UNINSTALL_PACKAGEは、呼び出しには危険でないREQUEST_DELETE_PACKAGES権限が必要になります。少なくともドキュメントによると。
Steve Blackwell

22

デバイス所有者(またはプロファイル所有者、私は試していません)の権限がある場合、デバイス所有者APIを使用してパッケージをサイレントインストール/アンインストールできます。

アンインストール用:

public boolean uninstallPackage(Context context, String packageName) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        int sessionId = 0;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        packageInstaller.uninstall(packageName, PendingIntent.getBroadcast(context, sessionId,
                new Intent("android.intent.action.MAIN"), 0).getIntentSender());
        return true;
    }
    System.err.println("old sdk");
    return false;
}

そしてパッケージをインストールするには:

public boolean installPackage(Context context,
                                     String packageName, String packagePath) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        try {
            int sessionId = packageInstaller.createSession(params);
            PackageInstaller.Session session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(packageName + ".apk", 0, -1);
            readTo(packagePath, out); //read the apk content and write it to out
            session.fsync(out);
            out.close();
            System.out.println("installing...");
            session.commit(PendingIntent.getBroadcast(context, sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    System.err.println("old sdk");
    return false;
}

所有者のデバイスでこれを行うことが可能でなければならないことを知っていました。答えてくれてありがとう!
Luke Cauthen

@sandeepこれはAPKのコンテンツを出力ストリームに読み込むだけです
Ohad Cohen

@LukeCauthenは、デバイスの所有者であることを試してみましたか?うまくいきましたか?
NetStarter 2017

@NetStarterはい、あります。アプリをデバイス所有者にするのはお尻の痛みです。これを実行すると、通常はrootを必要とする多くの力を手に入れます。
Luke Cauthen 2017

1
アンインストールを機能させるには、android.permission.DELETE_PACKAGESをマニフェストに追加する必要があることに注意してください(Apiレベル22以下でテスト済み)
ベンチク

4

これらのメソッドにアクセスする唯一の方法は、リフレクションを使用することです。これらのメソッドにリフレクションアクセスをPackageManager呼び出しgetApplicationContext().getPackageManager()て使用することにより、オブジェクトのハンドルを取得できます。チェックアウトこのチュートリアルを。


これは2.2ではうまく機能しますが、2.3ではうまくいきませんでした
Someone Somewhere

3
リフレクションはすべてのAPIバージョン全体で安定していません
HandlerExploit

3

Froyoソースコードによると、Intent.EXTRA_INSTALLER_PACKAGE_NAME追加キーは、PackageInstallerActivityでインストーラーパッケージ名を照会されます。


1
このコミットを見ると、
うまくいく

2

ルート権限を取得されたデバイスでは、次のものを使用できます。

String pkg = context.getPackageName();
String shellCmd = "rm -r /data/app/" + pkg + "*.apk\n"
                + "rm -r /data/data/" + pkg + "\n"
                // TODO remove data on the sd card
                + "sync\n"
                + "reboot\n";
Util.sudo(shellCmd);

Util.sudo() ここで定義されます。


ダウンロード済みのアプリケーションをSDカードにインストールする方法はありますか?または、Androidプラットフォームのシェルで使用できるコマンドを確認するために、いくつかのページを提案していただけますか?
yahya

1
@yahya developer.android.com/tools/help/shell.html "pm android"というフレーズで見つかりました、pm = package manager
18446744073709551615


どうもありがとう!これらのリンクは本当に:)で開始するようにガイドを冷却している
ヤヒヤ

@ V.Kalyuzhnyu 2015年に使用されていました。IIRCは、Samsung Galaxy、おそらくS5でした。
18446744073709551615 2017

2

パッケージ名をパラメーターとしてユーザー定義関数のいずれかに渡す場合は、以下のコードを使用します。

    Intent intent=new Intent(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+packageName));
    startActivity(intent);

0

Kotlin、API 14以降を使用していて、アプリのアンインストールダイアログを表示したい場合:

startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply {
    data = Uri.parse("package:$packageName")
})

packageNameデバイス上の別のアプリをアンインストールするようにユーザーに要求する場合は、他のパッケージ名に変更できます


0

前提条件:

以前に正しく指摘されているように、APKはシステムによって署名される必要があります。これを実現する1つの方法は、AOSPイメージを自分でビルドし、ビルドにソースコードを追加することです。

コード:

システムアプリとしてインストールしたら、パッケージマネージャーのメソッドを使用して、次のようにAPKをインストールおよびアンインストールできます。

インストール:

public boolean install(final String apkPath, final Context context) {
    Log.d(TAG, "Installing apk at " + apkPath);
    try {
        final Uri apkUri = Uri.fromFile(new File(apkPath));
        final String installerPackageName = "MyInstaller";
        context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

アンインストール:

public boolean uninstall(final String packageName, final Context context) {
    Log.d(TAG, "Uninstalling package " + packageName);
    try {
        context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

APKがインストール/アンインストールされたらコールバックするには、これを使用できます:

/**
 * Callback after a package was installed be it success or failure.
 */
private class InstallObserver implements IPackageInstallObserver {

    @Override
    public void packageInstalled(String packageName, int returnCode) throws RemoteException {

        if (packageName != null) {
            Log.d(TAG, "Successfully installed package " + packageName);
            callback.onAppInstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to install package.");
            callback.onAppInstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback after a package was deleted be it success or failure.
 */
private class DeleteObserver implements IPackageDeleteObserver {

    @Override
    public void packageDeleted(String packageName, int returnCode) throws RemoteException {
        if (packageName != null) {
            Log.d(TAG, "Successfully uninstalled package " + packageName);
            callback.onAppUninstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to uninstall package.");
            callback.onAppUninstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback to give the flow back to the calling class.
 */
public interface InstallerCallback {
    void onAppInstalled(final boolean success, final String packageName);
    void onAppUninstalled(final boolean success, final String packageName);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.