December 20, 2014

Scripting Your Android Device

One of the issues that arose when Apple released the iPhone SDK earlier this year was the restriction on language interpreters:

No interpreted code may be downloaded and used in an Application except for code that is interpreted and run by Apple’s Published APIs and builtin interpreter(s)…An Application may not itself install or launch other executable code by any means, including without limitation through the use of a plug-in architecture, calling other frameworks, other APIs or otherwise.

Presumably, these terms are in place for security reasons — Apple and AT&T may be worried that un-vetted code might corrupt phones, consume large amounts of bandwidth, etc.

To date, Android has no such restriction, and if it will indeed be released as open source, it may be difficult for it to ever have such a restriction. Moreover, the Dalvik VM can be used with Java scripting languages. Today, we’ll take a peek at one such scripting language, discuss what languages may or may not work, and review the ramifications of scripting on the device.

My all-time favorite Java scripting language is Beanshell. To some extent, Beanshell was Groovy before Groovy. It gives you Java-compatible syntax with implicit typing and no compilation required. And, it isn’t all that big of a JAR to include — 143KB for the core.

To use Beanshell with Android, download the bsh-core.jar file, put it in a /lib folder in your project, and adjust your Eclipse settings or Ant script to reference that JAR during compilation and packaging. Note: I haven’t tried any of the add-on JARs for Beanshell — more on this below.

For example, here is a snippet of an Ant script that shows compiling a project and incorporating the Beanshell JAR in the build:

[sourcecode language=”xml”]


srcdir="."
destdir="${outdir-classes}"
bootclasspath="${android-jar}">


















[/sourcecode]

From there, using Beanshell on Android is no different than using Beanshell in any other Java environment:

  1. Create an instance of the Beanshell Interpreter class
  2. Set any “globals” for the script’s use via Interpreter#set()
  3. Call Interpreter#eval() to run the script and, optionally, get the result of the last statement

For example, here is the XML layout for the world’s smallest Beanshell IDE:

[sourcecode language=”xml”]

apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:id="@+id/eval"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Go!"
/>
android:id="@+id/script"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:singleLine="false"
android:gravity="top"
/>
[/sourcecode]

Couple that with the following activity implementation:

[sourcecode language=”java”]
package com.commonsware.android.andshell;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import bsh.Interpreter;

public class MainActivity extends Activity {
private Interpreter i=new Interpreter();

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

Button btn=(Button)findViewById(R.id.eval);
final EditText script=(EditText)findViewById(R.id.script);

btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String src=script.getText().toString();

try {
i.set(“context”, MainActivity.this);
i.eval(src);
}
catch (bsh.EvalError e) {
Toast.makeText(MainActivity.this, e.toString(),
2000).show();
}
}
});
}
}
[/sourcecode]

Compile and run it (after replacing the aforementioned Ant steps as mentioned with the first source listing in this post). You’ll get the IDE:

Android Beanshell "IDE"

In the editor, enter the following code:

[sourcecode language=”java”]
import android.widget.Toast;

Toast.makeText(context, “Hello, world!”, 5000).show();
[/sourcecode]

Note the use of context to refer to the activity when making the Toast. That is the global set by the activity to reference back to itself. You could call this global variable anything you want, so long as the set() call and the script code use the same name.

Then, click the Go! button, and you get:

Beanshell Executing

And now, onto the caveats, after a word from our sponsor…

First, not all scripting languages will work. For example, those that implement their own form of just-in-time (JIT) compilation, generating Java bytecodes on the fly, would probably have to be augmented to generate Dalvik VM bytecodes instead of those for stock Java implementations. Simpler languages that execute off of parsed scripts, calling Java reflection APIs to call back into compiled classes, will likely work better. Even there, though, not every feature of the language may work, if it relies upon some facility in a traditional Java API that does not exist in Dalvik — for example, there could be stuff hidden inside Beanshell or the add-on JARs that does not work on today’s Android.

Second, scripting languages without JIT will inevitably be slower than compiled Dalvik applications. Slower may mean users experience sluggishness. Slower definitely means more battery life is consumed for the same amount of work. So, building a whole Android application in Beanshell, simply because you feel it is easier to program in, may cause your users to be unhappy.

Third, scripting languages that expose the whole Java API, like Beanshell, can pretty much do anything the underlying Android security model allows. So, if your application has the READ_CONTACTS permission, I would expect any Beanshell scripts your application runs to have the same permission.

This gets to the heart of Apple’s concern. It is one thing for a user to place some amount of blind trust in an application she may have purchased through an “app store”. But if that application in turn can be extended via scripting, now there’s a whole new level of trust that is implied — can the extensions themselves be trusted? — that the user may be unaware of.

To quote a random superhero movie, “with great power comes great responsibility” (and, apparently, a series of sequels, but that’s beside the point…).

If you are going to use a scripting language in your Android application — or any application, for that matter — it is your responsibility to take reasonable steps to ensure that griefers, crooks, and other no-good-niks do not harm your users. If you don’t, then we may be stuck with the Apple-style paternalistic model, which at best damages the open source vision of Android, and perhaps destroys it outright. We as a developer community must police ourselves if we do not want others to police us. Perhaps, over time, we can work out a scripting “code of conduct” we can all agree to.

On the flip side, this means that an Android version of GreasePocket might actually be “street legal”.



  • cabernet1976

    It is so cool.

  • ecompositor

    Nice – Article

    You could incorporate this into your book to easily test out examples on the emulator.

    Your book is very well done.

  • http://commonsware.com/Android/ Mark Murphy

    @ecompositor

    One limitation of scripting is that you can’t register a script in AndroidManifest.xml. And you can’t script layout XML files, resources, or anything else that gets packaged in the APK. So, you always need some Java code as the entry points into your application (e.g., your activity) and probably some other files as well. You can’t create an Android application that is 100% scripted, at least with the current frameworks at our disposal.

    While I will probably add a chapter on scripting to an Android book (possibly the Advanced Android one I have on the drawing board), I am a little hesitant to make it the foundation of all the examples, particularly in the current book.

    And thanks for the feedback on the article and book!

  • Phil

    Maybe there should be, or Android is already on top of it, a program or company that validates a new program/game/app/etc. before a user can dl it so the user knows it’s gonna be safe

  • http://commonsware.com/Android/ Mark Murphy

    @Phil:

    My hope is that it’s more of a “seal of approval” vs. the Apple-style “it only runs if *we* say it runs”. Prospective buyers/downloaders can look for the seal of approval and make their decision based upon whether it’s there or not.

  • Scott Webster

    I’m hoping for the same thing. A “plays nice with other apps” type sticker. Lets people feel comfortable downloading or installing onto a device. The term open source scares people a little bit and that would go a long way to calm fears. Not required by any means, just looks good if Google and the OHA say it’s A-OK.

  • http://blogs.zdnet.com/Burnette/images/android-architecture-485.jpg Ed Burnette

    Nice example, I’m fond of BeanShell too. Of course with BeanShell on Android 1.0 you’ve got an interpreted interpreter. I wonder if pnuts would work?

    Good luck with the book.

  • http://commonsware.com Mark Murphy

    @Mr. Burnette

    From their FAQ: “Pnuts runs with on-the-fly bytecode compiler by default.” A Dalvik-aware Pnuts might work, but I suspect otherwise it’ll try executing ordinary Java bytecodes on Dalvik and run into issues.

    I think most Java scripting languages went this route (Jython, Groovy, JRuby, etc.), which may limit one’s options in the near term. That’s one of the reasons I went with Beanshell, figuring its execute-off-an-AST approach might well hold up on Android.

    “Good luck with the book.”

    Likewise!

  • http://rageagainstthestatusquo.com/blog Truedat101

    Great example! I’ve made a few changes on mine to support 1.5 ‘droid, but as is the promise of Java, the code still runs on the new platform without major changes. I’m trying myself to put together some nice use cases for scripting language use on the platform.

  • Pingback: URL

  • Pingback: candlemas bedimple betulaceae