Skip to content

fix: bound the preferences custom modules may read from the host app - #939

Merged
thomson-t merged 2 commits into
mainfrom
fix/bound-custom-module-preferences
Oct 15, 2026
Merged

thomson-t merged 2 commits into
mainfrom
fix/bound-custom-module-preferences

Conversation

@thomson-t

@thomson-t thomson-t commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Why

The SDK's remote settings can name one of the host app's saved preference files and the keys to read from it, and the SDK uploads whatever values it finds. Nothing limited which keys could be named, so the settings could collect any preference an app stores, such as a sign-in token, and could also choose how the file was opened. Once this lands, the SDK reads only the Adobe values the feature was built to carry, plus any key the app itself allows, and an app can turn the feature off. The Apple SDK made the same change.

Programme

Standalone fix, no programme.

What changes

Before: custom module settings could read any key from any preference file they named, apart from the SDK's own files, which mParticle/mparticle-android-sdk#922 blocked. The file was opened with whatever mode the settings gave.

After:

  • the SDK reads only the keys the Adobe Mobile Services SDK uses: ADB_LIFETIME_VALUE, ADOBEMOBILE_STOREDDEFAULTS_AID and APP_MEASUREMENT_VISITOR_ID, plus install date, last-used date and launches under both their Android names (ADMS_InstallDate, ADMS_LastDateUsed, ADMS_Launches) and their iOS names (OMCK1, OMCK5, OMCK6); any other key is neither read nor cached, and each one ignored is logged
  • MParticleOptions.Builder.customModulePreferenceKeys(List<String>) lets an app allow more keys
  • MParticleOptions.Builder.collectCustomModulePreferences(false) stops the SDK reading preferences for custom modules at all; it defaults to true
  • preference files are always opened in private mode, and the configuration's m field, the file mode, is now ignored; the unused KEY_PERSISTENCE_MODE constant is removed with it. A mode taken from the configuration could make a file world-readable or world-writeable in apps targeting an API level below 24, and in apps targeting 24 or higher those modes throw, so reading fails. Reading another SDK's preferences never needs more than private mode. Configurations that send m keep working because the field is ignored, and one without m no longer fails to load.
  • when the SDK starts itself to handle a push message or a tapped notification, it does not know the app's options, so it reads no custom module preferences in that process
  • the configuration still chooses which preference file an allowed key is read from

Start reading at ProviderPersistence, and check first that a disallowed key is skipped before its cached mp:: value is read or written. Deliberately left alone: for an allowed key, the cached copy is still stored in the named file, so values already cached on devices keep working. The API dump gains four additive lines on MParticleOptions, a frozen contract, so this needs the api-change-approved label after API review. No documentation or changelog change is needed; the release notes are generated from the commit message.

Linked work

Related: mParticle/mparticle-apple-sdk#1053, the same change in the Apple SDK, whose option names this matches; its default keys are the iOS names, and the Android names come from the Adobe SDK's own record of where it stores them, in adobe/aepsdk-core-android; mParticle/mparticle-android-sdk#922, which blocked the SDK's own preference files.

Rollout

Path: merging changes main only. The fix ships in the next release of android-core, and apps get it when they upgrade. It is live as soon as an app runs that release, with no ordering constraint.

Feature flags: none. Apps control the behaviour with the two new options above.

Turning it off: an app that needs another key allows it with customModulePreferenceKeys, in its own next build. Otherwise, revert this pull request and publish a new release. A published version cannot be recalled, so apps on the release that carries it keep the change until they upgrade again.

What we watch: nothing monitors this path. We watch the issues reported on this repository after the release that carries it, and the Unit Tests, Lint Checks and Kotlin Lint Checks jobs on this pull request.

Risks

  • settings that name a key outside the default set stop sending its value; intended; the app can allow the key with customModulePreferenceKeys, and the SDK logs each key it ignores
  • the configuration may name an Adobe key this list leaves out; not addressed, as only these keys are known to be used; an app would see the value missing and the ignored key in its log, and can allow it with customModulePreferenceKeys
  • a device that already cached a value for a key now disallowed stops sending it; intended; visible as the value missing from uploads
  • uploads from a process the SDK started itself for a push message or a tapped notification carry no custom module values; intended, because the app's choice is unknown there; visible as the values missing from those uploads

Risk class: medium, because it adds public API and changes what the SDK reads by default.

Who

Written by: an automated coding agent (Claude Code), working for a maintainer who asked for this fix.

Code reviewed before opening: an independent review agent, 14 October 2026; it found that the push-message start-up path ignored an app's opt-out, which is fixed here, and nothing else blocking.

Design reviewed before opening: no one; the design follows the Apple SDK's change.

Decision this implements: a maintainer's request on 14 October 2026; there is no public record.

Checked: the ConfigManagerTest cases for these settings, where the allowed-keys case fails when the key check is disabled and another reads the Android lifecycle keys, a MParticleOptionsTest case for the push-message start-up path, plus the android-core unit tests, apiCheck, ktlintCheck and lint, on 14 October 2026.

Not checked: a configuration without m, which follows from the code but has no test; the instrumented tests; the private open mode on a device, because the unit-test context ignores the mode; and settings served to a real app.

Size

Hand-written: 187 lines changed (added plus removed) in 5 files.

Generated: 1 file, listed below.

For the record

Generated: android-core/api/android-core.api, by ./gradlew apiDump.

Hand-written: MParticleOptions.java, ConfigManager.java, ProviderPersistence.java, ConfigManagerTest.kt, MParticleOptionsTest.kt.

🤖 Generated with Claude Code

The configuration's custom module settings name a SharedPreferences file
and the keys to read from it, and the SDK uploads the values it finds.
Nothing limited which keys could be named, so the configuration could
read any preference the host app keeps, and it also chose the mode the
file was opened with.

The SDK now reads only the Adobe Mobile Services identifiers custom
modules were built to carry, matching the Apple SDK. A host app can
allow more keys with MParticleOptions.Builder.customModulePreferenceKeys
or stop the SDK reading them with collectCustomModulePreferences(false).
A key outside the allowed set is neither read nor cached, and is logged.
Preference files are always opened in private mode. When the SDK starts
itself to handle a push message, it does not know the host app's options,
so it reads no custom module preferences in that process.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Kotlin migration progress

⚠️ This pull request adds 96 lines of Java to the published modules.

Module Kotlin LOC Java LOC Java staying (facade) Java left to convert Kotlin share
android-core 3,249 22,690 9,716 12,974 12.5%
android-kit-base 601 5,405 1,651 3,754 10.0%
Total 3,850 28,095 11,367 16,728 12.1%

Conversion progress: 0.0% (16,728 of 16,550 baseline Java LOC left to convert). 132 Java files and 36 Kotlin files in scope.

@github-actions

Copy link
Copy Markdown

Binary compatibility

  • ✅ android-core is compatible with 6.1.8 (115 classes compared, 3 with additions only).
  • ✅ android-kit-base is compatible with 6.1.8 (44 classes compared, 2 with additions only).

Compares the release AAR against the same artifact at the last version published to Maven Central; R8-renamed classes and members are excluded (see the script docstring). Full japicmp HTML/XML reports are attached as the api-compat-reports workflow artifact. This check is not in the required-check set yet, so a red result here does not block merging.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

📦 SDK Size Impact Report

What the SDK adds to a minified release APK.

mParticle Core SDK

Measured against an empty baseline app. Unlike the Rokt kit, android-core ships no Compose and no resources, so there is nothing here that a host app would already provide.

Metric Target branch This PR Change
APK size 120.30 KB 120.62 KB +336 bytes
Download size 118.11 KB 118.44 KB +339 bytes
Dex bytes 214.54 KB 215.25 KB +720 bytes

mParticle Core + Rokt kit

Measured against a Compose + Material3 reference app, so these are the costs on top of an app that already ships Compose. The reference app's dependencies are a documented convention, not a measured average: see size-report-rokt/README.md.

Metric Target branch This PR Change
APK size 1.26 MB 1.26 MB 0 bytes
Download size 1.24 MB 1.24 MB +267 bytes
Dex bytes 2.23 MB 2.23 MB +688 bytes

Rokt SDK+ umbrella (adds the payment extension)

Metric Target branch This PR Change On top of mParticle Core + Rokt kit
APK size 6.55 MB 6.55 MB 0 bytes +5.29 MB
Download size 6.43 MB 6.43 MB +1.01 KB +5.19 MB
Dex bytes 7.11 MB 7.11 MB +692 bytes +4.88 MB

➡️ SDK size impact change is minimal.

Raw measurements

core, Target branch:

{"baseline_dex_bytes": 0, "baseline_download_bytes": 2513, "baseline_install_bytes": 7529, "core_dex_bytes": 219692, "core_download_bytes": 123460, "core_install_bytes": 130712}

core, This PR:

{"baseline_dex_bytes": 0, "baseline_download_bytes": 2516, "baseline_install_bytes": 7533, "core_dex_bytes": 220412, "core_download_bytes": 123802, "core_install_bytes": 131052}

rokt, Target branch:

{"baseline_dex_bytes": 1829108, "baseline_download_bytes": 1240062, "baseline_install_bytes": 1300934, "kit_dex_bytes": 4163396, "kit_download_bytes": 2544133, "kit_install_bytes": 2623275, "sdkplus_dex_bytes": 9284332, "sdkplus_download_bytes": 7981260, "sdkplus_install_bytes": 8165359}

rokt, This PR:

{"baseline_dex_bytes": 1829108, "baseline_download_bytes": 1240062, "baseline_install_bytes": 1300934, "kit_dex_bytes": 4164084, "kit_download_bytes": 2544400, "kit_install_bytes": 2623275, "sdkplus_dex_bytes": 9285024, "sdkplus_download_bytes": 7982298, "sdkplus_install_bytes": 8165359}

Measured 16cbf9f merged into 998eb29

@thomson-t
thomson-t marked this pull request as ready for review October 14, 2026 18:59
@thomson-t
thomson-t requested a review from a team as a code owner October 14, 2026 18:59
The Adobe Mobile Services SDK keeps install date, last-used date and
launches under ADMS_InstallDate, ADMS_LastDateUsed and ADMS_Launches on
Android, and under OMCK1, OMCK5 and OMCK6 on iOS. The default set of
custom module preference keys copied the Apple SDK's and so had only the
iOS names. Add the Android names, and keep the iOS names so a
configuration shared with iOS still resolves those keys as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@thomson-t
thomson-t merged commit abf750a into main Oct 15, 2026
178 of 181 checks passed
@thomson-t
thomson-t deleted the fix/bound-custom-module-preferences branch October 15, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants