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 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