Files

6.6 KiB

Android JNI regression checks

Issue #64 reported an UnsatisfiedLinkError when Java delivered a Google authorization result. NativeActivity opens the native library and calls ANativeActivity_onCreate, but this does not register the library with ART's Java native-method lookup. FlectarActivity must also call System.loadLibrary("flectar_mail_android") in its static initializer, before any native callback can run.

References:

Offline and packaged-library checks

python3 scripts/test-android-jni.py
python3 scripts/test-pdf-packaging.py
python3 scripts/verify-android-native.py path/to/flectar-mail.apk

The offline tests require a C compiler (cc) and GNU readelf or llvm-readelf. Set READELF to an NDK llvm-readelf path if needed. APK verification checks the dynamic symbol table for the native entry point and every Java callback, in every packaged ABI, after release stripping. Both Android release workflows already invoke this verifier through verify-android-apk.sh. Symbol checks alone cannot verify Java library loading.

Device JNI tests

Build and stage the release Rust library and PDFium with the normal test APK script, then run the instrumentation suite on a connected ARM64 test device:

./scripts/build-android-test-apk.sh
./platform/android/gradle/gradlew -p platform/android/gradle \
  :app:connectedDebugAndroidTest

This installs a debug application and a separate test APK. Use a test device or Android user profile without an existing Flectar installation signed by a different key. The Rust library is still the stripped release library staged by the build script. An emulator needs a matching native ABI staged under target/android/gradle-jni, or a Google APIs image with ARM64 translation. Both Android build workflows run these tests on the API 30 Google APIs x86_64 image, which supports ARM64 translation, and retain the instrumentation reports.

NativeCallbacksTest invokes all five native callbacks through the real FlectarActivity class and packaged Rust library. It intentionally does not load the library itself or launch the Activity. No Google account, network access, OAuth registration, or mailbox data is needed. Removing the static initializer must make the invocation tests fail with UnsatisfiedLinkError. These tests verify linkage, not OAuth authorization semantics.

LauncherIconsTest switches the launcher entry through every app icon offered in Settings and checks that exactly one entry stays visible, then restores the default orange icon. The app switches entries only after it leaves the foreground; check that by choosing an icon in Settings and returning to the home screen.

SpellingTest checks spelling through Spelling.java with the device's own spell checker: misspelled words and their UTF-16 places, a full stop at the end, corrections, and a session started again. It needs a spell checker that knows English, as Android's own keyboard does on the emulator's default images (target: default). Gboard on the Google APIs images answers late and passes every word, so the tests are skipped there, as on the release workflows' emulator.

Sign-in acceptance check

On the reported Pixel/GrapheneOS configuration and a supported stock Android device, use an APK whose signing certificate and package are registered for Google authorization. Exercise successful sign-in, consent cancellation, and an authorization failure. Check that a result reaches the app without UnsatisfiedLinkError; cancellation/failure must leave the app usable. Also background/resume the app and open/cancel the document picker, since those callbacks use the same library-loading path.

Record the APK version, Android version and test results before closing #64. If Google authorization still fails, preserve the provider's error separately from JNI linkage failures; never include access tokens or mailbox data in logs.

OAuth configuration and issue #65

Preview builds may omit either FLECTAR_GOOGLE_ANDROID_CLIENT_ID or FLECTAR_MICROSOFT_ANDROID_CLIENT_ID. No placeholder is substituted: that provider stays unavailable until a valid custom registration is saved through Sign-in settings. A supplied invalid ID fails the build before compilation. Production builds require both IDs and verify that the configured Microsoft redirect matches the APK signing certificate.

Microsoft client IDs must be the nonzero Application (client) ID from Entra app registration, in hyphenated GUID form. The app validates bundled, custom and runtime configuration before starting authorization. Existing invalid saved IDs remain editable, and unrelated settings remain usable. Invalid custom input is rejected without changing either provider's saved registration.

For working Outlook sign-in, register the Android package com.flectar.mail and the signing certificate of the APK being installed. Obtain the certificate hash and exact redirect with scripts/android-signing-identities.sh <apk>. The running app derives its redirect from its installed signing certificate; setting a build variable alone does not register that redirect with Microsoft. The app uses the common authority for work/school and personal Microsoft accounts, so choose the corresponding supported account types in Entra. This is a public client using authorization code with PKCE; do not add a client secret.

References: Microsoft app registration, Android client configuration, and Android signing redirects.

Run python3 scripts/test-android-oauth.py for the build configuration regression tests. Run cargo test -p flectar-mail-core --locked --test oauth_settings for saved configuration, atomic rejection, and the no-browser-on-invalid-ID check. Local syntax validation cannot establish that an ID is registered, that its redirect is registered, or that a tenant permits consent. Check real sign-in using the release registration and signing certificate before promising built-in Outlook availability.