Instale aplicativos silenciosamente, com permissão concedida de INSTALL_PACKAGES

86

Estou tentando instalar o apk no sistema silenciosamente. Meu aplicativo está localizado em / system / app e recebeu a permissão "android.permission.INSTALL_PACKAGES"

No entanto, não consigo encontrar em nenhum lugar como usar essa permissão. Tentei copiar arquivos para / data / app e não tive sucesso. Também tentei usar este código

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

Mas este código abre a caixa de diálogo de instalação padrão. Como posso instalar o aplicativo silenciosamente sem root com concedido android.permission.INSTALL_PACKAGES?

PS Estou escrevendo um aplicativo que irá instalar muitos apks da pasta no sistema na primeira inicialização (substitua o assistente de configuração). Preciso tornar o firmware mais leve.

Se você acha que estou escrevendo um vírus: Todos os programas são instalados em / data / app. A permissão Install_packages só pode ser concedida a programas de nível de sistema localizados em / system / app ou assinados com a chave do sistema. Portanto, o vírus não pode chegar lá.

Como disse http://www.mail-archive.com/[email protected]/msg06281.html, os aplicativos PODEM ser instalados silenciosamente se tiverem permissão install_packages. Além disso, você não precisa da permissão Install_packages para instalar pacotes não silenciosamente. Além disso, http://www.androidzoom.com/android_applications/tools/silent-installer_wgqi.html

POMATu
fonte
9
@Fosco Suponha que ele esteja fazendo tal dispositivo como fabricante de equipamento original?
dascandy,
41
@Fosco Quem se importa? É uma questão válida, como descreve ascandy. Geralmente é improdutivo quando as pessoas respondem a perguntas como essas com "Você não deveria estar fazendo isso". Que compreensão estamos obtendo com esse tipo de resposta?
ZachM
3
Falando em improdutivo ... você está um pouco mais de 2 anos atrasado para esta festa Zach, e a pergunta parece muito diferente do que originalmente.
Fosco
2
@Fosco LOL. Também posso confirmar que isso ainda está funcionando no mais novo sistema operacional Android.
POMATu
1
@LarsH Acabei de instalar o aplicativo F-Droid: eles não instalam as coisas silenciosamente, eles apenas solicitam o gerenciador de pacotes do Android.
Joaquin Iurchuk,

Respostas:

60

Sua primeira aposta é dar uma olhada no PackageInstaller nativo do Android . Eu recomendaria modificar esse aplicativo da maneira que você quiser ou apenas extrair a funcionalidade necessária.


Especificamente, se você olhar para PackageInstallerActivity e seu método 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();
    }
}

Em seguida, você notará que o instalador real está localizado na classe InstallAppProgress . Inspecionando essa classe você verá que initViewé a função de instalação do núcleo, ea última coisa que ele faz é chamada para PackageManager's installPackagefunção:

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

A próxima etapa é inspecionar PackageManager , que é uma classe abstrata. Você encontrará installPackage(...)função lá. A má notícia é que está marcado com @hide. Isso significa que não está disponível diretamente (você não conseguirá compilar com a chamada a este método).

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

Mas você poderá acessar esses métodos por meio de reflexão.

Se você estiver interessado em como PackageManagera installPackagefunção de é implementada, dê uma olhada em PackageManagerService .

Resumo

Você precisará obter o objeto gerenciador de pacotes por meio Contextde getPackageManager(). Em seguida, você chamará a installPackagefunção por meio de reflexão.

inazaruk
fonte
2
Socorro! Esta técnica resulta em: Causado por: java.lang.SecurityException: Nem o usuário 10101 nem o processo atual tem android.permission.INSTALL_PACKAGES. Mesmo que meu aplicativo tenha essa permissão
gregm
13
Seu aplicativo solicita essa permissão, mas o Android não concede essa permissão a aplicativos de terceiros. Consulte LogCatpara logs de permissão relacionados.
inazaruk
@inazaruk eu li que aplicativos instalados em / system / app ganharão automaticamente permissão para usar install_pakages, mas conforme descrito neste post , parece não funcionar. você pode me dar algumas dicas?
Zorb de
3
Como posso chamar installPackage via reflexão? na verdade, não consigo fazer Method method = aPackageManagerClass.getMethod ("installPackage", new Class [] {Uri.class, IPackageInstallObserver.class, Integer.TYPE, String.class}); dado que também IPackageInstallObserver está oculto, alguém pode me ajudar por favor, postando o código correto para fazer reflexão aqui.
Shilaghae
7
Todos os links na resposta estão mortos.
Mayur Gangurde
13

Eu verifiquei como o ADB instala aplicativos.
- Ele copia o APK para / data / local / tmp
- ele executa 'shell: pm install /data/local/tmp/app.apk'

Tentei replicar esse comportamento fazendo: (no pc, usando um cabo USB)
adb push app.apk /sdcard/app.apk
adb shell
$ pm install /sdcard/app.apk
Isso funciona. O aplicativo está instalado.

Eu fiz um aplicativo (chamado AppInstall) que deve instalar o outro aplicativo.
(instalado normalmente, dispositivo sem acesso root)
Sim:
Runtime.getRuntime().exec("pm install /sdcard/app.apk").waitFor();
Mas isso dá o erro:
java.lang.SecurityException: Neither user 10019 nor current process has android.permission.INSTALL_PACKAGES.
Parece que o erro é lançado por pm, não por AppInstall.
Porque a SecurityException não é capturada pelo AppInstall e o aplicativo não falha.

Eu tentei a mesma coisa em um dispositivo com acesso root (mesmo aplicativo e AppInstall) e funcionou perfeitamente.
(Também normalmente instalado, não em / system ou qualquer coisa)
AppInstall nem mesmo pediu permissão de root.
Mas isso é porque o shell está sempre em #vez de $naquele dispositivo.

A propósito, você precisa de root para instalar um aplicativo em / system, correto?
Eu tentei remount adb no dispositivo sem root e obteve:
remount failed: Operation not permitted.
É por isso que eu não pude tentar o / system no dispositivo sem root.

Conclusão: você deve usar um dispositivo com root
Espero que isso ajude :)

FrankkieNL
fonte
Não consigo fazer isso funcionar. É interessante que o comando funcione bem no shell do ADB e não pareça funcionar quando tento executá-lo programaticamente.
saulpower 01 de
Sim, tenho o mesmo problema - funciona em shell, mas não quando chamado do meu aplicativo, mesmo se eu chamar via su)
velis
Existe alguma outra maneira de trabalhar em dispositivos do Google, como obter permissões de superadministrador?
Ramesh Kumar
Se quiser permissões de superadministrador, você deve: 1) fazer o root no seu dispositivo, 2) ter o aplicativo instalado na partição do sistema, 3) ou o aplicativo deve ser assinado com a mesma chave do próprio sistema operacional.
FrankkieNL
1
Isso acontece porque o usuário ADB e o usuário do seu aplicativo têm permissões diferentes
PHPoenX
11

Eu tenho implementado a instalação sem o consentimento do usuário recentemente - era um aplicativo de quiosque para API nível 21+ onde eu tinha controle total sobre o ambiente.

Os requisitos básicos são

  • API de nível 21+
  • acesso root para instalar o atualizador como um aplicativo com privilégios de sistema.

O método a seguir lê e instala o APK do 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;
    }

O código a seguir chama a instalação

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

para que tudo funcione, você precisa desesperadamente de INSTALL_PACKAGESpermissão, ou o código acima falhará silenciosamente

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

para obter essa permissão, você deve instalar seu APK como um aplicativo de sistema que REQUER root (no entanto, APÓS instalar seu aplicativo de atualização, ele parece funcionar SEM root)

Para instalar como aplicativo do sistema, criei um APK assinado e empurrei-o com

adb push updater.apk /sdcard/updater.apk

e, em seguida, movido para system/priv-app- o que requer remontar FS (é por isso que a raiz é necessária)

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

por algum motivo ele não funcionou com a versão de depuração simples, mas o logcat mostra informações úteis se seu aplicativo priv-appnão for selecionado por algum motivo.

Boris Treukhov
fonte
"por algum motivo não funcionou com a versão de depuração simples" Você quer dizer que funcionou no lançamento ou alguma outra versão de depuração menos "simples"?
JM Lord
1
@JMLord Funcionou com a versão de lançamento assinada e não funcionou com as versões de depuração assinadas e não assinadas - acho estranho que não funcionou com a versão de depuração assinada I, mas não tive tempo para investigar. No entanto, o log do logcat do sistema é muito importante para descobrir se o APK está instalado ou não.
Boris Treukhov
Na verdade, por alguma razão, instalar como versão foi a chave para obter a permissão INSTALL_PACKAGES. O aplicativo pode ser executado em depuração assinada do priv-app, mas a solicitação de permissão foi rejeitada. Muito obrigado!
JM Lord
@BorisTreukhov Ei, posso verificar com você sobre o que é o parâmetro packageName? Alterei InputStream para FileInputStream, mas estou recebendo esta mensagem de erro: Falha ao analisar /data/app/vmdl676191029.tmp/COSU
como instalar o app priv do sistema após esses comandos?
user2323471
8

Você deve definir

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

em seu manifesto, se você estiver na partição do sistema (/ system / app) ou se tiver seu aplicativo assinado pelo fabricante, você terá permissão INSTALL_PACKAGES.

Minha sugestão é criar um pequeno projeto android com nível de compatibilidade 1.5 usado para chamar installPackages via reflexão e exportar um jar com métodos para instalar pacotes e chamar os métodos reais. Então, importando o jar em seu projeto, você estará pronto para instalar os pacotes.

iLRedEiMAtTi
fonte
5

Tentei no Android 4.2.2 com root e este método funciona para mim:

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();
        }
     }
}
Vladimir
fonte
2
A questão era sobre a instalação de aplicativos sem root, mas com permissão do sistema "INSTALL_PACKAGES". Vejo "su" em sua resposta, portanto, é necessário root.
POMATu de
Muito obrigado por esta resposta! Eu tinha o mesmo código, mas estava usando "adb install -r" em vez de "pm install -r" e NÃO funcionaria comigo. Você me salvou muito tempo aqui!
Jeff.H
@Vladmir, preciso de sua ajuda. Você pode me dar algum tempo. Ficarei muito grato a u
twana eng
4

Não tinha ideia de como fazer isso, pois ninguém respondeu naquela vez, e não encontrei documentação sobre essa permissão. Então, encontrei minha própria solução. É pior que o seu, mas é uma solução de qualquer maneira.

Eu instalei o busybox, que definiu 777 permissões para / data / app (não me importo com segurança). Em seguida, basta executar "busybox install" do aplicativo. Isso funciona, mas tem um grande vazamento de segurança. Se você definir as permissões 777, nenhuma raiz será necessária.

POMATu
fonte
3
você usou um código assim? Process install; install = Runtime.getRuntime().exec("/system/bin/busybox install " + myAPK.getAbsolutePath()); int iSuccess = install.waitFor();
Alguém em algum lugar
4

Você pode usar a API oculta android.content.pm.IPackageInstallObserverpor reflexão:

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

Importe android.content.pm.IPackageInstallObserverpara o seu projeto. Seu aplicativo deve ser do sistema. Você deve ativar a permissão android.permission.INSTALL_PACKAGESem seu arquivo de manifesto.

Hermann Poilpre
fonte
Você testou este código no Lollipop e no Marshamallow? Parece uma abordagem incrível para mim e eu quero usá-la
Farhad Faghihi
Eu testei do ICS para o Lollipop
Hermann Poilpre
A linha a ser observada é "seu aplicativo deve ser do sistema". Tenho quase certeza de que isso funcionaria no pirulito ou em qualquer outro lugar - desde que seu aplicativo seja "sistema", ou seja, seu aplicativo tenha permissões de sistema (basicamente root). Nesse caso, você também pode usar o instalador de linha de comando.
Lassi Kinnunen
1
como estou chamando o método installPackage? Ou seja, como faço para obter um IPackageInstallObserver, por exemplo?
Phoebus
Qual é o valor de installFlags?
Shivam Kumar
3

Você pode simplesmente usar o comando adb install para instalar / atualizar o APK silenciosamente. O código de amostra está abaixo

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();
        }
     }
  }
ZeeShaN AbbAs
fonte
Aqui, o que significa StringUtils? Estou enfrentando um erro que não pode ser resolvido?
AndroidOptimist
desculpe, eu deveria ter explicado. esta é minha classe personalizada para strings e este método garante que o caminho está correto. você pode ignorar isso
ZeeShaN AbbAs
3
o que é a classe StringUtil. você pode postar o método StringUtil.insertEscape () ?. beuause se eu comentar essa linha estou recebendo uma exceção Erro ao executar exec (). Comando: [su, -c, adb install -r /storage/sdcard0/Android/data/com.install.file Agradecemos antecipadamente
sandeepmaaram
java.io.IOException: Erro ao executar exec (). Comando: [su, -c, adb install -r /storage/emulated/0/download/SwipeAnimation.apk] Diretório de trabalho: null Ambiente: null - você pode explicar o erro ..? e se houver alguma permissão no manifesto
Sandeep P
1
@ZeeShaNAbbAs após o problema de enraizamento resolvido .. obrigado
Sandeep P
1

Pré-requisito:

Seu APK precisa ser assinado pelo sistema, conforme apontado corretamente anteriormente. Uma maneira de conseguir isso é construir você mesmo a imagem AOSP e adicionar o código-fonte à construção.

Código:

Depois de instalado como um aplicativo do sistema, você pode usar os métodos do gerenciador de pacotes para instalar e desinstalar um APK da seguinte forma:

Instalar:

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

Desinstalar:

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

Para ter um retorno de chamada depois que seu APK for instalado / desinstalado, você pode usar isto:

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

===> Testado em Android 8.1 e funcionou bem.

Phoebus
fonte
preciso da tua ajuda. Você pode me dar algum tempo. Ficarei muito grato a u
twana eng
Eu quero a sua ajuda. Preciso saber como podemos criar nosso próprio AOSP
twana eng
0

Fiz um aplicativo de teste para instalações silenciosas, usando o método PackageManager.installPackage.

Obtive o método installPackage por meio de reflexão e fiz a interface android.content.pm.IPackageInstallObserver na minha pasta src (porque está oculto no pacote android.content.pm).

Quando executo installPackage, recebo SecurityException com indicação de string, que meu aplicativo não tem android.permission.INSTALL_PACKAGES, mas é definido em AndroidManifest.xml.

Então, eu acho que não é possível usar esse método.

PS. Eu testei no Android SDK 2.3 e 4.0. Talvez funcione com versões anteriores.

Андрей Москвичёв
fonte
Ele está funcionando em 4.1 e 4.0, bem como em qualquer versão do Android superior a 2.1 (incluindo 2.1). Estou usando o SDK 4.0 agora. Você precisa adicioná-lo à pasta / system / app, caso contrário, seu aplicativo não terá as permissões necessárias no nível do sistema (não root). Também consigo excluir aplicativos silenciosamente com esse método, verifique minha outra pergunta relacionada à exclusão.
POMATu
Sim, funciona, mas apenas com permissões de root. E não é possível instalar o aplicativo no sistema / aplicativo em um dispositivo não rooteado.
Андрей Москвичёв
Não com root, mas com permissões de nível de sistema. Você pode adicioná-lo a / system / app se for um fabricante de firmware. No meu caso - eu sou (peço aos fabricantes de dispositivos para adicionar meus apks ao firmware). Você não será capaz de instalar aplicativos silenciosamente de outra maneira - caso contrário, seria uma grande falha de segurança.
POMATu de
Sobre a falha de segurança - concordo totalmente com você. E será uma surpresa para mim, se esse método funcionar com qualquer aplicativo. Como posso instalar o aplicativo em / system / app sem fazer root? Eu não sou fabricante.
Андрей Москвичёв
1
Sem root ou reflashing você não pode.
POMATu de
0

Experimente LD_LIBRARY_PATH=/vendor/lib:/system/libantes de instalar. Isso funciona bem.

Ankita Gujar
fonte
0

Um aplicativo de terceiros não pode instalar um aplicativo Android suavemente. No entanto, um aplicativo de terceiros pode solicitar que o sistema operacional Android instale um aplicativo.

Portanto, você deve definir isso:

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

Você também pode tentar instalá-lo como um aplicativo do sistema para conceder a permissão e ignorar esta definição. (Root Obrigatório)

Você pode executar o seguinte comando em seu aplicativo de terceiros para instalar um aplicativo no dispositivo com acesso root.

O código é:

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

Espero que esta resposta seja útil para você.


fonte
0

É possível fazer uma instalação silenciosa no Android 6 e superior. Usando a função fornecida na resposta por Boris Treukhov, ignore todo o resto do post, o root também não é necessário.

Instale seu aplicativo como administrador do dispositivo, você pode ter o modo quiosque completo com instalação silenciosa de atualizações em segundo plano.

Raymie
fonte
0

Eu verifiquei todas as respostas, a conclusão parece ser que você deve ter acesso root ao dispositivo primeiro para fazê-lo funcionar.

Mas então eu achei esses artigos muito úteis. Já que estou fazendo dispositivos "próprios".

Como atualizar o aplicativo Android silenciosamente, sem interação do usuário

Proprietário do dispositivo Android - aplicativo mínimo

Aqui está a documentação do Google sobre "dispositivo gerenciado"

Dispositivo totalmente gerenciado

h - n
fonte
0

você pode usar isso no terminal ou shell

adb shell install -g MyApp.apk

veja mais em Develope google

Rasoul Miri
fonte
-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

Isso funciona para mim. Eu executo isso no ubuntu 12.04, no terminal shell.

cox
fonte