summaryrefslogtreecommitdiff
path: root/static/src/_posts/2021-01-30-building-mobile-nebula.md
blob: 0645e70670f1c8106fcb138a4a0aaa4a0c784f3e (plain)
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
---
title: >-
    Building Mobile Nebula
description: >-
    Getting my hands dirty with Android development.
series: nebula
tags: tech
---

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/