ndk primer (wearable devcon 2014)

47
NDK Primer Ron Munitz Founder & CEO - The PSCG Founder & CTO - Nubo [email protected] [email protected] https://github.com/ronubo/ Wearables DevCon March 2014 @ronubo PSCG

Upload: ron-munitz

Post on 15-Aug-2015

64 views

Category:

Software


6 download

TRANSCRIPT

Page 1: NDK Primer (Wearable DevCon 2014)

NDK PrimerRon Munitz

Founder & CEO - The PSCGFounder & CTO - Nubo

[email protected]@android-x86.orghttps://github.com/ronubo/

WearablesDevConMarch 2014

@ronubo

PSCG

Page 2: NDK Primer (Wearable DevCon 2014)

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/

© Copyright Ron Munitz 2014

PSCG

Page 3: NDK Primer (Wearable DevCon 2014)

About://Ron Munitz

● Distributed Fault Tolerant Avionic Systems○ Linux, VxWorks, very esoteric libraries, 0’s and 1’s

● Highly distributed video routers○ Linux

● Real Time, Embedded, Server bringups○ Linux, Android , VxWorks, Windows, devices, BSPs, DSPs,...

● Distributed Android○ Rdroid? Cloudroid? Too busy working to get over the legal naming, so

no name is officially claimed for my open source

● What currently keeps me busy:○ Running the PSCG, a Embedded/Android consulting and Training ○ Managing R&D at Nubo○ Lecturing at Afeka’s college of Engineering ○ Amazing present, endless opportunities. (Wish flying took less time)

@ronubo

PSCG

Page 4: NDK Primer (Wearable DevCon 2014)

Agenda

● Part I: Java○ Java Native Interface (JNI) - Theory○ Hello World Tutorial - Practice

● Part II: JNI in the AOSP● Part III: Native Android Apps

Page 5: NDK Primer (Wearable DevCon 2014)

Part IThe Java Native Interface

PSCG

Page 6: NDK Primer (Wearable DevCon 2014)

IntroductionThe JNI is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly. All JVM implementation need to implement the JNI specification to allow compatibility with native code libraries.

JNI to Java ⇔ __asm to C

Page 7: NDK Primer (Wearable DevCon 2014)

Motivation● The standard Java class library does not support the

platform-dependent features needed by the application.○ For example NEON, SSSE3, ...

● You already have a library written in another language, and wish to make it accessible to Java code through the JNI.

● You want to implement a small portion of time-critical code in a lower-level language such as assembly.

Page 8: NDK Primer (Wearable DevCon 2014)

Wearable Motivation● Reduce CPU cycles (and battery drain rate!) on

intensive computations

● Add designated features for simple add-on hardware without having to support an entire ecosystem for that

● Media processing in Android: All is being done natively this way or another

● Porting legacy code

● Protecting your logic and algorithms from rev-eng

Page 9: NDK Primer (Wearable DevCon 2014)

JNI Pitfalls● Subtle errors in the use of JNI can destabilize the entire JVM in

ways that are very difficult to reproduce and debug.

● An application that relies on JNI loses the platform portability Java offers (a partial workaround is to write a separate implementation of JNI code for each platform and have Java detect the operating system and load the correct one at runtime).

● The JNI framework does not provide any automatic garbage collection for non-JVM memory resources allocated by code executing on the native side. Consequently, native side code (such as assembly language) must assume the responsibility for explicitly releasing any such memory resources that it itself acquires.

Page 10: NDK Primer (Wearable DevCon 2014)
Page 11: NDK Primer (Wearable DevCon 2014)

JNI environment pointers

The JNI interface is organized like a C++ virtual function table. It enables a VM to provide multiple versions of JNI function tables. The JNI interface pointer is only valid in the current thread. Native methods receive the JNI interface pointer as an argument. The VM is guaranteed to pass the same interface pointer to a native method when it makes multiple calls to the native method from the same Java thread.

Page 12: NDK Primer (Wearable DevCon 2014)

Loading and linking native methodsNative methods are loaded with the System.loadLibrary() method. In the following example, the class initialization method loads a platform-specific native library in which the native method f is defined:

class Cls { native boolean f(int i, String s);

static { System.loadLibrary(“pkg_Cls”); } } The library pkg_Cls is platform specific. On a Unix derived system it will be named libpkg_Cls.so and on windows system it will be named pkg_Cls.dll.

Page 13: NDK Primer (Wearable DevCon 2014)

Loading and linking native methods(cont.)

Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:

● the prefix Java_● a mangled fully-qualified class name● an underscore (“_”) separator● a mangled method name

The signature of function f above is, as created by javah:

JNIEXPORT jboolean JNICALL Java_Cls_f(JNIEnv *, jobject, jint, jstring);JNIEnv * - pointer to the JNI interface

jobject - a pointer to the calling java class for a static method or a pointer to the calling java object for non-static method.

Page 14: NDK Primer (Wearable DevCon 2014)

Referencing java objects● Primitive types, such as integers, characters, and so on, are

copied between Java and native code (call by value)● Arbitrary Java objects, on the other hand, are passed by

reference.● The VM must keep track of all objects that have been passed to

the native code, so that these objects are not freed by the garbage collector.

● The native code, in turn, must have a way to inform the VM that it no longer needs the objects.

● The JNI divides object references used by the native code into two categories: local and global references. ○ Local references are valid for the duration of a native method

call, and are automatically freed after the native method returns.

○ Global references remain valid until they are explicitly freed.

Page 15: NDK Primer (Wearable DevCon 2014)

Referencing java objects(Cont.)

● Objects are passed to native methods as local references. All Java objects returned by JNI functions are local references.

● The JNI allows the programmer to create global references from local references. JNI functions that expect Java objects accept both global and local references. A native method may return a local or global reference to the VM as its result.

● Since the VM needs a certain amount of space to keep track of a local reference, creating too many local references may cause the system to run out of memory.

● Local references are only valid in the thread in which they are created.

● Local references are automatically garbage collected once the function returns.

Page 16: NDK Primer (Wearable DevCon 2014)

Invocation API

Used to call java from c/c++ code. The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native thread works just like an ordinary Java thread running inside a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.

Page 17: NDK Primer (Wearable DevCon 2014)

Accessing fields and methods● The JNI allows native code to access the fields and to call the

methods of Java objects. ● The JNI identifies methods and fields by their symbolic names and

type signatures. ● A two-step process factors out the cost of locating the field or

method from its name and signature. For example, to call the method f in class cls, the native code first obtains a method ID, as follows:

jmethodID methodId = env->GetMethodID(cls, “f”, “(ILjava/lang/String;)D”);

The native code can then use the method ID repeatedly without the cost of method lookup, as follows: jdouble result = env->CallDoubleMethod(obj, methodID, 10, str);

Page 18: NDK Primer (Wearable DevCon 2014)

Exception handlingThere are two ways to handle an exception in native code:

● The native method can choose to return immediately, causing the exception to be thrown in the Java code that initiated the native method call.

● The native code can clear the exception by calling ExceptionClear(), and then execute its own exception-handling code.

After an exception has been raised, the native code must first clear the exception before making other JNI calls.

Page 19: NDK Primer (Wearable DevCon 2014)

JNI types and data structuresJava Type Native Type Description

boolean jboolean unsigned 8 bits

byte jbyte signed 8 bits

char jchar unsigned 16 bits

short jshort signed 16 bits

int jint signed 32 bits

long jlong signed 64 bits

float jfloat 32 bits

double jdouble 64 bits

Page 20: NDK Primer (Wearable DevCon 2014)

JNI types and data structures

Page 21: NDK Primer (Wearable DevCon 2014)

JVM Type SignaturesType Signature Java Type

Z boolean

B byte

C char

S short

I int

J long

F float

D double

L fully-qualified-class ; fully-qualified-class

[ type type[]

( arg-types ) ret-type method type

For example, the Java method:

long f(int n, String s, int[] arr);

has the following type signature:

(ILjava/lang/String;[I)J

Page 22: NDK Primer (Wearable DevCon 2014)

JNI types and data structuresJava Type Native Type Description

boolean jboolean unsigned 8 bits

byte jbyte signed 8 bits

char jchar unsigned 16 bits

short jshort signed 16 bits

int jint signed 32 bits

long jlong signed 64 bits

float jfloat 32 bits

double jdouble 64 bits

Page 23: NDK Primer (Wearable DevCon 2014)

JNI types and data structures

Page 24: NDK Primer (Wearable DevCon 2014)

Tutorial 1: DIY HelloWorld

Linux_x86-64/Oracle JDK 7.0/JNI

Page 25: NDK Primer (Wearable DevCon 2014)

Step 1: HelloWorld.javaclass HelloWorld{

private native void print();public static void main(String[] args){

new HelloWorld().print();}static {

System.loadLibrary("HelloWorld"); // Note: No “lib”, No “.so”.}

}

Compile: javac HelloWorld.java .Run: java HelloWorldFail: java.lang.UnsatisfiedLinkError: no HelloWorld in java.library.path . Not surprising…

Page 26: NDK Primer (Wearable DevCon 2014)

Step 2: Auto Generate Headers~/edu/jni$ javah HelloWorld # Note: javah takes a class name:~/edu/jni$ cat HelloWorld.h /* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class HelloWorld */

#ifndef _Included_HelloWorld

#define _Included_HelloWorld

#ifdef __cplusplus

extern "C" {

#endif

/* Class: HelloWorld Method: print Signature: ()V */

JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject); // “Java” - because I didn’t use a package.

#ifdef __cplusplus

}

#endif

#endif

Page 27: NDK Primer (Wearable DevCon 2014)

Step 3: HelloWorld.c #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj) { printf("Hello World!\n"); }

Compile: gcc -shared -fPIC -I<YourJvmIncludePaths> HelloWorld.c -o libHelloWorld.soRun: In a couple of slides.Fail: Easily. GCC flags are subtle.

Page 28: NDK Primer (Wearable DevCon 2014)

Step 3.5: Build the Shared Library

Let’s have a look at the following concrete example: gcc -shared -fPIC \ -I/usr/lib/jvm/jdk1.7.0/include/linux -I/usr/lib/jvm/jdk1.7.0/include \HelloWorld.c -o libHelloWorld.so

-shared: Create a shared object. (In Windows: a “DLL”)-fPIC: Position Independent Code - Always create Shared Object with that flag.-I<et. al>: Location of jni.h, and files included from there. -o libHelloWorld.so: Everything between “lib” and “.so” must match the name in loadLibrary().

Page 29: NDK Primer (Wearable DevCon 2014)

Step 4: Run

java -Djava.library.path=. HelloWorld

Note: It is important to tell java where to look for your shared libraries. Otherwise you will see the same error listed in Step 1.

Hello World!

Page 30: NDK Primer (Wearable DevCon 2014)

Part IISystemServer’s native side:

libandroid_services

PSCG

Page 31: NDK Primer (Wearable DevCon 2014)

The NDK in a nutshell

● Available as a JNI “SDK” to Android application developers.○ In the platform itself there is no need for it, just as

there is no need for the Android SDK● Eases porting of existing Linux code to

Android○ As long as it is not heavily depended on glibc

features, X, QT/GTK etc.● Enables common code for multiple

architectures ○ On the nominal portable C/C++ code case ○ @see Application.mk

Page 32: NDK Primer (Wearable DevCon 2014)

JNI in the Android platform

● @see frameworks/base/services/java/com/android/server/SystemServer.java

● This is where the Java Android OS starts.● And it is heavily relying on native functions

Page 33: NDK Primer (Wearable DevCon 2014)

SystemServer.java revisitedpublic class SystemServer { private static final String TAG = "SystemServer"; //...

private static native void nativeInit(); /*Called to initialize native system services.*/ public static void main(String[] args) { // ...

// Set runtime libary property (Dalvik/ART/..) ...

// Set initial system time (to workaround some negative value bugs) ...

// Enable profiler snapshot if profiler is enabled ...

// Take care of HeapSize for the System Server which never dies ... // ...

System.loadLibrary("android_servers"); Slog.i(TAG, "Entered the Android system server!");

nativeInit(); /* Initializes native services - This is a JNI call to frameworks/base/services/jni/com_android_server_SystemServer.cpp …

See next slide*/

ServerThread thr = new ServerThread();

thr.initAndLoop(); // Note to self: so long init2()!!!

}

}

Page 34: NDK Primer (Wearable DevCon 2014)

com_android_server_SystemServer.cpp

● Path: frameworks/base/services/jni/● namespace: Android● C, C++, java, JNI:

○ com/android/server/SystemServer.java○ com_android_server_SystemServer.cpp○ Note: Name convention should suffice.

However, when adding to the SystemServer, it is advised to also jniRegisterNativeMethods (@see libnativehelper/…)

● As per the code itself… static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) { char propBuf[PROPERTY_VALUE_MAX]; property_get("system_init.startsensorservice", propBuf, "1"); if (strcmp(propBuf, "1") == 0) { SensorService::instantiate(); // Start the sensor service which is pure native } // @see frameworks/native/services/sensorservice}

Page 35: NDK Primer (Wearable DevCon 2014)

onload.cpp

● jint JNI_OnLoad(JavaVM* vm, void* reserved) Called by default by the JVM upon System.loadLibrary()

● Loads the JNI Environment (uses JNI_VERSION_1_4)● Registers native servers by calling

register_android_server_<Name>(env); <Name> can be one of:PowerManagerService SerialServiceInputApplicationHandle InputWindowHandleInputManager LightsServiceAlarmManagerService UsbDeviceManagerUsbHostManager VibratorServiceSystemServer location_GpsLocationProvider location_FlpHardwareProvider connectivity_VpnAssetAtlasService ConsumerIrService

Page 36: NDK Primer (Wearable DevCon 2014)

register_android_server_<Name> and jniRegisterNativeMethods

● As we saw, libandroid_servers’s JNI_OnLoad() registers each of the JNI servers.

● Each explicitly registers the exported JNI constructs, by providing their signatures to libnativehelper’s jniRegisterNativeMethods() method

● Which in turns calls JNI’s RegisterNatives() method.● This is also essential because the AOSP uses naming

conventions that do not start with the “Java_” prefix, and therefore cannot be invoked unless explicitly registered by RegisterNatives()

Page 37: NDK Primer (Wearable DevCon 2014)

JNI_onLoad()

● The context of the VM is available only in the called thread.

● Which means that calling back Java is possible only if:○ It is a call back from a thread invoked by Java○ Somewhere a reference is stored globally for all/for

relevant threads● Do JVM caching on onLoad()● Free on onUnload()

Page 38: NDK Primer (Wearable DevCon 2014)

Part IIIWriting an Android App

PSCG

Page 39: NDK Primer (Wearable DevCon 2014)

Native project directory structure

● Source Control:○ ./ - Root. Home of manifest, ant/gradle, ...○ src - Java source files○ lib - Libaries. Including generated libraries and .so’s○ res - Resources○ assets - Assets (raw resources etc.)○ jni - Native code. Home of *.c*, *.h*, *.mk

■ Android.mk - Makefile■ Application.mk - Application definition (APP_*)

● Generated○ gen - generated java files○ obj - Native object files○ bin - Binaries

Page 40: NDK Primer (Wearable DevCon 2014)

NDK App development with Android

1. Java code - as in Java’s JNI.2. Native code - as in Java’s JNI3. javah - provide class path (bin/…)4. Makefiles

a. Android.mk - Android makefile.i. e.g. include $(BUILD_SHARED_LIBRARY)

b. Application.mk - Application definitionsi. e.g. APP_ABI := all -

5. ndk-build - builds native code6. ant/gradle - builds APK and bundles .so’s in

lib

Page 41: NDK Primer (Wearable DevCon 2014)

Tutorial 2: DIY HelloWorld

${NDK}/samples/hello-jni

Page 42: NDK Primer (Wearable DevCon 2014)

jni/Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello-jniLOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := x86 armeabi

obj/local/x86/libhello-jni.soobj/local/armeabi/libhello-jni.so

ndk-build

Page 43: NDK Primer (Wearable DevCon 2014)

src/com/example/hellojni/HelloJni.java

package com.example.hellojni;

import android.app.Activity;

import android.widget.TextView;

import android.os.Bundle;

public class HelloJni extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv = new TextView(this);

tv.setText( stringFromJNI() ); setContentView(tv);

}

public native String stringFromJNI(); public native String unimplementedStringFromJNI(); // Catch: Call --> exception static { System.loadLibrary("hello-jni"); }}

Page 44: NDK Primer (Wearable DevCon 2014)

The Native App Glue

● Enables writing fully native applications○ That is 0 java files

● Usually good for porting games● Or porting other heavy event machine logic● Requires implementation of your own Looper● Requires a manifest declaration:

<activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <!-- Tell NativeActivity the name of or .so --> <meta-data android:name="android.app.lib_name" android:value="native-activity" />

...

Page 45: NDK Primer (Wearable DevCon 2014)

Publishing Applications

● Two approaches each has its pros and cons● The first: Compile with all the libraries you

can (APP_ABI := all)○ Easier to maintain.○ At the cost of larger (and sometimes huge) APK’s

● The second: Create version per architecture○ Create application with a slightly modifed version

code at the MSB.○ This is the PlayStore way of identifying multiple

APKs for the same version

Page 46: NDK Primer (Wearable DevCon 2014)

References● http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html● http://developer.android.com/tools/sdk/ndk/index.html● Introduction to Android Internals - Ron Munitz, Afeka ● Native Android Programming course - @thePSCG