Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

隐私合规与权限治理

“在强监管时代, 因为隐私合规不达标导致 App 被全面下架的惨痛教训比比皆是. 合规不再是边角料工作, 而是生命线工程.”

面试策略: 如有可核验的 SDK 开发经历, 可结合其中收拢采集入口, 治理第三方 SDK 或设备标识符 (OAID 等) 演进的事实说明经验; 否则以本篇的治理原则, 练习和证据表为准.

一, 隐私合规的 “生死红线”

国内监管, Google Play 与 GDPR 等规则并不以一句绝对口号替代判断. 每项处理须按目的, 数据类型, 适用地区, 合法基础, 面向用户的披露与接收方逐项确认, 并实施最小化, 可撤回, 可审计的工程控制.

  • 隐私弹窗: 用户首次打开 App 时, 应在适用地区, 商店政策, 业务目的和合法基础的框架下展示隐私政策与同意选择. 网络本身不等同于个人信息采集: 例如加载本地同意页不需网络; 必要的安全, 崩溃防护或法律要求的联网能力是否可在同意前运行, 必须经隐私 / 法务评估, 最小化, 披露并留存依据, 不能由开发者一概断言.
  • 默认勾选: 注册 / 登录页面的协议同意框, 绝不能默认帮用户打勾.
  • 超范围收集: 不能因为是一个手电筒 App 就要求读取通讯录和位置信息, 这违反 “最小必要” 原则.
  • 频次限制: 后台高频轮询位置或剪切板是否违规, 取决于适用地区法规与商店政策对最小必要和频率的认定, 应逐项核验并留存依据; 不存在放之四海皆准的固定频次上限.

二, Android 权限治理演进

Android 系统本身的权限管理也顺应合规趋势, 逐年收紧:

权限变化里程碑影响与适配策略
Android 6.0 动态权限危险权限 (相机, 位置等) 不能在安装时一揽子授权, 必须在使用时动态弹窗申请.
Android 10 分区存储限制对 SD 卡的粗放读取. target 29+ 的 App 只能自由访问自身目录与 MediaStore, 读取其他文件需 SAF; 强制范围随 targetSdk 与系统版本而定.
Android 11 单次授权引入 “仅限这一次” 选项. App 进入后台且无前台服务在运行时, 系统会在短暂延迟后收回该权限. 代码里不能缓存授权状态, 每次使用前都要检查.
Android 12 近似位置用户可以选择只给你 “大致位置”(精确度几平方公里) 而不是 GPS 坐标. 位置功能必须兼容这种精度.
Android 13 通知权限发通知不再是默认开启的, 需要显式申请 POST_NOTIFICATIONS 权限.
Android 14 媒体部分授权允许用户只授权照片库中的 “几张指定图片”, 而不是全量相册读取权限.
Android 15 局部屏幕共享 + 敏感内容保护MediaProjection 可限定共享 / 录制单个 App 窗口; 屏幕共享时系统自动隐藏通知内容并支持标记敏感字段不入录屏; 后台身体传感器新增独立权限 BODY_SENSORS_BACKGROUND (API 35).
Android 16 本地网络与通知权限收紧局域网保护为 opt-in (compat flag 开启), 需 NEARBY_WIFI_DEVICES 恢复; 锁屏自动隐藏 OTP 等敏感通知 (API 36); POST_NOTIFICATIONS 未授权时通知仍为静默丢弃 (Android 13 起行为, 16 无新增异常).
Android 17 局域网权限 + OTP 访问延迟新增 ACCESS_LOCAL_NETWORK 运行时权限 (target 37 起默认拦截); 系统级联系人选择器与一次性授权所选联系人; 短信 OTP 程序化读取延迟 3 小时: 该机制源自 Android 16 QPR2, 17 起对 target 37 生效 (API 37, 2026-06 正式版, 部分细则以官方文档核验).

对风控 / 埋点 SDK 的影响: 部分屏幕共享与敏感内容保护会缩小录屏/屏幕采集窗口, 依赖屏幕内容的采集方案需改为逐窗口授权并处理黑屏与系统脱敏; 锁屏 OTP 隐藏与短信 OTP 延迟读取意味着依赖通知/短信验证码的流程需改用 SMS Retriever 等官方通道, 并避免采集已被系统脱敏的通知内容.

三, 唯一 ID 的作用域与隐私边界

唯一 ID 的选择首先由目的决定, 不是先收集再寻找用途. 登录账户, 一次安装, 同一应用数据去重, 广告归因和受管设备管理的标识范围不同, 不应互相替代或拼接为规避用户选择的跟踪方案.

标识类别常见作用域与可变性可接受的判断方向禁止的推断
应用自建安装 ID通常限单次安装; 卸载重装或清除数据后可变化用于本应用内非跨应用的安装级状态, 并定义删除与轮换规则不应承诺跨重装, 跨应用或跨设备稳定识别
Android IDAndroid 8.0+ 新安装时通常按应用签名 key, 用户和设备作用域; OTA/系统升级, 卸载重装, 恢复出厂和签名 key 轮换是不同场景, 不能概括为 “签名变化一般必然改变”. 具体稳定性及备份恢复/轮换能力须按当前官方文档和目标设备验证仅在经批准的应用内目的下评估, 验证多用户/工作资料及上述安装, 升级和恢复场景不将其视为所有 App 共享的永久设备 ID, 也不以其变化或未变化推断用户身份
广告标识符 (AAID/OAID)为广告或归因设计, 具有用户重置, 限制或地区/设备可用性边界仅在适用政策, 用户选择, 披露和接收方合同均满足时使用不得以重置后关联, 弱特征拼接或备用硬件 ID 绕过限制
IMEI, MAC 等硬件标识普通第三方应用通常受限; 例外依赖受管理设备角色, carrier privileges 或 default SMS 等条件仅在明确角色, 权限, 法律与商店政策均允许的受控场景使用不作为一般分析, 广告或设备指纹的默认方案

最小化策略按以下顺序决策: 能否不使用标识符; 能否使用账号或短期会话 ID; 能否缩小到应用内, 目的内和最短留存期; 是否可向用户提供清晰告知, 同意或撤回路径; 是否已限制接收方, 访问权限与二次使用. 每个结论应记录数据类别, 目的, 地区, 合法基础, 保留期, 接收方和删除 / 撤回后的处理.

OAID 是国内生态常见的匿名设备标识符方案, 通常允许用户在系统设置中重置. 可重置不等于天然合规: 使用 OAID 仍须按目的, 数据类型, 地区, 合法基础, 用户选择, 接收方披露, 平台 / 商店政策和留存期逐项确认. 当标识符不可用, 用户拒绝或政策不允许时, 应降级为不依赖跨应用跟踪的功能或聚合统计, 而不是寻找替代硬件标识或组合弱特征.

最小安全示意

以下代码和伪代码只说明空值, 异常, 作用域与降级边界, 不是已经通过隐私, 法务, 商店或地区政策审核的生产实现. 调用前仍须确认目的, 最小化, 告知同意, 留存期和接收方.

// 示意: Android ID 只作为经批准的应用内用途的可选输入.
// Android 8.0+ 新安装通常按应用签名 key, 用户和设备作用域; OTA/系统升级, 卸载重装, 恢复出厂和签名 key 轮换须分别验证.
// 不将 "签名变化" 概括为一般必然改变; 具体稳定性及备份恢复/轮换能力以当前官方文档和目标设备验证为准.
fun readApprovedAndroidId(context: Context): String? = try {
    Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
        ?.takeIf { it.isNotBlank() }
} catch (_: SecurityException) {
    null // 记录不含原值的失败类别, 调用方走无标识符路径.
}
// 示意: 安装 ID 是本应用的随机 UUID, 不从硬件或其他信号推导.
// 存在应用私有存储; 卸载, 清除数据或用户触发的重置会使其失效.
fun installationId(store: SharedPreferences): String {
    store.getString("installation_id", null)?.let { return it }
    return UUID.randomUUID().toString().also { id ->
        check(store.edit().putString("installation_id", id).commit())
    }
}

fun resetInstallationId(store: SharedPreferences) {
    store.edit().remove("installation_id").commit()
}

广告标识符只能通过发布时仍受支持的官方 API 或 SDK 获取, 并尊重用户选择, 地区与商店限制. 不硬编码过时接口, 不将缺失, 限制, 重置或异常解释为可以改用其他设备信号. 以下是失败降级伪代码:

if purpose is not approved or user choice does not permit advertising ID:
    use no advertising identifier
else if current official API/SDK is unavailable, returns unavailable, or reports restricted:
    use no advertising identifier and keep core function available
else:
    use the identifier only for the approved purpose and retention period

不能将 Android ID, 安装 ID, 广告标识符, 网络, 传感器或其他弱信号拼接成设备指纹, 也不能在用户限制, 重置或标识符不可用时寻找替代标识来绕过平台或监管限制. 降级路径应保留核心体验或采用经批准的聚合统计, 不产生跨应用或跨重置关联.

合规的设备标识怎么做

“禁止拼接设备指纹” 只是底线, 正面工程答案是不追求唯一 ID, 而用 “多维信号 + 概率性关联” 完成设备识别:

  • 信号构成: 组合硬件 / 系统 / 行为特征 (系统版本, 语言, 时区, 分辨率, 传感器可用性, 使用节奏等), 不依赖单一唯一 ID. 每类信号按隐私敏感度分级: 低敏感系统特征可低频采集; 高敏感可标识字段 (IMEI, 序列号, 精确位置) 默认不采集或需单独批准.
  • 不可逆处理: 对确需使用的可标识字段做不可逆哈希 / 加盐, 本地存储不落明文, 明文也不出现在日志, 埋点和崩溃附件.
  • 信号漂移与降级: 系统升级, 恢复出厂或权限重置后字段会变化, 设计上要容忍漂移 (同一设备允许部分信号不一致); 采集失败或权限拒绝时降级为更粗粒度信号或仅会话级关联, 不因缺一个信号中断核心功能.
  • 隐私最小化: 按用途收集 (只采风控所需字段), 提供删除 / 撤回路径, 明确保留期, 到期删除.
  • 与服务端风控的分工: 客户端只负责采集信号并上报, 关联 / 打分 / 拉黑等判定由服务端完成; 客户端不承担 “是不是同一人” 的判断, 也不在本地留存完整画像.

与对抗检测的边界: root/模拟器/端侧 Hook 检测输出的是 “环境风险信号”, 属于环境判断, 不等于采集敏感个人信息; 边界在于检测不应顺带读取或上传可标识用户内容, 检测结果本身也按目的限定留存.

四, 第三方 SDK 合规治理

因为第三方 SDK 的违规采集导致主 App 被下架, 这叫 “背锅”.所以大型 App 会对接入的 SDK 进行严苛治理.

  • 延迟初始化: SDK 不能按 “所有 SDK 同意后初始化” 的单一规则处理; 应由同意前允许/禁止/待法务确认矩阵驱动. 广告, 统计, 归因和设备标识 SDK 通常属于同意前禁止项; 安全, 反欺诈, 崩溃防护或履约相关 SDK 是否可早期初始化, 要结合实际数据流, 地区, 合法基础, 披露与最小化评估.
  • 采集点收口: 收拢项目里的定位, 剪切板, 设备信息读取入口, 封装成统一的 Manager 代理. 在 Manager 中增加鉴权, 缓存, 频次控制和合规拦截.
  • 静态扫描与字节码 Hook:
    • 对于不受控的第三方 SDK 或陈旧 Jar, 可在打包期以 ASM 发现 / 拦截已知敏感调用, 并使其进入受审计代理; 不能仅靠 “返回空值” 伪装授权, 也不能把 Hook 当成披露, 数据流审查或 SDK 治理的替代品.

同意门禁时序与敏感 API Manager

下面是伪代码, 描述门禁状态机而非可编译的 Android 实现. 它区分必要联网与采集目的, 具体状态, 数据字段和保留期要经过对应地区的隐私 / 法务复核.

app launch
  -> load local policy text and saved consent record
  -> consent unknown: show choice; initialize only policy matrix marked "allowed" SDKs
  -> consent granted: record policy version/time/source; initialize only matrix-approved, disclosed SDKs
  -> consent declined: start core non-collection experience; keep optional SDKs disabled
  -> consent withdrawn: stop future optional collection, notify SDKs, clear eligible local data,
     create auditable completion record and show the remaining service limitations

以下为上下文片段, Manager 是唯一业务调用入口; 省略权限请求 UI, 数据删除实现, 审计后端和地区策略注入. 它不应用 “返回空值” 伪装权限已授予, 而是让调用方处理明确状态.

sealed interface SensitiveResult<out T> {
    data class Allowed<T>(val value: T) : SensitiveResult<T>
    data object ConsentRequired : SensitiveResult<Nothing>
    data object PermissionRequired : SensitiveResult<Nothing>
    data object Unavailable : SensitiveResult<Nothing>
}

interface SensitiveApiManager {
    suspend fun currentCoarseLocation(purpose: String): SensitiveResult<Location>
}

Manager 应校验已批准目的, 同意状态, 运行时权限, 频次 / 前后台条件和地区策略, 并输出不含原始敏感值的审计事件. ASM 只能作为发现或防御深层 SDK 直连的补充, 不能替代 SDK 合同, 数据流审查和运行时验证; 其工程实现边界见 AGP 插件与字节码工程.

同意前初始化矩阵

矩阵是发布配置的一部分, 由地区策略, SDK 版本和数据流清单共同生成; 新增 SDK, 字段, 域名或目的时必须重新评审. 待法务确认 不得被初始化代码当作允许.

类别 / 示例数据与目的同意前状态初始化控制与所需证据
本地隐私页, 用户选择存储本地政策版本, 选择状态; 展示与记录选择允许不联网或不上传; 保留版本, 时间和策略版本审计
广告, 统计, 归因, OAID/AAID SDK标识符, 事件, 广告归因禁止延后类加载/初始化/网络调用; 同意后仍核对地区, 披露, 合法基础和商店政策
核心功能 SDK为用户请求的功能所必需的数据待法务确认逐地区确认合法基础, 最小字段, 披露, 接收方和留存; 未批准走本地降级路径
安全, 反欺诈, 崩溃防护 SDK安全事件, 诊断数据待法务确认证明必要性与比例性, 限制字段 / 网络与保留期; 批准前不采集非必要标识或内容
法律义务或受管设备能力法定字段或企业设备管理数据待法务确认确认适用义务 / 设备角色, 告知与审计; 不能以此扩展一般跟踪

披露与拒绝流程模板

披露模板 (文本模板, 须由隐私 / 法务批准后替换): 我们为 <REPLACE_WITH_PURPOSE> 在 <REPLACE_WITH_TRIGGER> 收集 <REPLACE_WITH_DATA_CATEGORY>,发送给 <REPLACE_WITH_RECIPIENT>,保留 <REPLACE_WITH_RETENTION>;您可在 <REPLACE_WITH_SETTINGS_PATH> 拒绝或撤回. 不要将模板中的占位符直接发布.

权限拒绝流程: 解释当前功能为何需要权限 -> 用户拒绝后保持无权限可用的核心路径或明确功能受限 -> 用户主动再次触发时再请求 -> shouldShowRequestPermissionRationale 为 false 且未授权时, 引导至系统设置但不循环弹窗 -> 从设置返回和每次敏感操作前重新检查权限. 永久拒绝, 单次授权, 部分照片授权和 AppOps 限制均可能让 “曾授权” 失效.

五, 敏感行为的安全保障

  • 剪切板滥用:
    • iOS 14 在顶部弹出横幅提示, Android 12 在底部弹出 toast 提示.
    • 如果应用为了淘口令等功能无脑轮询读取剪切板, 会让用户极其反感甚至遭遇投诉. 正确做法是切前台时且发现特定格式的文本才进行解析.
  • 日志安全 / 脱敏:
    • 首先禁止非必要原值进入日志, 埋点和崩溃附件, 不以 “加密后可采” 为默认理由. 确有已批准诊断目的时, 才在权限控制, 用途限定, 最短留存, 访问审计与密钥管理下受控加密; 密码, 认证凭据和密钥不应记录.
  • 权限被拒处理:
    • 不能因为用户拒绝了位置权限, 就不让用户使用 “扫一扫” 功能, 不能搞权限捆绑.

六, 平台政策与 SDK 合规工程化清单

隐私合规面试不要只背权限名, 要能讲 “政策披露 + 技术门禁 + 审计证据”.

国内与海外披露材料

场景需要准备什么重点
国内应用商店 / 监管个人信息清单, 第三方 SDK 清单, 权限使用说明, 注销账号入口采集目的, 频率, 共享对象, 撤回同意
Google PlayData Safety, 权限声明, SDK Index 风险提示数据类型, 用途, 是否共享, 是否加密传输
广告/归因Privacy Sandbox, SDK Runtime, AAID/OAID 合规说明不依赖不可重置标识, 用户可选择和撤回

SDK Runtime / Privacy Sandbox 怎么答

Privacy Sandbox 的方向是减少跨 App 跟踪, 把广告归因, 受众, SDK 隔离等能力平台化. 面试中不需要背 API, 但要表达趋势: 第三方 SDK 权限会被进一步收紧, 宿主 App 需要清楚 SDK 采集什么, 什么时候初始化, 数据发到哪里.

SDK 合规门禁

  1. 接入前审查: SDK 来源, 版本, 隐私政策, 采集字段, 域名, 权限, 包体积, 初始化成本.
  2. 初始化门禁: 按同意前允许/禁止/待法务确认矩阵控制 SDK; 未批准或用户拒绝时提供降级路径.
  3. 敏感 API 收口: 定位, 剪切板, 设备标识, 应用列表, 传感器等统一代理.
  4. 静态扫描: 扫描 Manifest 权限, 敏感 API 调用, 第三方域名, 隐私政策披露是否一致.
  5. 运行时审计: 记录权限请求来源, SDK 初始化时间, 敏感 API 调用栈, 网络域名.
  6. 撤回同意: 用户撤回后停止采集, 清理本地缓存, 通知 SDK 更新状态.

权限排查方法

  • Runtime permission: 检查是否按场景申请, 是否处理拒绝 / 永久拒绝.
  • AppOps: 排查系统层是否允许对应操作, 有些权限 “有授权但 AppOps 被限制” 仍会失败.
  • Photo Picker / 部分媒体授权: 不要假设拿到一次媒体权限就能遍历全相册.
  • 前台服务类型/精确闹钟/通知权限: 不仅要 Manifest 声明, 还要运行时路径和用户开关都正确.

七, 地区, 日期与法律复核

隐私法规, Android 权限, 应用商店政策和第三方 SDK 条款都具有地区与时间属性. 本章只提供工程检查框架, 不构成法律意见. 上线前应记录:

  • 目标国家/地区, 应用商店, OS/API 与 targetSdk.
  • 处理数据的目的, 字段, 合法基础 / 同意方式, 保留期和接收方.
  • SDK 名称, 版本, 数据流, 初始化时机和退出 / 删除路径.
  • 法务/隐私/安全 owner 的复核日期与批准记录.
  • 政策变化后的重新评估, 远程停用和删除补偿方案.

最后核验: 2026-08-07. 任何具体法规期限, 权限行为和商店要求都应在发布日重新核验官方文本.

一手政策来源, 适用地区与核验日期

来源用于核对适用地区 / 范围核验日期
Android permissions 与 TelephonyManager#getImei运行时权限及 IMEI 的受限角色 / API 行为Android 平台; 实际行为随 API, targetSdk 和设备角色变化2026-08-07
Google Play User Data policy 与 Data safety数据披露, 收集 / 共享与商店上架要求在 Google Play 分发的应用2026-08-07
GDPR(EUR-Lex Regulation (EU) 2016/679)合法基础, 透明度, 目的限制与数据主体权利欧盟 / 欧洲经济区及法规适用的处理活动2026-08-07
个人信息保护法 (中国政府网)个人信息处理, 告知同意与个人权利中华人民共和国境内及法律规定的域外处理2026-08-07

合规门禁自测与预期证据

  1. 在每个目标地区加载未同意, 已同意, 拒绝和撤回策略, 尝试初始化每类 SDK; 预期证据为初始化审计仅出现矩阵 “允许” 项, 待法务确认 和 “禁止” 项没有类初始化, 敏感 API 或网络域名记录.
  2. 为广告 / OAID SDK 与安全 SDK 分别抓取测试流量和字段清单; 预期证据为前者在同意前零请求, 后者仅在审批矩阵允许时发送经批准的最小字段, 并能关联政策版本, 合法基础, 披露和保留期记录.
  3. 触发诊断错误并检查日志, 埋点和崩溃附件; 预期证据为不含手机号, 证件号, 认证凭据, 设备标识等不必要原值. 若存在批准的受控加密日志, 应有访问授权, 留存删除和审计记录.
  4. 用普通第三方应用, default SMS/carrier privilege/device owner/profile owner 的测试配置分别覆盖设备标识路径; 预期证据为普通应用不依赖 IMEI, 受限角色仅在授权角色和适用策略下调用, 且不改变 OAID 的合规评估结论.

高频面试题

Q1: 在用户点击同意隐私政策前, App 到底能不能访问网络? 不能给出脱离地区, 目的和披露的绝对答案. 可选统计, 广告, 设备标识和未披露 SDK 初始化应在同意门禁后; 必要安全, 崩溃防护或履行服务所需的联网活动是否可在同意前发生, 应由隐私 / 法务按适用政策确认, 做到最小化, 可审计和透明披露. 开发者不能以 “预热” 为由绕过门禁.

Q2: 怎么防止项目中接入的第三方广告 SDK 在后台偷读用户剪切板或位置信息?

  1. 商务层面: 选用正规, 经过官方审核的版本, 并在应用内隐私政策明确披露该 SDK.
  2. 工程层面: 不在后台启动和保活该 SDK 所在的组件; 通过 ASM 字节码插桩, 在打包期间将该 SDK 调用敏感 API 的方法替换为我们自己的代理方法, 在代理中加入合规状态判断和频次拦截.

Q3: Android 10+ 无法获取 IMEI 后, 风控和广告归因怎么做? 不能将 OAID/AAID 或 “弱特征组合” 描述为天然合规的万能替代. 广告标识, 设备信号和归因 SDK 的使用须按地区, 平台政策, 用户选择与披露逐项确认; 风控应采用目的限定, 最小化, 保留期和人工复核等治理措施, 避免依赖不可重置或规避用户选择的跟踪标识.

Q4: 第三方 SDK 合规治理怎么落地? 答: 先做接入前审查, 明确 SDK 采集字段, 权限, 域名, 初始化时机和隐私披露; 工程上用初始化门禁保证同意前只启动矩阵明确允许的 SDK, 禁止项和待法务确认项不启动. 用户同意也不自动授权超出已披露目的的处理; 用敏感 API 代理 / ASM 扫描收口调用, 运行时记录权限请求和网络域名. 用户撤回同意后要停止采集并通知 SDK 降级.

Q5: Google Play Data Safety 或国内个人信息清单怎么和代码对应? 答: 不能只靠法务文案. 要把权限, SDK, 接口域名, 采集字段和代码调用点建立映射, 通过静态扫描和运行时审计证明 “文档披露的就是代码实际做的”.

Q6: 风控 SDK 怎么做设备标识才合规? 答: 不用唯一 ID 拼接, 而是 “多维信号 + 概率性关联”: 客户端按用途采集硬件 / 系统 / 行为特征, 对可标识字段做不可逆哈希 / 加盐, 本地不落明文; 容忍系统升级 / 重置带来的信号漂移, 采集失败或权限拒绝时降级为粗粒度信号; 明确保留期和删除路径; 判定交给服务端, 客户端只上报信号不承担 “是否同一人” 的判断. root/模拟器/Hook 检测属于环境风险信号, 本身不等于采集敏感数据, 但要限定目的与留存.

易错点 / 追问

  • 粗暴对待权限拒绝: 用户拒绝权限后不断弹窗骚扰, 或者直接 finish() 应用, 这会导致极差体验并被商店警告.
  • 缓存单次授权状态: 在 Android 11+ 把获得的权限记录在 SharedPreferences 中, 下次直接用, 结果权限已被系统回收导致崩溃.
  • 过度索权: 开发图省事, 不管用不用, 先把 Manifest 里的权限申请一大堆, 上架时很容易被拒.