Administrator
发布于 2026-08-26 / 35 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 83 篇:Android 用户数据采集合规:知情同意与最小必要

前言

上一篇聊了应用市场审核的全流程,本篇单挑最硬的一块——用户数据采集合规。PIPL、GDPR、Google Play Data Safety Form 都强制要求"知情同意"和"最小必要"原则。这些不是法律免责声明的事,而是要在代码层面改出来的工程实践。

一、合规三原则

原则

含义

工程要点

知情同意

收集前告诉用户收集什么、为什么

弹窗 + 用户主动 opt-in

最小必要

只收集不收集不到就不能干活的

业务上能不要的就不要

公开透明

政策可读、字段清楚

协议页 + 数据采集清单

不通过国内/海外审核的 80% 问题,都出在这三条上。

二、用户数据分类

2.1 个人信息

任何能识别用户身份的数据:

类型

例子

合规等级

设备标识

IMEI、MAC、Android ID

高敏感(注意 Android 12+ 限制)

联系方式

电话、邮箱

高敏感

身份信息

身份证、姓名

极高敏感

生物特征

人脸、指纹

极高敏感

位置

GPS、IP、基站

高敏感

行为数据

点击、停留

中敏感

2.2 设备数据(不算个人信息)

类型

例子

合规等级

设备型号、系统版本

小米 14、Android 14

低

应用版本

App 1.0.0

低

网络环境

WiFi/4G

中

设备设置

字体大小、主题

低

2.3 派生数据

类型

例子

合规等级

用户画像

兴趣标签

高敏感

统计结果

DAU、留存

低

模型输出

AI 生成内容

中

三、知情同意的工程实现

3.1 多阶段同意

最常见的合规模式是"分阶段弹窗":

class PrivacyManager(private val context: Context) {
    // 基础协议
    fun checkBaseConsent(): ConsentState {
        val accepted = DataStoreManager.getBool("base_consent")
        return ConsentState.ACCEPTED.takeIf { accepted }
            ?: ConsentState.NOT_ACCEPTED
    }

    // 单独 / 精细同意(如"是否允许设备识别")
    fun checkExtraConsent(key: String): ConsentState {
        val accepted = DataStoreManager.getBool("extra_$key")
        return ConsentState.ACCEPTED.takeIf { accepted } ?: ConsentState.NOT_ACCEPTED
    }

    fun recordConsent(key: String, accepted: Boolean) {
        DataStoreManager.setBool("base_$key", accepted)
    }
}

同意项

触发时机

基础协议

首次启动

推送通知

进入通知功能时

位置信息

进入地图/导航功能时

相机/麦克风

进入拍照/录音功能时

广告归因

首次启动后 24h 单独弹窗

3.2 同意弹窗设计要点

项目

要求

弹窗位置

启动后、SDK 初始化前

内容完整

列出所有收集项、列出 SDK

用户动作

同意/不同意按钮均可点

不预选

"同意"按钮不可默认勾选

不同意后果

明确说明只能使用基础功能

3.3 用户撤回

GDPR 给了用户撤回同意的权利。每个同意项必须有"撤销"功能:

val settingsComposable: @Composable (() -> Unit) = {
    Column {
        SwitchPreference(
            title = "个性化推荐",
            checked = PrivacyManager.checkExtraConsent("personalization"),
            onChange = { checked ->
                PrivacyManager.recordConsent("personalization", checked)
                if (!checked) PersonalizationEngine.clearUserProfile()
            }
        )
        SwitchPreference(
            title = "分析数据上报",
            checked = PrivacyManager.checkExtraConsent("analytics"),
            onChange = { checked ->
                PrivacyManager.recordConsent("analytics", checked)
                if (!checked) AnalyticsSDK.disable()
            }
        )
    }
}

用户撤回时,立刻停止相关 SDK 的采集、清理服务端缓存。

四、数据采集的工程约束

4.1 设备标识的获取

// 错误:直接拿 IMEI
val imei = telephonyManager.getDeviceId()

// 错误:拿 MAC 地址
val mac = wifiManager.connectionInfo.macAddress

// 推荐:用 Android ID + 安装 ID
val androidId = Settings.Secure.getString(
    contentResolver, Settings.Secure.ANDROID_ID
)
val installId = DataStoreManager.getString("install_id")

IMEI 和 MAC 在 Android 8+ 已经不可读,强行读取会被市场扫描拦截。推荐用 Android ID + 安装 UUID 组合作为设备标识。

4.2 上报数据的字段过滤

object DataFilter {
    fun filter(event: JSONObject): JSONObject {
        val allowed = listOf("event_id", "timestamp", "page_id", "action_type")
        val filtered = JSONObject()
        event.keys().asSequence().forEach { key ->
            if (key in allowed) {
                filtered.put(key, event.get(key))
            }
        }
        return filtered
    }
}

所有上报事件,先经过过滤器去掉敏感字段。

4.3 用户数据存储

数据类型

存储方式

加密

用户登录凭证

EncryptedSharedPreferences

是

行为数据

本地 SQLite + 加密

否(行为数据不算敏感)

PII 数据

EncryptedSharedPreferences + 后端

是

令牌 Token

KeyStore

是

示例:

val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()

val prefs = EncryptedSharedPreferences.create(
    context, "secure_prefs", masterKey,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV
)

prefs.edit().putString("token", "...").apply()

五、第三方 SDK 的数据流控制

每个第三方 SDK 都是潜在合规风险——它们也可能在悄悄上报。工程上做"白名单+过滤器":

class SdkWhitelist {
    private val blocked = setOf("e.163.com", "track.adnxs.com")
    private val allowedSdks = setOf("Bugly", "JPush", "UMeng")

    fun shouldAllow(host: String): Boolean {
        return blocked.none { host.contains(it) }
    }

    fun isSdkInitialized(name: String): Boolean {
        return allowedSdks.contains(name)
    }
}

实际做法:

控制点

实现

网络请求拦截

OkHttp Interceptor 拦非白名单 host

SDK 启动延迟

所有 SDK 在同意后才 init

用户上传字段审计

CI 中 grep SDK 路径+读源

出端 SDK 字符串扫描

静态扫描第三方库的字符串

六、海外合规要点(GDPR / CCPA)

海外合规更严,要求更高:

要求

实现

同意框无预选

Clear & Affirmative Action

拒绝与同意同样容易

同位置按钮

Cookie Banner

进入 EU 时单独弹

13岁以下禁止(COPPA)

Age Gate

数据删除请求

30 天内响应

跨境数据传输

SCC / BCR

// 同意工具
val consent = UserCentrics.getConsentStatus()
// 0 = 没决定,1 = 同意,2 = 拒绝

建议海外版本用合规工具:OneTrust / UserCentrics / Cookiebot,每个合规小项预先配置。

七、儿童模式合规

针对 13 岁以下用户的 App(教育、儿童类),必须:

  1. 不能 SDK 上报

  2. 不能做任何广告

  3. 不能定位

  4. 不能社交

  5. 不能有引导付费

Google Play 要求 Designed for Families 标签:

if (isKidsMode()) {
    PrivacyManager.disableAllSdks()
    AdsLoader.disable()
    LocationService.disable()
}

八、合规自检清单(CI 集成)

// privacy-lint.gradle
val allowedPermissions = setOf(
    "android.permission.INTERNET",
    "android.permission.ACCESS_NETWORK_STATE"
)
val declared = parseManifestPermissions()
declared.filterNot { it in allowedPermissions }
    .forEach { Lint.warn("Unexpected permission: $it") }

CI 跑 gradle lint privacyCheck,合规字段漂移立刻报警。

九、合规事故的应对

如果已经违规(泄露数据、被通报):

  1. 24h 内:停止相关 SDK / 关闭功能

  2. 72h 内:内部排查根因

  3. 7 天内:通知监管部门和受影响用户(PIPL 要求)

  4. 修复:修改代码 + 升级 SOP + 合规培训

PIPL 第 57 条规定:违反个人信息处理义务,造成严重后果的,由省级以上履行个人信息保护职责的部门责令改正,没收违法所得,并处 5000 万以下或者上一年度营业额 5% 以下罚款。

十、一个完整的合规工程设计

启动流程:
1. App.onCreate
   └─ 仅做最低运行(不初始化任何 SDK)
2. 检查用户同意
   ├─ 还没决定:跳到隐私弹窗
   └─ 已同意:继续
3. 初始化基础 SDK
   ├─ 网络、推送、崩溃监控
   └─ 同步 Privacy 中心状态
4. 后续业务功能按需开启 SDK
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        // 不再这里初始化任何 SDK
        PrivacyGate.start {
            if (it) {
                Bugly.init(this)
                JPush.init(this)
            }
        }
    }
}

小结

合规不是一次改完的事,是持续建设的工程能力。把"用户数据生命周期"画出来——采集、上报、存储、查询、删除,每个环节都有 SOP、可审计、可回滚。系统化做半年后,审核、市场、用户、监管四方都可以交代得过去。

下一篇聊 Android Studio Memory Insights——内存抖动之后的下一步:怎么用 Android Studio 自带工具做深度分析。

参考阅读

  • 个人信息保护法(PIPL)原文

  • GDPR 官方法律文本

  • Google Play:Data Safety Form


评论