[Internal] Migrate Age Signals integration to SDK 0.0.4 - #16376
JorgeMucientes merged 15 commits into
Conversation
Generated by 🚫 Danger |
Project dependencies changeslist! Upgraded Dependencies
com.google.android.play:age-signals:0.0.4, (changed from 0.0.3)tree-\--- com.google.android.play:age-signals:0.0.3
+\--- com.google.android.play:age-signals:0.0.4 |
Project manifest changes for WooCommerceThe following changes in the --- ./build/reports/diff_manifest/WooCommerce/vanillaRelease/base_manifest.txt 2026-08-28 10:00:25.054915841 +0000
+++ ./build/reports/diff_manifest/WooCommerce/vanillaRelease/head_manifest.txt 2026-08-28 10:00:27.789845993 +0000
@@ -665,6 +665,11 @@
android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:exported="false"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+ <activity
+ android:name="com.google.android.play.agesignals.AgeSharingConsentWrapperActivity"
+ android:exported="false"
+ android:stateNotNeeded="true"
+ android:theme="@style/Theme.Play.AgeSignals.Transparent" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"Go to https://buildkite.com/automattic/woocommerce-android/builds/41912/canvas?sid=01a047ce-71e2-4c4e-8eb2-6c613c1408d7, click on the |
|
|
|
Version |
02f91f2 to
af027a0
Compare
8e9c3ce to
722ba97
Compare
There was a problem hiding this comment.
AI Code Review - Found 1 potential issue
The SDK 0.0.4 migration is well structured: the two-stage access/check flow, bounded retry with backoff, and defensive mapping of unknown SDK values to UNEXPECTED are all solid, and the test coverage (retry budget, virtual-time backoff assertions, defensive mapping) is thorough. One behavior-change concern is noted inline on the evaluator.
PR housekeeping
- Applied repo
AGENTS.md/CLAUDE.mdguidance while reviewing (Store Management MVVM conventions).
Automatic review · claude-opus-4-8 · Workflow run
How to reply to a finding
Reply on this review (or inline at the line the finding refers to) with one of:
@claude addressed- I made the change. Bot verifies against the next diff before marking resolved.@claude rejected: <reason>- Will not fix; reason gets quoted on the next review.@claude not-applicable- Finding does not apply (wrong file, already covered elsewhere, etc.).
The bot honours these on the next review pass.
722ba97 to
eaf575b
Compare
420552e to
1f3bf26
Compare
This comment was marked as resolved.
This comment was marked as resolved.
1f3bf26 to
e12d776
Compare
eaf575b to
f3ab0ed
Compare
f3ab0ed to
37a19e6
Compare
AdamGrzybkowski
left a comment
There was a problem hiding this comment.
Looks good 👍 There's one thing worth addressing.
|
|
||
| return when { | ||
| ageLower >= WOOCOMMERCE_TOS_MINIMUM_AGE_FOR_APP_USE -> authoritativeAllowed() | ||
| else -> nonAuthoritative(priorRestriction) |
There was a problem hiding this comment.
When the range straddles the threshold (ageLower < 13 ≤ ageUpper), it returns nonAuthoritative → Allowed if there’s no prior restriction.
A test like this in AgeEligibilityEvaluatorTest shows this:
@Test
fun `given an ambiguous shared age range straddling 13 without a prior restriction, when evaluated, then access is allowed non-authoritatively`() {
listOf(
sharedResult(ageLower = 12, ageUpper = 15),
sharedResult(ageLower = 0, ageUpper = 13),
sharedResult(ageLower = 10, ageUpper = null)
).forEach { result ->
val evaluation = evaluate(result)
assertThat(evaluation).isEqualTo(
AgeEligibilityEvaluation(AgeEligibilityDecision.Allowed, isAuthoritative = false)
)
}
}None of the ranges above are conclusive, right?
There was a problem hiding this comment.
Hey @AdamGrzybkowski just trying to better understand your point. If your question/idea is along the lines of:
Are we intentionally failing open when the signal says the user might be under 13, but doesn’t prove it?
If so, then yes, it's intentional. While this might seem too relaxed, in practice it won't be. Google’s API default ranges are 0–12, 13–15, 16–17, and 18+, so a normal response cannot straddle 13 (our min required age). A straddling range would require us to configure custom ranges in Google Play Console, which we currently have no reason or plan to do.
This logic is therefore defensive handling for an unexpected or future configuration and I agree that a test for this could be useful to document that fallback explicitly, I'll add it.
EDIT: Pushed here 6038b35
| data class AgeSignalsRequestResult( | ||
| val accessStatus: AgeSignalsAccessStatus, | ||
| val ageSignals: SharedAgeSignals? = null, | ||
| val retryCount: Int = 0 |
There was a problem hiding this comment.
The retryCount values seem never to be used - do we need it?
There was a problem hiding this comment.
It is used in this PR that I'll set to ready for review in a couple hours 🙂
Co-authored-by: Adam Grzybkowski <agrzybkowski@outlook.com>
6306633 to
6038b35
Compare
08d1467 to
2d54fbb
Compare
AdamGrzybkowski
left a comment
There was a problem hiding this comment.
Thanks for the explanation and the changes! ![]()
…als-telemetry [Internal] Complete Age Signals telemetry and release validation
c99a6ff
into
issue/woomob-3766-age-verification-gate

Fixes WOOMOB-3767
Description
Activates the two-stage Play Age Signals SDK 0.0.4 integration behind the existing feature flag.
AgeSignalsManagerbehind the app-ownedAgeSignalsClientboundary.requestAgeSignalsAccess()and callscheckAgeSignals()only forSHARED.userStatushandling.FakeAgeSignalsManagerplus explicit stage, gating, mapping, and retry coverage.Stack: #16374 ← #16375 ← this PR. Merge bottom-up only after this activation layer is approved.
Test Steps
The manual-test patch replaces the production manager provided by Hilt with Google's SDK 0.0.4
FakeAgeSignalsManager. It is validated against commit6038b3502a2c9287686a1f1db0dfbe0165c48b79, is only for local review, and must not be committed.Start from a clean checkout of this PR, then download and apply the patch:
Modify the value for
ACTIVE_MANUAL_AGE_SIGNALS_SCENARIOinManualAgeSignalsTestScenario.ktwith the below values then install the Wasabi debug build, and cold-start both logged-in and logged-out flows:Cover the access-stage and recovery scenarios:
NOT_SHAREDcheckAgeSignals()is not called.VERIFICATION_REQUIREDVERIFICATION_REQUIRED_THEN_SHARED_18_PLUSSHARED, checks the adult result, clears a prior restriction, and dismisses the dialog.UNEXPECTED_ACCESS_STATUSNOT_SHARED: preserve a prior restriction; otherwise allow without crashing.Cover shared age ranges and significant changes:
SHARED_BELOW_13BELOW_MINIMUM_AGE, log out, and show the terminal restriction message.SHARED_13_TO_15,SHARED_16_TO_17, orSHARED_18_PLUSSHARED_MISSING_LOWER_BELOW_13BELOW_MINIMUM_AGE, log out, and show the terminal restriction message.SHARED_13_PLUS_OPEN_ENDEDSHARED_MISSING_BOUNDSorSHARED_CROSSING_BOUNDSSIGNIFICANT_CHANGE_PENDING,SIGNIFICANT_CHANGE_APPROVED, orSIGNIFICANT_CHANGE_DECLINEDCover failures and retries:
ACCESS_NETWORK_ERRORorCHECK_NETWORK_ERRORACCESS_TRANSIENT_ERRORorCHECK_TRANSIENT_ERRORTo verify preservation and clearing, first run
SHARED_BELOW_13; then change only the scenario and reinstall over the app without clearing data. Ambiguous, non-shared, unknown, and failure scenarios must retain the restriction; a conclusive 13+ shared scenario must clear it.Restore the scenario to the patch default (
VERIFICATION_REQUIRED_THEN_SHARED_18_PLUS), then remove the manual patch:The scenarios use the same
setNextAgeSignalsAccessResult,setNextAgeSignalsResult, and exception setters documented in Google's Age Signals testing guide. The verification-to-shared scenario composes two fake managers so both stages can be exercised in one app run.Play-installed internal-testing QA is still required separately for acceptance; the fake patch is deterministic integration/review support and does not validate Play ownership, account configuration, or the real Play UI.
Images/gif
No visual changes in this stack layer; the verification UI is provided by #16375.