v26.16.1.178666 #7

Open
opened 2026-09-11 15:13:58 +02:00 by hloth · 0 comments
Owner

New third-party fingerprinting SDK: SafetyKit

com.safetykit.android package (absent in 26.15.1), plus an obfuscated a/ package holding a "FingerprintFramework".

  • POST https://ingest.sk-diagnostics.com/v1/mobile-events (errors → /v1/mobile-events/errors)
  • Headers: Authorization: Bearer <hardcoded API key>, Content-Type: application/json; charset=utf-8
  • Body: { uuid, data: {<device fingerprint>}, event_name, params: { user_id } }
    • uuid = random UUID persisted in SharedPreferences com.safetykit, key uuid
    • event_name = "login" or "user_registration"; user_id = Grindr profile id
  • Fired from SafetyKit.recordLoginEvent() / recordUserRegistrationEvent(); SafetyKit.initialize() at startup. All three gated by flag safety_kit_sdk_integration.
  • Collects: emulator/root/ADB indicators (qemu.*, ro.bootloader, init.svc.qemud, adb_enabled, development_settings_enabled, mock_location), sensors, cameras, requested permissions + app signatures, wifi/network/routes, battery, locale, fonts, android_id, boot_count, proc_meminfo, proc_version, ~150 Settings.* keys.
  • Analytics on failure: safetykit_init_failure { reason }.

New endpoints

Method Path Notes
POST v1/popup-logic dark-shipped, see below
POST v1/popup-logic/ack dark-shipped
GET v5/push-settings flag push_settings_v5_migration
PUT v5/push-settings same flag
DELETE v3/push-tokens/{token} requireRealDeviceInfo: true, no body, fired on sign-out

Changed endpoint

  • POST web/v3/consent/grindr/getPOST web/v3/consent/grindr/get?includePurposeInfo=true

Popup-logic orchestration (new package, NOT wired up)

POST v1/popup-logic
  req  { sessionId: string, supportedStepKeys: string[], context: Map<string,string> }
  resp { schemaVersion: int, planVersion: string,
         steps: [{ key: string, type: string, params: Map<string,string>, triggerEvent: string? }] }

POST v1/popup-logic/ack
  req  { stepKey: string, action: string, planVersion: string, sessionId: string }
  resp empty

Server-driven, session-scoped modal sequencing with per-step acks. StepType = Mandatory | Optional (no wireName mapping present in the APK). PlanSource = Server | Default (client-only).

Nothing calls ModalPlanService — zero references outside its own file, and no DI module provides it. (Control: the 3 other new services each have 2 external refs.) Don't implement yet; expect it to go live in a later release. The existing popup/data/datasource/api/service/PopupModalService gate is untouched.

New / changed JSON fields

  • POST web/v3/consent/grindr/get gains purposeInfo, a required non-null Map<string, { allowed: string, allowsOptOut: bool, isRecorded: bool, requiresOptIn: bool, legalBasis: string, source: string, collectedAt: int64, issuedAt: int64 }>. A missing purposeInfo throws JsonDataException. requiresOptIn now decides whether the Ketch consent WebView is loaded at all (with flag ketch-webview-on-demand). Six previously-discarded top-level fields now reach the domain model (jurisdictionCode, hasUnrecordedOptInConsent, setConsentRequired, interactive, collectedAt, vendorConsents) — only jurisdictionCode and purposeInfo have readers.
  • POST v2/age-verification/verify response gains failureReason: string.
  • BannedTermsResponse (profile text moderation) gains code: int?.
  • GET /v1/store-and-profileupgrade_card gains ctaTextTrial, ctaTextTrialId, ctaPriceRoles: string[], productType.
  • GET v3/legal-agreements error body is now parsed: { code: int?, message: string? } (only code is consumed) → LegalAgreementFetchFailure(httpStatus, serverErrorCode, cause).
  • GET/PUT v5/push-settings: { tapPushNotification: bool|null, favoritePushNotification: bool|null }. The client only ever writes favoritePushNotification and reads only that back; the tap value is parsed and discarded. Failed GET leaves the previous state (no blanking).

New enum values

  • banSubReason (412 ban responses): +DRUG_SEEKING, DRUG_FACILITATION, NON_CONSENSUAL_INTIMATE_IMAGERY, SEXTORTION (29 → 33). None removed.
  • GET v2/warnings warning type: +DRUG_SEEKING, DRUG_FACILITATION, NON_CONSENSUAL_INTIMATE_IMAGERY (9 → 12). No SEXTORTION here.
  • New AgeVerificationFailureReason: LIVENESS_CHECK_FAILED, DOCUMENT_EXPIRED, DOCUMENT_VERIFICATION_FAILED, AGE_NOT_DETECTED, UNDERAGE_DETECTED.
  • ReferrerType +favorite_online_notification (24 → 25).
  • Store attribution source (POST /v1/store/attribution/tokens): +profile_tab-edge|xtra|unlimited|my_subscription|current_sku, −intro_offer_account_creation.

New cascade item type

{ "type": "ads_free_session_entry_point_v1",
  "data": { "duration": <int>, "interval": "Minutes" | "Hours" } }   // interval matched case-insensitively

Server-driven config

Added (17)atc-fullscreen-ad-gate, atc-fullscreen-iam-gate, push_settings_v5_migration, safety_kit_sdk_integration, edge-paywall-embedded-in-store, premium-tier-price-test-sep26, half-sheet-upsell-v2, health-center-woodwork-perk-at-subscription-purchase, rewarded-ads-free-session-v3, rewarded-ads-viewed-me-v3, right-now-text-status-enabled, ketch-webview-on-demand, boost-somewhere-else-v3, ads-disable-front-fill, braze-ad-clicked-analytics, disable-async-webview-startup, disable-maps-gl-warmup

Removed (5)ad-backfill, ads-ketch-consent-api-enabled, ads-new-applovin-privacy-flags-enabled, community_events, disable-defer-maps-warmup

New experiment payload (GET /v3/assignment): health-center-woodwork-perk-at-subscription-purchasepayload: { payload: { introSheetPostPurchase: string?, woodWorkTreatment: string? } }

Other changes

  • Push payload V2 shape and PushNotificationActionType are unchanged (package move only). POST v3/gcm-push-tokens moved LoginRestServicePushTokenRestService; body { vendorProvidedIdentifier, token } and requireRealDeviceInfo: true unchanged. It now also fires on FCM token rotation and on successful session refresh.
  • "Favorite comes online" push — new Notification Settings toggle backed by v5/push-settings.favoritePushNotification; push opens grindr://favorite-profile.
  • +grindr://favorite-profile?profileID=<id> (also accepts ?profileId=) — the only deeplink host added. Server sends it in the "favorite came online" push.
  • Ad-free session rewarded card in the cascade: server-inserted card → watch rewarded video → N minutes/hours ad-free ("No Ads for %1$d min", "Watch a quick video and be done").
  • Air Traffic Control: new client-side lease arbiter preventing two full-screen things from taking the screen at once. Analytics only (atc_block_reaped, atc_block_released, atc_ad_suppressed); enforcement gated by atc-fullscreen-ad-gate / atc-fullscreen-iam-gate.
  • 7-day-free-trial-at-signup removed entirely
  • Top Picks UI retired
  • New store tab bar with an embedded Edge tab + red dot, gated by edge-paywall-embedded-in-store AND premium-tier-price-test-sep26 AND the pre-existing edge-paywall-v2.
  • DRUG_PROMOTION re-tiered from warning to temporary suspension

Generated by Claude Opus 5 on September 11, 2026.

## New third-party fingerprinting SDK: SafetyKit `com.safetykit.android` package (absent in 26.15.1), plus an obfuscated `a/` package holding a "FingerprintFramework". - `POST https://ingest.sk-diagnostics.com/v1/mobile-events` (errors → `/v1/mobile-events/errors`) - Headers: `Authorization: Bearer <hardcoded API key>`, `Content-Type: application/json; charset=utf-8` - Body: `{ uuid, data: {<device fingerprint>}, event_name, params: { user_id } }` - `uuid` = random UUID persisted in SharedPreferences `com.safetykit`, key `uuid` - `event_name` = `"login"` or `"user_registration"`; `user_id` = Grindr profile id - Fired from `SafetyKit.recordLoginEvent()` / `recordUserRegistrationEvent()`; `SafetyKit.initialize()` at startup. All three gated by flag `safety_kit_sdk_integration`. - Collects: emulator/root/ADB indicators (`qemu.*`, `ro.bootloader`, `init.svc.qemud`, `adb_enabled`, `development_settings_enabled`, `mock_location`), sensors, cameras, requested permissions + app signatures, wifi/network/routes, battery, locale, fonts, `android_id`, `boot_count`, `proc_meminfo`, `proc_version`, ~150 Settings.* keys. - Analytics on failure: `safetykit_init_failure { reason }`. ## New endpoints | Method | Path | Notes | | -------- | ------------------------ | --------------------------------------------------------- | | `POST` | `v1/popup-logic` | **dark-shipped, see below** | | `POST` | `v1/popup-logic/ack` | **dark-shipped** | | `GET` | `v5/push-settings` | flag `push_settings_v5_migration` | | `PUT` | `v5/push-settings` | same flag | | `DELETE` | `v3/push-tokens/{token}` | `requireRealDeviceInfo: true`, no body, fired on sign-out | ## Changed endpoint - `POST web/v3/consent/grindr/get` → `POST web/v3/consent/grindr/get?includePurposeInfo=true` ## Popup-logic orchestration (new package, NOT wired up) ``` POST v1/popup-logic req { sessionId: string, supportedStepKeys: string[], context: Map<string,string> } resp { schemaVersion: int, planVersion: string, steps: [{ key: string, type: string, params: Map<string,string>, triggerEvent: string? }] } POST v1/popup-logic/ack req { stepKey: string, action: string, planVersion: string, sessionId: string } resp empty ``` Server-driven, session-scoped modal sequencing with per-step acks. `StepType` = `Mandatory | Optional` (no wireName mapping present in the APK). `PlanSource` = `Server | Default` (client-only). **Nothing calls `ModalPlanService`** — zero references outside its own file, and no DI module provides it. (Control: the 3 other new services each have 2 external refs.) Don't implement yet; expect it to go live in a later release. The existing `popup/data/datasource/api/service/PopupModalService` gate is untouched. ## New / changed JSON fields - **`POST web/v3/consent/grindr/get`** gains `purposeInfo`, a **required non-null** `Map<string, { allowed: string, allowsOptOut: bool, isRecorded: bool, requiresOptIn: bool, legalBasis: string, source: string, collectedAt: int64, issuedAt: int64 }>`. A missing `purposeInfo` throws `JsonDataException`. `requiresOptIn` now decides whether the Ketch consent WebView is loaded at all (with flag `ketch-webview-on-demand`). Six previously-discarded top-level fields now reach the domain model (`jurisdictionCode`, `hasUnrecordedOptInConsent`, `setConsentRequired`, `interactive`, `collectedAt`, `vendorConsents`) — only `jurisdictionCode` and `purposeInfo` have readers. - **`POST v2/age-verification/verify`** response gains `failureReason: string`. - **`BannedTermsResponse`** (profile text moderation) gains `code: int?`. - **`GET /v1/store-and-profile`** → `upgrade_card` gains `ctaTextTrial`, `ctaTextTrialId`, `ctaPriceRoles: string[]`, `productType`. - **`GET v3/legal-agreements`** error body is now parsed: `{ code: int?, message: string? }` (only `code` is consumed) → `LegalAgreementFetchFailure(httpStatus, serverErrorCode, cause)`. - **`GET/PUT v5/push-settings`**: `{ tapPushNotification: bool|null, favoritePushNotification: bool|null }`. The client only ever writes `favoritePushNotification` and reads only that back; the tap value is parsed and discarded. Failed GET leaves the previous state (no blanking). ## New enum values - `banSubReason` (412 ban responses): **+`DRUG_SEEKING`, `DRUG_FACILITATION`, `NON_CONSENSUAL_INTIMATE_IMAGERY`, `SEXTORTION`** (29 → 33). None removed. - `GET v2/warnings` warning type: **+`DRUG_SEEKING`, `DRUG_FACILITATION`, `NON_CONSENSUAL_INTIMATE_IMAGERY`** (9 → 12). No `SEXTORTION` here. - New `AgeVerificationFailureReason`: `LIVENESS_CHECK_FAILED`, `DOCUMENT_EXPIRED`, `DOCUMENT_VERIFICATION_FAILED`, `AGE_NOT_DETECTED`, `UNDERAGE_DETECTED`. - `ReferrerType` **+`favorite_online_notification`** (24 → 25). - Store attribution `source` (`POST /v1/store/attribution/tokens`): +`profile_tab-edge|xtra|unlimited|my_subscription|current_sku`, −`intro_offer_account_creation`. ## New cascade item type ```jsonc { "type": "ads_free_session_entry_point_v1", "data": { "duration": <int>, "interval": "Minutes" | "Hours" } } // interval matched case-insensitively ``` ## Server-driven config **Added (17)** — `atc-fullscreen-ad-gate`, `atc-fullscreen-iam-gate`, `push_settings_v5_migration`, `safety_kit_sdk_integration`, `edge-paywall-embedded-in-store`, `premium-tier-price-test-sep26`, `half-sheet-upsell-v2`, `health-center-woodwork-perk-at-subscription-purchase`, `rewarded-ads-free-session-v3`, `rewarded-ads-viewed-me-v3`, `right-now-text-status-enabled`, `ketch-webview-on-demand`, `boost-somewhere-else-v3`, `ads-disable-front-fill`, `braze-ad-clicked-analytics`, `disable-async-webview-startup`, `disable-maps-gl-warmup` **Removed (5)** — `ad-backfill`, `ads-ketch-consent-api-enabled`, `ads-new-applovin-privacy-flags-enabled`, `community_events`, `disable-defer-maps-warmup` New experiment payload (`GET /v3/assignment`): `health-center-woodwork-perk-at-subscription-purchase` → `payload: { payload: { introSheetPostPurchase: string?, woodWorkTreatment: string? } }` ## Other changes - Push payload V2 shape and `PushNotificationActionType` are unchanged (package move only). `POST v3/gcm-push-tokens` moved `LoginRestService` → `PushTokenRestService`; body `{ vendorProvidedIdentifier, token }` and `requireRealDeviceInfo: true` unchanged. It now also fires on FCM token rotation and on successful session refresh. - **"Favorite comes online" push** — new Notification Settings toggle backed by `v5/push-settings.favoritePushNotification`; push opens `grindr://favorite-profile`. - **+`grindr://favorite-profile?profileID=<id>`** (also accepts `?profileId=`) — the only deeplink host added. Server sends it in the "favorite came online" push. - **Ad-free session rewarded card** in the cascade: server-inserted card → watch rewarded video → N minutes/hours ad-free (`"No Ads for %1$d min"`, "Watch a quick video and be done"). - **Air Traffic Control**: new client-side lease arbiter preventing two full-screen things from taking the screen at once. Analytics only (`atc_block_reaped`, `atc_block_released`, `atc_ad_suppressed`); enforcement gated by `atc-fullscreen-ad-gate` / `atc-fullscreen-iam-gate`. - **7-day-free-trial-at-signup removed entirely** - **Top Picks UI retired** - **New store tab bar** with an embedded Edge tab + red dot, gated by `edge-paywall-embedded-in-store` AND `premium-tier-price-test-sep26` AND the pre-existing `edge-paywall-v2`. - `DRUG_PROMOTION` re-tiered from warning to temporary suspension Generated by Claude Opus 5 on September 11, 2026.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
open-grind/grindr.rs#7
No description provided.