How to install an application in background on Android
You need to attention to four things before use these codes:
1 – This technique is only useful and just will work for you if you will make an application for a specific device you have its certificate, just like me.
2 – Doing this technique you will access classes that the Google doesn’t guarantee its portability to future versions;
3 – I used and tested it in version 2.2;
4 – You need remember put the permission in your AndroidManifest.xml.
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
A video was added on Youtube to quickly show the feature:
I also recommend you read the previous post.
So, let’s go. Today I’ll pass for you guys something that I think is very interesting. I’ll talk about how you can install APKs in background (without user interaction). It’s a good tip because this issue isn’t easily found on the web. Beyond that, you can learn more about how the Android frameworks works.
I needed to find a solution for this because I had to develop an application that it needed to be updated remotely and it couldn’t be exposed in the Android Market. It was a private commercial application. The customer did not want to the user had the option to cancel the installation (which happens if you sends the user to the default screen installation). The update was an obligation.
So let’s begin where I began. First of all, I wondered how the Android System does to uninstall applications. You can see it in [Settings -> Applications -> Manage applications -> "choose any app" -> "click in Uninstall"]. So I searched for the source code this activity. There is a text “Uninstall application?”, then I searched for this string in the source code of Android:
find . -name *.xml | xargs grep Uninstall application ./packages/apps/PackageInstaller/res/values/strings.xml: <string name="uninstall_application_question">Uninstall application?</string>
I found the string with the name “uninstall_application_question”. So I searched for the activity that use it:
find . -name *.java | xargs grep uninstall_application_question ./packages/apps/PackageInstaller/src/com/android/ packageinstaller/UninstallerActivity.java: question.setText(R.string.uninstall_application_question);
I found UninstallerActivity.java. In UninstallerActivity class there is a button “mOk” that sends you to UninstallAppProgress.java. In this class we see PackageDeleteObserver that removes the application. It extends android.content.pm.IPackageDeleteObserver. Let’s search for this class:
find . -name IPackageDeleteObserver.java ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/ src/core/java/android/content/pm/IPackageDeleteObserver.java
In this package we also have IPackageInstallObserver.java. In this class we see:
/** * API for installation callbacks from the Package Manager. * @hide */
It’s what we want, but I tried to import this class and I saw that it doesn’t exist in android.jar. Apparently the Android system have access to it and we don’t. But, did you remember that in my previous post I said that the Google remove the access of some framework’s class? It puts “@hide” in comments on the methods and classes of the source code of the framework that it doesn’t want to expose to the developer.
Now the big tip: you can access these classes through reflection, because they have loaded into the system. I don’t know if this is a bug, but… this solves our issue!
But, why have theses classes been loaded into the system? As I said in previous post, the android.jar has everything you need to develop your application using Java, but the classes are in the jar just for you compile your aplication properly, ensuring that it will run on future versions of Android. The applications are compiled without them, the framework’s classes aren’t built into your apk. The codes inside the jar are stubbed out. This is because the code inside the jar is never runs, so there’s no reason to make your application bigger because of this. Beyond that, all applications on device share the same framework, so these classes are already running on the device actually. That means: you can load any framework’s class through reflection.
So finally let’s to do the code to install the application directly by API without interface. First, download my code, let’s see it:
final ApplicationManager am = new ApplicationManager(InstallInBackgroundSample.this);
am.setOnInstalledPackaged(new OnInstalledPackaged() {
public void packageInstalled(String packageName, int returnCode) {
if (returnCode == ApplicationManager.INSTALL_SUCCEEDED) {
Log.d(TAG, "Install succeeded");
} else {
Log.d(TAG, "Install failed: " + returnCode);
}
}
});
...
Button btnInstall = (Button) findViewById(R.id.btnInstall);
btnInstall.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
try {
am.installPackage(txtApkFilePath.getText().toString());
} catch (Exception e) {
logError(e);
}
}
});
Let’s see the ApplicationManager.java class that I created:
public void installPackage(Uri apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
method.invoke(pm, new Object[] {apkFile, observer, INSTALL_REPLACE_EXISTING, null});
}
As you can see, I invoked the method by reflection. To obtain the method I did:
public ApplicationManager(Context context) throws SecurityException, NoSuchMethodException {
observer = new PackageInstallObserver();
pm = context.getPackageManager();
Class<?>[] types = new Class[] {Uri.class, IPackageInstallObserver.class, int.class, String.class};
method = pm.getClass().getMethod("installPackage", types);
}
To determine whether the application is installed successfully, we need to create a class to implement the callback “packageInstalled”. We must pass an observer in the parameter of the method “installPackage”. Thus we need to extend IPackageInstallObserver.Stub to obtain the return as did UninstallAppProgress activity:
class PackageInstallObserver extends IPackageInstallObserver.Stub {
public void packageInstalled(String packageName, int returnCode) throws RemoteException {
if (onInstalledPackaged != null) {
onInstalledPackaged.packageInstalled(packageName, returnCode);
}
}
}
But we don’t have access to IPackageInstallObserver class (it is @hide in SDK) and how could we get the class by reflection and create a subclass of this? How would we do? Well, I did not know to do this, then I discovered an other way to do it.
I know that IPackageInstallObserver class has already loaded in the VM, so I decided to create a copy of the class in my project with the same package structure and the method signatures, but stubbed out, exactly as does android.jar! The VM loads the framework’s class first so I used my class just to compile. When is running, the VM uses the framework’s class and not the mine.
So that’s it, folks. I hope this has helped you. Download the code, run it and read it, everything will seem easier. Please, leave comments.
The complete code the project is exposed in the Github: here or direct download zip file here
hi,
I try use your project code without modify but i found an error:
“java.lang.reflect.InvocationTargetException……….”
I got the same exception. And SecurityException as cause, with string indication, that app has no android.permission.INSTALL_PACKAGES permission.
I tested in on SDK 2.3 and 4.0.
So, this method works only on earlier versions.
Hi! I tested it again in a device with Android 2.2 and also in the emulator with Android 2.2 and it works fine! I decided to test it in a device with Android 2.3.4 and I got the same error as you, so I think that it may have been blocked by Google, but I not sure, this is my conclusion for now. Please test the application in an emulator with the 2.2 version and see if works and let me know about that. Thanks for your feedback!
Hi
I tested it in emulator 2.2 but i have the same error
I used the project code provided by this link: ” https://github.com/paulononaka/Android-InstallInBackgroundSample“.
Hi
I am downloading apk file from remote server, i am storing it in SD card, and trying to install it using your code but i am getting protection level 3 exception, can you please help where should i keep my apk file , so your code will execute without any exception
Please help.
Hi guys, I found the problem and unfortunately my news is not good. I discovered that this technique only works for me because I use the security certificate of the simulator (the same certificate used by my device in my case). I researched and saw that there are two ways to use the API hidden:
- Put your application pre-installed into a system folder on the ROM
- Compile your application using the manufacturer’s security certificate
So this will just be useful for you if you will make an application to a specific device and you have the certificate of it, just like me. I apologize for not notifying it before, but I did not know that. I’ll update my post and I’ll warn about it at first. Thank you for the feedback.
Paulo,
Ótimo post, mas tive um problema diferente.
07-27 01:00:05.404: ERROR/AndroidRuntime(18551): FATAL EXCEPTION: main
07-27 01:00:05.404: ERROR/AndroidRuntime(18551): java.lang.NoClassDefFoundError: com.paulononaka.apihelper.ApplicationManager$PackageInstallObserver
07-27 01:00:05.404: ERROR/AndroidRuntime(18551): at com.paulononaka.apihelper.ApplicationManager.(ApplicationManager.java:36)
07-27 01:00:05.404: ERROR/AndroidRuntime(18551): at com.paulononaka.InstallInBackgroundSample.onCreate(InstallInBackgroundSample.java:28)
07-27 01:00:05.404: ERROR/AndroidRuntime(18551): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
Tem alguma ideia do que pode ser??
Estou fazendo um atualizador e o seu código é exatamente o que eu preciso. Como é um app privado, numa versão fixa do android e ele vai bloquear todos os acessos ao SO, não preciso me preocupar tanto, posso usar o certificado que vc falou, mas tenho um porém. Como fazer isso? vc tem alguma dica ou link pra me ajudar???
Parabéns pelo post e pelo blog!!
Abraços!
Opa, então vamos descobrir o teu problema! Algumas perguntas:
- Você está usando qual versão do Android?
- Experimentou no simulador? Fiz o vídeo acima no simulador com Android 2.2.
- Você manteve a classe IPackageInstallObserver dentro do pacote android.content.pm?
Quanto ao certificado, eu ainda vou escrever um post sobre como gerá-lo, mas por enquanto você pode baixar ele aqui: http://goo.gl/Onpfs
Você deve escolher esse certificado no momento de exportar a sua APK. A senha é “android”. Ambos os alias (plataform ou androiddebugkey) irão funcionar. Mas se você gostaria de assinar a sua apk com esse certificado em momento de debug, você precisa inseri-lo no Eclipse em [Window > Preferences > Android > Build > Custom debug keystore]. Nesse segundo caso, lembre-se de dar clean no projeto antes de rodar para que ele possa ser compilado e assinado com o novo certificado.
Paulo,
Funcionou perfeito!!!! Está de parabéns!!! Você é O CARA!!
Continue assim!!
Abraços!!
ola paulo, mt joia essa sua aplicaçao.
Vc que ja tem uma experiencia com aplicaçoes em background, eu precisava de uma aplicaçao que pegasse os dados de gps, e mandasse para um servidor, registrando de tempos em tempos, ate ai td bem, o problema é que eu preciso que ela seja totalmente em background, sem nenhuma interface grafica, eu tentei tornar ela um service, mas ai nao consigo enviar os dados de forma algum, nem por clientehttp. nem por socket, vc tem alguma luz pra me dar. vlw
Opa, blz Johann?
É isso mesmo, você deve criar um service. Fazer uma chamada http deve ser o suficiente para você. Criei aqui a aplicação que você precisa:
https://github.com/paulononaka/Android-ServiceLocalization
Tá lá, boa sorte!
opa paulo, ja implementei e funcionou perfeitamente. É pra conectar em um servlet que eu tenho pronto aqui, vou tentar fazer as adaptaçoes nessessarias pra isso.
Brigadao cara, salvo minha vida
Hi,
can you publish the source code of your project after the corrections because I can not download from megaupload.
Thanks for advance.
E ae paulo, fico show de bola a aplicaçao que vc mandou cara, adicionei umas coisas, um banco nativo, caso nao consiga conectar no banco grave os dados no banco, e assim que conectar manda td e apaga ^^.
So tendo problemas nos parametros, cada vez que manda o get, ele nao limpa os parametros, e manda o anterios, mais o atual, e assim vai acumulando ate estora o limite. to tentando da um jeito nisso, se quiser dar uma olhada como ficou, ainda ta dando uns errinhos bobos, mas com o tempo eu vou tentando arrumar. :p
http://www.megaupload.com/?d=W8FDGOUJ
caso nao consiga conectar no servidor grava no banco*
aqui uma versao pouco mais atualizada.
http://www.megaupload.com/?d=5616S6NU
can you post your version in another site please , I can’t access to this link.
Thanks
Unfortunately this has been patched. They up’d the permission for INSTALL_APP or whatever to applications signed with the manufacturer’s certificate, or if the phone is rooted. :/ Sorry guys.
Hello ,
Very tutorial and project ,,
However I am facing some problem in using the code might be some where i m wrong ………
i have modified ur code a little bit
I have changed this ……..
to
try
{
//my path to apk
am.installPackage(“/mnt/sdcard/PopupTest.apk”);
}
catch (Exception e)
{
logError(e);
}
then I am getting the Toast saying that “an error occured plz check log”;
But der is nothing in logcat…
Am I wrong some where Please help me out …
Thanks in advanced…..
it worked great for me using the path “/sdcard/your.apk” instead of “/mnt/sdcard/your.apk”
hello,
I try to use this project but i have the same problem like Mr.Vipin Sahu
Please there is any way to solve this problem?
thanks
I was able to get your awesome code to work on the emulator (in Eclipse I needed to set the path to the certificate/keystore you provided because the default debug keystore won’t permit the INSTALL_PACKAGES permission).
My question is, how did you get the google_certificate.keystore ? Is it publicly available ?
Also, now I want to give the app to the firmware engineer so that he can include it in the firmware. My question is: how do I build the APK ? Do I use the same Google keystore – but it asks me for a password when I export.
Hello all, I wrote a new post about sign you apk with system privileges.
http://paulononaka.wordpress.com/2011/10/19/apk-with-system-privileges/
Sorry for my delay in answer.
I have two final questions on this topic…
1) does the flag INSTALL_REPLACE_EXISTING ensure that app-data is kept, i.e. database ?
2) why does the ActivityManager force stop the newly installed/reinstalled package ? Is there a flag to prevent the force stop ? See my log below…
DEBUG/PackageManager(59): Scanning package hello.world
INFO/PackageManager(59): /data/app/hello.world-1.apk changed; unpacking
DEBUG/installd(35): DexInv: — BEGIN ‘/data/app/hello.world-1.apk’ —
DEBUG/dalvikvm(3861): DexOpt: load 23ms, verify 4ms, opt 1ms
DEBUG/installd(35): DexInv: — END ‘/data/app/hello.world-1.apk’ (success) —
DEBUG/PackageManager(59): Activities: hello.world.ActivityMain
INFO/ActivityManager(59): Force stopping package hello.world uid=10038
INFO/installd(35): move /data/dalvik-cache/data@app@hello.world-1.apk@classes.dex -> /data/dalvik-cache/data@app@hello.world-1.apk@classes.dex
DEBUG/PackageManager(59): New package installed in /data/app/hello.world-1.apk
1) Yes, the app-data are kept!
2) The new application will not be stopped! The old application is stopped to allow the new application replace it.
Hi!
thanks for a good tip!!
it` very useful for me.
I have a one question.
my customer request API to install apk in background.
but your project has many java files
he want to use it like a method, not including extra files
Is it possible to intergrate files or decrease the number of files?
thanks in advanced
I am not able to run keytool-importkeypair on windows 7?
could anyone help me?
paulonona, the firmware engineer I work with is upgrading from Android 2.2 to Android 2.3 and it seems that it’s no longer working:
D/installd( 885): DexInv: — BEGIN ‘/system/app/customInstaller.apk’ —
D/dalvikvm( 2011): creating instr width table
D/dalvikvm( 2011): DexOpt: ‘Landroid/content/pm/IPackageInstallObserver;’ has an earlier definition; blocking out
D/dalvikvm( 2011): DexOpt: ‘Landroid/content/pm/IPackageInstallObserver$Stub;’ has an earlier definition; blocking out
D/dalvikvm( 2011): DexOpt: not verifying ‘Landroid/content/pm/IPackageInstallObserver;’: multiple definitions
D/dalvikvm( 2011): DexOpt: not verifying ‘Landroid/content/pm/IPackageInstallObserver$Stub;’: multiple definitions
I/dalvikvm( 2011): DexOpt: not resolving ambiguous class ‘Landroid/content/pm/IPackageInstallObserver$Stub;’
I/dalvikvm( 2011): DexOpt: not resolving ambiguous class ‘Landroid/content/pm/IPackageInstallObserver;’
D/dalvikvm( 2011): DexOpt: load 9ms, verify+opt 10ms
D/installd( 885): DexInv: — END ‘/system/app/customInstaller.apk’ (success) —
Do you have any suggestions ?
Looking at the JavaDoc, it seems there’s no longer a IPackageInstallObserver
http://www.androidjavadoc.com/2.3/android/content/pm/package-summary.html
But I still can’t uninstall apps the same way
Can you check this? Where is a mistake?
http://stackoverflow.com/questions/10900928/uninstall-app-silently-with-system-priveleges
nevermind, figured that out
Here is the solution
http://stackoverflow.com/a/10967776/705297
What is the license of this API?
Can I use this API to products?
Dude, feel free to use it whatever you want
Hi thanks for sharing!
I have a question – for this to work, do I need to Export my application as signed Android application, using the “Android tools -> Export as signed..” with the certificate you provided in the certificate folder?
If so, what’s the certificate password? I used “android” and it seem to work… but I have to copy the APK and put it in /system/app and only then install it.
Can you verify what i’m doing is OK?
Hi thanks for code but I get the follow error:
08-31 16:14:33.271: W/System.err(5322): Caused by: java.lang.SecurityException: Neither user 10106 nor current process has android.permission.INSTALL_PACKAGES.
I already insert the INSTALL_PACKAGES in the manifest.
I’m using android 2.3.3
Hi i just download your project and run without modified the code in emulator its working properly but when i connect device it gives an error
[2012-11-21 15:45:55 - InstallInBackgroundSample] Installation error: INSTALL_FAILED_INVALID_INSTALL_LOCATION
[2012-11-21 15:45:55 - InstallInBackgroundSample] Please check logcat output for more details.
[2012-11-21 15:45:56 - InstallInBackgroundSample] Launch canceled!
Hi, I have tried this on my custom android build and it works like a charm. Thanks!
The issue I have is that I kick off downloading and installing of the apk with an intent service…
Downloading runs on the intent service fine, but the service is not blocked when installPackage is called (as this is handled and returns asynchronously).
Is there anyway I block the intent service thread until onPackageInstalled has completed.
Öncelikle yazınız için teşekkür.ederiz. Böyle yazıların bilgilendirici nitelikte olduğunu düşünüyoruz. Tekrar teşekkürler.
Hi, I have the following issue when the (installer) app is installed in the systems apps folder.
12-29 15:50:37.704 E/JavaBinder( 2592): *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
12-29 15:50:37.704 E/JavaBinder( 2592): java.lang.AbstractMethodError: abstract method not implemented
12-29 15:50:37.704 E/JavaBinder( 2592): at android.content.pm.IPackageInstallObserver$Stub.packageInstalled(IPackageInstallObserver.java)
12-29 15:50:37.704 E/JavaBinder( 2592): at android.content.pm.IPackageInstallObserver$Stub.onTransact(IPackageInstallObserver.java:56)
12-29 15:50:37.704 E/JavaBinder( 2592): at android.os.Binder.execTransact(Binder.java:338)
12-29 15:50:37.704 E/JavaBinder( 2592): at dalvik.system.NativeStart.run(Native Method)
12-29 15:50:37.704 W/dalvikvm( 2592): threadid=9: thread exiting with uncaught exception (group=0x40a5d9d8)
12-29 15:50:37.704 E/AndroidRuntime( 2592): FATAL EXCEPTION: Binder Thread #2
12-29 15:50:37.704 E/AndroidRuntime( 2592): java.lang.AbstractMethodError: abstract method not implemented
12-29 15:50:37.704 E/AndroidRuntime( 2592): at android.content.pm.IPackageInstallObserver$Stub.packageInstalled(IPackageInstallObserver.java)
12-29 15:50:37.704 E/AndroidRuntime( 2592): at android.content.pm.IPackageInstallObserver$Stub.onTransact(IPackageInstallObserver.java:56)
12-29 15:50:37.704 E/AndroidRuntime( 2592): at android.os.Binder.execTransact(Binder.java:338)
12-29 15:50:37.704 E/AndroidRuntime( 2592): at dalvik.system.NativeStart.run(Native Method)
Any clues?
Seems like proguard was the culprit obfuscating IPackageInstallObserver class. I have now added it to the proguard config file so it’s kept
Hi,
What is manufacturer’s security certificate and how we get it and compile with this certificate
Thanks in Adavance
Is there anyway to retrieve the permissions (and possibly the localized permission text) from the apk file that is about to be installed, prior to installing it?