From 1366e7e2312c2b9a37b2e6c0bcf01f89e52c8564 Mon Sep 17 00:00:00 2001 From: CRT Date: Tue, 13 Oct 2026 05:39:09 +0800 Subject: [PATCH] scan, privacy answers and credentials from a second game's first release (lessons 35-37): review-prompt needs rating context, not a bare "rate"; account signals skip import lines and module paths, config-only accounts say "from config"; accounts.inApp without deletion code stays a medium 5.1.1(v) finding; privacy-manifest --propose builds NSPrivacyCollectedDataTypes from appPrivacy.types and check warns on an empty list; appPrivacy.types is authoritative in the App Privacy sheet (unlisted = not collected), ad / support / AR types default to not collected without tracking or ad SDKs; Play advertising ID from SDKs, the AD_ID permission, app-ads.txt or the flag, never from a Device ID alone; scan.ignore[].evidence and warnings for entries that match nothing; empty credentials template, placeholder values count as missing (check, doctor) --- CHANGELOG.md | 30 ++ cli/check.ts | 18 +- cli/console/plan.ts | 7 + cli/data/flags.json | 29 +- cli/doctor.ts | 8 +- cli/lib/app-privacy.ts | 5 +- cli/lib/config.ts | 16 +- cli/lib/console-answers.ts | 28 +- cli/lib/console-plan.ts | 5 +- cli/lib/console-play-plans.ts | 36 +- cli/lib/credentials.ts | 48 ++- cli/lib/embedded.ts | 2 +- cli/lib/privacy-manifest.ts | 38 ++- cli/lib/project-signals.ts | 43 +++ cli/lib/scan/declare.ts | 8 +- cli/lib/scan/index.ts | 11 +- cli/lib/scan/rules.ts | 82 ++++- cli/privacy-manifest.ts | 25 +- docs/pitfalls.md | 3 + skills/store-pitfalls/SKILL.md | 41 +++ skills/store-preflight/SKILL.md | 2 +- templates/credentials.example.json | 24 +- tests/app-privacy.test.ts | 7 +- tests/fixtures/scan/capacitor-game/README.md | 7 + .../android/app/src/main/AndroidManifest.xml | 5 + .../scan/capacitor-game/capacitor.config.json | 5 + .../capacitor-game/ios/App/App/Info.plist | 10 + .../ios/App/App/PrivacyInfo.xcprivacy | 23 ++ .../fixtures/scan/capacitor-game/package.json | 10 + .../capacitor-game/src/buildings/register.ts | 7 + .../scan/capacitor-game/src/economy.ts | 11 + .../fixtures/scan/capacitor-game/src/main.ts | 10 + .../scan/capacitor-game/storebound.config.ts | 41 +++ tests/game-report.test.ts | 307 ++++++++++++++++++ 34 files changed, 881 insertions(+), 71 deletions(-) create mode 100644 cli/lib/project-signals.ts create mode 100644 tests/fixtures/scan/capacitor-game/README.md create mode 100644 tests/fixtures/scan/capacitor-game/android/app/src/main/AndroidManifest.xml create mode 100644 tests/fixtures/scan/capacitor-game/capacitor.config.json create mode 100644 tests/fixtures/scan/capacitor-game/ios/App/App/Info.plist create mode 100644 tests/fixtures/scan/capacitor-game/ios/App/App/PrivacyInfo.xcprivacy create mode 100644 tests/fixtures/scan/capacitor-game/package.json create mode 100644 tests/fixtures/scan/capacitor-game/src/buildings/register.ts create mode 100644 tests/fixtures/scan/capacitor-game/src/economy.ts create mode 100644 tests/fixtures/scan/capacitor-game/src/main.ts create mode 100644 tests/fixtures/scan/capacitor-game/storebound.config.ts create mode 100644 tests/game-report.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5b5ab..79e8039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## Unreleased + +Fixes from a real first release of a second game (Capacitor 8 monorepo, lessons 35 to 37): + +- Fix (`scan`, review-prompt 3.2.2(x)): a bare "rate" next to "bonus" (`const rate = F.genRate(lv, bonus.gen(r))`) is + no longer a reward for rating. The signal needs a rate-us / leave-a-review phrase next to a reward, a reward "for + rating / reviewing", or a reward granted within a few lines of a system review prompt call. +- Fix (`scan`, account findings and the declarations proposal): import, require and export-from lines are never + sign-in / sign-up / deletion evidence (`import './buildings/register'` was), auth endpoints must be an absolute path, + a URL or a `${base}/...` template, credential fields and guest-account creation count; accounts only the config + claims read "from config (accounts.enabled)". +- Fix (`scan`, 5.1.1(v)): `accounts.inApp` alone no longer clears the finding: without deletion code it stays, medium, + "claimed by config (accounts.inApp), not found in code". +- Fix (`privacy-manifest --propose`): NSPrivacyCollectedDataTypes is built from `appPrivacy.types` (Apple's manifest + keys for type and purposes, linked, tracking); NSPrivacyTracking stays true only with app-level domains. `check` + warns once when the app manifest's list is empty but `appPrivacy.types` marks types collected. +- Fix (`console plan apple-app-privacy`): `appPrivacy.types` is authoritative: a type it does not list is "not + collected" (certain), never derived from dataSafety or asked. Without it, `appPrivacy.tracking: false` and no ad / + attribution SDK answer Advertising Data, Customer Support, Environment Scanning, Hands and Head "not collected" + unless a support SDK or AR code is found. `console plan` scans the project for these signals. +- Fix (`console plan play-advertising-id` / `play-app-content`): "Does your app use advertising ID?" comes from ad / + attribution SDKs, the AD_ID permission in the source or merged manifest (or its `tools:node="remove"`), + `ads.appAdsTxt` or `console.appContent.advertisingId`; a Data safety Device ID alone is not the advertising ID. No + signal: No (certain); signals that disagree: asked. +- `scan.ignore[].evidence`: accept a finding by the text of an evidence line (`match` keeps matching the title). + `scan` and `check` name every entry that matches no finding. +- Fix (`init`): `.storebound/credentials.json` starts with empty strings only. Placeholder-looking values + ("your-…", `<…>`, "xxx", "/absolute/path/to/…", "example") count as missing in every credential read; `check` + warns about them and `doctor` lists each credential as set, missing or placeholder. + ## 0.7.1 · 2026-10-13 - Fix: README links and images on npmjs.com resolved against npmjs.com and gave 404 (`docs/README.md`, the logo). diff --git a/cli/check.ts b/cli/check.ts index 89475fb..0918113 100644 --- a/cli/check.ts +++ b/cli/check.ts @@ -6,7 +6,8 @@ // --json structured result ({ ok, items, warnings, hints, problems } or { findings, errors }), exit 1 on problems import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; import { dirname, join, relative } from 'node:path'; -import { appPrivacyMismatches, type PrivacyMismatch, trackingDeclaredIssues, type TrackingSources } from './lib/app-privacy'; +import { appPrivacyMismatches, type PrivacyMismatch, trackingDeclaredIssues, type TrackingSources, typeIdOf } from './lib/app-privacy'; +import { privacyTypes } from './lib/console-answers'; import { type BundleManifest, bundleTrackingIssues, ipaManifests, projectSdkManifests, sdkOfProjectPath } from './lib/privacy-files'; import { trackingIssue } from './lib/privacy-manifest'; import { type AsoFinding, auditAso } from './lib/aso'; @@ -17,7 +18,7 @@ import { playConsoleReminders } from './lib/console-plan'; import { APPLE_IAP_LIMITS, appleNameIssues, len, parseAppStoreListing, parsePlayListing } from './lib/copy'; import type { Ctx } from './lib/ctx'; import { dataSafetyIssues, dataSafetyRows } from './lib/data-safety'; -import { reviewDemoAccount } from './lib/credentials'; +import { credentialStatus, reviewDemoAccount } from './lib/credentials'; import { log, printJson, warn } from './lib/log'; import { profileIssues } from './lib/profile'; import { detectProject } from './lib/scan/project'; @@ -99,6 +100,11 @@ export async function run(ctx: Ctx): Promise { const apTypes = config.appPrivacy?.types; const hasTypes = !!apTypes && Object.keys(apTypes).length > 0; const facts = config.apple ? detectProject(ctx.loaded.root) : null; + // appPrivacy.types is authoritative (an unlisted type is answered "not collected"): name dataSafety types it leaves out + if (hasTypes && config.apple && config.dataSafety?.collectsData) { + const left = [...privacyTypes(config)].filter(([name]) => { const id = typeIdOf(name); return id && id !== 'OTHER_DATA_TYPES' && !apTypes![id]; }); + if (left.length) note(`appPrivacy.types does not list ${left.map(([name, g]) => `${typeIdOf(name)} (dataSafety ${g.from.join(', ')})`).join(', ')}: App Privacy answers ${left.length === 1 ? 'it' : 'them'} "not collected". Add ${left.length === 1 ? 'it' : 'them'} to appPrivacy.types if the app or an SDK collects ${left.length === 1 ? 'it' : 'them'}`); + } if (hasTypes && facts && !facts.ios) hints.push('appPrivacy.types: no iOS project found here, so it is not compared with Info.plist / PrivacyInfo.xcprivacy'); if (facts?.ios) { const own = facts.privacyManifests.find(m => !/(^|\/)(Pods|node_modules|SourcePackages|Carthage|\.build)\//.test(m.path)) ?? null; @@ -127,6 +133,14 @@ export async function run(ctx: Ctx): Promise { const typesTrack = hasTypes && Object.values(apTypes!).some(a => a?.tracking === true); if (config.appPrivacy?.tracking === true && !typesTrack && own) trackingDeclaredIssues('appPrivacy.tracking is true', own, tracking).forEach(say); } + // template placeholders in the credentials ("YOUR_KEY_ID", "/absolute/path/to/...", "<...>"): missing, never a credential + const ph = credentialStatus(ctx.loaded.root).filter(c => c.status === 'placeholder').map(c => c.key); + if (ph.length) note(`credentials: ${ph.join(', ')} still hold template placeholders (env or .storebound/credentials.json): treated as missing; put the real value in or leave it empty`); + // scan.ignore entries that accept no finding (a typo, a title that changed, a fixed issue) are never silent + if (config.scan?.ignore?.length) { + const { runScan } = await import('./lib/scan'); + try { for (const n of (await runScan(ctx.loaded.root, { config: ctx.loaded.path })).report.notes) if (n.startsWith('scan.ignore[')) note(n); } catch (e) { note(`scan.ignore not checked: ${(e as Error).message}`); } + } if (config.dataSafety) tryDo('data safety', () => { const rows = dataSafetyRows(config.dataSafety!, config.accounts); // conditional required rows (e.g. the delete-account URL once accounts exist): Play answers 400 without them diff --git a/cli/console/plan.ts b/cli/console/plan.ts index 1a6bbcc..c6fb91c 100644 --- a/cli/console/plan.ts +++ b/cli/console/plan.ts @@ -37,6 +37,13 @@ export async function run(ctx: Ctx): Promise { // without --lookup only the cache is read: the lookup refuses, so no request is made ids.appleAppId = await appleAppId(root, cfg, { say: () => { /* the plan's start says what it found */ }, ...(args.has('lookup') ? {} : { lookup: async () => { throw new Error('offline (add --lookup to ask the API)'); } }) }); } + // project signals (ad / attribution SDKs, the AD_ID permission, support and AR code) for the App Privacy and + // Advertising ID answers; a scan that fails leaves them unknown (asked) + try { + const { detectProject } = await import('../lib/scan/project'); + const { projectSignals } = await import('../lib/project-signals'); + ids.signals = projectSignals(detectProject(root)); + } catch { /* not scanned */ } const plan = consolePlan(form, cfg, ids); if (json) return printJson(plan); console.log(planText(plan)); diff --git a/cli/data/flags.json b/cli/data/flags.json index bc7f982..8909c14 100644 --- a/cli/data/flags.json +++ b/cli/data/flags.json @@ -312,35 +312,62 @@ "unity" ], "console fill": [ + "capacitor", "cdp", "chrome-profile", + "cordova", + "cordova-android", + "cordova-ios", "debug-dump", + "expo", + "flutter", "fresh-profile", + "godot", "profile", + "react-native", "start-url", "step-timeout", "store", + "unity", "use-chrome-session" ], "console plan": [ + "capacitor", "cdp", "chrome-profile", + "cordova", + "cordova-android", + "cordova-ios", "debug-dump", + "expo", + "flutter", "fresh-profile", + "godot", "lookup", "name", "profile", + "react-native", "sku", "start-url", "step-timeout", "store", + "unity", "use-chrome-session" ], "console verify": [ + "capacitor", + "cordova", + "cordova-android", + "cordova-ios", + "expo", + "flutter", "from-text", + "godot", "name", + "react-native", "sku", - "text" + "text", + "unity" ], "desktop": [ "channel", diff --git a/cli/doctor.ts b/cli/doctor.ts index b5eb0d6..0039c7e 100644 --- a/cli/doctor.ts +++ b/cli/doctor.ts @@ -7,6 +7,7 @@ import { join, resolve } from 'node:path'; import pkg from '../package.json' with { type: 'json' }; import type { Args } from './lib/args'; import { CONFIG_NAMES } from './lib/config'; +import { credentialStatus } from './lib/credentials'; import { log } from './lib/log'; import { textTable } from './lib/table'; import { androidSdk, type AndroidSdkLookup, detectTools, findPlaywright, installCommands, type ToolStatus } from './lib/tools'; @@ -28,6 +29,9 @@ export interface DoctorResult { storebound: string; bun: string | null; runtime: string; platform: string; project: string | null; tools: DoctorRow[]; missing: number; /** The Android SDK `play build` hands to Gradle, where it came from, and every place looked at. */ androidSdk: AndroidSdkLookup; + /** Store credentials of the project folder (env, .storebound/credentials.json, profile): set, missing or + * placeholder (a template value, treated as missing). Never the values. */ + credentials: { key: string; status: 'set' | 'missing' | 'placeholder' }[]; } export function doctor(cwd: string, env: Record = process.env): DoctorResult { @@ -38,7 +42,7 @@ export function doctor(cwd: string, env: Record = pr usedFor: 'screenshot renders and web captures from a terminal (the desktop app brings its own)', install: pw ? [] : installCommands('playwright'), url: 'https://playwright.dev/docs/intro' }); const project = CONFIG_NAMES.some(n => existsSync(join(cwd, n))) ? cwd : null; const sdk = androidSdk([join(cwd, 'android'), cwd], env); - return { storebound: pkg.version, bun: rt.bunVersion, runtime: `${rt.runtime.name} ${rt.runtime.version}`, platform: `${process.platform}-${process.arch}`, project, tools, missing: tools.filter(t => !t.found).length, androidSdk: sdk }; + return { storebound: pkg.version, bun: rt.bunVersion, runtime: `${rt.runtime.name} ${rt.runtime.version}`, platform: `${process.platform}-${process.arch}`, project, tools, missing: tools.filter(t => !t.found).length, androidSdk: sdk, credentials: credentialStatus(cwd) }; } export async function run(args: Args): Promise { @@ -48,6 +52,8 @@ export async function run(args: Args): Promise { console.log(textTable(['tool', 'status', 'version', 'used for'], r.tools.map(t => [t.name, t.found ? (t.source === 'app' ? 'ok (app copy)' : 'ok') : 'missing', t.version ?? '', t.usedFor]))); log('doctor', r.androidSdk.path ? `Android SDK: ${r.androidSdk.path} (from ${r.androidSdk.source})` : `Android SDK: not found (play build needs it). Checked: ${r.androidSdk.checked.join('; ')}`); + const set = r.credentials.filter(c => c.status === 'set').map(c => c.key), ph = r.credentials.filter(c => c.status === 'placeholder').map(c => c.key); + log('doctor', `credentials: ${set.length ? set.join(', ') : 'none set'}${ph.length ? ` · placeholders (treated as missing): ${ph.join(', ')}` : ''}`); const missing = r.tools.filter(t => !t.found); if (!missing.length) { log('doctor', 'everything storebound can use is here'); return; } console.log('\nTo install what is missing (pick one per tool; storebound never runs these for you):'); diff --git a/cli/lib/app-privacy.ts b/cli/lib/app-privacy.ts index ee9d63d..5f644c5 100644 --- a/cli/lib/app-privacy.ts +++ b/cli/lib/app-privacy.ts @@ -160,7 +160,10 @@ export function appPrivacyMismatches(types: Partial a.collected === true && !inManifest.has(id)).map(([id]) => id); + if (missing.length && !m.collectedTypes.length) + out.push({ level: 'warn', msg: `${m.path} has an empty NSPrivacyCollectedDataTypes, but appPrivacy.types marks ${missing.join(', ')} as collected: \`storebound privacy-manifest --ios --propose\` builds the list from appPrivacy.types (types, linked, tracking, purposes)` }); + else for (const id of missing) out.push({ level: 'warn', msg: `appPrivacy.types.${id} is collected, but ${m.path} has no ${APPLE_PRIVACY_TYPES[id][2]} in NSPrivacyCollectedDataTypes (fine if only an SDK collects it and declares it in its own manifest)` }); return out; } diff --git a/cli/lib/config.ts b/cli/lib/config.ts index fe44ad8..fa69be2 100644 --- a/cli/lib/config.ts +++ b/cli/lib/config.ts @@ -641,7 +641,17 @@ export interface ShipConfig { * them in App Store Connect). `storebound scan` proposes them from the detected SDKs and code. */ appPrivacy?: AppPrivacyConfig; /** `storebound scan`: findings the owner has looked at and accepts, by rule id (with the reason, shown in the report). */ - scan?: { ignore?: { rule: string; store?: 'apple' | 'play'; /** Only findings whose title contains this text (one check of a rule that has several). */ match?: string; reason: string }[] }; + scan?: { + ignore?: { + rule: string; store?: 'apple' | 'play'; + /** Only findings whose TITLE contains this text (one check of a rule that has several), case-insensitive. */ + match?: string; + /** Only findings with an evidence line ("file:line text", as the report prints it) containing this text, e.g. + * "src/main.ts:12" or a code fragment. Combine with match to narrow further. */ + evidence?: string; + reason: string; + }[]; + }; /** Ads: `validate` checks that /app-ads.txt is reachable and contains every line listed here. */ ads?: { appAdsTxt?: string[] }; /** Rendered store graphics dir (///NN.png|jpg). Defaults to shots.out. */ @@ -1069,8 +1079,8 @@ export function validateConfig(c: unknown): string[] { e.push(...validateAppPrivacyTypes(cfg.appPrivacy.types)); } for (const [i, x] of (cfg.scan?.ignore ?? []).entries()) - if (!x || typeof x.rule !== 'string' || !x.rule || typeof x.reason !== 'string' || !x.reason.trim() || (x.store !== undefined && x.store !== 'apple' && x.store !== 'play') || (x.match !== undefined && (typeof x.match !== 'string' || !x.match.trim()))) - e.push(`scan.ignore[${i}]: { rule: '', reason: 'why this is fine', store?: 'apple' | 'play', match?: '' }`); + if (!x || typeof x.rule !== 'string' || !x.rule || typeof x.reason !== 'string' || !x.reason.trim() || (x.store !== undefined && x.store !== 'apple' && x.store !== 'play') || (x.match !== undefined && (typeof x.match !== 'string' || !x.match.trim())) || (x.evidence !== undefined && (typeof x.evidence !== 'string' || !x.evidence.trim()))) + e.push(`scan.ignore[${i}]: { rule: '', reason: 'why this is fine', store?: 'apple' | 'play', match?: '', evidence?: '' }`); if (cfg.ads?.appAdsTxt && !cfg.ads.appAdsTxt.every(l => typeof l === 'string' && l.split(',').length >= 3)) e.push('ads.appAdsTxt: lines like "example-network.com, pub-123, DIRECT, abc123"'); const seen = new Set(); diff --git a/cli/lib/console-answers.ts b/cli/lib/console-answers.ts index 3e066ce..daa13a1 100644 --- a/cli/lib/console-answers.ts +++ b/cli/lib/console-answers.ts @@ -20,6 +20,7 @@ import { type AgeGroup, AGE_GROUPS, appleName, IARC_DETAIL_KEYS, IARC_DETAIL_SEC import { APPLE_PRIVACY, applePurpose } from './declarations'; import { APPLE_DATA_TYPE_IDS, APPLE_PRIVACY_TYPES, APPLE_PURPOSE_IDS, type AppleDataTypeId, type AppleTypeAnswer, purposeName } from './app-privacy'; import { maskUser } from './credentials'; +import type { ProjectSignals } from './project-signals'; export const FORM_IDS = ['apple-app-record', 'apple-age-rating', 'apple-app-privacy', 'play-app-record', 'play-content-rating', 'play-target-audience'] as const; export type FormId = typeof FORM_IDS[number]; @@ -218,8 +219,22 @@ export function privacyTypes(config: ShipConfig): Map the signal that keeps it a question. */ +const QUIET_TYPES: Partial string | null>> = { + ADVERTISING_DATA: () => null, + CUSTOMER_SUPPORT: s => s.support, + ENVIRONMENT_SCANNING: s => s.ar, + HANDS: s => s.ar, + HEAD: s => s.ar, +}; + +function appPrivacy(config: ShipConfig, signals?: ProjectSignals): Answer[] { const ds = config.dataSafety, ov: Partial> = config.appPrivacy?.types ?? {}; + // appPrivacy.types is authoritative: a type it does not list is not collected (not derived from dataSafety, not asked) + const authoritative = Object.keys(ov).length > 0; + // no tracking, no ad / attribution SDK, no app-ads.txt: the ad-related and AR types default to not collected + const quiet = !!signals && config.appPrivacy?.tracking === false && !signals.adSdks.length && !signals.attributionSdks.length && !config.ads?.appAdsTxt?.length; const label = 'Do you or your third-party partners collect data from this app?'; const ovIds = Object.keys(ov) as AppleDataTypeId[]; if (!ds && !ovIds.length) return [ask('collects', label, 'dataSafety', 'The config has no dataSafety answers and no appPrivacy.types: answer what the app and every SDK collect (store-declarations skill), then rerun.')]; @@ -237,6 +252,12 @@ function appPrivacy(config: ShipConfig): Answer[] { const [cat, name] = APPLE_PRIVACY_TYPES[id]; const g = got.get(name), o = ov[id], aid = `type:${name}`, l = `Data type: ${cat} > ${name}`; if (o?.collected !== undefined) { out.push(ok(aid, l, o.collected, `appPrivacy.types.${id}.collected`)); if (o.collected) collected.push(id); continue; } + if (authoritative && !o) { + out.push(ok(aid, l, false, 'appPrivacy.types (not listed: not collected)', g ? `dataSafety.data.${g.from.join(', ')} maps here, but appPrivacy.types does not list ${id}: add it there if the app or an SDK collects it.` : undefined)); + continue; + } + const quietWhy = quiet && QUIET_TYPES[id] ? QUIET_TYPES[id]!(signals!) : undefined; + if (quietWhy === null) { out.push(ok(aid, l, false, 'appPrivacy.tracking false, no ad / attribution SDK and no code signal in the project')); continue; } const set = `set appPrivacy.types.${id}.collected to have it filled`; if (g && name === 'Other Data Types') out.push(ask(aid, l, `dataSafety.data.${g.from.join(', ')}`, `${g.from.join(', ')} has no exact App Store type: decide where it belongs (${set}).`)); else if (g) { out.push(ok(aid, l, true, `dataSafety.data.${g.from.join(', ')}`)); collected.push(id); } @@ -378,11 +399,12 @@ function targetAudience(config: ShipConfig): Answer[] { return out; } -export function consoleAnswers(id: FormId, config: ShipConfig): Answer[] { +/** `signals`: what the project scan found (console plan passes it); without it nothing is derived from the project. */ +export function consoleAnswers(id: FormId, config: ShipConfig, signals?: ProjectSignals): Answer[] { switch (id) { case 'apple-app-record': return appRecord(config); case 'apple-age-rating': return ageRating(config); - case 'apple-app-privacy': return appPrivacy(config); + case 'apple-app-privacy': return appPrivacy(config, signals); case 'play-app-record': return playAppRecord(config); case 'play-content-rating': return contentRating(config); case 'play-target-audience': return targetAudience(config); diff --git a/cli/lib/console-plan.ts b/cli/lib/console-plan.ts index 880dd5d..e642795 100644 --- a/cli/lib/console-plan.ts +++ b/cli/lib/console-plan.ts @@ -6,6 +6,7 @@ // UI facts marked "verified" were read on a real console (App Privacy, 2026-10-12, /apps//distribution/privacy); // everything else is modeled on Apple's and Google's help pages (sources in console-answers.ts): the agent adapts. import { playCategoryOf, type ShipConfig } from './config'; +import type { ProjectSignals } from './project-signals'; import { type Answer, type AnswerValue, consoleAnswers, type FormId, formMeta, isPlanOnly, type PlanFormId, type Store, valueText } from './console-answers'; import { APP_CONTENT_CAPTURE, APP_CONTENT_CARDS, appContentStart, PLAY_MATERIAL_FACT, playAppPage, playOnlyPart, startLabel } from './console-play-plans'; import { FORM_BUTTONS, formDef, type Name, NEVER_PRESS, playAppList, playCreateApp, setUpTexts, typeLabels } from './console-forms'; @@ -87,6 +88,8 @@ export interface PlanIds { appleAppId?: string | null; playDeveloperId?: string | null; playAppId?: string | null; /** reviewDemoAccount exists in .storebound/credentials.json (only whether it exists: the values are never read here). */ demoAccount?: boolean; + /** What the project scan found (ad / attribution SDKs, the AD_ID permission, support and AR code); undefined: not scanned. */ + signals?: ProjectSignals; } const ASC = 'https://appstoreconnect.apple.com'; @@ -456,7 +459,7 @@ export function consolePlan(form: PlanFormId, config: ShipConfig, p: PlanIds = { }; } const meta = formMeta(form, cfg); - const raw = consoleAnswers(form, cfg); + const raw = consoleAnswers(form, cfg, p.signals); const answers = sheet(form, raw); const part = form === 'apple-app-privacy' ? appPrivacyPlan(answers, cfg.apple?.appId) : otherPlan(form, cfg, answers, p); const rules = FORM_BUTTONS[form]; diff --git a/cli/lib/console-play-plans.ts b/cli/lib/console-play-plans.ts index 009de09..077a150 100644 --- a/cli/lib/console-play-plans.ts +++ b/cli/lib/console-play-plans.ts @@ -18,6 +18,7 @@ import { PLAY_APP_CATEGORIES, PLAY_GAME_CATEGORIES, playCategoryOf, profileOf } import type { PlanFormId, PlanOnlyFormId } from './console-answers'; import { NEVER_PRESS, playAppList } from './console-forms'; import type { ConsolePlan, PlanAnswer, PlanIds, PlanStatus, PlanStep } from './console-plan'; +import { AD_ID_PERMISSION, type ProjectSignals } from './project-signals'; const PLAY = 'https://play.google.com/console'; @@ -236,13 +237,34 @@ const AD_ID_PURPOSES: Record = { fraud: 'Fraud prevention, security, and compliance', personalization: 'Personalization', account: 'Account management', }; +/** + * "Does your app use advertising ID?" from real signals: console.appContent.advertisingId, ad / attribution SDKs (scan), + * ads.appAdsTxt, the AD_ID permission in the source or merged AndroidManifest (or its tools:node="remove"). A Data + * safety Device ID (PSL_DEVICE_ID) alone is not the advertising ID (a random install id is not). No signal: No + * (certain) when the project was scanned; signals that disagree: ask. + */ +export function advertisingIdUse(config: ShipConfig, s: ProjectSignals | undefined): { v: boolean | null; src: string; note?: string } { + const flag = config.console?.appContent?.advertisingId; + const yes: string[] = [], no: string[] = []; + if (flag === true) yes.push('console.appContent.advertisingId'); else if (flag === false) no.push('console.appContent.advertisingId'); + if (config.ads?.appAdsTxt?.length) yes.push('ads.appAdsTxt (ad SDKs read the advertising ID)'); + if (s) { + if (s.adSdks.length || s.attributionSdks.length) yes.push(`ad / attribution SDKs: ${[...s.adSdks, ...s.attributionSdks].join(', ')}`); + if (s.adIdRemoved) no.push(`AD_ID removed (tools:node="remove") at ${s.adIdRemoved}`); + else if (s.adIdPermission) yes.push(`${AD_ID_PERMISSION} at ${s.adIdPermission}`); + } + if (yes.length && no.length) return { v: null, src: [...yes, ...no].join('; '), note: `The signals disagree (yes: ${yes.join('; ')}; no: ${no.join('; ')}). Decide and set console.appContent.advertisingId (an SDK that reads it needs the AD_ID permission in the merged manifest).` }; + if (yes.length) return { v: true, src: yes.join('; ') }; + if (no.length) return { v: false, src: no.join('; ') }; + if (s) return { v: false, src: `no ad / attribution SDK, no ${AD_ID_PERMISSION} in the manifests, no ads.appAdsTxt (a Device ID in dataSafety is not the advertising ID)` }; + return { v: null, src: 'console.appContent.advertisingId' }; +} + function advertisingId(config: ShipConfig, p: PlanIds): Part { - const ac = config.console?.appContent, dev = config.dataSafety?.data?.PSL_DEVICE_ID; - const uses: { v: boolean; src: string } | null = ac?.advertisingId !== undefined ? { v: ac.advertisingId, src: 'console.appContent.advertisingId' } - : config.ads?.appAdsTxt?.length ? { v: true, src: 'ads.appAdsTxt (ad SDKs read the advertising ID)' } - : dev ? { v: true, src: 'dataSafety.data.PSL_DEVICE_ID' } : null; - const out: PlanAnswer[] = [uses ? ans('uses', 'Does your app use advertising ID?', uses.v, 'certain', uses.src, { control: 'radio', action: 'pick', label: uses.v ? 'Yes' : 'No', where: 'Advertising ID' }) - : ans('uses', 'Does your app use advertising ID?', null, 'ask-owner', 'console.appContent.advertisingId', { control: 'radio', where: 'Advertising ID', note: 'Yes when the app or any SDK (ads, analytics, attribution) reads it; set console.appContent.advertisingId.' })]; + const dev = config.dataSafety?.data?.PSL_DEVICE_ID; + const uses = advertisingIdUse(config, p.signals); + const out: PlanAnswer[] = [uses.v !== null ? ans('uses', 'Does your app use advertising ID?', uses.v, 'certain', uses.src, { control: 'radio', action: 'pick', label: uses.v ? 'Yes' : 'No', where: 'Advertising ID' }) + : ans('uses', 'Does your app use advertising ID?', null, 'ask-owner', uses.src, { control: 'radio', where: 'Advertising ID', note: uses.note ?? 'Yes when the app or any SDK (ads, analytics, attribution) reads it; set console.appContent.advertisingId.' })]; if (uses?.v) { if (dev) { const purposes = new Set([...dev.collect, ...(dev.shared ? dev.share ?? [] : [])]); @@ -255,7 +277,7 @@ function advertisingId(config: ShipConfig, p: PlanIds): Part { steps: steps([ { text: 'Open App content > Advertising ID (/app-content/ad-id-declaration).' }, { text: 'Pick Yes or No (material radio: JS click on the inner [role=radio]).', answers: ['uses'] }, - uses?.v && { text: 'Tick exactly the purposes of the sheet (material checkboxes: JS click on the inner [role=checkbox]; read aria-checked back).', answers: out.filter(a => a.id.startsWith('purpose')).map(a => a.id) }, + uses.v === true && { text: 'Tick exactly the purposes of the sheet (material checkboxes: JS click on the inner [role=checkbox]; read aria-checked back).', answers: out.filter(a => a.id.startsWith('purpose')).map(a => a.id) }, { text: DECLARATION_SAVE }, { text: 'Verify: read the page text and pipe it to `storebound console verify play-advertising-id --from-text -`.' }, ]), diff --git a/cli/lib/credentials.ts b/cli/lib/credentials.ts index c7d8a47..d67fcbf 100644 --- a/cli/lib/credentials.ts +++ b/cli/lib/credentials.ts @@ -72,13 +72,49 @@ export function profileStatus(profile: string): { profile: string; source: 'file } } -function value(root: string, name: string): string | undefined { +/** + * A value that only looks like a credential: a template placeholder ("your-…", "YOUR_KEY_ID", "", "xxx", + * "/absolute/path/to/…", the all-zero Apple Ads client id, "example" as a word unless it is a file that exists). Treated as + * missing everywhere (check, apps check, doctor and every command), never sent to a store. `example: false` for the + * review contact and demo account (an app's own backend can have a real demo@example.com account). + */ +export function isPlaceholder(v: string, o: { example?: boolean } = {}): boolean { + const s = v.trim(); + if (!s || /BEGIN PRIVATE KEY/.test(s) || /^\s*\{/.test(s)) return false; + if (/(^|[_\-\s/])your([_\-\s]|$)/i.test(s) || /<[^<>\n]+>/.test(s) || /(^|[^a-z0-9])x{3,}([^a-z0-9]|$)/i.test(s) || /(^|\/)path\/to\//.test(s)) return true; + if (/^SEARCHADS\.0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(s)) return true; + if (o.example !== false && /(^|[^a-z0-9])example([^a-z0-9]|$)/i.test(s)) { + try { return !existsSync(s.replace(/^~(?=\/)/, homedir())); } catch { return true; } + } + return false; +} + +/** Credential names storebound reads (env, .storebound/credentials.json, a profile), for status reports. */ +export const CREDENTIAL_KEYS = ['PLAY_SERVICE_ACCOUNT', 'ASC_KEY_ID', 'ASC_ISSUER_ID', 'ASC_P8', 'ASC_VENDOR_NUMBER', 'APPLE_ADS_CLIENT_ID', 'APPLE_ADS_TEAM_ID', + 'APPLE_ADS_KEY_ID', 'APPLE_ADS_PRIVATE_KEY', 'APPLE_ADS_ACCOUNT_ID', 'REVENUECAT_SECRET_KEY', 'ANDROID_KEYSTORE', 'ANDROID_KEYSTORE_PASSWORD', 'ANDROID_KEY_ALIAS', 'ANDROID_KEY_PASSWORD'] as const; + +/** Where each known credential stands: set, missing, or placeholder (a template value, treated as missing). Never + * the values. */ +export function credentialStatus(root: string): { key: string; status: 'set' | 'missing' | 'placeholder' }[] { + let file: Store = {}; + try { file = fileStore(root); } catch { /* unreadable: reported by the commands that read it */ } const profile = process.env.STOREBOUND_PROFILE; - if (profile) { const v = profileEnv(profile)[name]; if (v) return v; } + let prof: Record = {}; + try { if (profile) prof = profileEnv(profile); } catch { /* invalid profile */ } + return CREDENTIAL_KEYS.map(key => { + const raw = [prof[key], process.env[key], typeof file[key] === 'string' ? file[key] as string : undefined].filter((x): x is string => !!x && !!x.trim()); + return { key, status: raw.some(x => !isPlaceholder(x)) ? 'set' as const : raw.length ? 'placeholder' as const : 'missing' as const }; + }); +} + +function value(root: string, name: string): string | undefined { + const real = (v: unknown): v is string => typeof v === 'string' && !!v && !isPlaceholder(v); + const profile = process.env.STOREBOUND_PROFILE; + if (profile) { const v = profileEnv(profile)[name]; if (real(v)) return v; } const env = process.env[name]; - if (env) return env; + if (real(env)) return env; const v = fileStore(root)[name]; - return typeof v === 'string' && v ? v : undefined; + return real(v) ? v : undefined; } /** A path or the inline content itself. */ @@ -109,7 +145,7 @@ export function ascKey(root: string): AscKey { /** App Review contact from .storebound/credentials.json (personal data, so never in the config). */ export function reviewContact(root: string): ReviewContact | null { const c = fileStore(root).reviewContact as Partial | undefined; - if (!c?.email) return null; + if (!c?.email || isPlaceholder(c.email, { example: false })) return null; return { firstName: c.firstName ?? '', lastName: c.lastName ?? '', phone: c.phone ?? '', email: c.email }; } @@ -121,7 +157,7 @@ export function reviewDemoAccount(root: string): DemoAccount | null { const eu = process.env.REVIEW_DEMO_USER, ep = process.env.REVIEW_DEMO_PASSWORD; if (eu && ep) return { username: eu, password: ep }; const d = fileStore(root).reviewDemoAccount as Partial | undefined; - if (typeof d?.username === 'string' && d.username && typeof d.password === 'string' && d.password) return { username: d.username, password: d.password }; + if (typeof d?.username === 'string' && d.username && !isPlaceholder(d.username, { example: false }) && typeof d.password === 'string' && d.password) return { username: d.username, password: d.password }; return null; } diff --git a/cli/lib/embedded.ts b/cli/lib/embedded.ts index d64ab2b..13ecd9e 100644 --- a/cli/lib/embedded.ts +++ b/cli/lib/embedded.ts @@ -6,7 +6,7 @@ export const TEMPLATE_TEXT: Record = { "app-privacy.md": "# Example Game · App Store \"App Privacy\" answers\n\nApp Store Connect > your app > App Privacy. **Web UI only, there is no API.** Fill it from this table, then press\n**Publish** at the top of the App Privacy page (saving alone does not publish it, and review checks the published one).\nKeep it consistent with the privacy policy and the Play Data safety answers (`dataSafety` in `storebound.config.ts`).\n\nTypical free-to-play game: analytics SDK + ad mediation (with ATT) + optional server backup, no accounts.\n\n**Do you or your third-party partners collect data from this app?** Yes\n\n**Tracking**: Yes, if any ad network uses the IDFA after the App Tracking Transparency prompt (third-party advertising).\nThen `NSUserTrackingUsageDescription` must be in Info.plist and the prompt must come before the ad SDK starts.\n\n| data type | collected | used to track | linked to the user | purposes |\n|---|---|---|---|---|\n| Identifiers > Device ID (IDFA, analytics random id) | yes | yes (IDFA, with ATT consent) | no | Third-Party Advertising, Analytics, App Functionality |\n| Location > Coarse Location (country from IP) | yes | yes | no | Third-Party Advertising, Analytics |\n| Usage Data > Advertising Data (ads shown, ad taps) | yes | yes | no | Third-Party Advertising, Analytics |\n| Usage Data > Product Interaction (sessions, level events) | yes | no | no | Analytics |\n| User Content > Gameplay Content (save backup, scores) | only with a server backup | no | no | App Functionality |\n| Diagnostics > Other Diagnostic Data (device model, OS version) | yes | no | no | Analytics, Third-Party Advertising |\n| Purchases > Purchase History | only if your server stores receipts | no | yes if tied to an account | App Functionality |\n\n\"Linked to you\" is **No** when there are no accounts and nothing is tied to a name, email or phone number.\nNot collected (typical): Contact Info, Health and Fitness, Financial Info, Precise Location, Sensitive Info, Contacts,\nEmails or Text Messages, Photos or Videos, Audio, Browsing History, Search History, Crash Data (unless you ship a crash\nreporter).\n\nPurposes follow Apple's definitions (https://developer.apple.com/app-store/app-privacy-details/): ads shown by an ad\nSDK are **Third-Party Advertising** only. **Developer's Advertising or Marketing** is your own ads or marketing to your\nusers; tick it only when that is true.\n\nPut the answers into `storebound.config.ts` so `storebound console fill apple-app-privacy` fills them and\n`storebound check` compares them with Info.plist and PrivacyInfo.xcprivacy (all type ids: storebound's\ncli/lib/app-privacy.ts):\n\n```ts\nappPrivacy: { tracking: true, types: {\n DEVICE_ID: { collected: true, linked: false, tracking: true, purposes: ['THIRD_PARTY_ADVERTISING', 'ANALYTICS', 'APP_FUNCTIONALITY'] },\n COARSE_LOCATION: { collected: true, linked: false, tracking: true, purposes: ['THIRD_PARTY_ADVERTISING', 'ANALYTICS'] },\n ADVERTISING_DATA: { collected: true, linked: false, tracking: true, purposes: ['THIRD_PARTY_ADVERTISING', 'ANALYTICS'] },\n PRODUCT_INTERACTION: { collected: true, linked: false, tracking: false, purposes: ['ANALYTICS'] },\n GAMEPLAY_CONTENT: { collected: false },\n} },\n```\n\nPer data type the UI asks, in order: purposes (checkboxes) > \"Is this data linked to the user's identity?\" >\n\"Do you or your third-party partners use this data for tracking purposes?\".\n\nPrivacy policy URL: App Information > Privacy Policy URL (set by `storebound apple listing`). Privacy choices URL:\noptional, leave empty unless you have a page for it.\n", "appstore.md": "# Example Game · App Store listing copy\n\nSource for `storebound apple listing`. One `## ` section per locale (the first word is the locale code:\n`en-US`, `tr`, `de-DE`, `ja`, `zh-Hans`, `ar-SA`, ...), one fenced block per `### `, field names exactly:\n`name` (optional, defaults to `apple.name`, else `app.name`), `subtitle` (30), `promotionalText` (170), `keywords` (100 bytes, comma\nseparated, no spaces after commas, do not repeat the app name), `description` (4000), `whatsNew` (optional; not allowed\non the first version). Shared values (privacy policy, support and marketing URL) come from `storebound.config.ts`.\n\n---\n\n## en-US · English (U.S.)\n\n### subtitle\n```\nShort hook under the name\n```\n\n### promotionalText\n```\nOne or two sentences you can change any time without a new version.\n```\n\n### keywords\n```\npuzzle,casual,relaxing,offline,brain,match,levels\n```\n\n### description\n```\nFirst paragraph: what the player does and why it feels good.\n\nFEATURES\n• One-thumb controls\n• 100+ levels\n• Play offline, no account needed\n\nFree to play. Contains ads and offers optional in-app purchases.\n```\n\n## tr · Türkçe\n\n### subtitle\n```\nİsmin altındaki kısa kanca\n```\n\n### promotionalText\n```\nYeni sürüm göndermeden istediğin zaman değiştirebileceğin bir iki cümle.\n```\n\n### keywords\n```\nbulmaca,rahatlatıcı,internetsiz,zeka,eşleştirme,bölüm\n```\n\n### description\n```\nİlk paragraf: oyuncu ne yapıyor ve neden keyifli.\n\nÖZELLİKLER\n• Tek parmakla kontrol\n• 100+ bölüm\n• İnternetsiz oyna, hesap gerekmez\n\nÜcretsiz oyun. Reklam içerir ve isteğe bağlı uygulama içi satın alımlar sunar.\n```\n", - "credentials.example.json": "{\n \"PLAY_SERVICE_ACCOUNT\": \"/absolute/path/to/play-service-account.json\",\n \"ASC_KEY_ID\": \"YOUR_KEY_ID\",\n \"ASC_ISSUER_ID\": \"00000000-0000-0000-0000-000000000000\",\n \"ASC_P8\": \"/absolute/path/to/AuthKey_.p8\",\n \"APPLE_ADS_CLIENT_ID\": \"SEARCHADS.00000000-0000-0000-0000-000000000000\",\n \"APPLE_ADS_TEAM_ID\": \"SEARCHADS.00000000-0000-0000-0000-000000000000\",\n \"APPLE_ADS_KEY_ID\": \"00000000-0000-0000-0000-000000000000\",\n \"APPLE_ADS_PRIVATE_KEY\": \"/absolute/path/to/apple-ads-private-key.pem\",\n \"APPLE_ADS_ACCOUNT_ID\": \"\",\n \"REVENUECAT_SECRET_KEY\": \"sk_your_revenuecat_v2_secret_key\",\n \"ANDROID_KEYSTORE\": \"/absolute/path/to/upload.p12\",\n \"ANDROID_KEYSTORE_PASSWORD\": \"\",\n \"ANDROID_KEY_ALIAS\": \"upload\",\n \"ANDROID_KEY_PASSWORD\": \"\",\n \"ASC_VENDOR_NUMBER\": \"\",\n \"reviewContact\": { \"firstName\": \"\", \"lastName\": \"\", \"phone\": \"+1 555 0100\", \"email\": \"review-contact@example.com\" },\n \"reviewDemoAccount\": { \"username\": \"\", \"password\": \"\" }\n}\n", + "credentials.example.json": "{\n \"PLAY_SERVICE_ACCOUNT\": \"\",\n \"ASC_KEY_ID\": \"\",\n \"ASC_ISSUER_ID\": \"\",\n \"ASC_P8\": \"\",\n \"APPLE_ADS_CLIENT_ID\": \"\",\n \"APPLE_ADS_TEAM_ID\": \"\",\n \"APPLE_ADS_KEY_ID\": \"\",\n \"APPLE_ADS_PRIVATE_KEY\": \"\",\n \"APPLE_ADS_ACCOUNT_ID\": \"\",\n \"REVENUECAT_SECRET_KEY\": \"\",\n \"ANDROID_KEYSTORE\": \"\",\n \"ANDROID_KEYSTORE_PASSWORD\": \"\",\n \"ANDROID_KEY_ALIAS\": \"\",\n \"ANDROID_KEY_PASSWORD\": \"\",\n \"ASC_VENDOR_NUMBER\": \"\",\n \"reviewContact\": { \"firstName\": \"\", \"lastName\": \"\", \"phone\": \"\", \"email\": \"\" },\n \"reviewDemoAccount\": { \"username\": \"\", \"password\": \"\" }\n}\n", "data-safety.csv": "Question ID (machine readable),Response ID (machine readable),Response value,Answer requirement,Human-friendly question label\nPSL_DATA_COLLECTION_COLLECTS_PERSONAL_DATA,,TRUE,REQUIRED,Does your app collect or share any of the required user data types?\nPSL_DATA_COLLECTION_ENCRYPTED_IN_TRANSIT,,TRUE,MAYBE_REQUIRED,Is all of the user data collected by your app encrypted in transit?\nPSL_SUPPORTED_ACCOUNT_CREATION_METHODS,PSL_ACM_NONE,TRUE,MULTIPLE_CHOICE,Which of the following methods of account creation does your app support? Select all that apply / My app does not allow users to create an account\nPSL_DATA_TYPES_PERSONAL,PSL_NAME,,MULTIPLE_CHOICE,Personal info / Name\nPSL_DATA_TYPES_PERSONAL,PSL_EMAIL,,MULTIPLE_CHOICE,Personal info / Email address\nPSL_DATA_TYPES_PERSONAL,PSL_USER_ACCOUNT,,MULTIPLE_CHOICE,Personal info / User IDs\nPSL_DATA_TYPES_PERSONAL,PSL_ADDRESS,,MULTIPLE_CHOICE,Personal info / Address\nPSL_DATA_TYPES_PERSONAL,PSL_PHONE,,MULTIPLE_CHOICE,Personal info / Phone number\nPSL_DATA_TYPES_PERSONAL,PSL_RACE_ETHNICITY,,MULTIPLE_CHOICE,Personal info / Race and ethnicity\nPSL_DATA_TYPES_PERSONAL,PSL_POLITICAL_RELIGIOUS,,MULTIPLE_CHOICE,Personal info / Political or religious beliefs\nPSL_DATA_TYPES_PERSONAL,PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY,,MULTIPLE_CHOICE,Personal info / Sexual orientation\nPSL_DATA_TYPES_PERSONAL,PSL_OTHER_PERSONAL,,MULTIPLE_CHOICE,Personal info / Other info\nPSL_DATA_TYPES_FINANCIAL,PSL_PURCHASE_HISTORY,TRUE,MULTIPLE_CHOICE,Financial info / Purchase history\nPSL_DATA_TYPES_FINANCIAL,PSL_CREDIT_SCORE,,MULTIPLE_CHOICE,Financial info / Credit score\nPSL_DATA_TYPES_FINANCIAL,PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER,,MULTIPLE_CHOICE,Financial info / User payment info\nPSL_DATA_TYPES_FINANCIAL,PSL_OTHER,,MULTIPLE_CHOICE,Financial info / Other financial info\nPSL_DATA_TYPES_LOCATION,PSL_APPROX_LOCATION,TRUE,MULTIPLE_CHOICE,Location / Approximate location\nPSL_DATA_TYPES_LOCATION,PSL_PRECISE_LOCATION,,MULTIPLE_CHOICE,Location / Precise location\nPSL_DATA_TYPES_SEARCH_AND_BROWSING,PSL_WEB_BROWSING_HISTORY,,MULTIPLE_CHOICE,Web browsing / Web browsing history\nPSL_DATA_TYPES_EMAIL_AND_TEXT,PSL_EMAILS,,MULTIPLE_CHOICE,Messages / Emails\nPSL_DATA_TYPES_EMAIL_AND_TEXT,PSL_SMS_CALL_LOG,,MULTIPLE_CHOICE,Messages / SMS or MMS\nPSL_DATA_TYPES_EMAIL_AND_TEXT,PSL_OTHER_MESSAGES,,MULTIPLE_CHOICE,Messages / Other in-app messages\nPSL_DATA_TYPES_PHOTOS_AND_VIDEOS,PSL_PHOTOS,,MULTIPLE_CHOICE,Photos and videos / Photos\nPSL_DATA_TYPES_PHOTOS_AND_VIDEOS,PSL_VIDEOS,,MULTIPLE_CHOICE,Photos and videos / Videos\nPSL_DATA_TYPES_AUDIO,PSL_AUDIO,,MULTIPLE_CHOICE,Audio files / Voice or sound recordings\nPSL_DATA_TYPES_AUDIO,PSL_MUSIC,,MULTIPLE_CHOICE,Audio files / Music files\nPSL_DATA_TYPES_AUDIO,PSL_OTHER_AUDIO,,MULTIPLE_CHOICE,Audio files / Other audio files\nPSL_DATA_TYPES_HEALTH_AND_FITNESS,PSL_HEALTH,,MULTIPLE_CHOICE,Health and fitness / Health info\nPSL_DATA_TYPES_HEALTH_AND_FITNESS,PSL_FITNESS,,MULTIPLE_CHOICE,Health and fitness / Fitness info\nPSL_DATA_TYPES_CONTACTS,PSL_CONTACTS,,MULTIPLE_CHOICE,Contacts / Contacts\nPSL_DATA_TYPES_CALENDAR,PSL_CALENDAR,,MULTIPLE_CHOICE,Calendar / Calendar events\nPSL_DATA_TYPES_APP_PERFORMANCE,PSL_CRASH_LOGS,,MULTIPLE_CHOICE,App info and performance / Crash logs\nPSL_DATA_TYPES_APP_PERFORMANCE,PSL_PERFORMANCE_DIAGNOSTICS,TRUE,MULTIPLE_CHOICE,App info and performance / Diagnostics\nPSL_DATA_TYPES_APP_PERFORMANCE,PSL_OTHER_PERFORMANCE,,MULTIPLE_CHOICE,App info and performance / Other app performance data\nPSL_DATA_TYPES_FILES_AND_DOCS,PSL_FILES_AND_DOCS,,MULTIPLE_CHOICE,Files and docs / Files and docs\nPSL_DATA_TYPES_APP_ACTIVITY,PSL_USER_INTERACTION,TRUE,MULTIPLE_CHOICE,App activity / App interactions\nPSL_DATA_TYPES_APP_ACTIVITY,PSL_IN_APP_SEARCH_HISTORY,,MULTIPLE_CHOICE,App activity / In-app search history\nPSL_DATA_TYPES_APP_ACTIVITY,PSL_APPS_ON_DEVICE,,MULTIPLE_CHOICE,App activity / Installed apps\nPSL_DATA_TYPES_APP_ACTIVITY,PSL_USER_GENERATED_CONTENT,,MULTIPLE_CHOICE,App activity / Other user-generated content\nPSL_DATA_TYPES_APP_ACTIVITY,PSL_OTHER_APP_ACTIVITY,TRUE,MULTIPLE_CHOICE,App activity / Other actions\nPSL_DATA_TYPES_IDENTIFIERS,PSL_DEVICE_ID,TRUE,MULTIPLE_CHOICE,Device or other IDs / Device or other IDs\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Name) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Name) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Name) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Name) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Name) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_NAME:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Name) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Email address) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Email address) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Email address) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Email address) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Email address) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_EMAIL:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Email address) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (User IDs) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (User IDs) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (User IDs) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (User IDs) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (User IDs) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_ACCOUNT:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (User IDs) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Address) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Address) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Address) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Address) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Address) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_ADDRESS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Address) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Phone number) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Phone number) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Phone number) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Phone number) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Phone number) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PHONE:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Phone number) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Race and ethnicity) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_RACE_ETHNICITY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Race and ethnicity) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Political or religious beliefs) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_POLITICAL_RELIGIOUS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Political or religious beliefs) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Sexual orientation) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Sexual orientation) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Sexual orientation) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Sexual orientation) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Sexual orientation) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_SEXUAL_ORIENTATION_GENDER_IDENTITY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Sexual orientation) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other info) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other info) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other info) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other info) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other info) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERSONAL:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other info) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Purchase history) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Purchase history) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (Purchase history) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Purchase history) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (Purchase history) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,TRUE,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PURCHASE_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Purchase history) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Credit score) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Credit score) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Credit score) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Credit score) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Credit score) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_SCORE:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Credit score) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (User payment info) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (User payment info) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (User payment info) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (User payment info) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (User payment info) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CREDIT_DEBIT_BANK_ACCOUNT_NUMBER:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (User payment info) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other financial info) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other financial info) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other financial info) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other financial info) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other financial info) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other financial info) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Approximate location) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Approximate location) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (Approximate location) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Approximate location) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (Approximate location) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_APPROX_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Approximate location) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Precise location) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Precise location) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Precise location) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Precise location) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Precise location) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PRECISE_LOCATION:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Precise location) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Web browsing history) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Web browsing history) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Web browsing history) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Web browsing history) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Web browsing history) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_WEB_BROWSING_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Web browsing history) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Emails) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Emails) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Emails) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Emails) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Emails) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_EMAILS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Emails) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (SMS or MMS) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (SMS or MMS) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (SMS or MMS) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (SMS or MMS) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (SMS or MMS) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_SMS_CALL_LOG:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (SMS or MMS) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other in-app messages) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other in-app messages) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other in-app messages) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other in-app messages) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other in-app messages) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_MESSAGES:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other in-app messages) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Photos) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Photos) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Photos) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Photos) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Photos) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PHOTOS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Photos) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Videos) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Videos) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Videos) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Videos) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Videos) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_VIDEOS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Videos) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Voice or sound recordings) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Voice or sound recordings) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Music files) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Music files) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Music files) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Music files) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Music files) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_MUSIC:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Music files) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other audio files) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other audio files) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other audio files) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other audio files) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other audio files) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_AUDIO:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other audio files) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Health info) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Health info) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Health info) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Health info) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Health info) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_HEALTH:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Health info) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Fitness info) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Fitness info) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Fitness info) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Fitness info) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Fitness info) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_FITNESS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Fitness info) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Contacts) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Contacts) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Contacts) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Contacts) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Contacts) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CONTACTS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Contacts) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Calendar events) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Calendar events) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Calendar events) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Calendar events) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Calendar events) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CALENDAR:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Calendar events) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Crash logs) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Crash logs) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Crash logs) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Crash logs) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Crash logs) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_CRASH_LOGS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Crash logs) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Diagnostics) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Diagnostics) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (Diagnostics) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Diagnostics) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (Diagnostics) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_PERFORMANCE_DIAGNOSTICS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Diagnostics) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other app performance data) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other app performance data) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other app performance data) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other app performance data) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other app performance data) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_PERFORMANCE:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other app performance data) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Files and docs) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Files and docs) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Files and docs) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Files and docs) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Files and docs) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_FILES_AND_DOCS:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Files and docs) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (App interactions) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (App interactions) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (App interactions) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (App interactions) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (App interactions) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_INTERACTION:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (App interactions) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (In-app search history) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (In-app search history) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (In-app search history) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (In-app search history) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (In-app search history) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_IN_APP_SEARCH_HISTORY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (In-app search history) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Installed apps) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Installed apps) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Installed apps) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Installed apps) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Installed apps) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_APPS_ON_DEVICE:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Installed apps) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,,MULTIPLE_CHOICE,\"Data usage and handling (Other user-generated content) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other user-generated content) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:PSL_DATA_USAGE_EPHEMERAL,,,MAYBE_REQUIRED,Data usage and handling (Other user-generated content) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other user-generated content) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,,SINGLE_CHOICE,\"Data usage and handling (Other user-generated content) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_USER_GENERATED_CONTENT:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other user-generated content) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Other actions) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,,MULTIPLE_CHOICE,\"Data usage and handling (Other actions) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (Other actions) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Other actions) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (Other actions) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,TRUE,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,,MULTIPLE_CHOICE,\"Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_OTHER_APP_ACTIVITY:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Other actions) / Why is this user data shared? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_COLLECTED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Device or other IDs) / Is this data collected, shared, or both? / Collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:PSL_DATA_USAGE_COLLECTION_AND_SHARING,PSL_DATA_USAGE_ONLY_SHARED,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Device or other IDs) / Is this data collected, shared, or both? / Shared\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:PSL_DATA_USAGE_EPHEMERAL,,FALSE,MAYBE_REQUIRED,Data usage and handling (Device or other IDs) / Is this data processed ephemerally?\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_OPTIONAL,,SINGLE_CHOICE,\"Data usage and handling (Device or other IDs) / Is this data required for your app, or can users choose whether it's collected? / Users can choose whether this data is collected\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_USER_CONTROL,PSL_DATA_USAGE_USER_CONTROL_REQUIRED,TRUE,SINGLE_CHOICE,\"Data usage and handling (Device or other IDs) / Is this data required for your app, or can users choose whether it's collected? / Data collection is required (users can't turn off this data collection)\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_APP_FUNCTIONALITY,TRUE,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_COLLECTION_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data collected? Select all that apply. / Account management\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_APP_FUNCTIONALITY,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / App functionality\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_ANALYTICS,TRUE,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Analytics\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_DEVELOPER_COMMUNICATIONS,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Developer communications\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_FRAUD_PREVENTION_SECURITY,TRUE,MULTIPLE_CHOICE,\"Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Fraud prevention, security, and compliance\"\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_ADVERTISING,TRUE,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Advertising or marketing\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_PERSONALIZATION,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Personalization\nPSL_DATA_USAGE_RESPONSES:PSL_DEVICE_ID:DATA_USAGE_SHARING_PURPOSE,PSL_ACCOUNT_MANAGEMENT,,MULTIPLE_CHOICE,Data usage and handling (Device or other IDs) / Why is this user data shared? Select all that apply. / Account management\n", "declarations.md": "# Example Game · store declarations (answers to copy into the consoles)\n\nAnswers for a typical single-player free-to-play game with ads (mediation) and in-app purchases, no accounts, no chat,\nno user-generated content, general audience 13+. Change what does not match your game. Everything marked **UI** has no\nAPI: Play Console > your app > Policy and programs > **App content** (or Dashboard > \"Set up your app\"), and App Store\nConnect for Apple. Data safety (Play) has an API: `storebound play data-safety`.\n\n## Google Play · App content (UI)\n\n| declaration | answer |\n|---|---|\n| Privacy policy | your policy URL (same one in the listing and inside the app) |\n| App access | All functionality is available without special access (no login) |\n| Ads | Yes, my app contains ads |\n| Content rating | IARC questionnaire, below |\n| Target audience and content | 13-15, 16-17, 18 and over; \"Could your app unintentionally appeal to children?\" No (only if true) |\n| News app | No |\n| COVID-19 contact tracing / status | No |\n| Data safety | `storebound play data-safety` (then check the summary in the Console) |\n| Government apps | No |\n| Financial features | My app doesn't provide any financial features |\n| Health apps | No health features |\n| Advertising ID | Yes, uses it; purposes: Advertising or marketing, Analytics, Fraud prevention / security / compliance |\n| Sign-in details (App access) | No sign-in needed |\n| Store settings | App category Game > (Casual, Puzzle, Arcade ...); tags; contact email + website (website serves app-ads.txt) |\n\nFamilies / Designed for Families: only if you really target children; it changes the ad SDK setup (certified networks,\nno ad id). If you leave children out, the policy and the ad mediation must say \"not directed at children\" too.\n\n## IARC content rating (Play UI; Apple has its own form, `storebound apple appinfo`)\n\nCategory: Game. Typical answers for a non-violent casual game:\n\n| topic | answer |\n|---|---|\n| Violence (realistic / cartoon / fantasy) | No |\n| Blood / gore | No |\n| Fear / horror | No |\n| Sexuality / nudity | No |\n| Language (profanity, crude humour) | No |\n| Controlled substances | No |\n| Gambling: simulated / real money | No / No |\n| Random paid items (loot boxes) | No (fixed contents only; answer Yes if a paid item has random contents) |\n| Users interact or exchange content | No (no chat; OS share sheet does not count) |\n| Shares location with other users | No |\n| Digital purchases | Yes (in-app purchases) |\n| Unrestricted internet / browser | No |\n\nExpected: PEGI 3, ESRB Everyone, USK 0, IARC 3+. Apple age rating 4+ (all content questions None; ads are declared as\n\"advertising: true\", which does not change 4+).\n\n## App Store (UI-only parts)\n\n- App record: App Store Connect > Apps > + > New App (platform, name, primary language, bundle id, SKU). No API.\n- App Privacy: `app-privacy.md`, then **Publish**.\n- Export compliance (export-compliance skill): `ITSAppUsesNonExemptEncryption = NO` in Info.plist only if true (HTTPS / OS encryption only) so\n every build skips the question.\n- First in-app purchases: from the IAP page \"Add for Review\" into the draft submission with the version (see the\n store-iap-setup skill).\n- Guideline 5.1.1: the privacy policy link must also be reachable **inside the app** (Settings > Privacy).\n\n## Ads\n\n- app-ads.txt at the root of the developer website named in both listings (`https://example.com/app-ads.txt`),\n with the mediation's DIRECT line plus each network's lines.\n- iOS: SKAdNetwork ids of every network in Info.plist, ATT prompt before the ad SDK starts.\n- EU / EEA / UK / CH: consent (CMP) or non-personalized ads.\n", "listing.md": "# Example Game · Google Play listing copy\n\nSource for `storebound play listing` (title, short + full description) and `storebound play release` (release notes).\nRules the parser follows:\n- every `## ...` section with a `` line is a listing; one section can serve several Play codes\n (``)\n- under it, `### ...` headings that contain **title** (optional, defaults to `app.name`), **short**, **full** and\n **what's new**, each followed by one fenced block\n- limits (checked before anything is sent): title 30, short 80, full 4000, what's new 500 characters\nOnly claim what is really in the game. No prices, no \"best / #1\", no other games' names.\n\n---\n\n## English (en-US)\n\n\n### Play short description (max 80)\n```\nShort, punchy promise of the core loop in one line\n```\n\n### Play full description (max 4000)\n```\nFirst paragraph: what the player does and why it feels good, in two or three sentences.\n\nFEATURES\n• One-thumb controls\n• 100+ levels and new ones every update\n• Play offline, no account needed\n\nFree to play. Contains ads and offers optional in-app purchases.\n```\n\n### What's new (max 500)\n```\nFirst release. Have fun!\n```\n\n## Türkçe (tr-TR)\n\n\n### Play kısa açıklama · short (max 80)\n```\nOyunun özünü anlatan kısa ve vurucu bir cümle\n```\n\n### Play tam açıklama · full (max 4000)\n```\nİlk paragraf: oyuncu ne yapıyor ve neden keyifli, iki üç cümlede.\n\nÖZELLİKLER\n• Tek parmakla kontrol\n• 100+ bölüm, her güncellemede yenileri\n• İnternetsiz oyna, hesap gerekmez\n\nÜcretsiz oyun. Reklam içerir ve isteğe bağlı uygulama içi satın alımlar sunar.\n```\n\n### Yenilikler · what's new (max 500)\n```\nİlk sürüm. İyi eğlenceler!\n```\n", diff --git a/cli/lib/privacy-manifest.ts b/cli/lib/privacy-manifest.ts index 18769b7..f5ef0f3 100644 --- a/cli/lib/privacy-manifest.ts +++ b/cli/lib/privacy-manifest.ts @@ -2,6 +2,7 @@ // What Apple requires (since May 2024): the app and every third-party SDK that uses a "required reason" API declare // the API category and an approved reason in a PrivacyInfo.xcprivacy; SDKs on Apple's list of commonly used SDKs // must ship their own manifest (and a signature when linked as a binary). +import { APPLE_DATA_TYPE_IDS, APPLE_PRIVACY_PURPOSES, APPLE_PRIVACY_TYPES, type AppleDataTypeId, type AppleTypeAnswer } from './app-privacy'; /** Required-reason API categories and the symbols that typically mean the code uses one. Heuristic, not a compiler. */ export const REQUIRED_REASON_APIS: Record = { @@ -103,20 +104,51 @@ const TYPICAL_REASON: Record = { * of app-level tracking domains (appPrivacy.trackingDomains); tracking done by an SDK is declared in the SDK's own * manifest, so `appPrivacy.tracking: true` alone writes false (true with an empty list is ITMS-91064). * `apis`: declared categories and reasons to keep; `used`: categories found in the sources (typical reason added); - * `collected`: the existing NSPrivacyCollectedDataTypes entries, kept as they are (the owner's answers). + * `collected`: the existing NSPrivacyCollectedDataTypes entries, kept as they are when there is no `types`. + * `types`: appPrivacy.types; when given, NSPrivacyCollectedDataTypes is built from it (collectedFromAppPrivacy). */ -export function proposeAppManifest(o: { trackingDomains?: string[]; apis?: Record; used?: Iterable; collected?: unknown[] }): Record { +export function proposeAppManifest(o: { trackingDomains?: string[]; apis?: Record; used?: Iterable; collected?: unknown[]; types?: Partial> }): Record { const domains = [...new Set((o.trackingDomains ?? []).map(s => s.trim()).filter(Boolean))]; const apis: Record = { ...(o.apis ?? {}) }; for (const cat of o.used ?? []) if (!apis[cat]?.length && TYPICAL_REASON[cat]) apis[cat] = [TYPICAL_REASON[cat]!]; return { NSPrivacyTracking: domains.length > 0, NSPrivacyTrackingDomains: domains, - NSPrivacyCollectedDataTypes: o.collected ?? [], + NSPrivacyCollectedDataTypes: o.types && Object.keys(o.types).length ? collectedFromAppPrivacy(o.types, o.collected).entries : o.collected ?? [], NSPrivacyAccessedAPITypes: Object.entries(apis).sort(([a], [b]) => a.localeCompare(b)).map(([cat, reasons]) => ({ NSPrivacyAccessedAPIType: cat, NSPrivacyAccessedAPITypeReasons: reasons })), }; } +/** + * NSPrivacyCollectedDataTypes built from `appPrivacy.types` (the owner's App Privacy answers, authoritative): one entry + * per type marked collected, with Apple's manifest keys for the type and purposes, linked and tracking as answered + * (per-type tracking is allowed without NSPrivacyTrackingDomains; only NSPrivacyTracking needs app-level domains). + * Existing manifest entries are replaced; notes say what was dropped or is still unanswered. + */ +export function collectedFromAppPrivacy(types: Partial>, existing: unknown[] = []): { entries: Record[]; notes: string[] } { + const entries: Record[] = []; + const notes: string[] = []; + for (const id of APPLE_DATA_TYPE_IDS) { + const a = types[id]; + if (!a || a.collected !== true) continue; + if (a.linked === undefined) notes.push(`appPrivacy.types.${id}.linked is not answered: wrote false`); + if (a.tracking === undefined) notes.push(`appPrivacy.types.${id}.tracking is not answered: wrote false`); + if (!a.purposes?.length) notes.push(`appPrivacy.types.${id}.purposes is empty: Apple needs at least one purpose`); + entries.push({ + NSPrivacyCollectedDataType: APPLE_PRIVACY_TYPES[id][2], + NSPrivacyCollectedDataTypeLinked: a.linked === true, + NSPrivacyCollectedDataTypeTracking: a.tracking === true, + NSPrivacyCollectedDataTypePurposes: (a.purposes ?? []).map(p => APPLE_PRIVACY_PURPOSES[p][1]), + }); + } + const now = new Set(entries.map(e => e.NSPrivacyCollectedDataType as string)); + for (const x of existing) { + const k = (x as Record)?.NSPrivacyCollectedDataType; + if (typeof k === 'string' && !now.has(k.trim())) notes.push(`${k.trim()} was in the manifest but appPrivacy.types does not mark it collected: dropped (add it to appPrivacy.types if the app's own code collects it)`); + } + return { entries, notes }; +} + /** Minimal plist reader for the keys a privacy manifest uses (no plist dependency). */ export function parseManifest(path: string, xml: string): Manifest { const apis: Record = {}; diff --git a/cli/lib/project-signals.ts b/cli/lib/project-signals.ts new file mode 100644 index 0000000..9cf1d51 --- /dev/null +++ b/cli/lib/project-signals.ts @@ -0,0 +1,43 @@ +// Project signals the console answer sheets use (`console plan`): which ad / attribution SDKs the project has, whether +// the Android manifest (source or the merged one of a local build) requests or removes the advertising ID permission, +// and code that suggests App Privacy types without a Play Data safety equivalent (customer support, AR scanning). +// Pure over the scan facts; null fields mean "not found", never "not scanned" (that is `undefined` signals). +import { grep, type ProjectFacts, where } from './scan/project'; + +export const AD_ID_PERMISSION = 'com.google.android.gms.permission.AD_ID'; + +export interface ProjectSignals { + /** Ad SDKs (names) found in the dependencies. */ + adSdks: string[]; + /** Attribution SDKs (names). */ + attributionSdks: string[]; + /** SDKs whose own manifest merges the AD_ID permission (names). */ + adIdSdks: string[]; + /** file:line of the AD_ID permission in a source or merged manifest. */ + adIdPermission: string | null; + /** file:line of . */ + adIdRemoved: string | null; + /** file:line of a customer support SDK or ticket code (App Privacy "Customer Support"). */ + support: string | null; + /** file:line of AR / XR, hand or face tracking code (App Privacy Environment Scanning, Hands, Head). */ + ar: string | null; +} + +const SUPPORT_DEP = /zendesk|intercom|helpshift|freshchat|freshdesk|crisp-sdk|tawk|gorgias/i; +const SUPPORT_CODE = /\b(createTicket|submitTicket|supportTicket|SupportTicket|contactSupport\s*\()/; +const AR_CODE = /\bARKit\b|\bARSession\b|ARWorldTrackingConfiguration|ARFaceTrackingConfiguration|ARBodyTrackingConfiguration|VNDetectHumanHandPoseRequest|sceneReconstruction|\bnavigator\.xr\b|@react-three\/xr|ARCore|com\.google\.ar\.core|UnityEngine\.XR\.ARFoundation/; + +export function projectSignals(p: ProjectFacts): ProjectSignals { + const names = (cat: string) => [...new Set(p.sdks.filter(s => s.sdk.category === cat).map(s => s.sdk.name))]; + const perm = p.androidInfo.permissions.find(x => x.name === AD_ID_PERMISSION); + const removed = p.androidInfo.removedPermissions.find(x => x.name === AD_ID_PERMISSION); + const dep = p.deps.find(d => SUPPORT_DEP.test(d.name)); + const support = dep ? `${dep.file}:${dep.line}` : grep(p.sources, SUPPORT_CODE, 1).map(h => where(h))[0] ?? null; + const arDep = p.deps.find(d => /arcore|arkit|ar-foundation|arfoundation|react-three\/xr|viro/i.test(d.name)); + const ar = arDep ? `${arDep.file}:${arDep.line}` : grep(p.sources, AR_CODE, 1).map(h => where(h))[0] ?? null; + return { + adSdks: names('ads'), attributionSdks: names('attribution'), + adIdSdks: [...new Set(p.sdks.filter(s => s.sdk.mergesAdId).map(s => s.sdk.name))], + adIdPermission: perm ? where(perm.at) : null, adIdRemoved: removed ? where(removed.at) : null, support, ar, + }; +} diff --git a/cli/lib/scan/declare.ts b/cli/lib/scan/declare.ts index f265c41..323ffbf 100644 --- a/cli/lib/scan/declare.ts +++ b/cli/lib/scan/declare.ts @@ -81,7 +81,7 @@ export function proposeDeclarations(p: ProjectFacts, c: ShipConfig | null, confi detected.set(t, e); }; if (accounts) { - markAccount('PSL_USER_ACCOUNT', signUp.length ? signUp : [cfgAt(/accounts\s*:/, 'accounts.enabled')], 'accounts'); + markAccount('PSL_USER_ACCOUNT', signUp.length ? signUp : [cfgAt(/accounts\s*:/, 'from config (accounts.enabled)')], 'accounts'); for (const s of signUpCfg) for (const t of ACCOUNT_SIGN_UP[s]?.data ?? []) if (t !== 'PSL_USER_ACCOUNT') markAccount(t, [cfgAt(/signUp\s*:/, `accounts.signUp: ${s}`)], `accounts.signUp ${s}`); const em = grepOwn(p, SIGNALS.email, 2); if (em.hits.length && !em.built) markAccount('PSL_EMAIL', em.hits, 'email in the sources'); @@ -147,8 +147,12 @@ export function proposeDeclarations(p: ProjectFacts, c: ShipConfig | null, confi // appPrivacy.types: propose collected + purposes for detected types the config does not answer (Apple apps only) const haveTypes = c?.appPrivacy?.types ?? {}; const typesAdded: Partial> = {}; + // appPrivacy.types is authoritative once it exists (a type it does not list is "not collected"), so a block this + // proposal writes also carries the types dataSafety already declares (kept) when the config has no types yet + const detectedNew = diff.some(d => d.change !== 'kept' && typeIdOf(d.apple.split(' / ')[1]!) && !haveTypes[typeIdOf(d.apple.split(' / ')[1]!)!]); + const withKept = detectedNew && !Object.keys(haveTypes).length; if (c?.apple) for (const d of diff) { - if (d.change === 'kept') continue; + if (d.change === 'kept' && !withKept) continue; const id = typeIdOf(d.apple.split(' / ')[1]!); if (!id || id === 'OTHER_DATA_TYPES' || haveTypes[id]) continue; const shared = d.after.shared ? d.after.share ?? [] : []; diff --git a/cli/lib/scan/index.ts b/cli/lib/scan/index.ts index 6cb15a8..ac9bccb 100644 --- a/cli/lib/scan/index.ts +++ b/cli/lib/scan/index.ts @@ -7,7 +7,7 @@ import { reviewDemoAccount } from '../credentials'; import { proposeDeclarations, type Proposal } from './declare'; import { detectProject, ECO_LABEL, type ProjectFacts, type Stack, where } from './project'; import type { OriginalityReport } from '../originality'; -import { APPLE_GUIDELINES, type ScanFinding, scanRules, type Severity, sortScan } from './rules'; +import { APPLE_GUIDELINES, applyIgnore, type ScanFinding, scanRules, type Severity, sortScan, unusedIgnores } from './rules'; import type { SdkCategory } from './sdks'; export interface ScanReport { @@ -68,6 +68,8 @@ export async function runScan(dir: string, o: { config?: string } = {}): Promise else if (facts.android && facts.androidInfo.manifests.length) notes.push('Android: libraries add permissions when the manifest is merged and no local build output (build/intermediates) was found; check the merged manifest (Android Studio > Merged Manifest, or the AAB) too.'); if (facts.ios && !facts.plistSources.length) notes.push('iOS: no Info.plist settings found (generated at build time?): purpose string, ATS and export compliance checks were skipped.'); if (!facts.sources.length) notes.push('No app source files found: code checks (sign-in, deletion, restore, ATT prompt) could not run.'); + // scan.ignore entries that accept nothing (a renamed title, a typo, a fixed issue) are said, never silently dropped + notes.push(...unusedIgnores(findings, config?.scan?.ignore ?? [])); const report: ScanReport = { root: dir, config: configPath ? rel(configPath) : null, configError: error, stacks: facts.stacks, platforms: { ios: facts.ios, android: facts.android }, @@ -149,10 +151,9 @@ export function addOriginality(r: ScanReport, o: OriginalityReport, stores: { ap if (stores.apple) r.findings.push({ ...base, guideline: '4.3', store: 'apple', ref: APPLE_GUIDELINES }); if (stores.play) r.findings.push({ ...base, guideline: 'Play: Repetitive content', store: 'play', ref: 'https://support.google.com/googleplay/android-developer/answer/9898783' }); } - for (const f of r.findings) if (f.rule === 'originality' && !f.accepted) { - const hit = ignore.find(i => i.rule === f.rule && (!i.store || i.store === f.store) && (!i.match || f.title.toLowerCase().includes(i.match.toLowerCase()))); - if (hit) f.accepted = hit.reason; - } + applyIgnore(r.findings.filter(f => f.rule === 'originality'), ignore); + // runScan skipped the originality entries (it cannot produce that rule); now they can be checked + r.notes.push(...unusedIgnores(r.findings, ignore, []).filter(n => n.includes('(rule originality'))); r.findings = sortScan(r.findings); r.counts = { blocker: 0, high: 0, medium: 0, low: 0, accepted: 0 }; for (const f of r.findings) { if (f.accepted) r.counts.accepted++; else r.counts[f.severity]++; } diff --git a/cli/lib/scan/rules.ts b/cli/lib/scan/rules.ts index 0c135ae..cf87b0d 100644 --- a/cli/lib/scan/rules.ts +++ b/cli/lib/scan/rules.ts @@ -85,16 +85,24 @@ export const PLAY_TARGET_SDK = { phone: 36, wear: 35, automotive: 35, tv: 34 } a export const PLAY_TARGET_SDK_SINCE = '2026-08-31'; // ---- code signals ---- +/** `re` only on lines that are not import / require / export-from statements (a module path is not evidence). Line + * anchored with the m flag, so a whole-file test and a per-line test agree. */ +export function codeLine(re: RegExp): RegExp { + return new RegExp(`^(?!\\s*import[\\s{*'"])(?!.*\\b(?:require|import)\\s*\\(\\s*['"\`])(?!.*\\bfrom\\s*['"\`]).*?(?:${re.source})`, `${re.flags.replace('m', '')}m`); +} export const SIGNALS = { appleSignIn: /ASAuthorizationAppleIDProvider|SignInWithAppleButton|AppleAuthenticationButton|AppleAuthentication\.signInAsync|appleAuth\.performRequest|SignInWithApple|OAuthProvider\(\s*['"]apple\.com|AppleAuthProvider|signInWithApple|AppleAuthManager/, thirdPartyLogin: /GIDSignIn|GoogleSignin\.signIn|GoogleSignIn\(|GoogleAuthProvider|signInWithGoogle|FacebookAuthProvider|LoginManager\(\)\.logIn|LoginManager\.logInWithPermissions|signInWithFacebook|FacebookLogin|TwitterAuthProvider|GithubAuthProvider|OAuthProvider\(\s*['"](microsoft\.com|yahoo\.com|twitter\.com|github\.com)/, - signIn: /signInWithEmailAndPassword|signInWithPassword|signInWithCredential|createUserWithEmailAndPassword|\bsupabase\.auth\.sign|Amplify\.Auth\.signIn|\bAuth0\b|\bloginWithPassword\b|\.signIn\(\s*\{|\/(api\/)?(auth\/)?(login|signin|sign-in)\b['"`]/, - signUp: /createUserWithEmailAndPassword|\bsignUp\s*\(|\bsupabase\.auth\.signUp|Amplify\.Auth\.signUp|\/(api\/)?(auth\/)?(register|signup|sign-up)\b['"`]|["'>](Create (an )?account|Sign up)["'<]/, + // sign-in / sign-up: auth SDK calls, credential fields, guest-account creation, auth endpoints (an absolute path, a + // URL or a `${base}/...` template, never a module path like './buildings/register') and screen strings. Import / + // require lines never count: a module named register, login or account is not an auth flow. + signIn: codeLine(/signInWithEmailAndPassword|signInWithPassword|signInWithCredential|createUserWithEmailAndPassword|\bsupabase\.auth\.sign|Amplify\.Auth\.signIn|\bAuth0\b|\bloginWithPassword\b|\.signIn\(\s*\{|autoComplete=["']current-password|textContentType=["']?\.?password\b|\btype=["']password["']|['"`](https?:\/\/[^'"`\s]*|\$\{[^}]*\})?\/(api\/|v\d+\/)*(auth\/|users?\/|accounts?\/)?(login|signin|sign-in)['"`?]/), + signUp: codeLine(/createUserWithEmailAndPassword|\bsignUp\s*\(|\bsupabase\.auth\.signUp|Amplify\.Auth\.signUp|autoComplete=["']new-password|textContentType=["']?\.?newPassword\b|\bcreate(Guest|Anonymous)(Account|User)\b|\bregisterGuest\b|['"`](https?:\/\/[^'"`\s]*|\$\{[^}]*\})?\/(api\/|v\d+\/)*(auth\/|users?\/|accounts?\/)?(register|signup|sign-up)['"`?]|["'>](Create (an )?account|Sign up)["'<]/), // real evidence that the app collects an email address: an email input or keyboard, an `email` property in a request // body / schema / type (not a quoted constant or label), an email validation, an email endpoint or an email sign-in // call. Accounts alone never mean email (a username with a device token has none). email: /type\s*[:=]\s*["'{]*email["'}]|inputMode=["']email["']|keyboardType=["']?email-address|autoComplete=["']email|textContentType=["']?(emailAddress|\.emailAddress)|UITextContentType\.emailAddress|\.keyboardType\(\.emailAddress\)|keyboardType\s*=\s*\.emailAddress|TextInputType\.emailAddress|TYPE_TEXT_VARIATION_EMAIL_ADDRESS|KeyboardType\.Email\b|(?]Delete (my |your )?account|\brevokeToken\b|auth\.admin\.deleteUser|Hesab(ı|ımı) sil/i, + deleteAccount: codeLine(/deleteUser\b|\buser\.delete\(|currentUser\??\.delete\(|\bdeleteAccount\b|delete_account|DeleteAccount|delete-account|["'>]Delete (my |your )?account|\brevokeToken\b|auth\.admin\.deleteUser|Hesab(ı|ımı) sil/i), restore: /restoreCompletedTransactions|AppStore\.sync\(|restorePurchases|restoreTransactions|RestoreTransactions|getAvailablePurchases|\brestorePurchase\b|\.restore\(\)|Restore Purchases|syncPurchases|Purchases\.shared\.restore|store\.restore\b|queryPurchasesAsync/i, subscriptionCode: /subscriptionPeriod|Product\.SubscriptionInfo|\.autoRenewable\b|entitlements\.active|SubscriptionStoreView|ProductType\.Subscription|ProductType\.SUBS|BillingClient\.ProductType\.SUBS/, attRequest: /requestTrackingAuthorization|requestTrackingPermissionsAsync|requestTrackingPermission\b|AppTrackingTransparency\.requestTrackingAuthorization|ATTrackingStatusBinding\.RequestAuthorizationTracking|TrackingTransparency\.requestPermission/, @@ -108,7 +116,13 @@ export const SIGNALS = { privateScheme: /["'`](App-Prefs|prefs):root\b/, ratePrompt: /["'`>](Rate (us|this app)|Enjoying [A-Z][\w ]{0,30}\?|Love (this|the) app\?)/, reviewApi: /SKStoreReviewController|requestReview|StoreReview\.requestReview|InAppReview|in_app_review|react-native-rate|requestReviewFlow|ReviewManager/, - incentiveRating: /(? = { }; const SMS_CALL_LOG = ['READ_SMS', 'RECEIVE_MMS', 'RECEIVE_SMS', 'RECEIVE_WAP_PUSH', 'SEND_SMS', 'WRITE_SMS', 'READ_CALL_LOG', 'WRITE_CALL_LOG', 'PROCESS_OUTGOING_CALLS']; +/** A system review prompt call with a reward grant within `win` lines of it (rewarding the rating): the call line. */ +export function rewardNearReviewApi(sources: { path: string; text: string }[], max = 2, win = 6): Hit[] { + const out: Hit[] = []; + for (const s of sources) { + if (!SIGNALS.reviewApiCall.test(s.text) || !SIGNALS.rewardGrant.test(s.text)) continue; + const lines = s.text.split('\n'); + lines.forEach((l, i) => { + if (out.length >= max || !SIGNALS.reviewApiCall.test(l) || /^\s*(\/\/|#|\*)/.test(l)) return; + if (lines.slice(Math.max(0, i - win), i + win + 1).some(x => SIGNALS.rewardGrant.test(x) && !/^\s*(\/\/|#|\*)/.test(x))) + out.push({ file: s.path, line: i + 1, text: l.trim().slice(0, 160) }); + }); + if (out.length >= max) break; + } + return out; +} + /** Text lines (inside fenced blocks of a listing file, or JSON string values) that match `re`. */ function listingHits(file: { path: string; text: string } | undefined, re: RegExp, o: { fenced?: boolean; field?: RegExp } = {}): Hit[] { if (!file) return []; @@ -172,10 +202,12 @@ export function scanRules(inp: ScanInput): ScanFinding[] { const authSdk = sdkHits(s => s.sdk.category === 'auth'); const signInCode = src(SIGNALS.signIn, 2); const signUpCode = src(SIGNALS.signUp, 2); - const accountsCfg = c?.accounts?.enabled ? cfg(/accounts\s*:/) : []; + const accountsCfg = c?.accounts?.enabled ? cfg(/accounts\s*:/, 'from config (accounts.enabled)') : []; const signIn = [...accountsCfg, ...authSdk, ...signInCode]; const creates = [...accountsCfg, ...signUpCode, ...thirdParty, ...siwa, ...sdkHits(s => s.sdk.login === 'firebase')]; - const deletion = [...(c?.accounts?.inApp ? cfg('inApp') : []), ...src(SIGNALS.deleteAccount, 2)]; + // in-app deletion needs code evidence (a delete-account call, function or UI string): accounts.inApp alone is a claim + const deletionCode = src(SIGNALS.deleteAccount, 2); + const deletionClaim = c?.accounts?.inApp ? cfg('inApp', `from config (accounts.inApp: ${c.accounts.inApp})`) : []; if (signIn.length) { const ra = c?.reviewAccess; @@ -193,7 +225,11 @@ export function scanRules(inp: ScanInput): ScanFinding[] { fix: 'Play Console > App content > App access: add the demo account and instructions (keep the credentials in .storebound/credentials.json only).', skill: 'review-access', ref: `${PLAY}9859455` }); } if (creates.length) { - if (apple && !deletion.length) + if (apple && !deletionCode.length && deletionClaim.length) + add({ rule: 'account-deletion', guideline: '5.1.1(v)', store: 'apple', severity: 'medium', + title: 'In-app account deletion is claimed by config (accounts.inApp), not found in code', evidence: [...deletionClaim, ...creates].slice(0, 4), + fix: 'Show the code path or the screen: a delete-account call (endpoint, deleteUser / user.delete()), a deletion function or a "Delete account" UI string in the app sources. If deletion lives behind a server-driven or web screen, check it on a device and accept this finding with scan.ignore (a reason). A config claim alone does not pass App Review.', skill: 'account-deletion' }); + else if (apple && !deletionCode.length) add({ rule: 'account-deletion', guideline: '5.1.1(v)', store: 'apple', severity: accountsCfg.length || signUpCode.length ? 'blocker' : 'high', title: 'Accounts can be created but no in-app account deletion was found', evidence: creates.slice(0, 4), fix: 'Add "Delete account" in the app (it deletes the account and its data, not only signs out; with Sign in with Apple also revoke the token), then set accounts.inApp to where it starts, e.g. "Settings > Account > Delete account".', skill: 'account-deletion' }); @@ -511,7 +547,7 @@ export function scanRules(inp: ScanInput): ScanFinding[] { if (priv.length) add({ rule: 'private-api', guideline: '2.5.1', store: 'apple', severity: 'high', title: 'Private settings URL scheme (App-Prefs: / prefs:root)', evidence: priv, fix: 'Open your own settings page with UIApplication.openSettingsURLString instead.', skill: 'review-rejections' }); - const incent = grep(p.sources, SIGNALS.incentiveRating, 2); + const incent = [...grep(p.sources, SIGNALS.incentiveRating, 2), ...rewardNearReviewApi(p.sources, 2)].slice(0, 2); if (incent.length) add({ rule: 'review-prompt', guideline: '3.2.2(x) / 5.6.3', store: 'apple', severity: 'high', title: 'A reward for rating or reviewing the app', evidence: incent, fix: 'Never reward or gate ratings; ask with the system review prompt only.', skill: 'review-replies' }); @@ -530,12 +566,34 @@ export function scanRules(inp: ScanInput): ScanFinding[] { playRules(inp, { add: f => add({ ...f, store: 'play' }), cfg, sdkHits, listingHits, creates, restorable, paySdk }); // ---- owner-accepted findings ---- - const ignore = c?.scan?.ignore ?? []; - for (const f of out) { - const hit = ignore.find(i => i.rule === f.rule && (!i.store || i.store === f.store) && (!i.match || f.title.toLowerCase().includes(i.match.toLowerCase()))); + applyIgnore(out, c?.scan?.ignore ?? []); + return sortScan(out); +} + +export type ScanIgnore = NonNullable['ignore']>[number]; + +/** Whether a `scan.ignore` entry accepts a finding: the same rule (and store, when given), `match` in the title and + * `evidence` in one evidence line ("file:line text"), both case-insensitive. */ +export function ignoreMatches(i: ScanIgnore, f: ScanFinding): boolean { + if (i.rule !== f.rule || (i.store && i.store !== f.store)) return false; + if (i.match && !f.title.toLowerCase().includes(i.match.toLowerCase())) return false; + const ev = i.evidence?.toLowerCase(); + return !ev || f.evidence.some(e => `${e.file}:${e.line} ${e.text}`.toLowerCase().includes(ev)); +} + +/** Marks the findings a `scan.ignore` entry accepts (the first matching entry's reason). */ +export function applyIgnore(findings: ScanFinding[], ignore: ScanIgnore[]): void { + for (const f of findings) if (!f.accepted) { + const hit = ignore.find(i => ignoreMatches(i, f)); if (hit) f.accepted = hit.reason; } - return sortScan(out); +} + +/** `scan.ignore` entries that accept no finding (a typo in the rule id, a title or evidence that changed, a fixed + * issue): each as a warning line. `skipRules`: rules this run could not produce (originality without --originality). */ +export function unusedIgnores(findings: ScanFinding[], ignore: ScanIgnore[], skipRules: string[] = ['originality']): string[] { + return ignore.flatMap((i, n) => skipRules.includes(i.rule) || findings.some(f => ignoreMatches(i, f)) ? [] + : [`scan.ignore[${n}] (rule ${i.rule}${i.store ? `, store ${i.store}` : ''}${i.match ? `, match "${i.match}"` : ''}${i.evidence ? `, evidence "${i.evidence}"` : ''}) matches no finding: check the rule id, the title text (match) and the evidence text (evidence), or remove it if the issue is fixed`]); } const RANK: Record = { blocker: 0, high: 1, medium: 2, low: 3 }; diff --git a/cli/privacy-manifest.ts b/cli/privacy-manifest.ts index f891b22..f5c901d 100644 --- a/cli/privacy-manifest.ts +++ b/cli/privacy-manifest.ts @@ -9,7 +9,8 @@ // ITMS-91064: NSPrivacyTracking and NSPrivacyTrackingDomains must agree in the app's manifest and in every SDK's (true // needs a non-empty list, a non-empty list needs true); a mismatch is a blocker. // --propose prints the app's PrivacyInfo.xcprivacy as storebound proposes it (declared + found API categories, the -// collected types it has): NSPrivacyTracking true only with app-level tracking domains (--tracking-domains, else +// collected types from appPrivacy.types of storebound.config.ts with linked, tracking and purposes, else the ones the +// manifest has): NSPrivacyTracking true only with app-level tracking domains (--tracking-domains, else // appPrivacy.trackingDomains of storebound.config.ts when there is one), else false; tracking done by an SDK is // declared in the SDK's own manifest. import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from 'node:fs'; @@ -19,7 +20,8 @@ import { log } from './lib/log'; import { nativeIosPlugins } from './lib/node-packages'; import { parsePlist, toXmlPlist, type PlistValue } from './lib/plist'; import { manifestFromBytes } from './lib/privacy-files'; -import { evaluatePrivacyManifest, type Manifest, parseManifest, proposeAppManifest, scanSources, type SdkInfo } from './lib/privacy-manifest'; +import type { AppleDataTypeId, AppleTypeAnswer } from './lib/app-privacy'; +import { collectedFromAppPrivacy, evaluatePrivacyManifest, type Manifest, parseManifest, proposeAppManifest, scanSources, type SdkInfo } from './lib/privacy-manifest'; const SKIP = new Set(['.git', 'build', 'DerivedData', 'node_modules', '.build', 'xcuserdata']); const SRC = /\.(swift|m|mm|c|cc|cpp|h|hpp)$/; @@ -54,11 +56,12 @@ function existingCollected(path: string | undefined): PlistValue[] { try { const v = parsePlist(readFileSync(path)) as Record; return Array.isArray(v.NSPrivacyCollectedDataTypes) ? v.NSPrivacyCollectedDataTypes : []; } catch { return []; } } -async function configTrackingDomains(): Promise { +async function configAppPrivacy(): Promise<{ trackingDomains: string[]; types: Partial> | undefined }> { try { const { loadConfig } = await import('./lib/config'); - return (await loadConfig()).config.appPrivacy?.trackingDomains ?? []; - } catch { return []; } + const ap = (await loadConfig()).config.appPrivacy; + return { trackingDomains: ap?.trackingDomains ?? [], types: ap?.types }; + } catch { return { trackingDomains: [], types: undefined }; } } export async function run(args: Args): Promise { @@ -110,8 +113,16 @@ export async function run(args: Args): Promise { log('privacy', `${sources.length} source files, ${sdks.length} SDKs, ${manifests.length} app manifest(s)`); const sourceHits = scanSources(sources); if (args.has('propose')) { - const domains = args.list('tracking-domains').length ? args.list('tracking-domains') : await configTrackingDomains(); - console.log(toXmlPlist(proposeAppManifest({ trackingDomains: domains, apis: appManifest?.apis, used: sourceHits.keys(), collected: existingCollected(manifests[0]) }) as PlistValue).trimEnd()); + const ap = await configAppPrivacy(); + const domains = args.list('tracking-domains').length ? args.list('tracking-domains') : ap.trackingDomains; + const existing = existingCollected(manifests[0]); + const types = ap.types && Object.keys(ap.types).length ? ap.types : undefined; + console.log(toXmlPlist(proposeAppManifest({ trackingDomains: domains, apis: appManifest?.apis, used: sourceHits.keys(), collected: existing, types }) as PlistValue).trimEnd()); + if (types) { + const c = collectedFromAppPrivacy(types, existing); + log('privacy', `NSPrivacyCollectedDataTypes: ${c.entries.length} type(s) from appPrivacy.types (the App Privacy answers)`); + for (const n of c.notes) log('privacy', n); + } else if (!existing.length) log('privacy', 'NSPrivacyCollectedDataTypes empty: no appPrivacy.types in storebound.config.ts and none in the manifest (answer appPrivacy.types first: storebound console plan apple-app-privacy)'); log('privacy', domains.length ? `NSPrivacyTracking true with ${domains.length} app-level tracking domain(s)` : 'NSPrivacyTracking false: no app-level tracking domains (tracking SDKs declare theirs in their own manifests; true with an empty list is ITMS-91064)'); return; } diff --git a/docs/pitfalls.md b/docs/pitfalls.md index ba3bc8a..70d37ee 100644 --- a/docs/pitfalls.md +++ b/docs/pitfalls.md @@ -9,6 +9,9 @@ fails without the fix. | # | date | lesson | guard | |---|---|---|---| +| 37 | 2026-10 | A `scan.ignore` entry whose `match` text was in the evidence (a code line) instead of the finding title accepted nothing and said nothing. `init --profile game` wrote example-looking values into `.storebound/credentials.json` (`YOUR_KEY_ID`, `/absolute/path/to/upload.p12`, `sk_your_revenuecat_v2_secret_key`), which commands read as real credentials. | `scan.ignore[].evidence` matches an evidence line ("file:line text"), `match` stays the title; `scan` (report notes) and `check` (warning) name every entry that accepts no finding (`ignoreMatches` / `applyIgnore` / `unusedIgnores` in `cli/lib/scan/rules.ts`). `templates/credentials.example.json` holds empty strings only; `isPlaceholder` (`cli/lib/credentials.ts`) makes "your-…", `<…>`, "xxx", "/absolute/path/to/…", "example" and the zero Apple Ads client id missing in every credential read; `check` warns, `doctor` shows set / missing / placeholder per key. `tests/game-report.test.ts`. | +| 36 | 2026-10 | Privacy answers derived from the wrong source: `privacy-manifest --propose` wrote an EMPTY NSPrivacyCollectedDataTypes while `appPrivacy.types` listed 5 collected types; `console plan apple-app-privacy` added "Other Usage Data" from dataSafety (PSL_OTHER_APP_ACTIVITY) although `appPrivacy.types` was given; `console plan play-advertising-id` answered "uses advertising ID" Yes because dataSafety declared PSL_DEVICE_ID (a random install id, not the advertising ID). | `--propose` builds NSPrivacyCollectedDataTypes from `appPrivacy.types` (types, linked, tracking, purposes as Apple's manifest keys; NSPrivacyTracking still only with app-level domains, lesson 29) and `check` warns on an empty list against collected types. `appPrivacy.types` is authoritative in the App Privacy sheet (unlisted = not collected, certain); without it, tracking false and no ad / attribution SDK make Advertising Data, Customer Support, Environment Scanning, Hands and Head "not collected" unless a project signal suggests them (`cli/lib/project-signals.ts`). Advertising ID comes from ad / attribution SDKs, the AD_ID permission (source or merged manifest, or its `tools:node="remove"`), ads.appAdsTxt or `console.appContent.advertisingId`; none: No (certain), a conflict: ask (`advertisingIdUse`). `tests/game-report.test.ts`. | +| 35 | 2026-10 | `scan` on a Capacitor game: rule `review-prompt` (3.2.2(x), reward for rating) fired on `const rate = F.genRate(lv, bonus.gen(r));` (a bare "rate" next to "bonus"); every account finding cited `import './buildings/register';` as sign-up evidence; and 5.1.1(v) cleared as soon as `accounts.inApp` named a screen the code did not have. | The incentive signal needs a rate-us / leave-a-review phrase next to a reward, a reward "for rating / reviewing", or a reward granted within a few lines of a system review prompt call (`rewardNearReviewApi`). Sign-in / sign-up / deletion signals skip import, require and export-from lines (`codeLine`), and auth endpoints must be an absolute path, URL or `${base}/...`, never a module path; config-only accounts say "from config (accounts.enabled)". A config claim of in-app deletion without code evidence stays a finding (medium, "claimed by config, not found in code"). `tests/game-report.test.ts` with `tests/fixtures/scan/capacitor-game`. | | 34 | 2026-10 | The macOS desktop release ran the universal app's x86_64 slice on the Apple silicon build Mac (`arch -x86_64 storebound-desktop --self-test`, and the x64 sidecar's `version`) to cover both CPU types. That starts Rosetta, and macOS showed the owner "App Update Required: this version of storebound includes a component that will not open in macOS 28". | `tools/desktop-release.ts` never runs an x86_64 slice: `sliceProblems` checks every Mach-O in the bundle with `lipo -archs` (each must carry arm64 and x86_64), the self-test and the sidecar `version` run natively (arm64) only; `tests/desktop-release.test.ts` (slice check, and no `arch -x86_64` / Rosetta launch in the release tools); docs/desktop-release.md. | | 33 | 2026-10 | Play Console: pressing "Submit N changes for review" in Publishing overview while a review was running opened a dialog "Do you want to restart your review?" with "Restart review". Pressing it restarts the running review with the new changes; the plan did not know the dialog. | `play-production-release` plan: the dialog is a known step and UI fact, "Restart review" is in neverPress (the owner's button), Cancel is allowed (closes it, nothing changes); play-first-release and release-management skills explain it. `tests/console-play-plans.test.ts`. | | 32 | 2026-10 | `play build` passed `-PversionCode` / `-PversionName` and `android.injected.version.*`, but a Capacitor `app/build.gradle` read versionCode from an env var, so the AAB stayed at versionCode 1 (the post-build check caught it). Checked with AGP 8.13: `-Pandroid.injected.version.code=42` on `versionCode 1` gives an AAB with versionCode 1, so the injected property does not override `defaultConfig` for `bundleRelease`. The Groovy line `versionCode (project.findProperty('versionCode') ?: 1) as Integer` fails ("Value is null": it parses as `versionCode(...) as Integer`); the cast belongs inside the parentheses. | `versionSourceIssues` (cli/lib/gradle.ts) reads `defaultConfig` of the app module's build.gradle(.kts): a literal, an env var or anything but the project property is a warning in `play build` (before Gradle, also in the dry run and `--json` as `versionWarnings`) and in `check`, with the exact line (Groovy `versionCode((project.findProperty('versionCode') ?: ) as Integer)`, `versionName project.findProperty('versionName') ?: ''`; Kotlin `versionCode = (project.findProperty("versionCode") as String?)?.toInt() ?: `, `versionName = project.findProperty("versionName") as String? ?: ""`), both lines built with AGP 8.13; the post-build versionCode check is now a hard stop (the AAB is not copied). `tests/build-sign.test.ts`, `tests/web-assets.test.ts`. | diff --git a/skills/store-pitfalls/SKILL.md b/skills/store-pitfalls/SKILL.md index a0b0765..67b13b4 100644 --- a/skills/store-pitfalls/SKILL.md +++ b/skills/store-pitfalls/SKILL.md @@ -470,3 +470,44 @@ that fails without the fix (see CONTRIBUTING.md). - **Guard:** `tools/desktop-release.ts` (`archProblems` before signing: every Mach-O must contain arm64, no Intel-only file, no Info.plist that forces Intel; `sliceProblems`: every Mach-O in the bundle must carry arm64 and x86_64, nothing x86_64 is run); `tests/desktop-release.test.ts`. + +## 35. Scan findings from a bare word, a module path or a config claim +- **Symptom:** `scan` reports "A reward for rating or reviewing the app" on `const rate = F.genRate(lv, bonus.gen(r));`; + account findings cite `import './buildings/register';`; 5.1.1(v) disappears once `accounts.inApp` is set although + the app has no delete-account code. +- **Cause:** the signals matched "rate" next to "bonus", and "/register'" anywhere on a line; the config's word was + taken as proof. +- **Do:** read every finding's evidence before acting on it. A finding whose evidence is not real code is a scan bug: + report it (and accept it with `scan.ignore` and the owner's reason meanwhile). For 5.1.1(v), show the deletion code + path or screen; a config line alone is a claim, not evidence. +- **Guard:** the incentive signal needs a rate-us / review phrase next to a reward, a reward "for rating", or a reward + next to a system review prompt call; sign-in / sign-up / deletion signals skip import and require lines and accept + only absolute auth paths or URLs; config-only accounts read "from config (accounts.enabled)"; deletion claimed only + by the config stays a medium finding ("claimed by config, not found in code"). `tests/game-report.test.ts`. + +## 36. Privacy answers derived from the wrong source +- **Symptom:** `privacy-manifest --propose` prints an empty NSPrivacyCollectedDataTypes while `appPrivacy.types` lists + collected types; the App Privacy plan adds "Other Usage Data" you never listed; the Play plan answers "uses + advertising ID: Yes" because Data safety declares a Device ID. +- **Cause:** the proposal only kept the manifest's own entries; the App Privacy sheet mixed in types derived from + dataSafety; the advertising ID answer read PSL_DEVICE_ID (a random install id is not the advertising ID). +- **Do:** treat `appPrivacy.types` as the App Privacy answer: a type it does not list is "not collected". Answer the + advertising ID from the SDKs and the merged manifest (`com.google.android.gms.permission.AD_ID`), never from Data + safety alone; set `console.appContent.advertisingId` when the signals disagree. +- **Guard:** `--propose` builds the list from `appPrivacy.types` (linked, tracking, purposes; NSPrivacyTracking true + only with app-level domains, lesson 29) and `check` warns on an empty list; `console plan` scans the project + (ad / attribution SDKs, AD_ID, support and AR code) and answers No with no signal, asks on a conflict. + `tests/game-report.test.ts`. + +## 37. `scan.ignore` that matches nothing; placeholder credentials +- **Symptom:** a `scan.ignore` entry does not accept the finding and nothing says why; commands treat + `YOUR_KEY_ID` or `/absolute/path/to/upload.p12` in `.storebound/credentials.json` as a credential. +- **Cause:** `match` is compared with the finding TITLE, not the evidence; the 0.7.1 credentials template held + example-looking values. +- **Do:** `scan.ignore: [{ rule, reason, match?, evidence?, store? }]`: `match` = text in the finding title (one check + of a rule with several), `evidence` = text in one evidence line as the report prints it (`src/main.ts:12` or a code + fragment); both together narrow further. Never add an entry without the owner's reason. Fill credentials with real + values or leave them empty. +- **Guard:** `scan` (notes) and `check` (warnings) name every entry that accepts no finding; the credentials template + is empty strings; "your-…", `<…>`, "xxx", "/absolute/path/to/…" and "example" values count as missing everywhere + (`check` warns, `doctor` lists them). `tests/game-report.test.ts`. diff --git a/skills/store-preflight/SKILL.md b/skills/store-preflight/SKILL.md index 670585d..95d6ba3 100644 --- a/skills/store-preflight/SKILL.md +++ b/skills/store-preflight/SKILL.md @@ -17,7 +17,7 @@ the stores; run them freely. Privacy / ATT / privacy manifest proposal, each item "detected (file:line)" or "ask owner", as a diff against the config. Exit 1 on an open blocker. `--write` adds only the detected answers to the config (show the diff and ask the owner first; MCP never writes). `--store apple|play`, `--json`. A finding the owner accepts goes into - `scan.ignore: [{ rule, reason, match? }]` (`match` = text in the finding title, to accept one check of a rule with several); never add one without the owner's reason. The Play rules also read + `scan.ignore: [{ rule, reason, match?, evidence? }]` (`match` = text in the finding title, to accept one check of a rule with several; `evidence` = text in one evidence line, e.g. `src/main.ts:12`); never add one without the owner's reason. `scan` and `check` name an entry that matches no finding. The Play rules also read `play.targetAges` (Families), `play.accountType` / `play.accountCreated` (closed-testing requirement) and `play.billingPrograms` (payments): ask the owner for them, never guess. 1. `storebound check`: config, both listing files (limits are checked locally: title 30, subtitle 30, keywords 100 diff --git a/templates/credentials.example.json b/templates/credentials.example.json index 6a6c66f..b2d6db2 100644 --- a/templates/credentials.example.json +++ b/templates/credentials.example.json @@ -1,19 +1,19 @@ { - "PLAY_SERVICE_ACCOUNT": "/absolute/path/to/play-service-account.json", - "ASC_KEY_ID": "YOUR_KEY_ID", - "ASC_ISSUER_ID": "00000000-0000-0000-0000-000000000000", - "ASC_P8": "/absolute/path/to/AuthKey_.p8", - "APPLE_ADS_CLIENT_ID": "SEARCHADS.00000000-0000-0000-0000-000000000000", - "APPLE_ADS_TEAM_ID": "SEARCHADS.00000000-0000-0000-0000-000000000000", - "APPLE_ADS_KEY_ID": "00000000-0000-0000-0000-000000000000", - "APPLE_ADS_PRIVATE_KEY": "/absolute/path/to/apple-ads-private-key.pem", + "PLAY_SERVICE_ACCOUNT": "", + "ASC_KEY_ID": "", + "ASC_ISSUER_ID": "", + "ASC_P8": "", + "APPLE_ADS_CLIENT_ID": "", + "APPLE_ADS_TEAM_ID": "", + "APPLE_ADS_KEY_ID": "", + "APPLE_ADS_PRIVATE_KEY": "", "APPLE_ADS_ACCOUNT_ID": "", - "REVENUECAT_SECRET_KEY": "sk_your_revenuecat_v2_secret_key", - "ANDROID_KEYSTORE": "/absolute/path/to/upload.p12", + "REVENUECAT_SECRET_KEY": "", + "ANDROID_KEYSTORE": "", "ANDROID_KEYSTORE_PASSWORD": "", - "ANDROID_KEY_ALIAS": "upload", + "ANDROID_KEY_ALIAS": "", "ANDROID_KEY_PASSWORD": "", "ASC_VENDOR_NUMBER": "", - "reviewContact": { "firstName": "", "lastName": "", "phone": "+1 555 0100", "email": "review-contact@example.com" }, + "reviewContact": { "firstName": "", "lastName": "", "phone": "", "email": "" }, "reviewDemoAccount": { "username": "", "password": "" } } diff --git a/tests/app-privacy.test.ts b/tests/app-privacy.test.ts index add0438..f27e83a 100644 --- a/tests/app-privacy.test.ts +++ b/tests/app-privacy.test.ts @@ -77,7 +77,8 @@ describe('appPrivacy.types (the owner\'s answers)', () => { expect(a.get('type:Gameplay Content')).toMatchObject({ value: false, certain: true }); expect(a.get('linked:Performance Data')).toMatchObject({ value: false, certain: true }); expect(a.get('tracking:Performance Data')).toMatchObject({ value: null, certain: false }); // not set: (you decide) - expect(a.get('type:Customer Support')).toMatchObject({ value: null, certain: false }); + // appPrivacy.types is authoritative: a type it does not list is not collected (not asked, not derived) + expect(a.get('type:Customer Support')).toMatchObject({ value: false, certain: true, source: 'appPrivacy.types (not listed: not collected)' }); // the Advertising Data dialog gets a section of its own expect(formDef('apple-app-privacy', [...a.values()]).sections.map(s => s.title)).toContain('Advertising Data: purposes'); }); @@ -86,7 +87,7 @@ describe('appPrivacy.types (the owner\'s answers)', () => { const a = answers(c); expect(a.get('collects')).toMatchObject({ value: true, certain: true }); expect(a.get('type:Hands')?.value).toBe(true); - expect(a.get('type:Device ID')).toMatchObject({ value: null, certain: false }); + expect(a.get('type:Device ID')).toMatchObject({ value: false, certain: true }); expect(a.get('linked:Hands')?.certain).toBe(false); const none = { ...base, dataSafety: undefined, appPrivacy: { types: Object.fromEntries(APPLE_DATA_TYPE_IDS.map(id => [id, { collected: false }])) } } as ShipConfig; expect([...answers(none).values()]).toEqual([expect.objectContaining({ id: 'collects', value: false, certain: true })]); @@ -136,7 +137,7 @@ describe('check: appPrivacy.types against the iOS project', () => { r = appPrivacyMismatches({ GAMEPLAY_CONTENT: { collected: true, tracking: false } }, { attString: true, manifest: m(true, []) }); expect(msgs(r)).toContain('warn: Info.plist has NSUserTrackingUsageDescription, but appPrivacy.types marks no collected type as used for tracking'); expect(msgs(r)).toContain('has NSPrivacyTracking true, but appPrivacy.types marks no collected type'); - expect(msgs(r)).toContain('appPrivacy.types.GAMEPLAY_CONTENT is collected, but App/PrivacyInfo.xcprivacy has no NSPrivacyCollectedDataTypeGameplayContent'); + expect(msgs(r)).toContain('App/PrivacyInfo.xcprivacy has an empty NSPrivacyCollectedDataTypes, but appPrivacy.types marks GAMEPLAY_CONTENT as collected'); expect(appPrivacyMismatches({ DEVICE_ID: { collected: true, tracking: true } }, { attString: true, manifest: m(true, ['NSPrivacyCollectedDataTypeDeviceID']) })).toEqual([]); }); test('storebound check: a missing accounts.deletionUrl is a Data safety problem (Play 400 otherwise)', async () => { diff --git a/tests/fixtures/scan/capacitor-game/README.md b/tests/fixtures/scan/capacitor-game/README.md new file mode 100644 index 0000000..5a7d141 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/README.md @@ -0,0 +1,7 @@ +# capacitor-game fixture + +A made-up Capacitor 8 farm game (first release) with the patterns a real-use report found: a module named +`buildings/register` imported for its side effects, production rates next to a `bonus` object, accounts and in-app +deletion claimed only by the config (no auth or deletion code), five App Privacy types in `appPrivacy.types` while the +app's PrivacyInfo.xcprivacy lists none, a random install id declared as Device ID in Data safety, and no ad SDK or +AD_ID permission. Not a buildable project. diff --git a/tests/fixtures/scan/capacitor-game/android/app/src/main/AndroidManifest.xml b/tests/fixtures/scan/capacitor-game/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..dc97ec0 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/fixtures/scan/capacitor-game/capacitor.config.json b/tests/fixtures/scan/capacitor-game/capacitor.config.json new file mode 100644 index 0000000..e5cd94a --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/capacitor.config.json @@ -0,0 +1,5 @@ +{ + "appId": "org.example.farmgame", + "appName": "Example Farm", + "webDir": "dist" +} diff --git a/tests/fixtures/scan/capacitor-game/ios/App/App/Info.plist b/tests/fixtures/scan/capacitor-game/ios/App/App/Info.plist new file mode 100644 index 0000000..2a7a4d3 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/ios/App/App/Info.plist @@ -0,0 +1,10 @@ + + + + + CFBundleDisplayName + Example Farm + ITSAppUsesNonExemptEncryption + + + diff --git a/tests/fixtures/scan/capacitor-game/ios/App/App/PrivacyInfo.xcprivacy b/tests/fixtures/scan/capacitor-game/ios/App/App/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..5704bed --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/ios/App/App/PrivacyInfo.xcprivacy @@ -0,0 +1,23 @@ + + + + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/tests/fixtures/scan/capacitor-game/package.json b/tests/fixtures/scan/capacitor-game/package.json new file mode 100644 index 0000000..071dfe7 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/package.json @@ -0,0 +1,10 @@ +{ + "name": "farm-game", + "private": true, + "dependencies": { + "@capacitor/android": "8.0.0", + "@capacitor/core": "8.0.0", + "@capacitor/ios": "8.0.0", + "@capacitor/preferences": "8.0.0" + } +} diff --git a/tests/fixtures/scan/capacitor-game/src/buildings/register.ts b/tests/fixtures/scan/capacitor-game/src/buildings/register.ts new file mode 100644 index 0000000..e87aff6 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/src/buildings/register.ts @@ -0,0 +1,7 @@ +import { registry } from './registry'; + +export class Farm { level = 1 } +export class Mill { level = 1 } + +registry.register('farm', Farm); +registry.register('mill', Mill); diff --git a/tests/fixtures/scan/capacitor-game/src/economy.ts b/tests/fixtures/scan/capacitor-game/src/economy.ts new file mode 100644 index 0000000..fc258b4 --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/src/economy.ts @@ -0,0 +1,11 @@ +export const bonus = { gen: (r: number) => 1 + r * 0.1 }; +export const F = { + genRate: (lv: number, mult: number) => lv * 2 * mult, +}; + +export function tick(f: typeof F, lv = 1, r = 0): number { + const rate = F.genRate(lv, bonus.gen(r)); + const frameRate = 60; // bonus coins drop every second + const exchangeRate = 10; // coins per gem + return rate * frameRate / exchangeRate; +} diff --git a/tests/fixtures/scan/capacitor-game/src/main.ts b/tests/fixtures/scan/capacitor-game/src/main.ts new file mode 100644 index 0000000..1e75afd --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/src/main.ts @@ -0,0 +1,10 @@ +import './buildings/register'; +import { Preferences } from '@capacitor/preferences'; +import { F, tick } from './economy'; + +export async function boot(): Promise { + const saved = await Preferences.get({ key: 'farm' }); + const installId = saved.value ?? crypto.randomUUID(); + await Preferences.set({ key: 'farm', value: installId }); + setInterval(() => tick(F), 1000); +} diff --git a/tests/fixtures/scan/capacitor-game/storebound.config.ts b/tests/fixtures/scan/capacitor-game/storebound.config.ts new file mode 100644 index 0000000..0ee3bbe --- /dev/null +++ b/tests/fixtures/scan/capacitor-game/storebound.config.ts @@ -0,0 +1,41 @@ +// Fixture config (made up, example.com placeholders only). +export default { + app: { + name: 'Example Farm', + profile: 'game', + privacyPolicyUrl: 'https://example.com/privacy', + supportUrl: 'https://example.com/support', + website: 'https://example.com', + contactEmail: 'support@example.com', + }, + accounts: { enabled: true, deletionUrl: 'https://example.com/delete-account', inApp: 'Settings > Delete account' }, + reviewAccess: { signInRequired: false, instructions: 'No sign-in needed.' }, + languages: [{ id: 'en', play: 'en-US', apple: 'en-US' }], + play: { packageName: 'org.example.farmgame', accountType: 'organization', targetAges: ['13-15', '16-17', '18+'] }, + apple: { bundleId: 'org.example.farmgame', usesNonExemptEncryption: false }, + appPrivacy: { + tracking: false, + types: { + USER_ID: { collected: true, linked: true, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + DEVICE_ID: { collected: true, linked: false, tracking: false, purposes: ['APP_FUNCTIONALITY', 'ANALYTICS'] }, + GAMEPLAY_CONTENT: { collected: true, linked: true, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + PRODUCT_INTERACTION: { collected: true, linked: false, tracking: false, purposes: ['ANALYTICS'] }, + CRASH_DATA: { collected: true, linked: false, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + }, + }, + dataSafety: { + collectsData: true, + encryptedInTransit: true, + data: { + PSL_USER_ACCOUNT: { shared: false, collect: ['account', 'functionality'] }, + PSL_DEVICE_ID: { shared: false, collect: ['functionality', 'analytics'] }, + PSL_OTHER_APP_ACTIVITY: { shared: false, collect: ['analytics'] }, + PSL_CRASH_LOGS: { shared: false, collect: ['analytics'] }, + }, + }, + scan: { + ignore: [ + { rule: 'privacy-labels', evidence: 'src/no-such-file.ts', reason: 'stale entry: matches nothing' }, + ], + }, +}; diff --git a/tests/game-report.test.ts b/tests/game-report.test.ts new file mode 100644 index 0000000..fd181f4 --- /dev/null +++ b/tests/game-report.test.ts @@ -0,0 +1,307 @@ +// A real-use report from a second game (Capacitor monorepo, first release): false positives and gaps in scan, check, +// privacy-manifest, the console answer sheets and init. The patterns are recreated with example code. +import { afterAll, describe, expect, test } from 'bun:test'; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { type AppleDataTypeId, type AppleTypeAnswer, appPrivacyMismatches } from '../cli/lib/app-privacy'; +import type { ShipConfig } from '../cli/lib/config'; +import { validateConfig } from '../cli/lib/config'; +import { consoleAnswers } from '../cli/lib/console-answers'; +import { consolePlan } from '../cli/lib/console-plan'; +import { advertisingIdUse } from '../cli/lib/console-play-plans'; +import { credentialStatus, credentialValue, isPlaceholder } from '../cli/lib/credentials'; +import { collectedFromAppPrivacy, proposeAppManifest } from '../cli/lib/privacy-manifest'; +import { type ProjectSignals, projectSignals } from '../cli/lib/project-signals'; +import { runScan } from '../cli/lib/scan'; +import { detectProject } from '../cli/lib/scan/project'; +import { applyIgnore, type ScanFinding, SIGNALS, rewardNearReviewApi, unusedIgnores } from '../cli/lib/scan/rules'; + +const ROOT = join(import.meta.dir, '..'); +const FX = join(import.meta.dir, 'fixtures', 'scan', 'capacitor-game'); +const tmp = mkdtempSync(join(tmpdir(), 'sb-game-report-')); +afterAll(() => rmSync(tmp, { recursive: true, force: true })); +/** A copy of the fixture with files replaced (path -> text). */ +function variant(name: string, files: Record): string { + const d = join(tmp, name); + cpSync(FX, d, { recursive: true }); + for (const [p, t] of Object.entries(files)) { mkdirSync(join(d, p, '..'), { recursive: true }); writeFileSync(join(d, p), t); } + return d; +} +const fxConfig = readFileSync(join(FX, 'storebound.config.ts'), 'utf8'); + +describe('review-prompt: a bare "rate" next to a reward word is not a reward for rating', () => { + test('negatives: production rates, frame rates, rate limits, exchange rates, code review', () => { + for (const t of [ + 'const rate = F.genRate(lv, bonus.gen(r));', + 'const frameRate = 60; // bonus coins drop at 30 fps', + 'if (rateLimit.hit) return bonus.coins;', + 'const exchangeRate = coins / gems;', + 'rate limit bonus reward', + 'review the code before the bonus round ships', + 'growth rate per level gives bonus coins', + ]) expect({ t, hit: SIGNALS.incentiveRating.test(t) }).toEqual({ t, hit: false }); + }); + test('positives: rate-us phrases next to a reward, rewards for rating', () => { + for (const t of [ + 'Rate us 5 stars and get 100 coins', + 'review the app to earn a reward', + 'Get 50 gems for rating us', + 'rate the game for a bonus', + 'Leave a review and get 20 gems!', + 'Write us a review for free spins', + ]) expect({ t, hit: SIGNALS.incentiveRating.test(t) }).toEqual({ t, hit: true }); + }); + test('a reward granted next to the system review prompt call', () => { + const yes = { path: 'src/rate.ts', text: 'export async function askRating() {\n await InAppReview.requestReview();\n wallet.addCoins(50);\n}\n' }; + expect(rewardNearReviewApi([yes]).map(h => h.line)).toEqual([2]); + const no = { path: 'src/rate.ts', text: 'export async function askRating() {\n await InAppReview.requestReview();\n}\n' + '\n'.repeat(20) + 'wallet.addCoins(50);\n' }; + expect(rewardNearReviewApi([no])).toEqual([]); + }); +}); + +describe('account signals: module paths are not auth code', () => { + test('negatives: import paths and module names with register / login / account', () => { + for (const t of [ + "import './buildings/register';", + "import { registerAll } from './modules/register';", + "export * from './account/login';", + "const m = require('./login');", + "const Screen = lazy(() => import('./screens/register'));", + "loadModule('./buildings/register');", + "registry.register('farm', Farm);", + "router.add({ path: './buildings/signup' });", + ]) expect({ t, up: SIGNALS.signUp.test(t), in: SIGNALS.signIn.test(t) }).toEqual({ t, up: false, in: false }); + }); + test('positives: auth endpoints, SDK calls, credential fields, guest accounts, screen strings', () => { + for (const t of [ + "await fetch(`${API}/auth/register`, { method: 'POST', body });", + "fetch('/api/signup', { method: 'POST' })", + "http.post('https://api.example.com/v1/register', body)", + '', + 'const guest = await createGuestAccount(deviceId);', + '', + 'await supabase.auth.signUp({ email, password })', + ]) expect({ t, hit: SIGNALS.signUp.test(t) }).toEqual({ t, hit: true }); + for (const t of ["fetch('/auth/login', { method: 'POST' })", '', 'await signInWithEmailAndPassword(auth, e, p)']) + expect({ t, hit: SIGNALS.signIn.test(t) }).toEqual({ t, hit: true }); + }); + test('a whole file: the import line is skipped, the real call on a later line still counts', () => { + expect(SIGNALS.signUp.test("import './buildings/register';\nconst x = 1;\n")).toBe(false); + expect(SIGNALS.signUp.test("import './buildings/register';\nawait fetch('/api/register', { method: 'POST' });\n")).toBe(true); + }); +}); + +describe('scan on the Capacitor game fixture', () => { + test('no reward-for-rating finding from production rates; account evidence never an import path', async () => { + const r = (await runScan(FX)).report; + expect(r.findings.filter(f => f.rule === 'review-prompt')).toEqual([]); + const account = r.findings.filter(f => ['account-deletion', 'play-deletion-url', 'demo-account', 'privacy-policy-link'].includes(f.rule)); + expect(account.length).toBeGreaterThan(0); + for (const f of account) for (const e of f.evidence) expect({ rule: f.rule, bad: /import|buildings\/register/.test(e.text) }).toEqual({ rule: f.rule, bad: false }); + const ua = r.declarations.items.find(i => i.what === 'PSL_USER_ACCOUNT')!; + expect(ua.evidence[0]!.text).toBe('from config (accounts.enabled)'); + }); + test('accounts.inApp alone does not clear 5.1.1(v): medium, "claimed by config"', async () => { + const r = (await runScan(FX)).report; + const del = r.findings.find(f => f.rule === 'account-deletion')!; + expect(del).toMatchObject({ severity: 'medium', store: 'apple' }); + expect(del.title).toContain('claimed by config'); + expect(del.evidence[0]!.text).toBe('from config (accounts.inApp: Settings > Delete account)'); + expect(del.fix).toContain('Show the code path or the screen'); + }); + test('deletion code clears it; no config claim and no code is a blocker; an import of a delete-account module is not code', async () => { + const withCode = variant('del-code', { 'src/settings.ts': "export async function deleteAccount(): Promise {\n await fetch('/api/account', { method: 'DELETE' });\n}\n" }); + expect((await runScan(withCode)).report.findings.filter(f => f.rule === 'account-deletion')).toEqual([]); + const noClaim = variant('del-none', { 'storebound.config.ts': fxConfig.replace(", inApp: 'Settings > Delete account'", '') }); + expect((await runScan(noClaim)).report.findings.find(f => f.rule === 'account-deletion')).toMatchObject({ severity: 'blocker', title: 'Accounts can be created but no in-app account deletion was found' }); + const imported = variant('del-import', { 'src/menu.ts': "import './screens/delete-account';\n" }); + expect((await runScan(imported)).report.findings.find(f => f.rule === 'account-deletion')!.severity).toBe('medium'); + }); + test('a scan.ignore entry that matches nothing is reported; evidence matching accepts by the evidence text', async () => { + const r = (await runScan(FX)).report; + expect(r.notes.some(n => n.startsWith('scan.ignore[0] (rule privacy-labels, evidence "src/no-such-file.ts") matches no finding'))).toBe(true); + const by = variant('ignore-evidence', { 'storebound.config.ts': fxConfig.replace("{ rule: 'privacy-labels', evidence: 'src/no-such-file.ts', reason: 'stale entry: matches nothing' }", + "{ rule: 'account-deletion', evidence: 'accounts.inApp', reason: 'deletion is a server-driven screen, checked on a device' }") }); + const r2 = (await runScan(by)).report; + expect(r2.findings.find(f => f.rule === 'account-deletion')!.accepted).toBe('deletion is a server-driven screen, checked on a device'); + expect(r2.notes.filter(n => n.startsWith('scan.ignore['))).toEqual([]); + }); +}); + +describe('scan.ignore: title (match) and evidence', () => { + const f = (): ScanFinding => ({ rule: 'privacy-labels', guideline: '5.1.2', store: 'apple', severity: 'medium', title: 'Data types the SDKs collect are missing', evidence: [{ file: 'src/sdk.ts', line: 7, text: 'Analytics.init(key)' }], fix: 'x', skill: 's', ref: 'https://example.com' }); + test('match keeps matching the title (backward compatible), evidence matches file:line or text, both together narrow', () => { + const cases: [{ rule: string; store?: 'apple' | 'play'; match?: string; evidence?: string; reason: string }, boolean][] = [ + [{ rule: 'privacy-labels', match: 'SDKS COLLECT', reason: 'r' }, true], + [{ rule: 'privacy-labels', match: 'Analytics.init', reason: 'r' }, false], + [{ rule: 'privacy-labels', evidence: 'src/sdk.ts:7', reason: 'r' }, true], + [{ rule: 'privacy-labels', evidence: 'analytics.init', reason: 'r' }, true], + [{ rule: 'privacy-labels', evidence: 'src/other.ts', reason: 'r' }, false], + [{ rule: 'privacy-labels', match: 'missing', evidence: 'src/sdk.ts', reason: 'r' }, true], + [{ rule: 'privacy-labels', match: 'missing', evidence: 'src/other.ts', reason: 'r' }, false], + [{ rule: 'privacy-labels', store: 'play', reason: 'r' }, false], + ]; + for (const [i, yes] of cases) { + const x = f(); + applyIgnore([x], [i]); + expect({ i, accepted: !!x.accepted }).toEqual({ i, accepted: yes }); + } + }); + test('unused entries are named; originality entries are skipped unless that rule ran', () => { + const msgs = unusedIgnores([f()], [{ rule: 'privacy-labels', evidence: 'src/sdk.ts', reason: 'r' }, { rule: 'privacy-label', reason: 'typo' }, { rule: 'originality', reason: 'r' }]); + expect(msgs.length).toBe(1); + expect(msgs[0]).toStartWith('scan.ignore[1] (rule privacy-label) matches no finding'); + }); + test('the config accepts evidence and rejects an empty one', () => { + const base = { app: { name: 'A', privacyPolicyUrl: 'https://example.com/p' }, languages: [{ id: 'en', play: 'en-US', apple: 'en-US' }] }; + const errs = (ignore: unknown[]) => validateConfig({ ...base, scan: { ignore } }).filter(e => e.startsWith('scan.ignore')); + expect(errs([{ rule: 'x', evidence: 'src/a.ts:3', reason: 'r' }])).toEqual([]); + expect(errs([{ rule: 'x', evidence: ' ', reason: 'r' }]).length).toBe(1); + }); +}); + +describe('privacy manifest from appPrivacy.types', () => { + const types: Partial> = { + USER_ID: { collected: true, linked: true, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + DEVICE_ID: { collected: true, linked: false, tracking: true, purposes: ['APP_FUNCTIONALITY', 'THIRD_PARTY_ADVERTISING'] }, + PRODUCT_INTERACTION: { collected: true, linked: false, purposes: ['ANALYTICS'] }, + CRASH_DATA: { collected: true, linked: false, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + GAMEPLAY_CONTENT: { collected: true, linked: true, tracking: false, purposes: ['APP_FUNCTIONALITY'] }, + NAME: { collected: false }, + }; + test('--propose lists every collected type with linked, tracking and purposes; NSPrivacyTracking stays false without domains', () => { + const m = proposeAppManifest({ types, collected: [] }) as { NSPrivacyTracking: boolean; NSPrivacyCollectedDataTypes: Record[] }; + expect(m.NSPrivacyTracking).toBe(false); + expect(m.NSPrivacyCollectedDataTypes.map(e => e.NSPrivacyCollectedDataType)).toEqual(['NSPrivacyCollectedDataTypeGameplayContent', 'NSPrivacyCollectedDataTypeUserID', + 'NSPrivacyCollectedDataTypeDeviceID', 'NSPrivacyCollectedDataTypeProductInteraction', 'NSPrivacyCollectedDataTypeCrashData']); + expect(m.NSPrivacyCollectedDataTypes.find(e => e.NSPrivacyCollectedDataType === 'NSPrivacyCollectedDataTypeDeviceID')).toEqual({ + NSPrivacyCollectedDataType: 'NSPrivacyCollectedDataTypeDeviceID', NSPrivacyCollectedDataTypeLinked: false, NSPrivacyCollectedDataTypeTracking: true, + NSPrivacyCollectedDataTypePurposes: ['NSPrivacyCollectedDataTypePurposeAppFunctionality', 'NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising'] }); + expect((proposeAppManifest({ types, trackingDomains: ['t.example.com'] }) as { NSPrivacyTracking: boolean }).NSPrivacyTracking).toBe(true); + // no types: the manifest's own entries stay + expect((proposeAppManifest({ collected: [{ NSPrivacyCollectedDataType: 'NSPrivacyCollectedDataTypeName' }] }) as { NSPrivacyCollectedDataTypes: unknown[] }).NSPrivacyCollectedDataTypes.length).toBe(1); + }); + test('notes: unanswered fields and manifest entries that appPrivacy.types does not mark collected', () => { + const { notes } = collectedFromAppPrivacy(types, [{ NSPrivacyCollectedDataType: 'NSPrivacyCollectedDataTypeName' }]); + expect(notes).toContain('appPrivacy.types.PRODUCT_INTERACTION.tracking is not answered: wrote false'); + expect(notes.some(n => n.startsWith('NSPrivacyCollectedDataTypeName was in the manifest'))).toBe(true); + }); + test('check: an empty NSPrivacyCollectedDataTypes against collected types is one warning naming --propose', () => { + const out = appPrivacyMismatches(types, { attString: true, manifest: { path: 'ios/App/App/PrivacyInfo.xcprivacy', tracking: false, collectedTypes: [] }, tracking: { trackerSdks: ['Example Ads'] } }); + const w = out.filter(x => x.msg.includes('empty NSPrivacyCollectedDataTypes')); + expect(w).toHaveLength(1); + expect(w[0]!.level).toBe('warn'); + expect(w[0]!.msg).toContain('--propose'); + expect(w[0]!.msg).toContain('USER_ID, DEVICE_ID, PRODUCT_INTERACTION, CRASH_DATA, GAMEPLAY_CONTENT'); + }); + test('the CLI proposes the fixture manifest from the config', () => { + const r = Bun.spawnSync(['bun', join(ROOT, 'cli', 'index.ts'), 'privacy-manifest', '--ios', 'ios/App', '--propose'], { cwd: FX, env: { ...process.env, STOREBOUND_PROFILE: '' } }); + const out = r.stdout.toString(); + expect(r.exitCode).toBe(0); + for (const k of ['UserID', 'DeviceID', 'GameplayContent', 'ProductInteraction', 'CrashData']) expect(out).toContain(`NSPrivacyCollectedDataType${k}`); + expect(out).toMatch(/NSPrivacyTracking<\/key>\s*/); + }); +}); + +const none: ProjectSignals = { adSdks: [], attributionSdks: [], adIdSdks: [], adIdPermission: null, adIdRemoved: null, support: null, ar: null }; + +describe('App Privacy answer sheet: appPrivacy.types is authoritative', () => { + const cfg = async () => (await runScan(FX)).config!; + test('unlisted types are not collected (certain), never derived from dataSafety (PSL_OTHER_APP_ACTIVITY) or asked', async () => { + const a = consoleAnswers('apple-app-privacy', await cfg(), none); + const types = a.filter(x => x.id.startsWith('type:')); + expect(types.filter(x => !x.certain)).toEqual([]); + expect(a.find(x => x.id === 'type:Other Usage Data')).toMatchObject({ value: false, certain: true, source: 'appPrivacy.types (not listed: not collected)' }); + expect(types.filter(x => x.value === true).map(x => x.id).sort()).toEqual(['type:Crash Data', 'type:Device ID', 'type:Gameplay Content', 'type:Product Interaction', 'type:User ID']); + // the same without project signals: authoritative does not need a scan + expect(consoleAnswers('apple-app-privacy', await cfg()).filter(x => x.id.startsWith('type:') && !x.certain)).toEqual([]); + }); + test('without appPrivacy.types: tracking false and no ad SDK default the ad, support and AR types to not collected, unless a signal says otherwise', async () => { + const c = { ...(await cfg()), appPrivacy: { tracking: false } } as ShipConfig; + const val = (s: ProjectSignals | undefined, t: string) => consoleAnswers('apple-app-privacy', c, s).find(x => x.id === `type:${t}`)!; + for (const t of ['Advertising Data', 'Customer Support', 'Environment Scanning', 'Hands', 'Head']) expect(val(none, t)).toMatchObject({ value: false, certain: true }); + expect(val(undefined, 'Advertising Data').certain).toBe(false); + expect(val({ ...none, ar: 'src/ar.swift:3' }, 'Hands').certain).toBe(false); + expect(val({ ...none, ar: 'src/ar.swift:3' }, 'Advertising Data').value).toBe(false); + expect(val({ ...none, support: 'package.json:9' }, 'Customer Support').certain).toBe(false); + expect(val({ ...none, adSdks: ['Google AdMob'] }, 'Advertising Data').certain).toBe(false); + expect(consoleAnswers('apple-app-privacy', { ...c, appPrivacy: { tracking: true } } as ShipConfig, none).find(x => x.id === 'type:Advertising Data')!.certain).toBe(false); + // dataSafety still answers the types it maps (Other Usage Data from PSL_OTHER_APP_ACTIVITY) when there are no types + expect(val(none, 'Other Usage Data')).toMatchObject({ value: true, certain: true }); + }); +}); + +describe('Play "Does your app use advertising ID?" from real signals', () => { + const c = { app: { name: 'Example Farm', profile: 'game' }, play: { packageName: 'org.example.farmgame' }, languages: [], + dataSafety: { collectsData: true, encryptedInTransit: true, data: { PSL_DEVICE_ID: { shared: false, collect: ['functionality'] } } } } as unknown as ShipConfig; + test('a Device ID in Data safety alone is No (certain) after a scan', () => { + expect(advertisingIdUse(c, none)).toMatchObject({ v: false }); + const p = consolePlan('play-advertising-id', c, { signals: none }); + expect(p.answers.find(a => a.id === 'uses')).toMatchObject({ value: false, status: 'certain', label: 'No' }); + expect(p.answers.filter(a => a.id.startsWith('purpose'))).toEqual([]); + }); + test('ad / attribution SDKs, the AD_ID permission, app-ads.txt or the flag say Yes; disagreeing signals ask', () => { + expect(advertisingIdUse(c, { ...none, adSdks: ['Google AdMob'] }).v).toBe(true); + expect(advertisingIdUse(c, { ...none, attributionSdks: ['Adjust'] }).v).toBe(true); + expect(advertisingIdUse(c, { ...none, adIdPermission: 'android/app/src/main/AndroidManifest.xml:3' }).v).toBe(true); + expect(advertisingIdUse({ ...c, console: { appContent: { advertisingId: true } } } as ShipConfig, none).v).toBe(true); + expect(advertisingIdUse({ ...c, console: { appContent: { advertisingId: false } } } as ShipConfig, none).v).toBe(false); + const clash = advertisingIdUse({ ...c, console: { appContent: { advertisingId: false } } } as ShipConfig, { ...none, adSdks: ['Google AdMob'] }); + expect(clash.v).toBeNull(); + expect(clash.note).toContain('disagree'); + expect(advertisingIdUse(c, { ...none, adSdks: ['Google AdMob'], adIdRemoved: 'android/app/src/main/AndroidManifest.xml:4' }).v).toBeNull(); + expect(consolePlan('play-advertising-id', { ...c, console: { appContent: { advertisingId: false } } } as ShipConfig, { signals: { ...none, adSdks: ['Google AdMob'] } }).answers.find(a => a.id === 'uses')!.status).toBe('ask-owner'); + // not scanned: ask, as before + expect(advertisingIdUse(c, undefined).v).toBeNull(); + }); + test('project signals read the AD_ID permission and its removal from the manifests', () => { + expect(projectSignals(detectProject(FX))).toMatchObject({ adSdks: [], adIdPermission: null, adIdRemoved: null, ar: null, support: null }); + const withPerm = variant('ad-id', { 'android/app/src/main/AndroidManifest.xml': '\n\n \n\n' }); + expect(projectSignals(detectProject(withPerm)).adIdPermission).toBe('android/app/src/main/AndroidManifest.xml:3'); + const removed = variant('ad-id-removed', { 'android/app/src/main/AndroidManifest.xml': '\n\n \n\n' }); + expect(projectSignals(detectProject(removed)).adIdRemoved).toBe('android/app/src/main/AndroidManifest.xml:3'); + }); +}); + +describe('credentials: template placeholders are missing, never credentials', () => { + test('the template has only empty values', () => { + const t = JSON.parse(readFileSync(join(ROOT, 'templates', 'credentials.example.json'), 'utf8')) as Record; + const strings = (v: unknown): string[] => typeof v === 'string' ? [v] : v && typeof v === 'object' ? Object.values(v).flatMap(strings) : []; + expect(strings(t).filter(Boolean)).toEqual([]); + }); + test('placeholder-looking values', () => { + for (const v of ['YOUR_KEY_ID', 'your-key', 'sk_your_revenuecat_v2_secret_key', '/absolute/path/to/upload.p12', '/absolute/path/to/AuthKey_.p8', '', 'xxx', 'XXXX-XXXX', + 'SEARCHADS.00000000-0000-0000-0000-000000000000', 'example', 'key-example', 'review-contact@example.com']) + expect({ v, p: isPlaceholder(v) }).toEqual({ v, p: true }); + for (const v of ['ABCDE12345', '00000000-0000-0000-0000-000000000000', 'sk_test_placeholder_secret_value', '/Users/me/keys/upload.p12', 'upload', '-----BEGIN PRIVATE KEY-----\nexample\n-----END PRIVATE KEY-----', '{"client_email":"x@example.com"}', '']) + expect({ v, p: isPlaceholder(v) }).toEqual({ v, p: false }); + expect(isPlaceholder('demo@example.com', { example: false })).toBe(false); + }); + test('the 0.7.1 template in .storebound/credentials.json counts as missing', () => { + const d = join(tmp, 'creds'); + mkdirSync(join(d, '.storebound'), { recursive: true }); + writeFileSync(join(d, '.storebound', 'credentials.json'), JSON.stringify({ + PLAY_SERVICE_ACCOUNT: '/absolute/path/to/play-service-account.json', ASC_KEY_ID: 'YOUR_KEY_ID', ASC_P8: '/absolute/path/to/AuthKey_.p8', + REVENUECAT_SECRET_KEY: 'sk_your_revenuecat_v2_secret_key', ANDROID_KEYSTORE: '/absolute/path/to/upload.p12', ANDROID_KEY_ALIAS: 'upload', ASC_VENDOR_NUMBER: '', + })); + const keep = Object.fromEntries(['PLAY_SERVICE_ACCOUNT', 'ASC_KEY_ID', 'ASC_P8', 'REVENUECAT_SECRET_KEY', 'ANDROID_KEYSTORE', 'ANDROID_KEY_ALIAS', 'STOREBOUND_PROFILE'].map(k => [k, process.env[k]])); + for (const k of Object.keys(keep)) delete process.env[k]; + try { + for (const k of ['PLAY_SERVICE_ACCOUNT', 'ASC_KEY_ID', 'ASC_P8', 'REVENUECAT_SECRET_KEY', 'ANDROID_KEYSTORE']) expect({ k, v: credentialValue(d, k) }).toEqual({ k, v: undefined }); + expect(credentialValue(d, 'ANDROID_KEY_ALIAS')).toBe('upload'); + const st = Object.fromEntries(credentialStatus(d).map(s => [s.key, s.status])); + expect(st).toMatchObject({ PLAY_SERVICE_ACCOUNT: 'placeholder', ASC_KEY_ID: 'placeholder', REVENUECAT_SECRET_KEY: 'placeholder', ANDROID_KEY_ALIAS: 'set', ASC_VENDOR_NUMBER: 'missing', ASC_ISSUER_ID: 'missing' }); + } finally { for (const [k, v] of Object.entries(keep)) if (v !== undefined) process.env[k] = v; } + }); + test('check warns about placeholders, the empty manifest list and an unused scan.ignore entry', () => { + const d = variant('check', { '.storebound/credentials.json': JSON.stringify({ ASC_KEY_ID: 'YOUR_KEY_ID', REVENUECAT_SECRET_KEY: 'sk_your_revenuecat_v2_secret_key' }) }); + const env = { ...process.env }; + for (const k of ['ASC_KEY_ID', 'REVENUECAT_SECRET_KEY', 'STOREBOUND_PROFILE']) delete env[k]; + const r = Bun.spawnSync(['bun', join(ROOT, 'cli', 'index.ts'), 'check'], { cwd: d, env }); + const err = r.stderr.toString() + r.stdout.toString(); + expect(err).toContain('credentials: ASC_KEY_ID, REVENUECAT_SECRET_KEY still hold template placeholders'); + expect(err).toContain('has an empty NSPrivacyCollectedDataTypes'); + expect(err).toContain('scan.ignore[0] (rule privacy-labels'); + }); +});