Set Up Android Development Environment

Android Studio is now mandatory if one needs to use aar files (e.g. VR SDK). Outside of that use case, the IDE is not needed. Despite Google forcefully pushing an extremely slow IDE compared to CLI, this has simplified the installation process. During the installation, select Custom to choose where the Android SDK will be installed. Once the installer completes, run ./bin/studio.sh to launch an instance of the IDE. Navigate to Configure ‣ SDK Manager ‣ SDK Tools and toggle the NDK checkbox in order to utilize C/C++ in the application. At the time of writing this post, one should prefer using Oracle’s JDK over OpenJDK.

All SDK/NDK updates should occur through Android Studio. It is highly recommended that native Android applications handle all UI interactions in the SDK Java layer and minimize the number of JNI calls. The alternative is to use the NDK’s NativeActivity wrapper, but NativeActivity itself is based on Java, so there is no such thing as a completely native C++ application. Moreover, the fact that the NDK is a strict subset of the SDK implies its sole purpose is to speed up the code that the JVM’s JIT compiler failed to optimize.

The following is an example of using a shell script to compile and install an Android application:

#!/bin/bash

#set to default values if not defined
ANDROID_SDK=${ANDROID_SDK-/absolute/path/to/android/sdk}
ANDROID_NDK=${ANDROID_NDK-/absolute/path/to/android/ndk}
JAVA_HOME=${JAVA_HOME-/absolute/path/to/jdk}

#add to path if not already there
if [[ ":$PATH:" != *$ANDROID_SDK* ]]; then
    PATH=$ANDROID_SDK/platform-tools:$ANDROID_SDK/tools:$PATH
fi

if [[ ":$PATH:" != *$ANDROID_NDK* ]]; then
    PATH=$ANDROID_NDK:$PATH
fi

if [[ ":$PATH:" != *$JAVA_HOME* ]]; then
    PATH=$JAVA_HOME/bin:$PATH
fi

#initialize Android project
if [[ ! -f "build.xml" ]]; then
    echo "Initializing Android project"
    android update project --path . --target android-<version-number>
fi

ndk-build -j<number-threads>;
ant debug;

adb uninstall <some-app-package-name>;
adb install <path-to-apk>;

Android.mk to Gradle

Google is pushing Gradle as the build tool for Android development. What follows is an example application that uses the NDK and Daydream SDK.

.
├── app
│   ├── src
│   │   └── main
│   │       ├── java
│   │       ├── jni
│   │       ├── res
│   │       └── AndroidManifest.xml
│   └── build.gradle
├── resources
│   └── libs
│       └── sdk-base-1.40.0.aar
├── build.gradle
└── settings.gradle

The build.gradle at the root directory defines the global scope.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    // This configures the Gradle build system, but not the GVR components.
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle-experimental:0.9.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        // For GVR components, the official source of the .aars is JCenter.
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

// The dependencies for NDK builds live inside the .aar files so they need to
// be extracted before NDK targets can build.
task extractGvrSo(type: Copy) {
    // Copying operation can only occur during execution phase.
    // Use the following block to execute during the default configuration phase
    copy {
        from zipTree(rootProject.file("resources/libs/sdk-base-1.40.0.aar"))
        into rootProject.file("resources/libs/")
        include "jni/**/libgvr.so"
    }
}

task extractNdk {}
extractNdk.dependsOn extractGvrSo

task deleteNdk(type: Delete) {
    delete rootProject.file("resources/libs/jni")
}
clean.dependsOn(deleteNdk)

A build.gradle can be defined for each module (e.g. app).

apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 25
        buildToolsVersion = "25.0.0"

        defaultConfig.with {
            applicationId = "com.qualcomm.graphicsrd.vr"
            minSdkVersion.apiLevel = 19
            targetSdkVersion.apiLevel = 24
            versionCode = 1
            versionName = "1.0"
        }
    }
    android.buildTypes {
        release {
            minifyEnabled = false
            // Add code obfuscation if necessary
            proguardFiles.add(file('proguard-android.txt'))
        }
    }
    android.ndk {
        moduleName = "main"
        cppFlags.add("-std=c++11")
        cppFlags.add("-I" + file("src/main/jni/include/").absolutePath)
        // Add the necessary GVR headers.
        cppFlags.add("-I" + rootProject.file("resources/libs/include").absolutePath)

        stl = "gnustl_shared"
        // Add the necessary GVR .so files for all architectures.
        ldFlags.add("-L" + rootProject.file("resources/libs/jni/armeabi-v7a").absolutePath)

        ldLibs.addAll(["log", "android", "EGL", "GLESv3"])

        // Specify the particular .so files this sample links against.
        ldLibs.add("gvr")
    }
    android.productFlavors {
        create ("arm7") {
            ndk.abiFilters.add("armeabi-v7a")
        }
    }
}

dependencies {
    compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
    compile 'com.google.vr:sdk-base:1.40.0'
}

// extractNdk task is defined in the root build.gradle
build.dependsOn(':extractNdk')

Lastly, the settings.gradle specifies which modules belong to the build.

//add sub-module to build system
include ":app"

Enable Root Capabilities

adb root
adb shell mount -o rw,remount /system

Note that rw can be replaced with ro to make the file system read-only.

Resolve adb / fastboot no permissions Error

Add the following rules to /etc/udev/rules.d/51-android.rules

SUBSYSTEM=="usb", ATTRS{idVendor}=="0502", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0b05", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="413c", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0489", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="04c5", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="091e", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="201E", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="109b", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bb4", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="12d1", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="24e3", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2116", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0482", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="17ef", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1004", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="22b8", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0e8d", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0409", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2080", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0955", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2257", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="10a9", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1d4d", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0471", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="04da", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="05c6", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1f53", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="04e8", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="04dd", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0fce", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2340", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTRS{idVendor}=="19d2", MODE="0666", GROUP="plugdev"

and run the following commands

sudo chmod a+r /etc/udev/rules.d/51-android.rules
sudo service udev restart
sudo killall adb