This talk goes over the different ways that you can run code on an Android system. It’s a big stack, so there are many places to do it: for most situations, you can add your own code almost anywhere.
Programming languages: if you have control over the platform, you can program in anything. Java and C are supported out of the box and even supported in apps. You could also write an app in js and run it in an existing js runtime, but Karim has heard from users that the look and feel ends up not being quite the same as native. Zymaran is a framework to run mono on Android, which you can add to your stack. But of course you can add anything to the stack.
In the App world, there are components. An App exists of several components which are started whenever needed, stopped automatically, and can be started by other apps. To interact between them, an Intent is called, which is basically an async message to another component. An App is specified with a manifest file, which declares the components and permissions. Remote procedure calls are done through Binder, which also talks to the non-Java world.
It’s important to realize that a component can be shut down at any time. You cannot have code that is running all the time. There are very few exceptions: the Phone app has the ‘android:persistent = true’ property: it will be restarted all the time when it dies for whatever reason. This is done with the Application component, which does have a kind of main() while normal components don’t. An app like this cannot be put on the app store. Otherwise, you have to present yourself as a foreground service (e.g. Skype): they put a sticky notification icon and this keeps them alive as long as that icon is there. You can also register a sync adapter, which is launched regularly. Similarly you can make a backup agent.
Components cannot directly access the outside world directly, always through Binder. So if you need anything special, you need to do it in several places.
Native utilities and daemons
In the lunch specification Android.mk, include $(BUILD_EXECUTABLE) and it will build your source as an executable in the usual Linux way. To start it, you can add it to init.rc in system/core/rootdir similar to upstart and other init systems. You can also write a Java app that runs a command line, so when this app is opened it will run your C utility.
You can also write daemons in Java. It is started from the command line just like any other java program, using app_process (which makes sure it gets a Zygote process). You build it with include $(BUILD_JAVA_LIBRARY) in Android.mk (or do it from Eclipse). Because it’s in java-land, you can talk straight to the framework. Note that this doesn’t mean that you can just create an Intent or something like that. This java application also has access to APIs that are not available to app developers. Anything that is marked with @hide in the sources is not available in the SDK, but is accessible when you build as part of AOSP. See framework/base/cmds for examples.
Even better, you can write an application as a System Service. These are started at boot and stay alive forever. These services give you the power to connect the app world to the linux world. It can be written in Java (as an app or a normal Java library) or in C (for time-critical things). How system services work is a bit complicated. On the app side, you don’t talk to the service directly, but first request a reference to it, which starts a service in SystemServer, then you can communicate with it. On that side, it is split into a HAL layer so the HAL can be proprietary. To create a system service, you create an aidl file that specifies its API and include that explicitly in your device.
Shell scripts: since 4.0 it’s MirBSD Korn Shell. It can be started from init.rc, and is a nice way to do a little extra when starting a daemon.
The init.rc contains two types of things: actions and service declarations. Actions start with ‘on’. They have a list of commands to run. Services have a single executable and a list of properties. One property is the class, which determines when the service is started, e.g. class_start core and class_start main. system/core/root/init.rc is common for all devices, device/vendor/product/init.rc contains the device-specific stuff. They’re just concatenated – the syntax is declarative.
C libraries can be loaded fro Java code explicitly with System.loadLibrary.
When extending the API, you can also create an SDK add-on that you can distribute to third parties.
And you can also just build a linux distro in the normal way and install it in the same rootfs as Android. Since Android is in /system you will have no filename conflicts. You can communicate through e.g. sockets or IP. To build it, you add it somewhere in the AOSP tree and add an Android.mk and add the commands to a target that is added to the ALL_PREBUILT variable (which doesn’t exist anymore in 4.1 so you have to add it). Caveat: there is no /etc/passwd, access to files is defined in system/core/include/private/android_system_config.h; you have to add /lib/* to the list of executable files or ld.so will end up not being executable.