I would like my app to support any Android phone api 3 and above.
Why? Because I dislike waste and these older phones are perfectly good enough for the task at hand.
Sadly getting info about basic pre-api-9 SDK builds has proven far harder than getting NDK working.
I was unable to find a mechanism to build Android applications api < 9 using any of the Android Studio releases available at developer.android.com.
However, it is possible to build them using older command-line tools. (I haven't researched api < 9 use of ActivityCompat libraries nor ndk-builds for debugging JNI apps - These are left as exercises for the reader :-)
NOTE: use of these techniques requires familiarity with present-day command-line builds, without which there is little point reading on.
This task breaks into four:
https://android.googlesource.com/platform/development/+refs
Has everything from donut-release to android-s-beta-4.
Each has a 'samples' subdirectory, which is a good place to start.
https://dl.google.com/android/repository/repository-10.xml
lists all downloads with their digest/checksum, located at:
https://dl.google.com/android/repository/(repo-filename)
You'll need to find, download and install:
Note that api < 4 devices seem to prefer linking shared symbols from the device OS rather than from your JNI library, so avoid publishing conflicting symbols from your libraries and statically link any third party libs (license permitting.)
Example Makefile:
API = 3
NDK = ~/Android/Sdk/ndk/r11c
CC = $(NDK)/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc
SR = $(NDK)/platforms/android-$(API)/arch-arm
INC = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
objs = obj/thing1.o obj/thing2.o ...
libexample.so : $(objs)
$(CC) --sysroot=$(SR) -fPIC -Wall -shared -o libexample.so -O $^
obj/%.o : %.c
$(CC) --sysroot=$(SR) -fPIC -Wall -mthumb -c $< -o $@ $(INC)
Should you be building openssl, you'll need an extra configure flag:
--with-rand-seed=devrandom
as well as no-threads no-asm.
Sadly I was unable to find a way to use present-day Android Studio to build these low api projects, so below is some nasty command line stuff to do it instead.
You'll need to find, download and install:
(See above for how to get these.)
project/
Makefile
app/
build/
src/
main/
AndroidManifest.xml
assets/
jniLibs/
armeabi/
libexample.so
res/
layout/
activity_main.xml
java/
com/
example/
project/
MainActivity.java
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
>
<uses-sdk android:minSdkVersion="3" />
<activity android:name="com.example.project.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.project.MainActivity"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/outer"
android:orientation="vertical"
>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="run"
/><!--note: no onClick-->
...
package com.example.project;
import android.os.Bundle;
import android.app.Activity;
import android.widget.Button;
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle bdl) {
super.onCreate(bdl);
setContentView(R.layout.activity_main);
final Button btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
...
}
});
}
};
API = 3
KEY_PATH = ~/jkeystore
DROID_HOME = ~/Android/Sdk
JAVA_HOME = /usr/lib/jvm/java-8-openjdk-amd64
BLD_TOOLS=17.0.0
APK_SIGNR=24.0.3
PKG_PATH = com/example/project
JVA_ROOT = app/src/main/java
RES_ROOT = app/src/main/res
AST_ROOT = app/src/main/assets
AMF_PNAM = app/src/main/AndroidManifest.xml
LIB_REAL = app/src/main/jniLibs
CLS_ROOT = app/build/intermediates/javac/debug/classes
DEX_PATH = app/build/intermediates/dex/debug/mergeDexDebug
APK_PATH = app/build/outputs/apk/debug
APK_NAME = app-debug
LIB_TEMP = lib
JVA_PRJT = $(JVA_ROOT)/$(PKG_PATH)
CLS_PRJT = $(CLS_ROOT)/$(PKG_PATH)
APK_PNAM = $(APK_PATH)/$(APK_NAME)
DRD_JAR = $(DROID_HOME)/platforms/android-$(API)/android.jar
jopt = -proc:none -Xlint:all -Xlint:-fallthrough -Werror -Xmaxerrs 5
jopt += -implicit:none -target 1.6 -source 1,6 -bootclasspath ~/jre1.6.0_45/lib/rt.jar
# wildcard matches symlinks awa directories. dir filters, sort dedupes.
JVA_DIRS := $(dir $(wildcard $(JVA_PRJT)/*/) )
JVA_DIRS := $(sort $(JVA_DIRS) )
JVA_DIRS += $(JVA_PRJT)/
# all in java tree except R.java (and thus R.class)
temp_jva := $(foreach pth, $(JVA_DIRS), $(wildcard $(pth)*.java) )
JVA_LIST := $(patsubst $(JVA_PRJT)/R.java, , $(temp_jva))
temp_cls := $(patsubst $(JVA_PRJT)/%.java,$(CLS_PRJT)/%.class, $(JVA_LIST))
CLS_LIST := $(patsubst $(JVA_PRJT)/%,$(CLS_PRJT)/%, $(temp_cls))
LIB_LIST := $(shell find $(LIB_REAL) -type f 2> /dev/null)
LIB_TLST := $(patsubst $(LIB_REAL)/%, $(LIB_TEMP)/%, $(LIB_LIST))
AST_LIST := $(shell find $(AST_ROOT) -type f 2> /dev/null)
ifneq ($(strip $(AST_LIST)),)
AST_OPTN = -A $(AST_ROOT)
endif
RES_LIST := $(shell find $(RES_ROOT) -type f)
.DELETE_ON_ERROR:
assembleDebug: $(APK_PNAM).apk
$(APK_PNAM).apk : $(APK_PNAM).nosig.apk $(AMF_PNAM) $(RES_LIST) $(AST_LIST) $(LIB_LIST)
#****************************** SIGN **********************************
$(DROID_HOME)/build-tools/$(APK_SIGNR)/apksigner sign -v --verbose \
--ks $(KEY_PATH) --ks-pass pass:debug_pw --min-sdk-version $(API) \
--out $@ $(APK_PNAM).nosig.apk
$(APK_PNAM).nosig.apk : $(DEX_PATH)/classes.dex $(AMF_PNAM) $(RES_LIST) $(AST_LIST) $(LIB_LIST)
#****************************** PACKAGE *******************************
@-mkdir -p $(APK_PATH)
$(DROID_HOME)/build-tools/$(BLD_TOOLS)/aapt package -v -f -M $(AMF_PNAM) \
-S $(RES_ROOT) $(AST_OPTN) -I $(DRD_JAR) -F $(APK_PNAM).nosig.apk $(DEX_PATH)/
ifneq ($(strip $(LIB_LIST)),)
#****************************** LIBS **********************************
cp -r $(LIB_REAL) $(LIB_TEMP)/
$(DROID_HOME)/build-tools/$(BLD_TOOLS)/aapt add -v $(APK_PNAM).nosig.apk $(LIB_TLST)
@-rm -r $(LIB_TEMP)
endif
$(DEX_PATH)/classes.dex : $(CLS_LIST) $(CLS_PRJT)/R.class
#****************************** LINK **********************************
$(JAVA_HOME)/bin/javac $(jopt) -sourcepath $(JVA_ROOT)/ -cp $(DRD_JAR) \
-d $(CLS_ROOT)/ $(JVA_PRJT)/$(START).java
@-mkdir -p $(DEX_PATH)
$(DROID_HOME)/build-tools/$(BLD_TOOLS)/dx --dex --verbose --output=$@ $(CLS_ROOT)
$(CLS_PRJT)/%.class : $(JVA_PRJT)/%.java $(CLS_PRJT)/R.class
@-mkdir -p $(CLS_PRJT)
$(JAVA_HOME)/bin/javac $(jopt) -sourcepath $(JVA_ROOT)/ -cp $(DRD_JAR) -d $(CLS_ROOT)/ $<
$(CLS_PRJT)/R.class : $(JVA_PRJT)/R.java
#****************************** R.CLASS *******************************
@-mkdir -p $(CLS_PRJT)
$(JAVA_HOME)/bin/javac $(jopt) -sourcepath $(JVA_ROOT)/ -cp $(DRD_JAR) -d $(CLS_ROOT)/ $<
$(JVA_PRJT)/R.java : $(RES_LIST) $(AMF_PNAM)
#****************************** R.JAVA ********************************
$(DROID_HOME)/build-tools/$(BLD_TOOLS)/aapt package -v -f -M $(AMF_PNAM) \
-S $(RES_ROOT) -m -J $(JVA_ROOT) -I $(DRD_JAR)
You will probably need a 32 bit zlib:
sudo apt install zlib1g:i386
...and should really get java 1.6 for bootstrapping:
https://www.oracle.com/uk/java/technologies/javase-java-archive-javase6-downloads.html
download jre-6u45-linux-x64.bin (requires registration)
You also need to configure a keystore for apk signing:
/usr/lib/jvm/java-8-openjdk-amd64/bin/keytool \
-genkeypair -validity 1000 -dname "CN=some company,O=Android,C=JPN" -keystore ~/jkeystore \
-storepass debug_pw -keypass debug_pw -alias cert -keyalg RSA -v
I should point out that api < 9 devices are best considered hopelessly vulnerable to attack via Mobile Data, wifi, tethering, adb, bluetooth, GSM etc.