From a083661e0012f7a01eafe1d65b7349b52428d782 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 30 Jan 2021 16:00:33 -0700 Subject: [PATCH] building mobile nebula --- .../2021-01-30-building-mobile-nebula.md | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 src/_posts/2021-01-30-building-mobile-nebula.md diff --git a/src/_posts/2021-01-30-building-mobile-nebula.md b/src/_posts/2021-01-30-building-mobile-nebula.md new file mode 100644 index 0000000..312d8c3 --- /dev/null +++ b/src/_posts/2021-01-30-building-mobile-nebula.md @@ -0,0 +1,388 @@ +--- +title: >- + Building Mobile Nebula +description: >- + Getting my hands dirty with Android development. +--- + +This post is going to be cheating a bit. I want to start working on adding DNS +resolver configuration to the [mobile nebula][mobile_nebula] app (if you don't +know nebula, [check it out][nebula], it's well worth knowing about), but I also +need to write a blog post for this week, so I'm combining the two exercises. +This post will essentially be my notes from my progress on today's task. + +(Protip: listen to [this][heilung] while following along to achieve the proper +open-source programming aesthetic.) + +The current mobile nebula app works very well, but it is lacking one major +feature: the ability to specify custom DNS resolvers. This is important because +I want to be able to access resources on my nebula network by their hostname, +not their IP. Android does everything in its power to make DNS configuration +impossible, and essentially the only way to actually accomplish this is by +specifying the DNS resolvers within the app. I go into more details about why +Android is broken [here][dns_issue]. + +## Setup + +Before I can make changes to the app I need to make sure I can correctly build +it in the first place, so that's the major task for today. The first step to +doing so is to install the project's dependencies. As described in the +[mobile_nebula][mobile_nebula] README, the dependencies are: + +- [`flutter`](https://flutter.dev/docs/get-started/install) +- [`gomobile`](https://godoc.org/golang.org/x/mobile/cmd/gomobile) +- [`android-studio`](https://developer.android.com/studio) +- [Enable NDK](https://developer.android.com/studio/projects/install-ndk) + +It should be noted that as of writing I haven't used any of these tools ever, +and have only done a small amount of android programming, probably 7 or 8 years +ago, so I'm going to have to walk the line between figuring out problems on the +fly and not having to completely learning these entire ecosystems; there's only +so many hours in a weekend, after all. + +I'm running [Archlinux][arch] so I install android-studio and flutter by +doing: + +```bash +yay -Sy android-studio flutter +``` + +And I install `gomobile`, according to its [documentation][gomobile] via: + +```bash +go get golang.org/x/mobile/cmd/gomobile +gomobile init +``` + +Now I startup android-studio and go through the setup wizard for it. I choose +standard setup because customized setup doesn't actually offer any interesting +options. Next android-studio spends approximately two lifetimes downloading +dependencies while my eyesight goes blurry because I'm drinking my coffee too +fast. + +It's annoying that I need to install these dependencies, especially +android-studio, in order to build this project. A future goal of mine is to nix +this whole thing up, and make a build pipeline where you can provide a full +nebula configuration file and it outputs a custom APK file for that specific +config; zero configuration required at runtime. This will be useful for +lazy/non-technical users who want to be part of the nebula network. + +Once android-studio starts up I'm not quite done yet: there's still the NDK +which must be enabled. The instructions given by the link in +[mobile_nebula][mobile_nebula]'s README explain doing this pretty well, but it's +important to install the specific version indicated in the mobile_nebula repo +(`21.0.6113669` at time of writing). Only another 1GB of dependency downloading +to go.... + +While waiting for the NDK to download I run `flutter doctor` to make sure +flutter is working, and it gives me some permissions errors. [This blog +post][flutter_blog] gives some tips on setting up, and after running the +following... + +```bash +sudo groupadd flutterusers +sudo gpasswd -a $USER flutterusers +sudo chown -R :flutterusers /opt/flutter +sudo chmod -R g+w /opt/flutter/ +newgrp flutterusers +``` + +... I'm able to run `flutter doctor`. It gives the following output: + +``` +[✓] Flutter (Channel stable, 1.22.6, on Linux, locale en_US.UTF-8) + +[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3) + ✗ Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses +[!] Android Studio + ✗ Flutter plugin not installed; this adds Flutter specific functionality. + ✗ Dart plugin not installed; this adds Dart specific functionality. +[!] Connected device + ! No devices available + +! Doctor found issues in 3 categories. +``` + +The first issue is easily solved as per the instructions given. The second is +solved by finding the plugin manager in android-studio and installing the +flutter plugin (which installs the dart plugin as a dependency, we call that a +twofer). + +After installing the plugin the doctor command still complains about not finding +the plugins, but the above mentioned blog post indicates to me that this is +expected. It's comforting to know that the problems indicated by the doctor may +or may not be real problems. + +The [blog post][flutter_blog] also indicates that I need `openjdk-8` installed, +so I do: + +```bash +yay -S jdk8-openjdk +``` + +And use the `archlinux-java` command to confirm that that is indeed the default +version for my shell. The [mobile_nebula][mobile_nebula] helpfully expects an +`env.sh` file to exist in the root, so if openjdk-8 wasn't already the default I +could make it so within that file. + +## Build + +At this point I think I'm ready to try actually building an APK. Thoughts and +prayers required. I run the following in a terminal, since for some reason the +`Build > Flutter > Build APK` dropdown button in android-studio did nothing. + +``` +flutter build apk +``` + +It takes quite a while to run, but in the end it errors with: + +``` +make: 'mobileNebula.aar' is up to date. +cp: cannot create regular file '../android/app/src/main/libs/mobileNebula.aar': No such file or directory + +FAILURE: Build failed with an exception. + +* Where: +Build file '/tmp/src/mobile_nebula/android/app/build.gradle' line: 95 + +* What went wrong: +A problem occurred evaluating project ':app'. +> Process 'command './gen-artifacts.sh'' finished with non-zero exit value 1 + +* Try: +Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. + +* Get more help at https://help.gradle.org + +BUILD FAILED in 1s +Running Gradle task 'bundleRelease'... +Running Gradle task 'bundleRelease'... Done 1.7s +Gradle task bundleRelease failed with exit code 1 +``` + +I narrow down the problem to the `./gen-artifacts.sh` script in the repo's root, +which takes in either `android` or `ios` as an argument. Running it directly +as `./gen-artifacts.sh android` results in the same error: + +```bash +make: 'mobileNebula.aar' is up to date. +cp: cannot create regular file '../android/app/src/main/libs/mobileNebula.aar': No such file or directory +``` + +So now I gotta figure out wtf that `mobileNebula.aar` file is. The first thing I +note is that not only is that file not there, but the `libs` directory it's +supposed to be present in is also not there. So I suspect that there's a missing +build step somewhere. + +I search for the string `mobileNebula.aar` within the project using +[ag][silver_searcher] and find that it's built by `nebula/Makefile` as follows: + +```make +mobileNebula.aar: *.go + gomobile bind -trimpath -v --target=android +``` + +So that file is made by `gomobile`, good to know! Additionally the file is +actually there in the `nebula` directory, so I suspect there's just a missing +build step to move it into `android/app/src/main/libs`. Via some more `ag`-ing I +find that the code which is supposed to move the `mobileNebula.aar` file is in +the `gen-artifacts.sh` script, but that script doesn't create the `libs` folder +as it ought to. I apply the following diff: + +```bash +diff --git a/gen-artifacts.sh b/gen-artifacts.sh +index 601ed7b..4f73b4c 100755 +--- a/gen-artifacts.sh ++++ b/gen-artifacts.sh +@@ -16,7 +16,7 @@ if [ "$1" = "ios" ]; then + elif [ "$1" = "android" ]; then + # Build nebula for android + make mobileNebula.aar +- rm -rf ../android/app/src/main/libs/mobileNebula.aar ++ mkdir -p ../android/app/src/main/libs + cp mobileNebula.aar ../android/app/src/main/libs/mobileNebula.aar + + else +``` + +(The `rm -rf` isn't necessary, since a) that file is about to be overwritten by +the subsequent `cp` whether or not it's there, and b) it's just deleting a +single file so the `-rf` is an unnecessary risk). + +At this point I re-run `flutter build apk` and receive a new error. Progress! + +``` +A problem occurred evaluating root project 'android'. +> A problem occurred configuring project ':app'. + > Removing unused resources requires unused code shrinking to be turned on. See http://d.android.com/r/tools/shrink-resources.html for more information. +``` + +I recall that in the original [mobile_nebula][mobile_nebula] README it mentions +to run the `flutter build` command with the `--no-shrink` option, so I try: + +```bash +flutter build apk --no-shrink +``` + +Finally we really get somewhere. The command takes a very long time to run as it +downloads yet more dependencies (mostly android SDK stuff from the looks of it), +but unfortunately still errors out: + +``` +Execution failed for task ':app:processReleaseResources'. +> Could not resolve all files for configuration ':app:releaseRuntimeClasspath'. + > Failed to transform mobileNebula-.aar (:mobileNebula:) to match attributes {artifactType=android-compiled-dependencies-resources, org.gradle.status=integration}. + > Execution failed for AarResourcesCompilerTransform: /home/mediocregopher/.gradle/caches/transforms-2/files-2.1/735fc805916d942f5311063c106e7363/jetified-mobileNebula. + > /home/mediocregopher/.gradle/caches/transforms-2/files-2.1/735fc805916d942f5311063c106e7363/jetified-mobileNebula/AndroidManifest.xml +``` + +Time for more `ag`-ing. I find the file `android/app/build.gradle`, which has +the following block: + +``` + implementation (name:'mobileNebula', ext:'aar') { + exec { + workingDir '../../' + environment("ANDROID_NDK_HOME", android.ndkDirectory) + environment("ANDROID_HOME", android.sdkDirectory) + commandLine './gen-artifacts.sh', 'android' + } + } +``` + +I never set up the `ANDROID_HOME` or `ANDROID_NDK_HOME` environment variables, +and I suppose that if I'm running the flutter command outside of android-studio +there wouldn't be a way for flutter to know those values, so I try setting them +within my `env.sh`: + +```bash +export ANDROID_HOME=~/Android/Sdk +export ANDROID_NDK_HOME=~/Android/Sdk/ndk/21.0.6113669 +``` + +Re-running the build command still results in the same error. But it occurs to +me that I probably had built the `mobileNebula.aar` without those set +previously, so maybe it was built with the wrong NDK version or something. I +tried deleting `nebula/mobileNebula.aar` and try building again. This time... +new errors! Lots of them! Big ones and small ones! + +At this point I'm a bit fed up, and want to try a completely fresh build. I back +up my modified `env.sh` and `gen-artifacts.sh` files, delete the `mobile_nebula` +repo, re-clone it, reinstall those files, and try building again. This time just +a single error: + +``` +Execution failed for task ':app:lintVitalRelease'. +> Could not resolve all artifacts for configuration ':app:debugRuntimeClasspath'. + > Failed to transform libs.jar to match attributes {artifactType=processed-jar, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}. + > Execution failed for JetifyTransform: /tmp/src/mobile_nebula/build/app/intermediates/flutter/debug/libs.jar. + > Failed to transform '/tmp/src/mobile_nebula/build/app/intermediates/flutter/debug/libs.jar' using Jetifier. Reason: FileNotFoundException, message: /tmp/src/mobile_nebula/build/app/intermediates/flutter/debug/libs.jar (No such file or directory). (Run with --stacktrace for more details.) + Please file a bug at http://issuetracker.google.com/issues/new?component=460323. +``` + +So that's cool, apparently there's a bug with flutter and I should file a +support ticket? Well, probably not. It seems that while +`build/app/intermediates/flutter/debug/libs.jar` indeed doesn't exist in the +repo, `build/app/intermediates/flutter/release/libs.jar` _does_, so this appears +to possibly be an issue in declaring which build environment is being used. + +After some googling I found [this flutter issue][flutter_issue] related to the +error. Tldr: gradle's not playing nicely with flutter. Downgrading could help, +but apparently building with the `--debug` flag also works. I don't want to +build a release version anyway, so this sits fine with me. I run... + +```bash +flutter build apk --no-shrink --debug +``` + +And would you look at that, I got a result! + +``` +✓ Built build/app/outputs/flutter-apk/app-debug.apk. +``` + +## Install + +Building was probably the hard part, but I'm not totally out of the woods yet. +Theoretically I could email this apk to my phone or something, but I'd like +something with a faster turnover time; I need `adb`. + +I install `adb` via the `android-tools` package: + +```bash +yay -S android-tools +``` + +Before `adb` will work, however, I need to turn on USB debugging on my phone, +which I do by following [this article][usb_debugging]. Once connected I confirm +that `adb` can talk to my phone by doing: + +```bash +adb devices +``` + +And then, finally, I can install the apk: + +``` +adb install build/app/outputs/flutter-apk/app-debug.apk +``` + +NOT SO FAST! MORE ERRORS! + +``` +adb: failed to install build/app/outputs/flutter-apk/app-debug.apk: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package net.defined.mobile_nebula signatures do not match previously installed version; ignoring!] +``` + +I'm guessing this is because I already have the real nebula app installed. I +uninstall it and try again. + +AND IT WORKS!!! FUCK YEAH! + +``` +Performing Streamed Install +Success +``` + +I can open the nebula app on my phone and it works... fine. There's some +pre-existing networks already installed, which isn't the case for the Play Store +version as far as I can remember, so I suspect those are only there in the +debugging build. Unfortunately the presence of these test networks causes the +app the throw a bunch of errors because it can't contact those networks. Oh well. + +The presence of those test networks, in a way, is actually a good thing, as it +means there's probably already a starting point for what I want to do: building +a per-device nebula app with a config preloaded into it. + +## Further Steps + +Beyond continuing on towards my actual goal of adding DNS resolvers to this app, +there's a couple of other paths I could potentially go down at this point. + +* As mentioned, nixify the whole thing. I'm 99% sure the android-studio GUI + isn't actually needed at all, and I only used it for installing the CMake and + NDK plugins because I didn't bother to look up how to do it on the CLI. + +* Figuring out how to do a proper release build would be great, just for my own + education. Based on the [flutter issue][flutter_issue] it's possible that all + that's needed is to downgrade gradle, but maybe that's not so easy. + +* Get an android emulator working so that I don't have to install to my phone + everytime I want to test the app out. I'm not sure if that will also work for + the VPN aspect of the app, but it will at least help me iterate on UI changes + faster. + +But at this point I'm done for the day, I'll continue on this project some other +time. + +[mobile_nebula]: https://github.com/DefinedNet/mobile_nebula +[nebula]: https://slack.engineering/introducing-nebula-the-open-source-global-overlay-network-from-slack/ +[dns_issue]: https://github.com/DefinedNet/mobile_nebula/issues/9 +[arch]: https://archlinux.org/ +[android_wiki]: https://wiki.archlinux.org/index.php/Android#Making_/opt/android-sdk_group-writeable +[heilung]: https://youtu.be/SMJ7pxqk5d4?t=220 +[flutter_blog]: https://www.rockyourcode.com/how-to-get-flutter-and-android-working-on-arch-linux/ +[gomobile]: https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile +[silver_searcher]: https://github.com/ggreer/the_silver_searcher +[flutter_issue]: https://github.com/flutter/flutter/issues/58247 +[usb_debugging]: https://www.droidviews.com/how-to-enable-developer-optionsusb-debugging-mode-on-devices-with-android-4-2-jelly-bean/