Showing posts with label instrumentation. Show all posts
Showing posts with label instrumentation. Show all posts

Monday, January 19, 2009

Generating keypresses programmatically

There was already a blog entry about programmatically generating key events but I was not satisfied with the outcome. The Instrumentation framework is nice but is somewhat heavyweight. If one wants to generate just keypresses, why to have all those entries in the manifest, instrumentation object, etc? So I used the wonderful new dedexer tool to go after the inner workings of the instrumentation framework.

The critical method in android.app.Instrumentation is this:


.method public sendKeySync(Landroid/view/KeyEvent;)V
.catch android/os/RemoteException from le5cf6 to le5d12 using le5d14
invoke-direct {v2},android/app/Instrumentation/validateNotAppThread
; validateNotAppThread()V
le5cf6:
const-string v0,"window"
invoke-static {v0},android/os/ServiceManager/getService
; getService(Ljava/lang/String;)Landroid/os/IBinder;
move-result-object v0
invoke-static {v0},android/view/IWindowManager$Stub/asInterface
; asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
move-result-object v0
const/4 v1,1
invoke-interface {v0,v3,v1},android/view/IWindowManager/injectKeyEvent
; injectKeyEvent(Landroid/view/KeyEvent;Z)Z
le5d12:
return-void
le5d14:
move-exception v0
goto le5d12
.end method


This is actually a public API method. The method obtains the binder of the WindowManagerService from the ServiceManager then it invokes the injectKeyEvent method on WindowManagerService's public interface. That is something we can do ourselves, can't we?

Click here to download the example program.

Unfortunately the situation is not that simple. Ever since the non-public classes like android.os.ServiceManager, android.view.IWindowManager, etc. were removed from android.jar, applications using non-public API cannot be compiled as easily as before. If you check the example program, you will see that I created stubs for these classes. These stubs are only there to allow compilation of the program, they don't get packaged into the apk file. On the platform itself, there will be real versions of them. In order to accomodate the stub compilation step, I modified somewhat the machine-generated build.xml file too.

It is also important to notice the validateNotAppThread private method invocation at the beginning of the method. Events cannot be generated neither from the application's own thread, nor from the UI thread. That's the reason we prepare another thread and install a looper into it. Then we post our actions into that thread and that thread will be able to generate keypresses.


If you launch the program and press the "Generate keypresses" button, you will see how the text field above the button receives the keypresses we generated programmatically. At this point you may get excited and would try to generate keypresses for other applications. This will not work. Android applications are allowed to generate keypresses only for themselves. In WindowManagerService class, you will find a permission check controlling this behaviour.


iget-object v1,v12,com/android/server/WindowManagerService.mContext Landroid/content/Context;
const-string v2,"android.permission.INJECT_EVENTS"
invoke-virtual {v1,v2,v14,v15},android/content/Context/checkPermission
; checkPermission(Ljava/lang/String;II)I
move-result v1
if-eqz v1,l4a7e0
const-string v1,"WindowManager"
new-instance v1,java/lang/StringBuilder
invoke-direct {v1},java/lang/StringBuilder/
; ()V
const-string v2,"Permission denied: injecting key event from pid "


The android.permission.INJECT_EVENT permission is not that of the application, the WindowManagerService must have this permission otherwise requests to inject events into other applications' windows will be rejected. By default, WindowManagerService does not have this permission, that is why it is impossible to generate events for somebody else's window.

After all this diving into Android's internals, I show that there is a simpler way to generate key events by abusing the android.app.Instrumentation class. Check out the generateKeysWInst method in the example program. This method simply instantiates the Instrumentation class and calls only the sendKeyDownUpSync public method. This works because we know that sendKeyDownUpSync is so simple that it will function even with this bizarrely created instrumentation object. I don't know which method is more futureproof: using internal, non-public API or abusing a public API. Decide yourself.

Tuesday, December 2, 2008

Instrumentation and JUnit

JUnit has been nicely integrated into Android. JUnit classes are specialized to facilitate common Android testing tasks. In this post, I will talk about my experiences with InstrumentationTestRunner and the facilities it provides to integrate Android instrumentation with JUnit test execution.

As we have seen previously, the difficulty in JUnit-instrumentation integration stems from the fact that an instrumentation is an entire Android application, started, managed and terminated for testing purposes. One has to work quite a bit to make sure that the test controller is not terminated along with the application under test. InstrumentationTestRunner solves this problem by launching the Dalvik VM in special, instrumentation mode.

You can download the example program from here.

The test subject is our well known Calculator program that was renamed as Calculator2 so that we do not confuse it with the version coming from previous application packages, other than that it is the same simple program. Under aexp.calculator.tests, you will find two JUnit test classes. There are things to note, however.
  • Instead of TestCase, the test classes inherit from ActivityInstrumentationTestCase, templated by the activity class under test.
  • The constructor of the test class defines the activity under test class precisely.
One such test class or collection of test classes can be executed by InstrumentationTestRunner. Execute the following command (from command prompt).

adb shell am instrument -w aexp.calculator/android.test.InstrumentationTestRunner

The console of the command prompt reads like this:

aexp.calculator.tests.AddFunctionalTests:...
aexp.calculator.tests.SubFunctionalTests:...
Test results for InstrumentationTestRunner=......
Time: 11.448

OK (6 tests)

Meanwhile, the emulator main window flashes with action. Calculator2 is launched 4 times, each time the key input is automatically provided by the test case. All this is arranged by the Android specialization of TestCase/TestRunner.

Please observe the AndroidManifest.xml of the project. Check out the uses-library tag and the use of instrumentation tag, particularly the targetPackage attribute that refers to the application package under test (and not the Java package name of the test classes). InstrumentationTestRunner automagically looks for test classes that fit the test execution criteria (again, check out InstrumentationTestCase documentation for options), there was no need to organize the test classes into suites.

Note that the arrangement of this project is not typical. I mixed the test classes and the activity under test into the same application. This eliminates a lot of deployment problems that caused so much trouble for so many people when playing with the Android test framework. The typical deployment, however, is to place the test classes and application under test into separate application packages. I will get to that in the next post.

Wednesday, November 19, 2008

Controlling instrumentation

Those who are familiar with test frameworks could have felt something missing after the previous instrumentation example. The test did not finish very elegantly, both the test launcher and the test program were terminated and the results should be fished out from the logs. The reason is that when the instrumentation finishes, it terminates the entire application, not just the activity that was instrumented. Although application as a notion exists since the beginning of Android's public life, developers usually concentrate on Activities. Application is a bunch of artifacts packed into one apk file, having one manifest file, running in the same JVM slice (seemingly independent JVM simulated for each application by the Dalvik virtual machine), represented by the same Application object. Instrumentation works on application level (as opposed to activity level) and when an instrumentation is finished, the entire application is terminated. That's why our instrumentation launcher did not survive the end of instrumentation - as it lived in the same application, it was also terminated along with the instrumentation object and the target application.

If we want to build a decent automated test framework, we have to control it from a separate application. This example does just that.

You can download the example program from here.

In the package, you will find two application directories. The instrumentation1 directory contains our friends, the slightly modified calculator test target, the instrumentation object and the instrumentation launcher that we will not use now. The instrcontroller directory contains a separate application, our instrumentation controller that starts the instrumentation and receives the test result. Like this:


The only interesting part is the communication between the instrumentation object and the launcher. The instrumentation object has a finish() method that is seemingly capable of sending back a result Bundle to the instrumentation's starter but I was not able to figure out, how that bundle is received by the instrumentation's starter (my Android newsgroup thread also remained unanswered). Therefore I had no better idea than to create a not too elegant do-it-yourself method of sending back the test result using broadcast intents. If anyone knows how to do it right, please notify me.

And before I finish, something completely different.

I received a mail from the Phoload people that they launched their Android content and they asked me to advertise this fact in one of my posts. Certainly, it is a new market, there is a lot of risk taking here so the early birds definitely deserve help - however small mine can be. I did have my concerns, however, how this Phoload site would coexist e.g. with Android Marketplace but the Phoload people are convinced that they can carve out a nice piece of market by offering mobile applications for multiple platforms and by providing easier navigation and more visible place for applications. Let they be right and have success!

Sunday, November 16, 2008

Test-driving Android GUI

It happened again that I received an e-mail from somebody asking whether there is a way to automate the testing of Android user interfaces. I did not know so I went after the issue and to my utter surprise, I discovered that an entire unit test framework has been developed into Android while I looked the other way. In this blog entry, I will describe my experiences with the Android instrumentation framework.

You can download the example program from here.

The idea behind Android instrumentation framework is that there are instrumentation components that resemble a lot the usual Activities. The purpose of these instrumentation components, however, is to test-drive other, normal Activities. Let's see the example program!

Install the package in the bin directory as usual:

adb install instrumentation-debug.apk

If the package has been installed before, you first have to uninstall it. This is new in the 1.0 version of the SDK, previously the new installation would simply overwrite the old one.

adb uninstall aexp.instrumentation

Two applications appear in the application folder. "Calculator" is a primitive calculator application used as test target. You can launch it independently and try it yourself. If you launch "Calculator instrumentation launcher", you will be confronted with a button to launch Calculator instrumentation. Once you push it, the calculator launches but its input keypresses will come from the instrumentation component. You will see the input fields filled up automatically, the addition button pushed and the result displayed. Then both the calculator instance and the launcher will disappear but if you check the logs (adb logcat), you will see that the test was successful.


The key instruction is in CalculatorInstrumentationLauncher.java where the instrumentation component is launched:

startInstrumentation(
new ComponentName(
CalculatorInstrumentationLauncher.this,
CalculatorInstrumentation.class ),
null,
null );

This launches CalculatorInstrumentation component which is a descendant of android.app.Instrumentation. The instrumentation component resembles a normal Activity, except that it is able to drive user input of other activities. This time we launch the Calculator, feed in keypresses (sendCharacterSync()) and invoke one of its method to obtain the result. Note the usage of runOnMainSync() method when we invoke Calculator's method to obtain the result.

It is definitely cool that Android has a user interface-oriented unit testing framework built into the platform. Maybe I am ignorant but I am not aware of any other platform that has this capability. In order to Test Driven Development (TDD), we need a proper unit testing framework and Android has even JUnit integrated. More about that later.