web-dev-qa-db-de.com

aPKs programmgesteuert installieren/deinstallieren (PackageManager vs Intents)

Meine Anwendung installiert andere Anwendungen, und es muss verfolgt werden, welche Anwendungen installiert wurden. Dies kann natürlich erreicht werden, indem einfach eine Liste der installierten Anwendungen geführt wird. Das sollte aber nicht nötig sein! Es sollte in der Verantwortung des PackageManagers liegen, die installierteBy-Beziehung (a, b) aufrecht zu erhalten. In der Tat ist es laut API:

public abstract String getInstallerPackageName(String packageName) - Rufen Sie den Paketnamen der Anwendung ab, die ein Paket installiert hat. Dies gibt an, aus welchem ​​Markt das Paket stammt.

Der aktuelle Ansatz

Installieren Sie APK mit Intent

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

Deinstallieren Sie APK mit Intent:

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

Dies ist offensichtlich nicht der Weg, z. Android Market installiert/deinstalliert Pakete. Sie verwenden eine umfangreichere Version des PackageManagers. Dies kann man sehen, wenn der Android-Quellcode aus dem Android-Git-Repository heruntergeladen wird. Nachfolgend sind die beiden versteckten Methoden aufgeführt, die dem Intent-Ansatz entsprechen. Leider stehen sie externen Entwicklern nicht zur Verfügung. Aber vielleicht werden sie in der Zukunft sein?

Der bessere Ansatz

Installieren von APK mit dem PackageManager

/**
 * @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);

APK mit dem PackageManager deinstallieren

/**
 * 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);

Unterschiede

  • Bei Verwendung von Absichten wird dem lokalen Paketmanager nicht mitgeteilt, von welcher Anwendung die Installation stammt. GetInstallerPackageName (...) gibt insbesondere null zurück.

  • Die verborgene Methode installPackage (...) verwendet den Namen des Installationspakets als Parameter und kann diesen Wert höchstwahrscheinlich festlegen.

Frage

Kann der Name des Paketinstallationsprogramms mit Absichten angegeben werden?(Vielleicht kann der Name des Installationspakets der Installationsabsicht zusätzlich hinzugefügt werden?)

Tipp: Wenn Sie den Android-Quellcode herunterladen möchten, führen Sie die folgenden Schritte aus: Herunterladen des Quellbaums. Um die * .Java-Dateien zu extrahieren und entsprechend der Pakethierarchie in Ordner zu legen, können Sie dieses übersichtliche Skript auschecken: View Source Code von Android in Eclipse .

134
Håvard Geithus

Dies ist derzeit nicht für Anwendungen von Drittanbietern verfügbar. Beachten Sie, dass selbst die Verwendung von Reflection oder anderen Tricks für den Zugriff auf installPackage () nicht hilfreich ist, da dies nur von Systemanwendungen verwendet werden kann. (Dies liegt daran, dass es sich um einen einfachen Installationsmechanismus handelt, nachdem die Berechtigungen vom Benutzer genehmigt wurden. Daher ist es für normale Anwendungen nicht sicher, auf sie zuzugreifen.)

Auch die Argumente der Funktion installPackage () haben sich häufig zwischen den Plattformversionen geändert. Wenn Sie also versuchen, auf sie zuzugreifen, schlägt dies bei verschiedenen anderen Versionen der Plattform fehl.

BEARBEITEN:

Erwähnenswert ist auch, dass dieses Installationspaket erst vor kurzem der Plattform hinzugefügt wurde (2.2?) Und ursprünglich nicht für die Nachverfolgung verwendet wurde, wer die App installiert hat. Es wird von der Plattform verwendet, um zu bestimmen, wer gestartet werden soll, wenn Fehler gemeldet werden die App, um Android Feedback zu implementieren. (Dies war auch einer der Zeitpunkt, zu dem sich die Argumente der API-Methode geändert haben.) Mindestens eine lange Zeit nach seiner Einführung wurde sie von Market immer noch nicht für die Verfolgung der installierten Apps verwendet (und möglicherweise auch nicht verwendet.) ), aber stattdessen nur verwendet, um die Android-Feedback-App (die von Market getrennt war) als "Eigentümer" festzulegen, um sich um Feedback zu kümmern.

66
hackbod

[Deinstallieren]

Wie wäre es mit:

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

für die Deinstallation. Scheint einfacher ...

65
JohnyTex

API-Ebene 14 führte zwei neue Aktionen ein: ACTION_INSTALL_PACKAGE und ACTION_UNINSTALL_PACKAGE . Mit diesen Aktionen können Sie EXTRA_RETURN_RESULT boolean extra übergeben, um eine Benachrichtigung über das (Un) Installationsergebnis zu erhalten.

Beispielcode zum Aufrufen des Deinstallationsdialogs:

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

Und erhalten Sie die Benachrichtigung in Ihrer Activity # onActivityResult -Methode:

@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");
        }
    }
}
36
Pir Fahim Shah

Wenn Sie über die Berechtigung Geräteeigentümer (oder Profilbesitzer, die ich nicht versucht habe) verfügen, können Sie Pakete mithilfe der Gerätebesitzer-API im Hintergrund installieren/deinstallieren.

zur Deinstallation:

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

und um das Paket zu installieren:

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;
}
19
Ohad Cohen

Die einzige Möglichkeit, auf diese Methoden zuzugreifen, ist die Reflexion. Sie können ein Handle für ein PackageManager-Objekt erhalten, indem Sie getApplicationContext().getPackageManager() aufrufen und auf diese Methoden zugreifen. Kasse dies Tutorial.

4
HandlerExploit

Gemäß dem Quellcode von Froyo wird der Zusatzschlüssel Intent.EXTRA_INSTALLER_PACKAGE_NAME für den Namen des Installationspakets in PackageInstallerActivity abgefragt.

3
njzk2

Auf einem gerooteten Gerät könnten Sie Folgendes verwenden:

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() wird hier definiert.

Wenn Sie den Paketnamen als Parameter an eine beliebige benutzerdefinierte Funktion übergeben, verwenden Sie den folgenden Code:

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

Voraussetzung:

Ihre APK muss vom System signiert werden, wie es zuvor richtig angegeben wurde. Eine Möglichkeit, dies zu erreichen, besteht darin, das AOSP-Image selbst zu erstellen und den Quellcode zum Build hinzuzufügen.

Code:

Nach der Installation als System-App können Sie die Paketmanager-Methoden verwenden, um einen APK wie folgt zu installieren und zu deinstallieren:

Installieren:

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

Deinstallation:

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

Für einen Rückruf nach der Installation/Deinstallation Ihres APK können Sie Folgendes verwenden:

/**
 * 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);
}
0
phoebus

Wenn Sie Kotlin API 14+ verwenden und nur das Deinstallationsdialogfeld für Ihre App anzeigen möchten:

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

Sie können packageName in einen beliebigen anderen Paketnamen ändern, wenn Sie den Benutzer auffordern möchten, eine andere App auf dem Gerät zu deinstallieren

0
Louis CAD