javaandroidandroid-studiojava-native-interfaceandroid-1.6-donut

Can Android Studio 4 build projects with API < 9?


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.


Solution

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

    Android sample projects for api < 9

    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.

    Android tools for building api < 9 projects

    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:

    Build Android JNI libraries for api < 9

    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.

    Build Android Java apps for api < 9

    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 tree

    project/
        Makefile
        app/
            build/
            src/
                main/
                    AndroidManifest.xml
                    assets/
                    jniLibs/
                        armeabi/
                            libexample.so
                    res/
                        layout/
                            activity_main.xml
                    java/
                        com/
                            example/
                                project/
                                    MainActivity.java
    
    

    AndroidManifest.xml

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

    activity_main.xml

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

    MainActivity.java

    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) {
                    ...
                }
            });
        }
    };
    

    Makefile

    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
    

    IMPORTANT:

    I should point out that api < 9 devices are best considered hopelessly vulnerable to attack via Mobile Data, wifi, tethering, adb, bluetooth, GSM etc.