Skip to content

Add Remote Config and Installations support - #70

Open
angelix wants to merge 33 commits into
GitLiveApp:masterfrom
angelix:feature/remote-config
Open

angelix wants to merge 33 commits into
GitLiveApp:masterfrom
angelix:feature/remote-config

Conversation

@angelix

@angelix angelix commented Oct 7, 2026 •

Copy link
Copy Markdown

Makes the bundled Remote Config (21.6.0) and Installations (17.2.0) libraries work on the JVM. Like the other products, it runs the unmodified Android code and fills in the Android shims it needs.

What works

  • Remote Config setDefaultsAsync(Map), fetch, activate, fetchAndActivate, get*/getValue, getAll, getKeysByPrefix, getInfo, setConfigSettingsAsync, and reset.
  • Firebase Installations registration and auth tokens, which Remote Config needs.
  • Firebase heartbeats (firebase-common). They failed for every product before this change, and Installations logged "Failed to get heartbeats header".
  • FirebaseApp.setDataCollectionDefaultEnabled, which previously crashed because the shims had no SharedPreferences.putBoolean or Context.createDeviceProtectedStorageContext.

Not supported: realtime updates (addOnConfigUpdateListener), and defaults loaded from XML resources.

Changes

  • FirebasePlatform.getFilesDir() is a new overridable method. It defaults to <tmpdir>/firebase-files, and Context.filesDir, openFileInput, openFileOutput, and deleteFile use it. File names are URL-encoded, because Remote Config puts the app ID (which contains :) in its file names.
  • SharedPreferences are stored as files, the way Android stores them. Each preferences file is one JSON file at getFilesDir()/shared_prefs/<name>.json, handled by the new PreferencesFile: values keep their types, edits are batched until commit()/apply() as on Android, and each commit syncs a temporary file to disk and moves it over the old one atomically. As on Android, memory is updated even if the disk write fails, and a later commit writes the values again. This replaces the strict whitelist in Context.getSharedPreferences, which threw on unknown keys. Keeping preferences next to Remote Config's config files means the two can't drift apart when only one of them is persisted. The shim interface gains getInt, getBoolean, getStringSet, putInt, putBoolean, putStringSet, remove, and clear, and getAll() now has Android's Map<String, ?> signature.
  • Ported from AOSP (android14-release) without changes, with a header comment saying what needs them: android.util.JsonReader (with JsonToken, JsonScope, MalformedJsonException, and com.android.internal.util.StringPool), plus Base64DataException. The existing Base64OutputStream shim already referenced Base64DataException but nobody had ported it.
  • New small shims: TrafficStats (no-op), DateUtils.formatElapsedTime, Configuration.locale, and Resources.getConfiguration().
  • RemoteConfigRegistrar and AbtRegistrar are registered in the PackageManager shim. Remote Config starts with the default app and casts its context to Application, so Context.applicationContext now returns an Application for a plain Context, and Firebase.initialize(Context(), options) keeps working.
  • README: the Remote Config and Installations rows are no longer struck through, and the README documents getFilesDir. It also states that FirebasePlatform's key-value storage must be thread-safe, because Auth writes to it from network threads, and the example now uses a ConcurrentHashMap.

Note for FirebasePlatform implementers

Only Auth uses store/retrieve/clear, as before; Firebase-internal preferences don't reach them. The default getFilesDir() is in the OS temp folder, like getDatabasePath(). OS cleaners can delete Remote Config's config files while its preferences remain, which leaves it serving default values until the template changes. The README therefore recommends that production apps override getFilesDir() with a persistent folder.

Tests

  • FirebaseRemoteConfigTest ports the Remote Config tests from firebase-kotlin-sdk's firebase-config common tests one-to-one onto the Android API. The two fetch tests that need a published test_remote_string parameter keep their @Ignore, as they have upstream. A live fetchAndActivate succeeds test runs against the fir-java-sdk project. The expected JVM warnings (no package info to fingerprint, no Analytics SDK for A/B testing) are captured and asserted, and any other log fails the test.
  • Unit tests cover the Context file APIs, PreferencesFile (types, isolation between files, batched edits, reading back a written file, concurrent commits, corrupt files, and failed and retried writes), JsonReader, and DateUtils. HeartBeatTest registers a heartbeat and checks that it reached the file on disk. FirebaseAppTest covers initializing with a plain Context and changing the data collection default.
  • Each test that extends FirebaseTest gets its own folder for files and databases under build/test-data, kept until the next run, so Firebase's background work can't leak into later tests. FirebaseFirestoreTest terminates Firestore after its test, and the test stores are thread-safe. Tests that capture logs fail on any line they don't expect.
  • firebase emulators:exec ... './gradlew cleanTest build': 59 tests, 57 pass and 2 are ignored, and ktlint is clean.

Known limitations

  • Windows: Installations saves its state by writing a temp file and renaming it over the existing file (File.renameTo). On Windows that rename fails when the target exists. The library swallows the IOException, so the installation would probably register again on every token fetch, and temp files would accumulate in getFilesDir(). I confirmed this from the bytecode and JDK behaviour; I have not tested it on Windows. The shims can't intercept the rename.
  • Shared default folder on Linux: the default getFilesDir() there is /tmp/firebase-files, shared by every OS user, so other local users can read the installation data and Remote Config files in it. Apps on shared machines should override getFilesDir(), as the README already recommends for production apps. getDatabasePath()'s default has the same exposure and predates this change.

angelix added 30 commits October 7, 2026 20:22
… Storage request without swallowing cancellation
… in tests, and keep FirebaseAppTest data in its temporary folder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant