Installa le app in silenzio, con l'autorizzazione INSTALL_PACKAGES concessa


86

Sto cercando di installare silenziosamente apk nel sistema. La mia app si trova in / system / app e ha ottenuto l'autorizzazione "android.permission.INSTALL_PACKAGES"

Tuttavia non riesco a trovare da nessuna parte come utilizzare questa autorizzazione. Ho provato a copiare i file in / data / app e non ho avuto successo. Inoltre ho provato a usare questo codice

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

Ma questo codice apre la finestra di dialogo di installazione standard. Come posso installare l'app in modo silenzioso senza root con concesso android.permission.INSTALL_PACKAGES?

PS Sto scrivendo un'app che installerà molti apk dalla cartella nel sistema al primo avvio (sostituisci Setup Wizard). Ne ho bisogno per rendere il firmware più leggero.

Se pensi che stia scrivendo un virus: tutti i programmi sono installati in / data / app. L'autorizzazione Install_packages può essere concessa solo ai programmi a livello di sistema che si trovano in / system / app o firmati con la chiave di sistema. Quindi il virus non può arrivarci.

Come detto http://www.mail-archive.com/android-porting@googlegroups.com/msg06281.html le app POSSONO essere installate silenziosamente se hanno l'autorizzazione install_packages. Inoltre non è necessario il permesso Install_packages per installare i pacchetti in modo non silenzioso. Inoltre http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html


9
@Fosco Supponi che stia realizzando un dispositivo come produttore di apparecchiature originali?
dascandy

41
@Fosco Chi se ne frega? È una domanda valida come descrive Dascandy. In genere è improduttivo quando le persone rispondono a domande come queste con "Non dovresti farlo". Che comprensione stiamo ottenendo con questo tipo di risposta?
ZachM

3
A proposito di improduttivo ... sei in ritardo di poco più di 2 anni per questa festa Zach, e la domanda sembra molto diversa da come era originariamente.
Fosco

2
@Fosco LOL. Inoltre posso confermare che funziona ancora nel nuovo sistema operativo Android.
POMA

1
@LarsH Ho appena installato l'app F-Droid: non installano le cose in silenzio, richiedono solo il gestore dei pacchetti di Android.
Joaquin Iurchuk

Risposte:


60

La tua prima scommessa è esaminare il PackageInstaller nativo di Android . Consiglierei di modificare l'app nel modo che preferisci o di estrarre la funzionalità richiesta.


In particolare, se guardi in PackageInstallerActivity e il suo metodo 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();
    }
}

Quindi noterai che il programma di installazione effettivo si trova nella classe InstallAppProgress . Ispezionando quella classe scoprirai che initViewè la funzione di installazione di base e l'ultima cosa che fa è chiamare PackageManagerla installPackagefunzione di:

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

Il prossimo passo è ispezionare PackageManager , che è una classe astratta. Troverai la installPackage(...)funzione lì. La cattiva notizia è che è contrassegnato con @hide. Ciò significa che non è direttamente disponibile (non sarai in grado di compilare con la chiamata a questo metodo).

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

Ma sarai in grado di accedere a questi metodi tramite la riflessione.

Se siete interessati a come PackageManager's installPackageè implementata la funzione, dare un'occhiata a PackageManagerService .

Sommario

Avrai bisogno di ottenere l'oggetto del gestore di pacchetti tramite Context's getPackageManager(). Quindi chiamerai la installPackagefunzione tramite la riflessione.


2
Aiuto! Questa tecnica si traduce in: Causato da: java.lang.SecurityException: né l'utente 10101 né il processo corrente hanno android.permission.INSTALL_PACKAGES. Anche se la mia app dispone di tale autorizzazione
gregm

13
La tua app richiede tale autorizzazione, ma Android non concede tale autorizzazione alle app di terze parti. Vedere LogCatper i registri relativi alle autorizzazioni.
inazaruk

@inazaruk ho letto che le app installate in / system / app otterranno automaticamente il permesso di usare install_pakages ma come descritto in questo post sembra non funzionare. puoi darmi qualche suggerimento?
Zorb

3
Come posso chiamare installPackage tramite reflection? in realtà non posso fare Method method = aPackageManagerClass.getMethod ("installPackage", new Class [] {Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class}); dato che anche IPackageInstallObserver è nascosto, qualcuno può aiutarmi a plese, postando il codice corretto per fare riflessione qui.
Shilaghae

7
Tutti i link nella risposta sono morti.
Mayur Gangurde

13

Ho verificato come ADB installa le app.
-
Copia l'APK in / data / local / tmp - esegue 'shell: pm install /data/local/tmp/app.apk'

Ho provato a replicare questo comportamento facendo: (su pc, usando il cavo USB)
adb push app.apk /sdcard/app.apk
adb shell
$ pm install /sdcard/app.apk
Funziona. L'app è installata.

Ho creato un'applicazione (denominata AppInstall) che dovrebbe installare l'altra app.
(installato normalmente, dispositivo non rooted) Sì
:
Runtime.getRuntime().exec("pm install /sdcard/app.apk").waitFor();
ma questo dà l'errore:
java.lang.SecurityException: Neither user 10019 nor current process has android.permission.INSTALL_PACKAGES.
sembra che l'errore sia stato lanciato da pm, non da AppInstall.
Perché SecurityException non viene rilevata da AppInstall e l'app non si arresta in modo anomalo.

Ho provato la stessa cosa su un dispositivo rooted (stessa app e AppInstall) e ha funzionato a meraviglia.
(Anche normalmente installato, non in / system o altro)
AppInstall non ha nemmeno chiesto il permesso di root.
Ma è perché la shell è sempre al #posto di $quel dispositivo.

A proposito, è necessario il root per installare un'app in / system, corretto?
Ho provato adb remount sul dispositivo non rootato e ho ottenuto:
remount failed: Operation not permitted.
Ecco perché non ho potuto provare la cosa / system sul dispositivo non rootato.

Conclusione: dovresti usare un dispositivo rooted
Spero che questo aiuti :)


Non riesco a farlo funzionare. È interessante che il comando funzioni bene nella shell ADB e quindi non sembra funzionare quando provo a eseguirlo a livello di programmazione.
saulpower

Sì, ho lo stesso problema: funziona in shell, ma non quando viene chiamato dalla mia app, anche se lo chiamo tramite su)
velis

Esiste un altro modo per lavorare sui dispositivi Google come ottenere le autorizzazioni di super amministratore?
Ramesh Kumar

Se desideri i permessi di super amministratore dovresti: 1) eseguire il root del tuo dispositivo, 2) avere l'app installata sulla partizione di sistema, 3) o l'app deve essere firmata con la stessa chiave del sistema operativo stesso.
FrankkieNL

1
Succede perché l'utente ADB e l'utente della tua applicazione hanno autorizzazioni diverse
PHPoenX

11

Recentemente ho implementato l'installazione senza il consenso dell'utente: si trattava di un'applicazione kiosk per API di livello 21+ in cui avevo il pieno controllo sull'ambiente.

I requisiti di base sono

  • Livello API 21+
  • accesso root per installare il programma di aggiornamento come app con privilegi di sistema.

Il metodo seguente legge e installa l'APK da 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;
    }

Il codice seguente chiama l'installazione

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

perché tutto funzioni, hai un disperato bisogno del INSTALL_PACKAGESpermesso, altrimenti il ​​codice sopra fallirà silenziosamente

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

per ottenere questa autorizzazione devi installare il tuo APK come applicazione di sistema che RICHIEDE root (tuttavia DOPO aver installato l'applicazione di aggiornamento sembra funzionare SENZA root)

Per installare come applicazione di sistema ho creato un APK firmato e l'ho inviato con

adb push updater.apk /sdcard/updater.apk

e poi spostato in system/priv-app- che richiede il rimontaggio di FS (questo è il motivo per cui è richiesta la radice)

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

per qualche motivo non ha funzionato con la semplice versione di debug, ma logcat mostra informazioni utili se la tua applicazione priv-appnon viene presa per qualche motivo.


"per qualche motivo non ha funzionato con la versione di debug semplice" Vuoi dire che ha funzionato in versione di rilascio, o qualche altra versione di debug meno "semplice"?
JM Lord

1
@ JMLord Ha funzionato con la versione di rilascio firmata e non ha funzionato sia con le versioni di debug firmate che non firmate - Trovo strano che non funzionasse con la versione di debug firmata I ma non ho avuto il tempo di indagare. Tuttavia, il registro di sistema è molto importante per scoprire se l'APK è installato o meno.
Boris Treukhov

In effetti, per qualche motivo, l'installazione come versione è stata la chiave per ottenere l'autorizzazione INSTALL_PACKAGES. L'app potrebbe essere eseguita in debug firmato da priv-app, ma la richiesta di autorizzazione è stata rifiutata. Molte grazie!
JM Lord

@ BorisTreukhov Hey, posso verificare con voi a cosa serve il parametro packageName? Ho cambiato InputStream in FileInputStream ma ricevo questo messaggio di errore: Impossibile analizzare /data/app/vmdl676191029.tmp/COSU

come installare l'app priv di sistema dopo questi comandi?
user2323471

8

Dovresti definire

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

nel tuo manifest, quindi se sei nella partizione di sistema (/ system / app) o hai la tua applicazione firmata dal produttore, avrai l'autorizzazione INSTALL_PACKAGES.

Il mio suggerimento è di creare un piccolo progetto Android con livello di compatibilità 1.5 usato per chiamare installPackages tramite reflection e per esportare un jar con metodi per installare pacchetti e per chiamare i metodi reali. Quindi, importando il jar nel tuo progetto sarai pronto per installare i pacchetti.


5

Ho provato su Android 4.2.2 con root e questo metodo funziona per me:

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

2
La domanda riguardava l'installazione di app senza root, ma con l'autorizzazione di sistema "INSTALL_PACKAGES". Vedo "su" nella tua risposta, quindi richiede root.
POMATu

Grazie mille per questa risposta! Avevo lo stesso codice ma stavo usando "adb install -r" invece di "pm install -r" e NON avrebbe funzionato per me. Mi hai risparmiato un sacco di tempo qui!
Jeff.H

@Vladmir ho bisogno del tuo aiuto. Puoi darmi un po 'di tempo. Sarò molto grato a u
twana l'

4

Non avevo idea di come farlo, perché quella volta nessuno ha risposto e non ho trovato documentazione su questo permesso. Così ho trovato la mia soluzione. È peggio del tuo, ma questa è comunque una soluzione.

Ho installato busybox, che ha impostato l'autorizzazione 777 su / data / app (non mi interessa la sicurezza). Quindi ho appena eseguito "busybox install" dall'app. Funziona, ma presenta una grossa falla nella sicurezza. Se imposti i permessi 777, non è richiesta la root.


3
hai usato un codice come questo? Process install; install = Runtime.getRuntime().exec("/system/bin/busybox install " + myAPK.getAbsolutePath()); int iSuccess = install.waitFor();
Someone Somewhere

4

Puoi utilizzare l'API nascosta android.content.pm.IPackageInstallObserverper riflessione:

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

Importa android.content.pm.IPackageInstallObservernel tuo progetto. La tua app deve essere di sistema. Devi attivare l'autorizzazione android.permission.INSTALL_PACKAGESnel tuo file manifest.


Hai testato questo codice su Lollipop e Marshamallow? Sembra un approccio fantastico per me e voglio usarlo
Farhad Faghihi

Ho provato da ICS a Lollipop
Hermann Poilpre

La linea da guardare è "la tua app deve essere di sistema". Sono abbastanza sicuro che funzionerebbe su lollipop o qualsiasi altra cosa, ammesso che la tua app sia "di sistema", cioè la tua app abbia le autorizzazioni di sistema (fondamentalmente root). In tal caso, tuttavia, puoi anche utilizzare il programma di installazione della riga di comando.
Lassi Kinnunen

1
come chiamo il metodo installPackage? Significato, come posso ottenere un IPackageInstallObserver per esempio?
phoebus

Qual è il valore di installFlags?
Shivam Kumar

3

Puoi semplicemente usare il comando adb install per installare / aggiornare l'APK in silenzio. Il codice di esempio è di seguito

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

Qui cosa si intende per StringUtils? Sto affrontando un errore perché non può essere risolto?
AndroidOptimist

scusa avrei dovuto spiegare. questa è la mia classe personalizzata per le stringhe e questo metodo garantisce che il percorso sia corretto. puoi ignorarlo
ZeeShaN AbbAs

3
cos'è la classe StringUtil. puoi pubblicare il metodo StringUtil.insertEscape () ?. beuause se commento quella riga ottengo un'eccezione Errore durante l'esecuzione di exec (). Comando: [su, -c, adb install -r /storage/sdcard0/Android/data/com.install.file Grazie in anticipo
sandeepmaaram

java.io.IOException: errore durante l'esecuzione di exec (). Comando: [su, -c, adb install -r /storage/emulated/0/download/SwipeAnimation.apk] Directory di lavoro: null Ambiente: null - puoi spiegare l'errore ..? e se eventuali autorizzazioni in manifest
Sandeep P

1
@ZeeShaNAbbAs dopo aver risolto il problema del rooting .. grazie
Sandeep P

1

Prerequisito:

Il tuo APK deve essere firmato dal sistema come indicato correttamente in precedenza. Un modo per ottenere ciò è creare da soli l'immagine AOSP e aggiungere il codice sorgente nella build.

Codice:

Una volta installato come app di sistema, puoi utilizzare i metodi del gestore di pacchetti per installare e disinstallare un APK come segue:

Installare:

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

Disinstalla:

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

Per richiamare una volta che il tuo APK è installato / disinstallato puoi usare questo:

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

===> Testato su Android 8.1 e ha funzionato bene.


ho bisogno del tuo aiuto. Puoi darmi un po 'di tempo. Sarò molto grato a u
twana l'

Voglio il tuo aiuto. Ho bisogno di sapere come possiamo creare il nostro AOSP
twana eng

0

Ho creato un'app di prova per installazioni silenziose, utilizzando il metodo PackageManager.installPackage.

Ottengo il metodo installPackage tramite la reflection e ho creato l'interfaccia android.content.pm.IPackageInstallObserver nella mia cartella src (perché è nascosta nel pacchetto android.content.pm).

Quando eseguo installPackage, ho ricevuto SecurityException con indicazione di stringa, che la mia app non ha android.permission.INSTALL_PACKAGES, ma è definita in AndroidManifest.xml.

Quindi, penso, non è possibile utilizzare questo metodo.

PS. Ho provato su Android SDK 2.3 e 4.0. Forse funzionerà con le versioni precedenti.


Funziona in 4.1 e 4.0 così come in qualsiasi versione di Android superiore alla 2.1 (inclusa la 2.1). Sto usando 4.0 SDK ora. Devi aggiungerlo alla cartella / system / app, altrimenti la tua app non avrà le autorizzazioni a livello di sistema (non root) richieste. Inoltre, essendo in grado di eliminare le app silenziosamente con questo metodo, controlla la mia altra domanda relativa alla cancellazione.
POMATu

Sì, funziona, ma solo con i permessi di root. E non è possibile installare l'app nel sistema / app su un dispositivo non rootato.
Андрей Москвичёв

Non con root, ma con autorizzazioni a livello di sistema. Puoi aggiungerlo a / system / app se sei un produttore di firmware. Nel mio caso, lo sono (chiedo ai produttori di dispositivi di aggiungere i miei apk al firmware). Non sarai in grado di installare le app silenziosamente in nessun altro modo, altrimenti sarebbe un grande buco di sicurezza.
POMATu

Riguardo al buco di sicurezza - sono assolutamente d'accordo con te. E sarà una sorpresa per me se questo metodo funziona con qualsiasi app. Come posso installare l'app in / system / app senza eseguire il root? Non sono un produttore.
Андрей Москвичёв

1
Senza root o riflesso non puoi.
POMATu

0

Prova questo LD_LIBRARY_PATH=/vendor/lib:/system/libprima dell'installazione di pm. Funziona bene.


0

Un'applicazione di terze parti non può installare un'app Android in modo scivoloso. Tuttavia, un'applicazione di terze parti può chiedere al sistema operativo Android di installare un'applicazione.

Quindi dovresti definire questo:

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

Puoi anche provare a installarlo come app di sistema per concedere l'autorizzazione e ignorare questa definizione. (Root richiesto)

Puoi eseguire il seguente comando sulla tua app di terze parti per installare un'app sul dispositivo rooted.

Il codice è:

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

Spero che questa risposta ti sia utile.


0

È possibile eseguire un'installazione silenziosa su Android 6 e versioni successive. Usando la funzione fornita nella risposta da Boris Treukhov, ignora tutto il resto nel post, nemmeno root.

Installa la tua app come amministratore del dispositivo, puoi avere la modalità kiosk completa con installazione silenziosa degli aggiornamenti in background.




-6

! / bin / bash

f=/home/cox/myapp.apk   #or $1 if input from terminal.

#backup env var
backup=$LD_LIBRARY_PATH
LD_LIBRARY_PATH=/vendor/lib:/system/lib
myTemp=/sdcard/temp.apk
adb push $f $myTemp
adb shell pm install -r $myTemp
#restore env var
LD_LIBRARY_PATH=$backup

Questo funziona per me. Lo eseguo su Ubuntu 12.04, sul terminale della shell.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.