commit e17e5e436c530d58d1ffd24b5c69f70b40d1a11f parent 999dfe06ecfe09211c109ef2902328d464523591 Author: John Oberhauser <j.git-global@obez.io> Date: Tue, 21 Oct 2025 15:38:38 +0000 Bug 1993034: Adding a list of installed addons to the nimbus recorded context r=android-reviewers,rebecatudor273,twhite Differential Revision: https://phabricator.services.mozilla.com/D268931 Diffstat:
4 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/mobile/android/fenix/app/metrics.yaml b/mobile/android/fenix/app/metrics.yaml @@ -12925,6 +12925,10 @@ nimbus_system: type: boolean no_shortcuts_or_stories_opt_outs: type: boolean + addon_ids: + type: array + items: + type: string description: | The Nimbus context object that is recorded to Glean bugs: @@ -12934,6 +12938,7 @@ nimbus_system: - 'https://bugzilla.mozilla.org/show_bug.cgi?id=1981877' - 'https://bugzilla.mozilla.org/show_bug.cgi?id=1984639' - 'https://bugzilla.mozilla.org/show_bug.cgi?id=1991528' + - 'https://bugzilla.mozilla.org/show_bug.cgi?id=1993034' data_reviews: - 'https://bugzilla.mozilla.org/show_bug.cgi?id=1898552#c3' - 'https://phabricator.services.mozilla.com/D258942' @@ -12941,6 +12946,7 @@ nimbus_system: - 'https://phabricator.services.mozilla.com/D260845' - 'https://phabricator.services.mozilla.com/D262139' - 'https://phabricator.services.mozilla.com/D268391' + - 'https://phabricator.services.mozilla.com/D268931' data_sensitivity: - interaction notification_emails: diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/RecordedNimbusContext.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/RecordedNimbusContext.kt @@ -8,6 +8,7 @@ import android.content.Context import android.os.Build import androidx.annotation.VisibleForTesting import mozilla.components.support.utils.ext.getPackageInfoCompat +import org.json.JSONArray import org.json.JSONObject import org.mozilla.experiments.nimbus.NIMBUS_DATA_DIR import org.mozilla.experiments.nimbus.NimbusDeviceInfo @@ -62,6 +63,7 @@ class RecordedNimbusContext( val deviceModel: String = Build.MODEL, val userAcceptedTou: Boolean, val noShortcutsOrStoriesOptOuts: Boolean, + val addonIds: List<String>, ) : RecordedContext { /** * [getEventQueries] is called by the Nimbus SDK Rust code to retrieve the map of event @@ -102,6 +104,7 @@ class RecordedNimbusContext( deviceModel = deviceModel, userAcceptedTou = userAcceptedTou, noShortcutsOrStoriesOptOuts = noShortcutsOrStoriesOptOuts, + addonIds = NimbusSystem.RecordedNimbusContextObjectAddonIds(addonIds.toMutableList()), ), ) Pings.nimbus.submit() @@ -147,6 +150,7 @@ class RecordedNimbusContext( "device_model" to deviceModel, "user_accepted_tou" to userAcceptedTou, "no_shortcuts_or_stories_opt_outs" to noShortcutsOrStoriesOptOuts, + "addon_ids" to JSONArray(addonIds), ), ) return obj @@ -194,9 +198,14 @@ class RecordedNimbusContext( region = calculatedAttributes.region, userAcceptedTou = settings.hasAcceptedTermsOfService, noShortcutsOrStoriesOptOuts = settings.noShortcutsOrStoriesOptOuts(context), + addonIds = getFormattedAddons(settings), ) } + @VisibleForTesting + internal fun getFormattedAddons(settings: Settings): List<String> = + settings.installedAddonsList.split(",").map { it.trim() } + /** * Checks whether an eligible user has opted out of any sponsored top sites or stories. * @@ -232,6 +241,7 @@ class RecordedNimbusContext( isFirstRun: Boolean = false, eventQueries: Map<String, String> = EVENT_QUERIES, eventQueryValues: Map<String, Double> = mapOf(), + addonIds: List<String> = emptyList(), ): RecordedNimbusContext { return RecordedNimbusContext( isFirstRun = isFirstRun, @@ -250,6 +260,7 @@ class RecordedNimbusContext( region = "US", userAcceptedTou = true, noShortcutsOrStoriesOptOuts = true, + addonIds = addonIds, ) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -1890,6 +1890,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { /** * Storing the list of installed add-ons for telemetry purposes + * Addons are separated by a comma, e.g. "addon1,addon2,addon3" */ var installedAddonsList by stringPreference( appContext.getPreferenceKey(R.string.pref_key_installed_addons_list), diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/RecordedNimbusContextTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/RecordedNimbusContextTest.kt @@ -11,6 +11,7 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.put +import kotlinx.serialization.json.putJsonArray import kotlinx.serialization.json.putJsonObject import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals @@ -21,6 +22,7 @@ import org.junit.runner.RunWith import org.mozilla.experiments.nimbus.internal.validateEventQueries import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.helpers.FenixGleanTestRule +import org.mozilla.fenix.utils.Settings import org.robolectric.RobolectricTestRunner import org.mozilla.fenix.GleanMetrics.NimbusSystem as GleanNimbus @@ -71,6 +73,7 @@ class RecordedNimbusContextTest { put("device_model", Build.MODEL) put("user_accepted_tou", true) put("no_shortcuts_or_stories_opt_outs", true) + putJsonArray("addon_ids") {} }, contextAsJson, ) @@ -144,4 +147,28 @@ class RecordedNimbusContextTest { assertEquals(1.0, context.toJson().getJSONObject("events").get("TEST")) } + + @Test + fun `WHEN addonIds has values THEN the json object should reflect those values`() { + val context = RecordedNimbusContext.createForTest( + addonIds = listOf( + "addon@example.com", + "d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d", + ), + ) + + assertEquals("addon@example.com", context.toJson().getJSONArray("addon_ids")[0]) + assertEquals("d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d", context.toJson().getJSONArray("addon_ids")[1]) + } + + @Test + fun `WHEN we fetch stored addon IDs THEN a list is returned`() { + val settings = Settings(testContext) + settings.installedAddonsList = "addon@example.com,d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d" + + val addons = RecordedNimbusContext.getFormattedAddons(settings) + + assertEquals("addon@example.com", addons[0]) + assertEquals("d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d", addons[1]) + } }