web-dev-qa-db-fra.com

installer/désinstaller des APK par programme (PackageManager vs Intents)

Mon application installe d’autres applications et doit savoir quelles applications elle a installées. Bien entendu, cela pourrait être réalisé simplement en conservant une liste des applications installées. Mais cela ne devrait pas être nécessaire! PackageManager devrait avoir la responsabilité de maintenir la relation installedBy (a, b). En fait, selon l’API, c’est:

résumé public String getInstallerPackageName(String packageName) - Récupérez le nom du package de l'application qui a installé un package. Ceci identifie le marché d'où provient le paquet.

L'approche actuelle

Installer l'APK en utilisant l'intention

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

Désinstallez APK en utilisant Intent:

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

Ce n’est évidemment pas le cas, par exemple. Android Market installe/désinstalle les packages. Ils utilisent une version plus riche de PackageManager. Cela peut être vu en téléchargeant le code source Android à partir du référentiel Android Git. Vous trouverez ci-dessous les deux méthodes cachées correspondant à l’approche Intention. Malheureusement, ils ne sont pas disponibles pour les développeurs externes. Mais peut-être qu'ils seront dans le futur?

La meilleure approche

Installation de APK à l'aide de 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);

Désinstallation de APK à l'aide de PackageManager

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

Différences

  • Lors de l'utilisation d'intents, le gestionnaire de package local n'est pas informé de l'application à l'origine de l'installation. Plus précisément, getInstallerPackageName (...) renvoie null.

  • La méthode cachée installPackage (...) prend le nom du package d'installation en tant que paramètre et est probablement capable de définir cette valeur.

Question

Est-il possible de spécifier le nom du programme d'installation du package à l'aide d'intents?(Peut-être que le nom du paquet d'installation peut être ajouté en plus de l'intention de l'installation?)

Conseil: Si vous souhaitez télécharger le code source Android, suivez les étapes décrites ci-après: Téléchargement de l’arbre source. Pour extraire les fichiers * .Java et les mettre dans des dossiers en fonction de la hiérarchie des paquets, vous pouvez consulter ce script soigné: Afficher le code source Android dans Eclipse .

134
Håvard Geithus

Ce n'est pas actuellement disponible pour les applications tierces. Notez que même l’utilisation de réflexions ou d’autres astuces pour accéder à installPackage () n’aidera pas, car seules les applications système peuvent l’utiliser. (En effet, il s’agit du mécanisme d’installation de bas niveau, une fois que les autorisations ont été approuvées par l’utilisateur, il est donc dangereux pour les applications classiques d’y avoir accès.)

De plus, les arguments de la fonction installPackage () ont souvent changé d'une version à l'autre de la plate-forme. Par conséquent, si vous essayez d'y accéder, toutes les versions de la plate-forme échouent.

MODIFIER:

Il est également intéressant de noter que cet installateurPackage n’a été ajouté que récemment à la plate-forme (2.2?) Et n’a pas été utilisé à l’origine pour le suivi de l’installation de l’application - il est utilisé par la plate-forme pour déterminer qui lancer lorsqu’il signale l'application, pour la mise en œuvre Android Feedback. (C’était également l’une des circonstances dans lesquelles les arguments de la méthode API ont été modifiés.) Pendant au moins un certain temps après son introduction, Market ne l’utilisait toujours pas pour suivre les applications qu’il avait installées (et il se peut très bien qu’il ne l’utilise toujours pas. ), mais l’a simplement utilisé pour définir l’application Android Feedback (distincte de Market) en tant que "propriétaire" pour prendre en charge les commentaires.

66
hackbod

[Désinstaller]

Que diriez-vous:

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

pour la désinstallation. Cela semble plus facile ...

65
JohnyTex

L'API de niveau 14 a introduit deux nouvelles actions: ACTION_INSTALL_PACKAGE et ACTION_UNINSTALL_PACKAGE . Ces actions vous permettent de passer EXTRA_RETURN_RESULT boolean extra pour obtenir une notification du résultat de (dés) installation.

Exemple de code pour appeler la boîte de dialogue de désinstallation:

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

Et recevez la notification dans votre Activité # onActivityResult méthode:

@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

Si vous disposez d'une autorisation de propriétaire de périphérique (ou de propriétaire de profil, je n'ai pas essayé), vous pouvez installer/désinstaller des packages en mode silencieux à l'aide de l'API du propriétaire de périphérique.

pour la désinstallation:

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

et pour installer le paquet:

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

Le seul moyen d'accéder à ces méthodes est la réflexion. Vous pouvez obtenir un descripteur sur un objet PackageManager en appelant getApplicationContext().getPackageManager() et en utilisant la méthode d'accès par réflexion. Commander ceci tutoriel.

4
HandlerExploit

Selon le code source de Froyo, la clé supplémentaire Intent.EXTRA_INSTALLER_PACKAGE_NAME est demandée pour connaître le nom du package du programme d'installation dans PackageInstallerActivity.

3
njzk2

Sur un appareil enraciné, vous pouvez utiliser:

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() est défini ici.

Si vous transmettez le nom du paquet en tant que paramètre à l'une de vos fonctions définies par l'utilisateur, utilisez le code ci-dessous:

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

Prérequis:

Votre fichier APK doit être signé par le système, comme indiqué précédemment. Une façon d'y parvenir est de construire vous-même l'image AOSP et d'ajouter le code source à la construction.

Code:

Une fois installée en tant qu'application système, vous pouvez utiliser les méthodes du gestionnaire de packages pour installer et désinstaller un APK comme suit:

Installer:

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

Désinstaller:

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

Pour avoir un rappel une fois que votre APK est installé/désinstallé, vous pouvez utiliser ceci:

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

Si vous utilisez Kotlin, API 14+, et souhaitez simplement afficher la boîte de dialogue de désinstallation de votre application:

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

Vous pouvez remplacer packageName par tout autre nom de package si vous souhaitez inviter l'utilisateur à désinstaller une autre application du périphérique.

0
Louis CAD