User:Joel Reymont/Android Notes

From MozillaWiki
Jump to: navigation, search

Intro

This page collects my notes on porting Firefox to Android.

Current status

October 12, 2009

Via Chris Lord...

The headless branch of Mozilla that we work on renders to system memory and it is up to the embedder to get this to a texture however they like. We have our own embedding library that uses this with Clutter clutter-mozembed and we just upload each changed region to the texture.

A possible speed-up is to use a shared memory pixmap and texture-from-pixmap (or whatever the platform's equivalent is), which would remove the need to copy from system to video memory.

I wrote a (now out-dated, but still relevant) guide on building the Moblin browser components on my blog. There's also a short demo application I wrote that just uses mozilla-headless alone to take a screenshot of a web-page.

Unfortunately, there is no documentation beyond the standard Mozilla documentation and the source-code (and the above-mentioned links).

I think the easiest way to port Firefox to android would be to write an android-backend (which could well be based on our headless backend) - I didn't think that the Android native development kit was fully featured enough to allow something like this though?

...

You can't run Firefox with the headless backend as it is, it's designed to be embedded and for libxul to be used. The headless backend is also heavily dependent on GLib (for the main loop, signals and idle handlers).

If you wanted to run Firefox, you'd have to write a new backend, or modify an existing one, to provide the application framework. In the gtk2 and headless backends, this is GLib-based. The Gtk2 backend creates a GtkWindow and that is how the user sees and interacts with the application. On the headless backend, there is no visible window. It can only be used for embedding via libxul.

---

Chris has a presentation that explains the headless branch a bit and instructions on how to build it.

---

There's an existing bug that deals with headless UI.

Previous statuses

Android Notes

Can Android run a completely native app?

According to Anatomy and physiology and Android Fundamentals, each Android process has its own Java virtual machine (VM), so application code runs in isolation from the code of all other applications.

Android processes (Java VMs) communicate with the Home App and various system services via the Binder IPC described using Android Interface Definition Language (AIDL).

The Home Application (Launcher) is the app that displays the Android desktop and allows the launching of other apps by clicking on their icons.

An activity presents a visual user interface for one focused endeavor the user can undertake. For example, an activity might present a list of menu items users can choose from or it might display photographs along with their captions. A text messaging application might have one activity that shows a list of contacts to send messages to, a second activity to write the message to the chosen contact, and other activities to review old messages or change settings.

A task is a group of related activities. May include activities from other apps, running in other processes, e.g. map viewer. A task has an activity stack.

Any given activity

  • Has a content view that is created using a layout resource.
  • Can be floating or embedded inside another activity.
  • Has a pause callback invoked when the user is leaving the activity.
  • Is visible when it's at the top of the stack for the current task.
  • Is paused when there's another activity on top of it but remains visible. It has lost focus but remains completely alive and attached to the window manager but can be killed in low memory conditions.
  • Is stopped when it's completely obscured by another activity. The window is hidden and activity will be killed when memory is needed elsewhere.
  • Has a finish callback that may be called when the activity is being evicted from memory.
  • Generates a thumbnail of the activity via a callback that's called before pausing.
  • Receives trackball and key events via callbacks.

When an app icon is clicked, the Home app uses the package manager to retrive app info and package it into an intent, an asynchronous message with information necessary to start a given activity. The Home app then uses startActivity, a method of Context to launch the activity. Context is an abstract class whose implementation is provided by the Android system.

The launch request is sent via the Binder IPC to the Activity Manager Service which uses the Process class to start an Activity Thread in a new instance of the Dalvik Java VM via the Zygote.

The mechanism above assumes Java is used to implement activities. For example, the first non-option argument to Zygote should be a class name in the system class path. Also, aidl command-line tool only generates Java code for Binder IPCs.

While I'm confident that I could write a native application that plugs into the Android event stream as well as the activity, window, etc. managers, doing so would require adding C++ code generation to the aidl tool and implementing the portion of the Android Java SDK that deals with the activity life cycle.

Still, it will not be possible to launch a native app from the Home app due to the Java assumptions in the core launching mechanism, so a Java shim will be required. There may be a great deal of flexibility in running a completely native app since, for example, OpenGL ES is not required and the screen (surface) buffer can be written to directly.

Another alternative is to let the Java shim handle act as an invisible activity that uses the NDK to pass events to native code running in a shared library. OpenGL ES will be required in the latter case to write to a pre-configured screen context.

To summarize...

The Android activity and application launching mechanism assumes Java. Apps run in separate processes (and Java VMs) and talk to system services via the Binder IPC.

There's no C++ code generation from the AIDL that describes the IPC so we'll need to write that. We'll also need to write a whole bunch of other C++ code that covers the portion of the Android Java SDK that deals with the application life cycle to be able to suspend, resume, stop and generally be a good citizen of the Android desktop (Home application).

We won't be able to launch a native app right from the Android desktop so a Java shim will be needed. If we go the long route and talk to system services using Binder IPC, then we won't need OpenGL ES and will be able to write directly to the screen buffer (surface). If we make our Java shim a proper Android activity that feeds all kinds of events to our shared library using the NDK (JNI), then we'll be required to use OpenGL ES to write to a pre-configured context as well as potentially implement calls back to Java from our native libraries.

I volunteer to write the C++ from AIDL code generator, as well as re-implement necessary portions of the Android Java SDK in C++. Many a C++ programmer will thank us for that and announcing a Mozilla Android C++ SDK may actually force Google's hand and make them release something official.

Going the officially sanctioned way and using a proper Java activity shim with the NDK will positively be easier initially but may turn out to be a pain down the road what with OpenGL ES and all the data flying back and forth between Java and native code.

Sample external application Makefile

Here's how you can build your own native apps without being shoehorned into the Android SDK build structure. I got this by massaging the output of make showcommands.

SDK := /Volumes/android/mydroid
TOOLCHAIN := $(SDK)/prebuilt/darwin-x86/toolchain/arm-eabi-4.2.1
ABILIB := $(TOOLCHAIN)/lib/gcc/arm-eabi/4.2.1/
BIN := $(TOOLCHAIN)/bin
C++ := $(BIN)/arm-eabi-g++
CC := $(BIN)/arm-eabi-gcc

INCLUDE := \
  -I$(SDK)/system/core/include \
  -I$(SDK)/hardware/libhardware/include \
  -I$(SDK)/hardware/libhardware_legacy/include \
  -I$(SDK)/hardware/ril/include \
  -I$(SDK)/dalvik/libnativehelper/include \
  -I$(SDK)/frameworks/base/include \
  -I$(SDK)/frameworks/base/opengl/include \
  -I$(SDK)/external/skia/include \
  -I$(SDK)/out/target/product/generic/obj/include \
  -I$(SDK)/bionic/libc/arch-arm/include \
  -I$(SDK)/bionic/libc/include \
  -I$(SDK)/bionic/libstdc++/include \
  -I$(SDK)/bionic/libc/kernel/common \
  -I$(SDK)/bionic/libc/kernel/arch-arm \
  -I$(SDK)/bionic/libm/include \
  -I$(SDK)/bionic/libm/include/arch/arm \
  -I$(SDK)/bionic/libthread_db/include \
  -I$(SDK)/frameworks/base/cmds/demo \
  -I$(SDK)/system/core/include/arch/linux-arm \
  -include $(SDK)/system/core/include/arch/linux-arm/AndroidConfig.h \
  $(NULL)
  
CFLAGS = \
  -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__ -DANDROID \
  -DSK_RELEASE -DNDEBUG -UDEBUG \
  -mthumb -mthumb-interwork -msoft-float -march=armv5te -mtune=xscale \
  -fpic -fno-exceptions -ffunction-sections -funwind-tables -fstack-protector \
  -fno-short-enums -fmessage-length=0 -finline-functions -fno-inline-functions-called-once \
  -fgcse-after-reload -frerun-cse-after-loop -frename-registers -fvisibility-inlines-hidden \
  -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -fno-rtti \
  -W -Wall -Wno-multichar -Wno-unused -Werror=return-type -Wstrict-aliasing=2 \
  -Wnon-virtual-dtor -Werror=return-type \
  -MD -Os -g \
  $(NULL)
 
LIBDIR := $(SDK)/out/target/product/generic/obj/lib

LIBS := -lui -llog -lutils -lc -lstdc++ -lm $(LIBDIR)/crtbegin_dynamic.o 
  
LDFLAGS := \
  -nostdlib -Bdynamic -Wl,--no-undefined -Wl,-T,$(SDK)/build/core/armelf.x \
  -Wl,-dynamic-linker,$(SDK)/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc \
  -L$(LIBDIR) -Wl,-rpath-link=$(LIBDIR) \
  $(NULL)
  

all: demo

demo: surfaceflinger.o demo.o
	$(C++) $(LDFLAGS) -o demo $(LIBS) surfaceflinger.o demo.o \
		$(ABILIB)/interwork/libgcc.a $(LIBDIR)/crtend_android.o

surfaceflinger.o: 
	$(C++) $(INCLUDE) -c $(CFLAGS) surfaceflinger.cpp

demo.o:
	$(CC) $(INCLUDE) -c $(CFLAGS) demo.c

clean:
	rm -f demo *.o	

Introduction to the Android Window System

Slides and demo code.

Building and running the OpenGL ES NDK example

Here's how to build and run the san-angeles application from NDK 1.6. This program demonstrates how to use a GLSurfaceView from Java along with native OpenGL calls to perform frame rendering.

 
cd /Volumes/android/mydroid/
. build/envsetup.sh 
including vendor/aosp/vendorsetup.sh
lunch sdk-eng

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=1.6
TARGET_PRODUCT=sdk
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=darwin
HOST_BUILD_TYPE=release
BUILD_ID=Donut
============================================

First, we need to build the shared library of native code

cd ~/work/android/android-ndk-1.6_r1/
build/host-setup.sh
make APP=san-angeles

Android NDK: Building for application 'san-angeles'    
Compile thumb  : sanangeles <= apps/san-angeles/project/jni/importgl.c
Compile thumb  : sanangeles <= apps/san-angeles/project/jni/demo.c
Compile thumb  : sanangeles <= apps/san-angeles/project/jni/app-android.c
SharedLibrary  : libsanangeles.so
Install        : libsanangeles.so => apps/san-angeles/project/libs/armeabi

Next, we need to make sure we can build the Java portion of the project

android update project --path apps/san-angeles/project

Updated local.properties
File build.xml is too old and needs to be updated.
Added file apps/san-angeles/project/build.xml

Now, let's build the debug release of project

cd apps/san-angeles/project/
ant debug

Buildfile: build.xml
    [setup] Project Target: Android 1.6
    [setup] API level: 4

dirs:
     [echo] Creating output directories if needed...

resource-src:
     [echo] Generating R.java / Manifest.java from the resources...

aidl:
     [echo] Compiling aidl files into Java classes...

compile:
    [javac] Compiling 1 source file to /Users/joelr/Work/android/android-ndk-1.6_r1/apps/san-angeles/project/bin/classes

dex:
     [echo] Converting compiled files and external libraries into bin/classes.dex...

package-resources:
     [echo] Packaging resources
 [aaptexec] Creating full resource package...

debug-sign:

package:
[apkbuilder] Creating .DemoActivity-debug-unaligned.apk and signing it with a debug key...
[apkbuilder] Using keystore: /Users/joelr/.android/debug.keystore
[apkbuilder] /Users/joelr/Work/android/android-ndk-1.6_r1/apps/san-angeles/project/bin/classes.dex => classes.dex
[apkbuilder] /Users/joelr/Work/android/android-ndk-1.6_r1/apps/san-angeles/project/libs/armeabi/libsanangeles.so => lib/armeabi/libsanangeles.so

debug:
     [echo] Running zip align on final apk...
     [echo] Debug Package: bin/.DemoActivity-debug.apk

BUILD SUCCESSFUL
Total time: 1 second

Finally, let's install the app on the device

adb install -r bin/.DemoActivity-debug.apk

1107 KB/s (13707 bytes in 0.012s)
	pkg: /data/local/tmp/.DemoActivity-debug.apk
Success

To run the app click on the "tab" at the bottom of the Android emulator screen and then on the DemoActivity icon in the list of applications. Enjoy!

Drawing and input from native code

Via Jack Palevich...

Input is done by subclassing a view, and override the various input methods. Make sure your view can get focus, using setFocusableInTouchMode(true); 
Otherwise you won't get input events. You could use any View class as your base class, but I suggest that you subclass GLSurfaceView because that makes it easier to use OpenGL graphics for drawing.

Probably the fastest way to get output from native code is done using OpenGL ES, which is newly available in NDK 1.6. Create a texture the size of the 
screen ( it has to be a power of two on each side, so 512 x 256 if your screen is 320 x 240.) Then update the texture and render it to the screen as a screen aligned quad.

For native OpenGL ES APIs the drawing surface is implicit in the thread. For Quake, GLSurfaceView's rendering thread sets up the context and calls the QuakeView's Renderer, which calls the native code, which makes the native GL calls.

Because the context is implicit in the thread, there's nothing that needs to be passed between Java and native code.

The downside of this approach is that you can't wrap the native GL calls as easily as you can wrap the JSR 239 Java OpenGL APIs. It's sometimes useful to wrap the GL APIs to keep track of the matrix stack, or log debugging messages, or check for error messages.

Building Android on Snow Leopard

1. Get the sources.

2. Apply these changes

cd system/core && git pull git://android.git.kernel.org/platform/system/core refs/changes/45/11845/2 && cd ../..
cd external/qemu && git pull git://android.git.kernel.org/platform/external/qemu refs/changes/46/11846/2 && cd ../..
cd prebuilt && git pull git://android.git.kernel.org/platform/prebuilt refs/changes/14/12014/1 && cd ..
cd prebuilt && git pull git://android.git.kernel.org/platform/prebuilt refs/changes/75/12075/1 && cd ..
cd build && git pull git://android.git.kernel.org/platform/build refs/changes/74/12074/1 && cd ..
cd build && git pull git://android.git.kernel.org/platform/build refs/changes/93/12093/1 && cd .. 

The following almost works but resets the head each time. Both of the last two changes are needed but the Java one gets written over by the very last change. Also, changes downloaded this way get blown away on repo sync.

repo download platform/system/core 11845/2
repo download platform/external/qemu 11846/2
repo download platform/prebuilt 12014/1
repo download platform/prebuilt 12075/1
repo download platform/build 12074/1
repo download platform/build 12093/1

3. lunch sdk-eng && make sdk

You should see something like this if everything goes well

ls out/host/darwin-x86/sdk/
android-sdk_eng.joelr_mac-x86		sdk_deps.mk
android-sdk_eng.joelr_mac-x86.zip

One more step is needed

export PATH=$PATH:`pwd`/out/host/darwin-x86/sdk/android-sdk_eng.joelr_mac-x86/tools/
pushd prebuilt && ln -s darwin-x86 darwin-x86_64 && popd
pushd out/host/darwin-x86/sdk/android-sdk_eng.joelr_mac-x86/tools/lib
ln -s x86 x86_64 && popd

Verify that everything works

android list targets

Available Android targets:
id: 1
     Name: Android 1.6
     Type: Platform
     API level: 4
     Revision: 1
     Skins: HVGA (default), QVGA, WVGA800, WVGA854

and

     
android list avd

Available Android Virtual Devices:

4. You are set and you can now run the emulator, e.g.

export AND_HOME=/Volumes/android/mydroid
export AND_SDK=$AND_HOME/out/host/darwin-x86/sdk/android-sdk_eng.joelr_mac-x86
export AND_BIN=$AND_SDK/tools
export AND_IMG=$AND_SDK/platforms/android-1.6/images
export AND_KERNEL=$AND_IMG/kernel-qemu
export PATH=$PATH:$AND_BIN

hdiutil attach ~/work/android/android.dmg -mountpoint /Volumes/android
cd /Volumes/android/mydroid
. ./build/envsetup.sh
emulator

You should see home screen in a minute or less.

You can also run the emulator in verbose mode if you want to troubleshoot or just see lots of interesting output, e.g.

emulator -verbose -logcat "*:v"

emulator: found Android build root: /Volumes/android/mydroid
emulator: found Android build out:  /Volumes/android/mydroid/out/target/product/generic
emulator:     locking user data image at /Volumes/android/mydroid/out/target/product/generic/userdata-qemu.img
emulator: selecting default skin name 'HVGA'
emulator: autoconfig: -skin HVGA
emulator: autoconfig: -skindir /Volumes/android/mydroid/development/emulator/skins
emulator: keyset loaded from: /Users/joelr/.android/default.keyset
emulator: trying to load skin file '/Volumes/android/mydroid/development/emulator/skins/HVGA/layout'
emulator: skin network speed: 'full'
emulator: skin network delay: 'none'
emulator: no SD Card image at '/Volumes/android/mydroid/out/target/product/generic/sdcard.img'
emulator: registered 'boot-properties' qemud service
emulator: registered 'boot-properties' qemud service
emulator: Adding boot property: 'qemu.sf.lcd_density' = '160'
...

Vlad's Android Notes

Can be found here. Italic text