Building an android app with free software only
Published on May 08, 2020
Table of contents
- Current state
- F-droid
- Android SDK licensing
- Debian based Dockerfile for android builds
- Trying to build `android-platform-tools-base`
- Dockerfile using `debian:stretch`
- Conclusions
I built a couple of personal android applications a few years ago. One of them
is an overlay thing for pokemon go and the other one is an image uploader for
dl.hugopeixoto.net
, my personal image hosting service. I would like to be
able to build them without having to accept EULAs.
Current state
I have a half-uncommited private repository for my image uploader app. I don’t remember why I didn’t publish this. Probably because I wanted to clean the repository before publishing it, classic mistake.
The app has two parts (from what I recall):
- a settings screen, which allows you to configure the server access
- a “share” screen, accessible through the standard share buttons, that uploads the image to the server and places the resulting URL in the clipboard
Let’s look at the tree of this directory to see what’s up:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
hugopeixoto@laptop:~/w/p/http-share-android$ tree -L 3
.
├── android.iml
├── app
│ ├── app.iml
│ ├── build
│ │ ├── generated
│ │ ├── intermediates
│ │ ├── outputs
│ │ ├── retrolambda
│ │ └── tmp
│ ├── build.gradle
│ └── src
│ └── main
├── app-signed.apk
├── build
│ ├── generated
│ │ └── mockable-android-23.jar
│ └── intermediates
│ ├── dex-cache
│ └── lint-cache
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
/build/
and /app/build/
, that sounds promising. I must have been in the
process of messing with the build process. I’ll remove both these directories.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
hugopeixoto@laptop:~/w/p/http-share-android$ tree -I "drawable-*|mipmap-*"
.
├── android.iml
├── app
│ ├── app.iml
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── net
│ │ └── hugopeixoto
│ │ └── customsharing
│ │ ├── AppCompatPreferenceActivity.java
│ │ ├── RxUtils.java
│ │ ├── SettingsActivity.java
│ │ ├── UploadActivity.java
│ │ └── Uploader.java
│ └── res
│ ├── layout
│ │ ├── activity_main.xml
│ │ └── content_main.xml
│ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v21
│ │ └── styles.xml
│ ├── values-w820dp
│ │ └── dimens.xml
│ └── xml
│ └── preferences.xml
├── app-signed.apk
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
15 directories, 26 files
I ignored app/src/res/{drawable,mipmap}-*
because it contains a bunch of
icons in different resolutions that I don’t want to think about just yet.
So, what have we got?
*.iml
files are android studio project filesapp-signed.apk
is the result of a previous build- tons of gradle files, a build tool
AndroidManifest.xml
, which describes the permissions, activities, and intents of this app- source code in
app/src/main/java/net/hugopeixoto/customsharing/
- layouts in
app/src/main/res/layout/
- preferences in
app/src/main/res/xml/preferences.xml
, referenced in the code - application texts in
app/src/main/res/values/strings.xml
- some extra xml files with.. stuff
Looking at the source files:
1
2
3
4
5
6
7
8
9
hugopeixoto@laptop:~/w/p/http-share-android$ tree app/src/main/java/net/hugopeixoto/customsharing/
app/src/main/java/net/hugopeixoto/customsharing/
├── AppCompatPreferenceActivity.java
├── RxUtils.java
├── SettingsActivity.java
├── UploadActivity.java
└── Uploader.java
0 directories, 5 files
I see the three activity files, AppCompatPreferenceActivity
,
SettingsActivity
and UploadActivity
. The last two match the parts I
mentioned at the beginning. SettingsActivity
inherits from
AppCompatPreferenceActivity
, so it could be related to some android
compatibility wrapper.
RxUtils
contains a single static method, but it lets me know that I’m using
some functional library. I recall playing around with lambdas, and having to
use a transpiler of sorts to get it to work. Retrolambda. Since I don’t
really care about compatibility with old devices for my personal apps, maybe I
don’t need to use this anymore.
I should have left myself a README with instructions. I remember that whenever I had to build this, I had to run some commands. One for building, one for signing. I don’t have those written anywhere, except in my bash history. I’ll go fetch them.
Found this:
1
2
3
$ gradle assembleRelease
$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore ./app/build/outputs/apk/app-release-unsigned.apk app
$ http-share httpshare.apk ./app/build/outputs/apk/app-release-unsigned.apk
The first command builds the apk
, the second one signs it, and the fourth one
uploads the signed version to a web server, using
http-share-cli.
I know that I wanted to build this from a docker image, instead of relying on
having everything installed on the OS. I also remember that that didn’t work
properly with openjdk, so I stopped there. I still have a Dockerfile
somewhere.
1
2
3
4
5
6
7
8
9
FROM debian
#alpine:3.6
RUN apt update
RUN apt install -y openjdk-8-jdk unzip wget
RUN wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip -O android.zip && mkdir android && unzip android.zip -d android/ && rm -r android.zip
#RUN echo y | android/tools/android update sdk --no-ui -a --filter tools,platform-tools,extra-android-m2repository,extra-android-support,extra-google-google_play_services,extra-google-m2repository,android-23,build-tools-25.0.2,build-tools-23.0.1,build-tools-25.0.0
#
#ENV PATH ${PATH}:/android/tools/:/android/tools/bin/:/android/platform-tools/
This looks very much like work in progress. I’ll start over.
F-droid
I use F-droid. They’re focused on free software, and they build everything from source. It might be a good starting point.
Sidenote: I saw a talk about OpenPush in FOSDEM, I need to see if there are any news
The following page contains some interesting notes:
https://f-droid.org/en/2020/04/29/big-website-update.html
all APK files must be signed to be installable on Android
it is not possible to upgrade it in place to a new version signed with a different key without first uninstalling the original. This may present an inconvenience to users, as the process of uninstalling loses any data associated with the previous installation.
Run
fdroid build
to build any applications that are not already built.
There seems to be a fdroid
cli that handles building
More about
fdroid build
Yes please.
To build a single version of a single application, you could run the following:
fdroid build org.fdroid.fdroid:16
Another option for using
fdroid build
is to use a metadata file that is included in the app’s source itself, rather than in ametadata/
folder with lots of other apps. The.fdroid.yml
metadata file should be in the root of your source repo.
Now, how does this fdroid build
work, exactly, and how do I get my app
compiled using this? Let’s see if the FAQ helps.
https://f-droid.org/en/docs/FAQ_-_App_Developers/
The quickest way to get an app included is to make a merge request to fdroiddata, following these instructions.
We also support reproducible builds, so we can build a version from source and check against your official release. If they match (ignoring the signature) we can then publish your official APK with your signature used. This is a tedious task, since we have to standardize on the build parameters and tools, but it should be worth it in the long run. We also try to verify our own builds and get a lot of binary differences, see our verification server results. However, things will improve over time.
Reproducible builds is an interesting topic, but I’m probably not going to tackle this right now.
Which build system to use?
We have good support for “ant” and “gradle” based builds, while “maven” was only used for a short period of time and for dependencies. For other build systems, you might have to provide us some detailed information on how to handle that, so we can setup the app correctly or maybe even incorporate them into our server tools.
So this means that I’ll probably continue to keep using gradle
.
Let’s look at the
CONTRIBUTING.md
,
referenced above.
OK, this seems to describe how to create the metadata file.
I also found a docker image for the fdroid server:
https://gitlab.com/fdroid/docker-executable-fdroidserver
But it mentions that:
It does not include a full Android SDK, so if you want to use the full Android SDK with this image, you’ll need to mount your own local copy.
This smells of licensing issues. Let’s look for other docs that might point out to how this whole thing works.
https://f-droid.org/en/docs/Installing_the_Server_and_Repo_Tools
Proprietary, Non-Free libraries
The Android SDK is made available by Google under a proprietary license. Within that, the essential build tools, SDK platforms, support library and some other components are under the Apache license and source code is provided.
So, the Android SDK is proprietary? How can this be, if AOSP is free? Doesn’t make much sense. Let me explore this a bit.
Android SDK licensing
There’s a debian package package called android-sdk
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
hugopeixoto@hylia:/m/h/h/hugopeixoto$ apt show android-sdk
Package: android-sdk
Version: 25.0.0+12
Priority: optional
Section: metapackages
Source: android-sdk-meta
Maintainer: Android Tools Maintainers <android-tools-devel@lists.alioth.debian.org>
Installed-Size: 30.7 kB
Depends: android-sdk-build-tools, android-sdk-common (>= 25.0.0+12), android-sdk-platform-tools (>= 20), proguard-cli
Recommends: gradle, default-jdk-headless
Suggests: android-sdk-platform-23, maven, proguard-gui
Download-Size: 4824 B
APT-Sources: http://deb.debian.org/debian testing/main amd64 Packages
Description: Software development kit for Android platform
The Android SDK includes a variety of tools that help you develop mobile
applications for the Android platform. The tools are classified into 3 groups:
SDK Tools, Platform-tools and Build-tools.
.
SDK Tools are platform independent and are required no matter which Android
platform you are developing on. It is the base toolset of Android SDK.
.
This metapackage pulls the entire Android SDK.
So this in debian, in the main section, so how could this be proprietary? It
could just be a downloader for the proprietary binaries, but then it would have
to be in contrib
, not in main
. Let me check Debian’s wiki:
https://wiki.debian.org/AndroidTools
Sidenote: the wiki page points to https://guardianproject.info/2015/04/30/getting-android-tools-into-debian/, which seems to be an interesting read.
The binaries for the Android SDK downloadable from Google have a proprietary license but the source code is free software so Debian is packaging it. Not all Android SDK packages can be installed from Debian, some never will be in Debian because they are too specific to Android. Sylvain Beucler’s libre Android rebuilds and/or Google’s non-free binaries can also be used with the Debian Android SDK.
OK, so the binaries are proprietary, but the source is not. This makes some sense. So going back to the fdroid quote:
The Android SDK is made available by Google under a proprietary license. Within that, the essential build tools, SDK platforms, support library and some other components are under the Apache license and source code is provided.
When they say that the SDK is made available under a proprietary license, that means the binaries. Makes sense, since they follow up with “x, y, z are under the Apache license”. So that is just weird phrasing / lack of reading.
So this means that ideally, I’d be installing these from source, and not using Google’s binaries.
This also means that there are some packages that are installable via google’s android sdk thingy that are proprietary. This includes Google services. No surprises here, this is something I was aware of.
Let me check that Sylvain Beucler’s libre Android rebuilds about page page.
Android Studio, which includes the SDK and NDK, is supposed to be freely licensed (mainly Apache 2.0) but the official binaries aren’t.
On download, we are requested to accept conditions such as:
“3.2 You may not use this SDK to develop applications for other platforms (including non-compatible implementations of Android) or to develop another SDK.”
“4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) the License Agreement and (b) any applicable law, regulation or [etc.]”
This is all great.
I also saw some references to micro-g while searching. It’s a free implementation of some google libraries that are widely used. Let me just browse a list or something to get acquainted with it, in case I need it:
Service Core (GmsCore) is a library app, providing the functionality required to run apps that use Google Play Services or Google Maps Android API (v2).
Services Framework Proxy (GsfProxy) is a small helper utility to allow apps developed for Google Cloud to Device Messaging (C2DM) to use the compatible Google Cloud Messaging service included with GmsCore.
Unified Network Location Provider (UnifiedNlp) is a library that provides Wi-Fi- and Cell-tower-based geolocation to applications that use Google’s network location provider. It is included in GmsCore but can also run independently on most Android systems.
[some deprecated maps api replacement]
Store (Phonesky) is a frontend application providing access to the Google Play Store to download and update applications. Development is in early stage and there is no usable application yet.
OK, so they’re basically free implementations of interfaces to proprietary services. I don’t see myself needing these for now, but the UnifiedNlp might be useful in the future. I may be using Google Play Services accidentally, so this is good to know.
I hope I’m not accidentally using any proprietary libraries, but the compat libraries might fall into that category.
So, back to building an application. If debian packages android-sdk
, let’s
try to build a docker file using those.
Sidenote: I randomly learned that the F-Droid client started out as a fork of Aptoide.
Debian based Dockerfile for android builds
I don’t even know where to start. Let’s go back to the Debian wiki page:
https://wiki.debian.org/AndroidTools
There are many advantages to having the SDK and tools in Debian, rather than relying only on the Google distributions:
- easy install and update channel that all Debian users already know
- automatic trustworthy downloads, no need to verify hash sums
- eliminate need for insecure wrapper scripts, like ./gradlew
- trivial install for specific tools, like adb, fastboot, etc.
Point 1, 2, and 3 sound great. I kinda dislike having random gradlew
files in
my repository.
If you’re just starting out with building apps, we suggest that you first read the Introduction to build packages with Debian’s Android SDK.
The linked page also contains a walkthrough. Might come in handy.
Currently there is only the target platform of API Level 23 packaged, so only apps targeted at android-23 can be built with only Debian packages. We will add more API platform packages via backports afterwards. Only Build-Tools 24.0.0 is available, so in order to use the SDK, build scripts need to be modified. Beware that the Lint in this version of Gradle Android Plugin is still problematic, so running the :lint tasks might not work.
This is going to be interesting. Let me check what version I was using.
1
2
3
4
hugopeixoto@laptop:~/w/p/http-share-android$ grep 'android {' app/build.gradle -A2
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
Two 23s. That means I’ll probably have to change buildTools
to 24. This is,
assuming that the wiki information is still up to date. Let’s double check.
1
2
3
4
5
6
7
8
9
10
11
hugopeixoto@laptop:~/w/p/http-share-android$ apt search -q --names-only android-sdk-platform
Sorting...
Full Text Search...
android-sdk-platform-23/testing,unstable 6.0.1+r72-5 all
Android SDK Platform for API Level 23 (6.0 Marshmallow)
android-sdk-platform-tools/testing,unstable 27.0.0+12 amd64
Tools for interacting with an Android platform
android-sdk-platform-tools-common/testing,unstable,now 27.0.0+12 all [installed,automatic]
Tools for interacting with an Android platform - Common files
Confirmed, 23. the -tools
packages mention a version 27, though. Let me check
what are the most recent versions in upstream.
https://developer.android.com/studio/releases/platforms
Android 10 (API level 29) … Android 8.1 (API level 27) … Android 6.0 (API level 23)
So this is kind of lagging behind. I have no idea what any of these mean, and from https://wiki.debian.org/AndroidTools#Android.27s_upstream_version_names, it seems to be a bit crazy to begin with. I’ll get back to it later.
Let’s start writing the Dockerfile.
First hiccup: I had to use debian:sid
. This is what I have so far:
1
2
3
4
5
6
7
8
9
FROM debian:sid
RUN apt update
RUN apt -y install android-sdk android-sdk-platform-23 git libgradle-android-plugin-java
ADD . /app
WORKDIR /app
CMD bash
I’m using CMD bash
because I’ll be exploring things through bash before fully
automating this.
1
$ docker build . -t http-share-android && docker run -ti http-share-android
Running gradle build
downloaded a bunch of POM files and then it complained about
mismatched gradle
versions:
Gradle version 2.10 is required. Current version is 4.4.1. If using the gradle wrapper, try editing the distributionUrl in /root/.gradle/daemon/4.4.1/gradle/wrapper/gradle-wrapper.properties to gradle-2.10-all.zip
This is because I need to adapt build.gradle
to use the debian packages
instead of the android ones. I’m having some trouble getting that to work.
Instructions may be out of date. There is an android-sdk-helper
package that
patches existing gradle files, but I rather write them manually for now to
understand what’s going on.
They’re telling me to change build.gradle
to include:
1
2
3
4
5
6
7
8
9
buildscript {
repositories {
maven { url 'file:///usr/share/maven-repo' }
}
dependencies {
compile 'com.android.tools.build:gradle:debian'
}
}
But the compile line doesn’t… compile. Where is this compile
thing defined?
I’m going to guess that you’re referencing compile and classpath within the dependencies {} block. If that is so, those are dependency Configurations.
The compile configuration is created by the Java plugin. The classpath configuration is commonly seen in the buildSrc {} block where one needs to declare dependencies for the build.gradle, itself (for plugins, perhaps).
Checking gradle docs:
I see a compileClasspath
and compileOnly
, but no compile
. This may have
changed in gradle. Let me check versions.
Debian has 4.4.1
while the latest gradle is… 6.x
. 4.4.1
is from 2017,
ouch. Let me check debian’s tracker to see if there’s anything that points to a
new version being worked on:
https://tracker.debian.org/pkg/gradle
Checking all bugs associated with this package, here’s a relevant one:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=933264
[29 Jul 2019] Newer version of Gradle requires Kotlin, which is being worked on right now. See https://java-team.pages.debian.net/gsoc-kotlin-blog
So gradle is stuck on 4.4.1 because it depends on Kotlin, and Kotlin is still not packaged in Debian. And one of the reasons kotlin is not packaged, I bet, is because it requires a more recent version of gradle.
Downgrade the build from gradle 5.1 to gradle 4.4.1 which is the version available in Debian Sid.
I knew it. I wanted to eventually move the apps from kotlin to java, but I guess that’s going to have to wait.
I have finished downgrading the project to be buildable using gradle 4.4.1. The project still needed a part of gradle 4.8 that I have successfully patched into sid gradle. here is the link to the changes that I have made.
So, Debian’s gradle 4.4.1
contains some features of 4.8
. The docs for 4.8
are still available on gradle.org
, so let’s check that.
No compile
there. Maybe they meant classpath
. Let me try that, and let me
change the build tools version to 27 while I’m at it.
I got some other errors:
java.lang.ClassNotFoundException: org.debian.gradle.plugin.MavenResolverHook
java.io.FileNotFoundException: /root/.android/analytics.settings (No such file or directory)
These errors didn’t seem to stop the compilation from going. It then failed with
The SDK directory ‘/home/hugopeixoto/work/contrib/android-sdk’ does not exist.
This is my old android sdk path. I probably have to change something to the
debian one. Changed local.properties
:
1
sdk.dir=/usr/lib/android-sdk
Still getting a ton of exceptions, but it’s moving still along… and it failed after 33 seconds:
1
2
3
* What went wrong:
A problem occurred configuring project ':app'.
> java.lang.NullPointerException (no error message)
I created an empty analytics.settings
file just to get those warnings out of
the way, but the NPE persists. Running it again with --stacktrace
:
1
2
3
4
5
6
7
8
9
10
org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':app'.
at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:94)
...
Caused by: java.lang.NullPointerException
at com.android.repository.impl.meta.SchemaModuleUtil.marshal(SchemaModuleUtil.java:270)
at com.android.repository.impl.manager.LocalRepoLoaderImpl.writePackage(LocalRepoLoaderImpl.java:285)
at com.android.repository.impl.manager.LocalRepoLoaderImpl.parsePackages(LocalRepoLoaderImpl.java:178)
at com.android.repository.impl.manager.LocalRepoLoaderImpl.getPackages(LocalRepoLoaderImpl.java:154)
at com.android.repository.impl.manager.RepoManagerImpl$LoadTask.run(RepoManagerImpl.java:653)
at com.android.repository.api.RepoManager$DummyProgressRunner.runSyncWithProgress(RepoManager.java:398)
Ah, this is a bit more helpful. Let me quickly search for the source:
And some stack overflow links that I found along the way:
https://stackoverflow.com/questions/30643802/what-is-jaxbcontext-newinstancestring-contextpath
OK, the source isn’t being that helpful. Let me try to delete most of the
contents in app/build.gradle
to see if it changes something. This is the
current state:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
plugins {
id "me.tatarka.retrolambda" version "3.2.5"
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "27.0.1"
defaultConfig {
applicationId "net.hugopeixoto.customsharing"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.squareup.okhttp3:okhttp:3.1.2'
compile 'com.google.code.gson:gson:2.2.+'
compile 'com.google.guava:guava:12.0'
compile 'com.android.support:support-v4:23.1.1'
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.5'
}
I can get different errors if I delete the android
section. It starts
complaining about the lack of buildToolsVersion
, but if I just add that line,
it NPEs again.
Could this be related to the java version I’m running?
Let me find a sample app. I was looking through the bugs on android-sdk-meta
,
and came across this:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=854483
I tested it using https://gitlab.com/cde/AndroidHelloWorld, as seamlik proposed in #debian-mobile.
Let me clone this and try to build it.
1
2
3
4
5
6
7
8
hugopeixoto@laptop:~/w/p/http-share-android$ git clone https://gitlab.com/cde/AndroidHelloWorld
hugopeixoto@laptop:~/w/p/http-share-android$ docker build . -t http-share-android && docker run -ti http-share-android
root@56951ec38e98:/app# cd AndroidHelloWorld/
root@56951ec38e98:/app/AndroidHelloWorld# gradle build
> Gradle version 2.10 is required. Current version is 4.4.1. If using the
> gradle wrapper, try editing the distributionUrl in
> /root/.gradle/daemon/4.4.1/gradle/wrapper/gradle-wrapper.properties to
> gradle-2.10-all.zip
Derp. After adapting build.gradle
according to the debian instructions, I got
the same NPE error. Let me try the android-sdk-helper
package.
1
2
root@56951ec38e98:/app/AndroidHelloWorld# apt install android-sdk-helper
root@56951ec38e98:/app/AndroidHelloWorld# gradle build --init-script /usr/share/android-sdk-helper/init.gradle
It was unable to detect ANDROID_HOME
, so I set it through sdk.dir
in
local.properties
.
1
2
cp ../local.properties .
root@56951ec38e98:/app/AndroidHelloWorld# gradle build --init-script /usr/share/android-sdk-helper/init.gradle
Same NPE as before. So it seems that I’m not doing anything wrong when editing
build.gradle
. I could try to build these dependencies with some extra debug
statements.
Trying to build android-platform-tools-base
After looking through the wrong package, I ran the following commands:
1
2
3
4
5
6
7
8
hugopeixoto@laptop:~/w/contrib$ apt source libgradle-android-plugin-java
hugopeixoto@laptop:~/w/contrib$ cd android-platform-tools-base-2.2.2/
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ ack SchemaModuleUtil
repository/src/main/java/com/android/repository/impl/manager/LocalRepoLoaderImpl.java
31:import com.android.repository.impl.meta.SchemaModuleUtil;
285: SchemaModuleUtil.marshal(factory.generateRepository(repo),
311: repo = (Repository) SchemaModuleUtil.unmarshal(mFop.newFileInputStream(packageXml),
[...]
I got a bunch of matches, but the first one matches the stacktrace and line number, so it sounds like a good starting point. Cross-referencing that with the JetBrains link.
Can I build this? I don’t remember anything of building debian packages.
Running dpkg-buildpackage -us -uc
tells me I need a lot of build
dependencies. There’s an apt build-dep
command.
1
2
3
4
5
6
root@laptop:~# apt build-dep android-platform-tools-base
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ dpkg-buildpackage -us -uc
[...]
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':base:common:compileJava'.
[...]
Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see the compiler error output for details.
Great. I joined #debian-mobile to see if I can get some help while I try to fix this.
Looking through the output of dpkg-buildpackage -us -uc
, I found this error:
1
2
3
4
5
6
7
8
9
10
11
12
13
[...]/common/src/main/java/com/android/xml/AndroidXPathFactory.java:40:
error: AndroidNamespaceContext is not abstract and does not override abstract method getPrefixes(String) in NamespaceContext
private static class AndroidNamespaceContext implements NamespaceContext {
^
[...]/common/src/main/java/com/android/xml/AndroidXPathFactory.java:84:
error: getPrefixes(String) in AndroidNamespaceContext cannot implement getPrefixes(String) in NamespaceContext
public Iterator<?> getPrefixes(String namespaceURI) {
^
return type Iterator<?> is not compatible with Iterator<String>
[...]/common/src/main/java/com/android/xml/AndroidXPathFactory.java:83:
error: method does not override or implement a method from a supertype
@Override
^
So it seems like the inheritance thing is mismatching on the return type. Let
me swap Iterator<?>
with Iterator<String>
.
OK, this unblocked the compilation, but now I’m getting a ton of errors. Some examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[...]/repository/src/main/java/com/android/repository/api/RepoPackage.java:28:
error: package javax.xml.bind.annotation does not exist
import javax.xml.bind.annotation.XmlTransient;
^
[...]/repository/src/main/java/com/android/repository/api/RepoPackage.java:34:
error: cannot find symbol
@XmlTransient
^
symbol: class XmlTransient
[...]/repository/src/main/java/com/android/repository/impl/meta/TypeDetails.java:21:
error: package javax.xml.bind does not exist
import javax.xml.bind.JAXBElement;
^
[...]/repository/src/main/java/com/android/repository/impl/meta/TypeDetails.java:22:
error: package javax.xml.bind.annotation does not exist
import javax.xml.bind.annotation.XmlTransient;
^
[...]/repository/src/main/java/com/android/repository/impl/meta/TypeDetails.java:35:
error: cannot find symbol
@XmlTransient
^
symbol: class XmlTransient
This is ringing bells. Earlier, I saw this stackoverflow question:
The JAXB APIs are considered to be Java EE APIs and therefore are no longer contained on the default classpath in Java SE 9. In Java 11, they are completely removed from the JDK.
The extra Java EE APIs are provided in the following modules:
- java.activation
- java.corba
- java.transaction
- java.xml.bind « This one contains the JAXB APIs
- java.xml.ws
- java.xml.ws.annotation
So there’s java.xml.bind
, which was completely removed in Java 11. This is
another indicator that I should probably be compiling this with an older
version of java. Let me find any other evidence that point to the usage of a
specific java version.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ ack -i "java 11"
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ ack -i "java 10"
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ ack -i "openjdk"
debian/additionalSrc/intellij-community/platform/util/src/com/intellij/Patches.java
75: * https://bugs.openjdk.java.net/browse/JDK-8020443
89: * See https://bugs.openjdk.java.net/browse/JDK-7179799.
95: * See http://bugs.openjdk.java.net/browse/JDK-8007219
122: * See <a href="https://bugs.openjdk.java.net/browse/JDK-8042123">JDK-8042123</a>
129: * Corresponding JDK request for enhancement - <a href="https://bugs.openjdk.java.net/browse/JDK-8139151">JDK-8139151</a>.
135: * The issue was fixed in JDK 1.8.0_60 as part of <a href="https://bugs.openjdk.java.net/browse/JDK-8064833">JDK-8064833</a>.
146: * See https://bugs.openjdk.java.net/browse/JDK-8004103.
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ ack -i "java 8"
[~20 matches, including:]
debian/changelog
19: * Depends on JDK8 since the code uses Java 8 Streams and lambda.
.idea/inspectionProfiles/Java_8.xml
3: <option name="myName" value="Java 8" />
Searching for 1_8
also yields a bunch of matches.
The stackoverflow answer also mentions this:
For Gradle or Android Studio developer: (JDK 9 and beyond)
Add the following dependencies to your build.gradle file:
1
2
3
4
5
dependencies {
// JAX-B dependencies for JDK 9+
implementation "jakarta.xml.bind:jakarta.xml.bind-api:2.3.2"
implementation "org.glassfish.jaxb:jaxb-runtime:2.3.2"
}
Now I have two choices. Either try to make this work using openjdk11
, or try
to use openjdk8
. Let’s try to downgrade to openjdk8
.
1
2
3
root@laptop:~# apt purge openjdk-11-{jre,jdk-headless,jre-headless}
root@laptop:~# apt install openjdk-8-{jre,jdk,jdk-headless,jre-headless}
root@laptop:~# apt build-dep android-platform-tools-base
I’m going in circles here. build-dep
is reinstalling openjdk11
. I’ll use
the update-alternatives
command to set 8
as the default.
1
2
3
4
5
6
7
8
9
10
11
root@laptop:~# update-alternatives --config java
There are 2 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 auto mode
1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode
2 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode
Press <enter> to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java to provide /usr/bin/java (java) in manual mode
Is this good enough to change something? Who knows. Trying to build again.
1
error: package javax.xml.bind.annotation does not exist
I guess it isn’t. I also tried update-alternatives --config javac
, no difference.
1
2
root@laptop:~# update-alternatives --get-selections | grep 11-openjdk | wc -l
32
This doesn’t seem like a good way forward. Purging openjdk-8 and going the other route.
1
2
3
4
5
root@laptop:~# apt purge openjdk-8-{jdk,jre,jdk-headless,jre-headless}
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ java --version
openjdk 11.0.7 2020-04-14
OpenJDK Runtime Environment (build 11.0.7+10-post-Debian-3)
OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Debian-3, mixed mode, sharing)
OK, so what’s up with this implementation
gradle thing? Where does it go? Do I need to install any packages?
Let me see what’s in my system:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ dpkg -l | grep jaxb
ii libjaxb-api-java 2.3.1-1 all Java Architecture for XML Binding API
hugopeixoto@laptop:~/w/c/android-platform-tools-base-2.2.2$ dpkg-query -L libjaxb-api-java
/.
/usr
/usr/share
/usr/share/doc
/usr/share/doc/libjaxb-api-java
/usr/share/doc/libjaxb-api-java/changelog.Debian.gz
/usr/share/doc/libjaxb-api-java/copyright
/usr/share/java
/usr/share/java/jaxb-api.jar
/usr/share/maven-repo
/usr/share/maven-repo/javax
/usr/share/maven-repo/javax/xml
/usr/share/maven-repo/javax/xml/bind
/usr/share/maven-repo/javax/xml/bind/jaxb-api
/usr/share/maven-repo/javax/xml/bind/jaxb-api/2.3.1
/usr/share/maven-repo/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.pom
/usr/share/maven-repo/javax/xml/bind/jaxb-api/debian
/usr/share/maven-repo/javax/xml/bind/jaxb-api/debian/jaxb-api-debian.pom
/usr/share/maven-repo/javax/xml/bind/jaxb-api-parent
/usr/share/maven-repo/javax/xml/bind/jaxb-api-parent/2.3.1
/usr/share/maven-repo/javax/xml/bind/jaxb-api-parent/2.3.1/jaxb-api-parent-2.3.1.pom
/usr/share/maven-repo/javax/xml/bind/jaxb-api-parent/debian
/usr/share/maven-repo/javax/xml/bind/jaxb-api-parent/debian/jaxb-api-parent-debian.pom
/usr/share/java/jaxb-api-2.3.1.jar
/usr/share/maven-repo/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar
/usr/share/maven-repo/javax/xml/bind/jaxb-api/debian/jaxb-api-debian.jar
So there’s a few javax/xml/bind/
files. Why is this not picking them up? Do I
need the maven { url 'file:///usr/share/maven-repo' }
thing here as well? Searching a bit more.
https://tracker.debian.org/pkg/android-platform-tools-base
https://bugs.debian.org/cgi-bin/pkgreport.cgi?repeatmerged=no&src=android-platform-tools-base
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=894284
This bug seems similar to the one I’m having.
The javax.xml.bind errors can be simply fixed by adding a dependency on jaxb-api: [adding “compile ‘javax.xml.bind:jaxb-api:debian’” to repository/build.gradle]
Cool, this seems simple.
There is another error with Java 10 caused by a change in the return type of javax.xml.namespace.NamespaceContext.getPrefixes(). It’s easily fixed with: […]
Hey, there’s the getPrefixes
bug I fixed.
Unfortunately there is another issue with SignedJarBuilder: […]
Once kotlin is in Debian, then we can use newer upstream versions, which support the latest JDK.
So it seems that I’ve hit the kotlin hole again. Fixing the javax.xml.bind
does hit the SignedJarBuilder issue they mention:
1
2
3
4
5
6
7
8
9
10
11
12
13
[...]/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java:22: error: cannot find symbol
import sun.misc.BASE64Encoder;
^
symbol: class BASE64Encoder
location: package sun.misc
[...]/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java:23: error: package sun.security.pkcs is not visible
import sun.security.pkcs.ContentInfo;
^
(package sun.security.pkcs is declared in module java.base, which does not export it to the unnamed module)
[...]/sdklib/src/main/java/com/android/sdklib/internal/build/SignedJarBuilder.java:26: error: package sun.security.x509 is not visible
import sun.security.x509.AlgorithmId;
^
(package sun.security.x509 is declared in module java.base, which does not export it to the unnamed module)
I can try to replace BASE64Encoder
with java.util.Base64
. I should be
tracking these changes in git or something. Getting the Base64 thing to compile
was straightforward. Unsure what to do with the sun.security.*
packages.
SignedJarBuilder
is deprecated. Maybe I can just remove its implementation
and side step the errors completely? This is going to be great. Removing this
probably means that I will be unable to generate signed APKs, but if I can live
with that for now.
1
2
3
4
5
6
7
8
9
10
11
Compiling with JDK Java compiler API.
[...]/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java:23:
error: package com.sun.org.apache.xml.internal.serialize is not visible
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
^
(package com.sun.org.apache.xml.internal.serialize is declared in module java.xml, which does not export it)
[...]/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java:24:
error: package com.sun.org.apache.xml.internal.serialize is not visible
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
^
(package com.sun.org.apache.xml.internal.serialize is declared in module java.xml, which does not export it)
This is not getting me anywhere. I’m just removing code that doesn’t compile. I’m pretending to port android-platform-tools base to openjdk11, this can’t be easy or a quick way to get this done. Let’s backtrack a bit.
When I started setting up the Dockerfile, I had to use sid
because
libgradle-android-plugin-java
was not available in testing
. But it is
available in another release: stretch
. Let’s try using that instead.
Dockerfile using debian:stretch
It seems that android-sdk-helper
was not available in stretch, so I’ll need to patch build.gradle
manually.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
root@781970948c66:/app/AndroidHelloWorld# git diff
diff --git a/build.gradle b/build.gradle
index c3ec2c3..54c4f60 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,10 +1,11 @@
buildscript {
repositories {
mavenCentral()
+ maven { url 'file:///usr/share/maven-repo' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.2'
+ classpath 'com.android.tools.build:gradle:debian'
}
}
@@ -12,7 +13,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
- buildToolsVersion "23.0.2"
+ buildToolsVersion "24.0.0"
lintOptions {
abortOnError false
root@781970948c66:/app/AndroidHelloWorld# tail -n1 local.properties
sdk.dir=/usr/lib/android-sdk
root@781970948c66:/app/AndroidHelloWorld# gradle build
[...]
ECJ compiler crashed
java.lang.RuntimeException: ECJ module temporarily disabled!
[...]
[repeat ECJ exception a few more times]
[...]
BUILD SUCCESSFUL
Total time: 9.303 secs
root@781970948c66:/app/AndroidHelloWorld# find . -name "*.apk"
./build/outputs/apk/AndroidHelloWorld-release-unsigned.apk
./build/outputs/apk/AndroidHelloWorld-debug.apk
Well, at least I got something working. Kind of sad that kotlin doesn’t work
and that I need to use stretch. Here’s the current Dockerfile
:
1
2
3
4
5
6
7
8
9
10
11
12
13
FROM debian:stretch
RUN apt update
RUN apt -y install android-sdk android-sdk-platform-23 git libgradle-android-plugin-java
RUN apt -y install vim
ADD . /app
WORKDIR /app
RUN mkdir -p /root/.android/
RUN touch /root/.android/analytics.settings
CMD bash
Let’s go back to my app. Same thing, setting buildToolsVersion "24.0.0"
and running gradle build
:
1
2
> You have not accepted the license agreements of the following SDK components:
[Android Support Repository].
Ah, here we go. I have these two dependencies:
1
2
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
And Android Support Library
(com.android.support
) is in the “Needs doing /
low hanging fruit” section of Debian’s wiki page. And I seem to be using it:
1
2
3
4
5
6
7
8
9
10
11
12
hugopeixoto@laptop:~/w/p/http-share-android$ ack android\.support app/src/main/java/net/hugopeixoto/
app/src/main/java/net/hugopeixoto/customsharing/UploadActivity.java
19:import android.support.design.widget.FloatingActionButton;
20:import android.support.v4.app.NotificationCompat;
21:import android.support.v7.app.AppCompatActivity;
app/src/main/java/net/hugopeixoto/customsharing/AppCompatPreferenceActivity.java
6:import android.support.annotation.LayoutRes;
7:import android.support.annotation.Nullable;
8:import android.support.v7.app.ActionBar;
9:import android.support.v7.app.AppCompatDelegate;
10:import android.support.v7.widget.Toolbar;
I guess I could try to build this from source, or even package it. But that’s going to be for another time.
Conclusions
To build an android application, you need (maybe you don’t?) the Android SDK. Google distributes Android SDK binaries, but you need to accept an EULA to use these. The source code is free, though.
Debian builds android-sdk
packages. This sidesteps the need for accepting
EULAs, along with some other advantages, listed in their
wiki.
Recent versions of android-sdk depend on Kotlin, and Debian does not package
kotlin yet. Until this dependency
is solved, the only versions that are available in Debian are in Debian
stretch, since it is based openjdk8. Debian Sid, for example, is based on
openjdk11
, so things don’t work that well.
Even though I was able to compile a sample app in debian:stretch
, I can’t
build my application, as it depends on com.android.support
, which isn’t
packaged yet.