Archive

Archive for July, 2011

How to install an application in background on Android

2 de July de 2011 56 comments

First of all:

1 – This technique is only useful if you are building an application for a specific device that you have its certificate (ROM’s certificate) or if your device is rooted.
2 – Doing this technique you will access classes that the Google doesn’t guarantee its portability for future versions;
3 – This post has been written at the moment Android was 2.2, but other people have proved it still works for 4.4;
4 – Add the correct permission in your AndroidManifest.xml.

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

A video have been added to Youtube to showcase the feature working:

I also recommend you read the previous post.

Let’s get started. Today I’ll tell my story of how digged into Android’s code to figure out how install APKs in background without any user interaction. At the moment I’m writing this post there were no other source talking about this on the web (at least I haven’t been able to find any useful help Googling it). Beyond that, reading this post you can learn a little more about how the Android framework works.

I needed this solution so much because in my case the app is a private app where the user can’t get out the app and can’t use other APKs or any other activity from Android. Besides that, the update is mandatory. The user can’t choose for not update the app or cancel the installation (option that is given if you send the user to the default screen installation). As I said, a case where the update should happen and is mandatory, the user liking it or not.

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”]. I digged into Android’s code and searched for the source code of this activity. There is a text “Uninstall application?”, then I searched for this string:

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 uses that:

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
 */

Looks like we found what we want, although it is not possible to import this class. It throws an error saying that it doesn’t exist in android.jar. Bad news, Android system have access to it and we don’t. But, have you read my previous post where I say how Google removes access from its Android’s framework? They have this @hide annotation on the methods and classes that should not be exposed to us developers.

Here is the trick: even though theses classes and method are not in android.jar, we still can access those through reflection since they are loaded into the JVM. Taaa-Daaa!

Android.jar is basically our interface and the proper and safe way to talk with the SO, but it doesn’t mean that the other stuffs that we can’t access through this jar is not there. Android.jar is meant anyway just to compile our app. It doesn’t even have any code, only classes and methods signatures. Android.jar is a stub! In run time android.jar is not used and is replaced somehow with the real code. It is logical, all applications on device share the same Android framework, so those classes that I digged for, are already loaded and running on the device even before the app starts. That means: you can access any of those framework’s class through reflection.

So finally let’s to write the code to install the app skipping android.jar API. First, download my code and find the code bellow:

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

Check ApplicationManager.java:

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 through reflection:

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 app 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 result:

class PackageInstallObserver extends IPackageInstallObserver.Stub {

	public void packageInstalled(String packageName, int returnCode) throws RemoteException {
		if (onInstalledPackaged != null) {
			onInstalledPackaged.packageInstalled(packageName, returnCode);
		}
	}
}

IPackageInstallObserver class is @hide in SDK as the other ones. The problem here is that how to extend a class using reflection? Well, I still don’t have the answer, but I do have a workaround to solve this.

We know that IPackageInstallObserver class is already loaded in the VM, so how about we create our own stub? The class exists there, so make a copy of it. Copy the class with the same same package structure and the method signatures, all stubbed out, such as android.jar does. So now you can normally compile your app. In run time, Android will ensure that the original class will be used, not yours stubbed class ;).

That’s it. I hope this has helped you. If you use this code sample and improve it, please consider making a contribution making a pull request. 🙂

The complete source code is in Github here

Thanks! 🙂

Advertisements