web-dev-qa-db-fra.com

Installer des applications en mode silencieux, avec l'autorisation INSTALL_PACKAGES accordée

J'essaie d'installer silencieusement apk dans le système. Mon application se trouve dans/system/app et l'autorisation "Android.permission.INSTALL_PACKAGES" a été accordée.

Cependant, je ne trouve nulle part comment utiliser cette autorisation. J'ai essayé de copier des fichiers dans/data/app et sans succès. J'ai aussi essayé d'utiliser ce code

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(
            Uri.parse("file:///sdcard/app.apk"),
            "application/vnd.Android.package-archive");
    startActivity(intent);

Mais ce code ouvre la boîte de dialogue d'installation standard. Comment puis-je installer l'application en mode silencieux sans root avec Android.permission.INSTALL_PACKAGES accordé?

Post-scriptum J'écris une application qui va installer de nombreux apks de dossier dans le système au premier démarrage (remplace l'assistant d'installation). J'en ai besoin pour alléger le firmware. 

Si vous pensez que je suis en train d'écrire un virus: Tous les programmes sont installés dans/data/app. Permission Install_packages ne peut être accordé qu'aux programmes système situés dans/system/app ou signés avec la clé système. Donc, le virus ne peut pas y arriver.

Comme dit http://www.mail-archive.com/[email protected]/msg06281.html apps PEUT être installé en mode silencieux s'ils ont la permission install_packages. De plus, vous n'avez pas besoin de l'autorisation Install_packages pour installer des packages de manière silencieuse. Plus http://www.androidzoom.com/Android_applications/tools/silent-installer_wgqi.html

82
POMATu

Votre premier pari est de regarder dans le fichier natif d'Android PackageInstaller . Je recommanderais de modifier cette application comme bon vous semble ou d'extraire simplement les fonctionnalités requises. 


Plus précisément, si vous examinez PackageInstallerActivity et sa méthode onClickListener:

 public void onClick(View v) {
    if(v == mOk) {
        // Start subactivity to actually install the application
        Intent newIntent = new Intent();
        ...
        newIntent.setClass(this, InstallAppProgress.class);
        ...
        startActivity(newIntent);
        finish();
    } else if(v == mCancel) {
        // Cancel and finish
        finish();
    }
}

Vous remarquerez ensuite que le programme d’installation actuel se trouve dans InstallAppProgress class. En inspectant cette classe, vous constaterez que initView est la fonction principale de l’installateur et que la dernière chose qu'il fait est d’appeler la fonction PackageManager's installPackage:

public void initView() {
...
pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
}

La prochaine étape consiste à inspecter PackageManager , qui est une classe abstraite. Vous y trouverez la fonction installPackage(...). La mauvaise nouvelle est qu'il est marqué avec @hide. Cela signifie que ce n'est pas directement disponible (vous ne pourrez pas compiler avec un appel à cette méthode).

 /**
  * @hide
  * ....
  */
  public abstract void installPackage(Uri packageURI,
             IPackageInstallObserver observer, 
             int flags,String installerPackageName); 

Mais vous pourrez accéder à ces méthodes via la réflexion.

Si vous êtes intéressé par la manière dont la fonction PackageManager de installPackage est implémentée, consultez PackageManagerService .

Résumé

Vous devrez obtenir un objet gestionnaire de paquets via Context's getPackageManager(). Ensuite, vous appellerez la fonction installPackage par réflexion. 

59
inazaruk

J'ai vérifié comment ADB installe les applications.
- Il copie l'APK dans/data/local/tmp
- il exécute 'Shell: pm install /data/local/tmp/app.apk'

J'ai essayé de reproduire ce comportement en faisant: (sur pc, en utilisant un câble USB)
adb Push app.apk /sdcard/app.apk
adb Shell
$ pm install /sdcard/app.apk
Cela marche. L'application est installée.

J'ai fait une application (nommée AppInstall) qui devrait installer l'autre application.
(installé normalement, périphérique non rooté)
Cela fait:
Runtime.getRuntime().exec("pm install /sdcard/app.apk").waitFor();
Mais ceci donne l'erreur:
Java.lang.SecurityException: Neither user 10019 nor current process has Android.permission.INSTALL_PACKAGES.
Il semble que l'erreur soit générée par pm, et non par AppInstall.
Parce que SecurityException n'est pas intercepté par AppInstall et que l'application ne se bloque pas.

J'ai essayé la même chose sur un appareil rooté (même application et AppInstall) et cela a fonctionné à merveille.
(Également installé normalement, pas dans/system ou quoi que ce soit)
AppInstall n'a même pas demandé la permission root.
Mais c’est parce que le shell est toujours # au lieu de $ sur ce périphérique.

Btw, vous avez besoin de root pour installer une application dans/system, correct?
J'ai essayé de remonter adb sur un périphérique non root et j'ai obtenu:
remount failed: Operation not permitted.
C'est pourquoi je ne pouvais pas essayer le système/sur le périphérique non rooté.

Conclusion: vous devez utiliser un périphérique rooté
J'espère que cela t'aides :)

12
FrankkieNL

Vous devriez définir 

<uses-permission
    Android:name="Android.permission.INSTALL_PACKAGES" />

dans votre manifeste, si vous êtes sur la partition système (/ system/app) ou si votre application est signée par le fabricant, vous aurez l'autorisation INSTALL_PACKAGES.

Ma suggestion est de créer un petit projet Android avec un niveau de compatibilité de 1,5 utilisé pour appeler installPackages via réflexion et pour exporter un fichier jar avec des méthodes permettant d'installer des packages et d'appeler les méthodes réelles . être prêt à installer des paquets.

8
iLRedEiMAtTi

J'ai récemment implémenté l'installation sans le consentement de l'utilisateur. Il s'agissait d'une application de kiosque pour API de niveau 21+ où j'avais le contrôle total de l'environnement.

Les exigences de base sont 

  • Niveau de l'API 21+ 
  • accès root pour installer le programme de mise à jour en tant qu'application privilégiée du système.

La méthode suivante lit et installe APK depuis InputStream:

public static boolean installPackage(Context context, InputStream in, String packageName)
            throws IOException {
        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        // set params
        int sessionId = packageInstaller.createSession(params);
        PackageInstaller.Session session = packageInstaller.openSession(sessionId);
        OutputStream out = session.openWrite("COSU", 0, -1);
        byte[] buffer = new byte[65536];
        int c;
        while ((c = in.read(buffer)) != -1) {
            out.write(buffer, 0, c);
        }
        session.fsync(out);
        in.close();
        out.close();

        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra("info", "somedata");  // for extra data if needed..

        Random generator = new Random();

        PendingIntent i = PendingIntent.getActivity(context, generator.nextInt(), intent,PendingIntent.FLAG_UPDATE_CURRENT);
            session.commit(i.getIntentSender());


        return true;
    }

Le code suivant appelle l'installation

 try {
     InputStream is = getResources().openRawResource(R.raw.someapk_source);
                    installPackage(MainActivity.this, is, "com.example.apk");
     } catch (IOException e) {
                    Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
     }

pour que tout fonctionne, vous avez désespérément besoin de l'autorisation INSTALL_PACKAGES, sinon le code ci-dessus échouera en silence

<uses-permission
        Android:name="Android.permission.INSTALL_PACKAGES" />

pour obtenir cette autorisation, vous devez installer votre APK en tant qu'application système nécessitant root (toutefois, APRÈS que vous ayez installé votre application de mise à jour, il semble fonctionner sans racine)

Pour installer en tant qu'application système, j'ai créé un fichier APK signé et l'ai poussé avec 

adb Push updater.apk /sdcard/updater.apk

puis déplacé vers system/priv-app - ce qui nécessite de remonter FS (raison pour laquelle la racine est requise)

adb Shell
su
mount -o rw,remount /system
mv /sdcard/updater.apk /system/priv-app
chmod 644 /system/priv-app/updater.apk

pour une raison quelconque, cela ne fonctionnait pas avec une version de débogage simple, mais logcat affiche des informations utiles si votre application dans priv-app n'est pas reprise pour une raison quelconque.

8
Boris Treukhov

J'ai essayé sur rooted Android 4.2.2 et cette méthode marche pour moi:

private void installApk(String filename) {
    File file = new File(filename); 
    if(file.exists()){
        try {   
            final String command = "pm install -r " + file.getAbsolutePath();
            Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command });
            proc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
     }
}
5
Vladimir

Je n'avais aucune idée de la façon de procéder, car personne n'a répondu à cette heure et je n'ai trouvé aucun document concernant cette autorisation. J'ai donc trouvé ma propre solution. C’est pire que le vôtre, mais c’est quand même une solution.

J'ai installé busybox, qui a défini l'autorisation 777 sur/data/app (je ne me soucie pas de la sécurité). Ensuite, vous venez d'exécuter "busybox install" à partir de l'application. Cela fonctionne, mais a une grande fuite de sécurité. Si vous définissez les autorisations 777, aucune racine n'est requise.

4
POMATu

Vous pouvez utiliser l'API masquée Android.content.pm.IPackageInstallObserver par réflexion:

public class PackageManagement {
    public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
    public static final int INSTALL_SUCCEEDED = 1;

    private static Method installPackageMethod;
    private static Method deletePackageMethod;

    static {
        try {
            installPackageMethod = PackageManager.class.getMethod("installPackage", Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void installPackage(PackageManager pm, Uri mPackageUri, IPackageInstallObserver observer, int installFlags, String installerPackageName) {
        try {
            installPackageMethod.invoke(pm, mPackageUri, observer, installFlags, installerPackageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Importez Android.content.pm.IPackageInstallObserver dans votre projet. Votre application doit être système. Vous devez activer l'autorisation Android.permission.INSTALL_PACKAGES dans votre fichier manifeste.

4
Hermann Poilpre

Vous pouvez simplement utiliser la commande adb install pour installer/mettre à jour APK en silence. Exemple de code ci-dessous

public static void InstallAPK(String filename){
    File file = new File(filename); 
    if(file.exists()){
        try {   
            String command;
            filename = StringUtil.insertEscape(filename);
            command = "adb install -r " + filename;
            Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command });
            proc.waitFor();
        } catch (Exception e) {
        e.printStackTrace();
        }
     }
  }
3
ZeeShaN AbbAs

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

===> Testé sur Android 8.1 et a bien fonctionné.

0
phoebus

Une application tierce ne peut pas installer une application Android de manière lente . Cependant, une application tierce peut demander au système d'exploitation Android d'installer une application.

Donc, vous devriez définir ceci:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file:///sdcard/app.apk", "application/vnd.Android.package-archive");
startActivity(intent);

Vous pouvez également essayer de l'installer en tant qu'application système pour accorder l'autorisation et ignorer cette définition. (Racine requise)

Vous pouvez exécuter la commande suivante sur votre application tierce pour installer une application sur le périphérique enraciné.

Le code est:

private void installApk(String filename) {
File file = new File(filename); 
if(file.exists()){
    try {   
        final String command = "pm install -r " + file.getAbsolutePath();
        Process proc = Runtime.getRuntime().exec(new String[] { "su", "-c", command });
        proc.waitFor();
    } catch (Exception e) {
        e.printStackTrace();
    }
 }
}

J'espère que cette réponse vous sera utile.

0
user9370880

Essayez ce LD_LIBRARY_PATH=/vendor/lib:/system/lib avant l’installation de pm. Ça marche bien.

0
Ankita Gujar

Il est possible de faire une installation silencieuse sur Android 6 et supérieur. En utilisant la fonction fournie dans la réponse de Boris Treukhov, ignorez tout le reste de la publication, root n’est pas requis non plus.

Installez votre application en tant qu'administrateur de périphérique. Vous pouvez utiliser le mode kiosque complet avec l'installation silencieuse des mises à jour en arrière-plan.

0
Raymie

J'ai créé une application de test pour les installations silencieuses à l'aide de la méthode PackageManager.installPackage.

J'obtiens la méthode installPackage par réflexion et ai créé l'interface Android.content.pm.IPackageInstallObserver dans mon dossier src (car elle est cachée dans le paquet Android.content.pm). 

Lorsque j'exécute installPackage, j'ai obtenu SecurityException avec une indication de chaîne, indiquant que mon application n'a pas d'Android.permission.INSTALL_PACKAGES, mais définie dans AndroidManifest.xml. 

Donc, je pense, il n'est pas possible d'utiliser cette méthode. 

PS. J'ai testé sur Android SDK 2.3 et 4.0. Peut-être que cela fonctionnera avec les versions précédentes.

J'ai vérifié toutes les réponses, la conclusion semble être que vous devez d'abord avoir un accès root à l'appareil pour le faire fonctionner.

Mais ensuite, j'ai trouvé ces articles très utiles. Depuis que je fabrique des appareils "appartenant à l'entreprise".

Comment mettre à jour l'application Android en silence, sans interaction de l'utilisateur

Propriétaire d'un appareil Android - Application minimale

Voici la documentation de Google sur "périphérique géré"

Dispositif entièrement géré

0
h--n