web-dev-qa-db-fra.com

Commande Installer/Désactiver à partir de Shell sous Android

Je souhaite implémenter un package d'installation à partir d'un fichier apk et un package unistaller silencieux sous Android. Le sujet a été largement discuté sur SO et ailleurs, mais je ne peux en appliquer aucune pour une raison qui me manque. La portée est évidemment difficile à atteindre car, en cas de succès, ce serait une grave atteinte à la sécurité sous Android. MAIS, je dois l’appliquer pour un projet spécial, pas pour le marché grand public. Il y a deux approches:

  1. générer un ROM personnalisé à partir d'un code source (mod AOSP ou Cyanogen, par exemple), en modifiant le programme d'installation de PackageManager (en fait, simplement pour supprimer les boîtes de dialogue d'acceptation de l'utilisateur).
  2. pour le faire par programme en créant un processus en tant que super utilisateur et en exécutant une "installation pm d'adb shell". J'ai précédemment installé 'su' dans/system/xbin et je teste pendant l'exécution que RootTools.rootIsAvailable ().

Pour le premier cas, j'ai fouillé dans le code source Froyo, mais je me suis retrouvé dans une impasse avec une méthode marquée @hide. Pour la seconde, j'ai d'abord essayé les commandes du terminal

adb Shell pm install /mnt/sdcard/HelloAndroid.apk

et

adb Shell pm uninstall com.example.helloandroid

Les deux fonctionnent bien. Ensuite, j'ai utilisé le code suivant, le développement étant testé sur un émulateur enraciné (2.2 - Froyo):

@Override
    public void onClick(View v) {
        switch (v.getId())
           {
              case R.id.btnInstall:
                  try {  
                      install = Runtime.getRuntime().exec("su\n");   
                      DataOutputStream os = new DataOutputStream(install.getOutputStream());
                      os.writeBytes("pm install /mnt/sdcard/HelloAndroid.apk\n"); 
                      os.writeBytes("exit\n"); 
                      os.flush();
                      install.waitFor();

                              if (install.exitValue() == 0) {  
                                  Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_LONG).show();
                              }  
                              else {  
                                  Toast.makeText(MainActivity.this, "Failure. Exit code: "+String.valueOf(install.exitValue()), Toast.LENGTH_LONG).show();
                              }
                  }
                  catch (InterruptedException e) {  
                      logError(e);
                  }
                  catch (IOException e) {  
                  logError(e);
                  } 
                  break;

              case R.id.btnUninstall:
                  try {
                      install = Runtime.getRuntime().exec("su\n"); 
                      install=Runtime.getRuntime().exec("pm uninstall "+txtPackageName.getText().toString()+"\n");

                } catch (Exception e) {
                    logError(e);
                }
                  break;
           }

    }

Pour éviter les fautes de frappe et autres corrections, j'ai codé en dur le paramètre de fichier apk de la commande pour l'installation; sur 'case R.id.btnInstall', la commande n'est pas exécutée et l'exit est sur "Failure" avec la valeur de sortie 1, ce qui signifie que "la classe ne peut pas être trouvée"; aucune idée de ce que cela signifie ... J'apprécie votre aide!

EDITED: J'ai la solution propre, je posterai la réponse de A à Z dès que j'en aurai le temps et le code sous la bonne forme !!

10
Ginger Opariti

Comme je vous l'avais promis, voici la solution à ce problème, sans forcer le système à installer le logiciel dans le répertoire/system/app. J'ai suivi, puis fait quelques corrections sur l'excellent article ici: http://paulononaka.wordpress.com/2011/07/02/how-to-install-a-application-in-background-on-Android/ . J'ai alors téléchargé le fichier Zip référencé dans l'article ((j'ai essayé de conserver les mêmes noms de classe, si possible)):

  1. créé un nouveau projet et une activité principale comme point d'entrée

package com.example.silentinstuninst;

import Java.io.File;
import Java.lang.reflect.InvocationTargetException;

import com.example.instuninsthelper.ApplicationManager;
import com.example.instuninsthelper.OnDeletedPackage;
import com.example.instuninsthelper.OnInstalledPackage;

import Android.os.Bundle;
import Android.os.Environment;
import Android.app.Activity;
import Android.util.Log;
import Android.view.View;
import Android.view.View.OnClickListener;
import Android.widget.Button;
import Android.widget.EditText;
import Android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

    Process install;
    Button btnInstall, btnUninstall;
    EditText txtApkFileName, txtPackageName; 

    public static final String TAG = "SilentInstall/Uninstall";

    private static ApplicationManager am;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initializeValues();

    }

    private void initializeValues() {

        btnInstall = (Button) findViewById(R.id.btnInstall);
        btnUninstall = (Button) findViewById(R.id.btnUninstall);
        txtApkFileName = (EditText) findViewById(R.id.txtApkFilePath);
        txtPackageName = (EditText) findViewById(R.id.txtPackageName);

        btnInstall.setOnClickListener(this);
        btnUninstall.setOnClickListener(this);

        try {
            am = new ApplicationManager(this);
            am.setOnInstalledPackage(new OnInstalledPackage() {

                public void packageInstalled(String packageName, int returnCode) {
                    if (returnCode == ApplicationManager.INSTALL_SUCCEEDED) {
                        Log.d(TAG, "Install succeeded");
                    } else {
                        Log.d(TAG, "Install failed: " + returnCode);
                    }
                }
            });

            am.setOnDeletedPackage(new OnDeletedPackage() {
                public void packageDeleted(boolean succeeded) {
                    Log.d(TAG, "Uninstall succeeded");  
                }
            });

        } catch (Exception e) {
            logError(e);
        }
    }

    private void logError(Exception e) {
        e.printStackTrace();
        Toast.makeText(this, R.string.error+e.getMessage(), Toast.LENGTH_LONG).show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId())
           {
              case R.id.btnInstall:
                  // InstallUninstall.Install(txtApkFileName.getText().toString());
            try {
                am.installPackage(Environment.getExternalStorageDirectory() +
                        File.separator + txtApkFileName.getText().toString());
            } catch (IllegalArgumentException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (InvocationTargetException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } // install package
                  break;

              case R.id.btnUninstall:
                  // InstallUninstall.Uninstall(txtPackageName.getText().toString());
            try {
                am.uninstallPackage(txtPackageName.getText().toString());
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                logError(e);
            }
                  break;
           }    
    }
}
  1. créez dans/src le paquet com.example.instuninsthelper . J'ai ajouté les fichiers ApplicationManager.Java et OnInstalledPackage.Java
  2. inséré le code suivant dans la classe ApplicationManager:

private OnDeletedPackage onDeletedPackage;
class PackageDeleteObserver extends IPackageDeleteObserver.Stub { 

        public void packageDeleted(boolean succeeded) throws RemoteException {
            if (onDeletedPackage != null) {
                onDeletedPackage.packageDeleted(succeeded);
            }
        }

    }
  1. créé, sous le même com.example.instuninsthelper emballe le fichier OnDeletedPackage.Java avec le code suivant:

package com.example.instuninsthelper;
public interface OnDeletedPackage {
    public void packageDeleted(boolean succeeded);
}
  1. dans le package Android.content.pm (l'espace de noms NE DEVRAIT PAS être modifié), j'ai modifié IPackageDeleteObserver.Java, avec le résultat suivant:

package Android.content.pm;

public interface IPackageDeleteObserver extends Android.os.IInterface {

    public abstract static class Stub extends Android.os.Binder implements Android.content.pm.IPackageDeleteObserver {
        public Stub() {
            throw new RuntimeException("Stub!");
        }

        public static Android.content.pm.IPackageDeleteObserver asInterface(Android.os.IBinder obj) {
            throw new RuntimeException("Stub!");
        }

        public Android.os.IBinder asBinder() {
            throw new RuntimeException("Stub!");
        }

        public boolean onTransact(int code, Android.os.Parcel data, Android.os.Parcel reply, int flags)
                throws Android.os.RemoteException {
            throw new RuntimeException("Stub!");
        }
    }

    public abstract void packageDeleted(boolean succeeded)
            throws Android.os.RemoteException;
}
  1. construire l'application dans Eclipse et la déployer sur l'émulateur
  2. dans l'émulateur: bouton d'accueil> Paramètres> applications> ... désinstaller l'application (car elle n'est pas installée dans/system/app, et nous n'avions besoin que de la génération du fichier apk)
  3. faites ce qui suit pour rooter l'émulateur (pour que nous puissions écrire dans/system/app; une autre solution que j'ai utilisée consiste à générer un ROM personnalisé avec cette application incluse dans/system/app) :
  4. dans la console, accédez au répertoire/bin du projet, puis entrez: * adb Push .apk/system/app
  5. enfin, toujours depuis la console, entrez: * adb Shell am start -n com.example.silentinstuninst/com.example.silentinstuninst.MainActivity
  6. prendre plaisir!
6
Ginger Opariti

Je ne sais pas, mais juste une idée:

Je pense que vous écrivez en standarout, n'exécutez pas de commande ni ne donnez plus de données au processus via son entrée. Je pense que cela devrait être:

Runtime.getRuntime().exec("pm install /mnt/sdcard/HelloAndroid.apk\n"); 

J'espère que cela t'aides.

1
inigoD

L'installation dans le répertoire /system/app est essentiellement la même chose que si vous avez besoin de root.

En supposant que vous ayez la racine, consultez RootTools . Ensuite, vous pouvez faire:

if (RootTools.isAccessGiven()) {
    CommandCapture command = new CommandCapture(0, "pm install " + PATH_TO_APK);
    RootTools.getShell(true).add(command).waitForFinish();
}

Notez que waitForFinish() est un appel bloquant!

1
nickaknudson

Vous pouvez aussi le faire directement avec le PackageManager (nécessite un accès root):

  • Créez une application avec un sdk de plate-forme disposant publiquement des interfaces (créez-la ou téléchargez-la, puis configurez Eclipse)
  • Dans l'application, appelez directement les fonctions d'API masquées qui permettent l'installation/la suppression silencieuse.
  • Installez l'APK sur votre appareil en tant qu'application système en le copiant dans/système/application (racine requise).

Voir ceci: http://forum.xda-developers.com/showthread.php?t=1711653

0
RvdK

Runtime.getRuntime (). Exec ("pm install /mnt/sdcard/HelloAndroid.apk\n");

Cela fonctionne pour moi, bien que deux autres détails supplémentaires doivent être apportés:

  1. Ajouter Android: sharedUserId = "Android.uid.system" dans AndroidManifest.xml.

  2. Signé l'apk avec la clé système.

    Mais de cette manière, il semble qu'il n'y ait aucun moyen de savoir si l'installation est réussie. Je vais donc essayer la méthode de @ Ginger plus tard.

0
yiyang

Pour tous ceux qui ont encore des problèmes: vous aurez besoin d’un appareil enraciné et vous utiliserez

Process result = Runtime.getRuntime().exec("pm install -r -d MyApp.apk /system/app")

Si vous obtenez le code de résultat 9 (code d'erreur 9), vous devez supprimer votre apk de l'appareil et le repousser (ne pas appuyer sur INSTAL!).

Allez à l'appareil Shell et appuyez sur l'apk

launcher=MyApp.apk
$adb Shell su -c "mount -o remount,rw -t rfs /dev/stl5 /system"
$adb Push $launcher /sdcard/$launcher
$adb Shell su -c "chmod 644 /system/app/$launcher"

Vous pouvez maintenant utiliser pm install sans erreur. J'espère que ça va aider quelqu'un.

0
jirkarrrr