[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7491":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},7491,"PrivacySentry","allenymt\u002FPrivacySentry","allenymt","Android隐私合规整改检测工具，注解+Asm修改字节码的检测方案","",null,"Kotlin",2277,302,21,18,0,7,60.14,"MIT License",false,"main",true,[],"2026-06-12 04:00:33","# PrivacySentry\n\n[![](https:\u002F\u002Fjitpack.io\u002Fv\u002Fallenymt\u002FPrivacySentry.svg)](https:\u002F\u002Fjitpack.io\u002F#allenymt\u002FPrivacySentry)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-blue.svg)](LICENSE)\n[![API](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAPI-19%2B-brightgreen.svg?style=flat)](https:\u002F\u002Fandroid-arsenal.com\u002Fapi?level=19)\n\n> Android 隐私合规检测工具，可规避应用市场上架合规检测的大部分问题\n\n## ✨ 核心特性\n\n- ✅ **编译期字节码插桩**：基于 ASM + Booster 框架，零运行时性能损耗\n- 🎯 **完整的拦截方案**：支持 60+ 敏感 API 拦截，覆盖工信部合规要求\n- 📊 **自动化检测报告**：生成 Excel 格式的详细调用记录和统计分析\n- 🔄 **智能缓存机制**：内存\u002F磁盘多级缓存，优化性能\n- 🌐 **多进程支持**：自动处理多进程场景，独立输出日志\n- 🔧 **灵活可扩展**：支持自定义拦截规则，黑名单配置\n\n## 📚 文档导航\n\n- **[快速开始](#快速开始)** - 5 分钟集成使用\n- **[插件配置详解](#插件配置详解)** - 完整配置说明\n- **[SDK 初始化](#sdk-初始化)** - 运行时配置\n- **[自定义拦截](#自定义拦截)** - 扩展拦截规则\n- **[架构文档](.\u002Fdocs\u002Farchitecture.md)** - 完整技术架构解析\n- **[开发指南](.\u002FCLAUDE.md)** - Claude Code 开发指南\n\n## 📱 社区支持\n\n加作者个人微信，备注来意 PrivacySentry，进社区群\n\n\u003Cimg width=\"290\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fallenymt\u002FPrivacySentry\u002Fassets\u002F8003195\u002F76f2124e-f58d-4420-ac2d-8d33b1093907\">\n\n## 🚀 快速开始\n\n### 版本要求\n\n| 组件 | 版本要求 |\n|------|---------|\n| **AGP** | 8.0+ (推荐 8.2.0) |\n| **Gradle** | 8.0+ |\n| **Kotlin** | 1.8.10+ |\n| **minSdk** | 19+ |\n| **compileSdk** | 34+ |\n\n> **注意**：AGP 8.0 以下版本请使用 `1.3.6` 版本\n\n### Step 1: 添加插件依赖\n\n在项目根目录的 `build.gradle` 中添加：\n\n```gradle\nbuildscript {\n    repositories {\n        maven { url 'https:\u002F\u002Fjitpack.io' }\n        mavenCentral()\n        google()\n    }\n\n    dependencies {\n        classpath 'com.github.allenymt.PrivacySentry:plugin-sentry:1.3.7_v820_beta4'\n    }\n}\n\nallprojects {\n    repositories {\n        maven { url 'https:\u002F\u002Fjitpack.io' }\n        mavenCentral()\n        google()\n    }\n}\n```\n\n### Step 2: 应用插件和依赖\n\n在 app 模块的 `build.gradle` 中：\n\n```gradle\n\u002F\u002F 应用插件\napply plugin: 'privacy-sentry-plugin'\n\ndependencies {\n    def privacyVersion = \"1.3.7_v820_beta4\"\n\n    \u002F\u002F 核心库（必须）\n    implementation \"com.github.allenymt.PrivacySentry:hook-sentry:$privacyVersion\"\n    implementation \"com.github.allenymt.PrivacySentry:privacy-annotation:$privacyVersion\"\n\n    \u002F\u002F 预置拦截实现（强烈推荐）\n    implementation \"com.github.allenymt.PrivacySentry:privacy-proxy:$privacyVersion\"\n\n    \u002F\u002F 类替换功能（已废弃，不推荐使用）\n    \u002F\u002F implementation \"com.github.allenymt.PrivacySentry:privacy-replace:$privacyVersion\"\n}\n```\n\n### Step 3: 插件配置\n\n在 app 模块的 `build.gradle` 中添加 `privacy` 配置块：\n\n```gradle\nprivacy {\n    \u002F\u002F ========== 核心配置 ==========\n\n    \u002F**\n     * 插件功能总开关\n     * 类型：Boolean\n     * 默认值：true\n     * 说明：控制 PrivacySentry 插件是否生效\n     *      - true: 启用插件，执行字节码转换\n     *      - false: 禁用插件，相当于未集成\n     *\u002F\n    enablePrivacy = true\n\n    \u002F**\n     * 黑名单配置\n     * 类型：Set\u003CString>\n     * 默认值：null (空列表)\n     * 说明：指定不进行字节码修改的包名列表\n     *      - 适用场景：\n     *        1. 使用了其他 ASM 字节码修改工具的三方库（如高德地图）\n     *        2. 已知会导致冲突的 SDK\n     *        3. 不需要监控的系统库或三方库\n     *      - 注意：blackList 中的包不会被插件修改，也无法拦截其中的敏感 API\n     *\u002F\n    blackList = []\n\n    \u002F\u002F 常见黑名单配置示例：\n    \u002F\u002F blackList = [\n    \u002F\u002F     \"com.loc\",              \u002F\u002F 高德地图（ASM 版本冲突）\n    \u002F\u002F     \"com.amap.api\",         \u002F\u002F 高德地图 API\n    \u002F\u002F     \"io.openinstall.sdk\",   \u002F\u002F OpenInstall SDK\n    \u002F\u002F     \"com.google.android\",   \u002F\u002F Google 服务（可选）\n    \u002F\u002F     \"androidx\"              \u002F\u002F AndroidX 库（可选）\n    \u002F\u002F ]\n\n    \u002F**\n     * 静态扫描结果文件名\n     * 类型：String\n     * 默认值：\"privacy_hook.json\"\n     * 说明：记录所有被代理的方法名和类名的文件名\n     *      - 文件位置：项目根目录\n     *      - 文件格式：JSON\n     *      - 内容包含：\n     *        1. hookServiceList: 被 hook 的 Service 列表\n     *        2. replaceMethodMap: 被替换的方法映射表\n     *      - 设置为 null 或空字符串则不生成文件\n     *\u002F\n    replaceFileName = \"privacy_hook.json\"\n\n    \u002F\u002F ========== 反射 Hook 配置（可选）==========\n\n    \u002F**\n     * 反射方法 Hook 开关\n     * 类型：Boolean\n     * 默认值：false\n     * 说明：是否拦截通过反射调用的敏感方法\n     *      - true: 拦截反射调用（需配合 reflexMap 使用）\n     *      - false: 不拦截反射调用\n     *      - 适用场景：\n     *        1. 三方 SDK 通过反射获取设备信息（如小米 OAID）\n     *        2. 极光推送、个推、穿山甲等 SDK 的设备标识获取\n     *      - 性能影响：轻微（仅影响 LDC 指令的匹配）\n     *\u002F\n    hookReflex = false\n\n    \u002F**\n     * 反射拦截配置映射\n     * 类型：Map\u003CString, List\u003CString>>\n     * 默认值：null\n     * 说明：配置需要拦截的反射调用\n     *      - Key: 类的全限定名\n     *      - Value: 该类中需要拦截的方法名列表\n     *      - 只有 hookReflex = true 时才生效\n     *      - 匹配原理：检测字节码中的 LDC 指令加载的字符串常量\n     *\u002F\n    reflexMap = [:]\n\n    \u002F\u002F 反射拦截配置示例：\n    \u002F\u002F reflexMap = [\n    \u002F\u002F     \u002F\u002F 小米设备标识服务\n    \u002F\u002F     \"com.android.id.impl.IdProviderImpl\": [\n    \u002F\u002F         \"getOAID\",   \u002F\u002F 开放匿名设备标识符\n    \u002F\u002F         \"getAAID\",   \u002F\u002F 应用匿名设备标识符\n    \u002F\u002F         \"getVAID\"    \u002F\u002F 开发者匿名设备标识符\n    \u002F\u002F     ],\n    \u002F\u002F     \u002F\u002F 自定义类的反射方法\n    \u002F\u002F     \"com.example.utils.DeviceUtils\": [\n    \u002F\u002F         \"getDeviceId\",\n    \u002F\u002F         \"getIMEI\"\n    \u002F\u002F     ]\n    \u002F\u002F ]\n\n    \u002F\u002F ========== 已废弃功能（不推荐使用，仅供参考）==========\n\n    \u002F**\n     * 字段 Hook 开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：几乎没有业务场景，功能不稳定\n     * 说明：是否 hook 字段访问（如 Build.SERIAL）\n     *      - 不推荐使用，请使用方法 hook 代替\n     *\u002F\n    \u002F\u002F hookField = false\n\n    \u002F**\n     * 构造函数 Hook 开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：实现复杂，稳定性差，已停止维护\n     * 说明：是否 hook 构造函数（主要用于拦截 File 构造函数参数）\n     *      - 相关模块：privacy-replace\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F hookConstructor = false\n\n    \u002F**\n     * Manifest 处理开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：功能边界不清晰，已停止维护\n     * 说明：是否处理 AndroidManifest.xml 文件\n     *      - 主要用于处理 Service 的 Priority 和 Export\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F enableProcessManifest = false\n\n    \u002F**\n     * Service Priority 替换开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：针对 MIUI 自启动问题的特殊方案，已停止维护\n     * 说明：是否替换 Service 的 Priority 值\n     *      - 部分 Service 设置 Priority = 1000 导致自启动\n     *      - 开启后会将 Priority 替换为 replacePriority 的值\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F enableReplacePriority = false\n\n    \u002F**\n     * Service Priority 替换值（已废弃）\n     * 类型：Int\n     * 默认值：0\n     * 说明：替换后的 Priority 值\n     *      - 配合 enableReplacePriority 使用\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F replacePriority = 0\n\n    \u002F**\n     * Service Export 关闭开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：可能影响厂商推送功能，已停止维护\n     * 说明：是否关闭 Service 的 Export 功能\n     *      - 注意：部分厂商推送（小米、VIVO、华为）的 PushService 不能关闭\n     *      - 配合 serviceExportPkgWhiteList 使用\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F enableCloseServiceExport = false\n\n    \u002F**\n     * Service Export 白名单（已废弃）\n     * 类型：Set\u003CString>\n     * 默认值：null\n     * 说明：允许保持 Export 的 Service 包名列表\n     *      - 配合 enableCloseServiceExport 使用\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F serviceExportPkgWhiteList = []\n\n    \u002F**\n     * Service StartCommand Hook 开关（已废弃）\n     * 类型：Boolean\n     * 默认值：false\n     * 废弃原因：针对 MIUI 自启动问题的特殊方案，已停止维护\n     * 说明：是否 hook Service 的 startCommand 方法\n     *      - 不推荐使用\n     *\u002F\n    \u002F\u002F enableHookServiceStartCommand = false\n}\n```\n\n**配置优先级说明**：\n1. **必须配置**：`enablePrivacy`（总开关）\n2. **强烈推荐**：`blackList`（避免冲突）\n3. **按需配置**：`hookReflex` + `reflexMap`（反射拦截）\n4. **可选配置**：`replaceFileName`（静态扫描文件）\n5. **不推荐使用**：所有标记为 `@Deprecated` 的配置项\n\n### Step 4: SDK 初始化\n\n在 `Application` 中初始化 SDK：\n\n**Kotlin 示例**:\n\n```kotlin\nclass MyApplication : Application() {\n\n    override fun attachBaseContext(base: Context?) {\n        super.attachBaseContext(base)\n\n        \u002F\u002F ⚠️ 重要：尽可能在 attachBaseContext 中第一个调用\n        \u002F\u002F 这样可以确保捕获所有敏感 API 调用\n        initPrivacySentry()\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n\n        \u002F\u002F 展示隐私协议弹窗\n        showPrivacyDialog {\n            \u002F\u002F 用户同意后，必须调用此方法\n            PrivacySentry.Privacy.updatePrivacyShow()\n        }\n    }\n\n    private fun initPrivacySentry() {\n        val builder = PrivacySentryBuilder()\n            \u002F\u002F 自定义输出文件名\n            .configResultFileName(\"privacy_result\")\n\n            \u002F\u002F 开启 debug 模式（可在 logcat 查看日志）\n            .syncDebug(BuildConfig.DEBUG)\n\n            \u002F\u002F 开启文件输出（⚠️ 线上版本请关闭）\n            .enableFileResult(BuildConfig.DEBUG)\n\n            \u002F\u002F 监控时长（30 分钟）\n            .configWatchTime(30 * 60 * 1000)\n\n            \u002F\u002F 文件输出完成回调\n            .configResultCallBack(object : PrivacyResultCallBack {\n                override fun onResultCallBack(filePath: String) {\n                    Log.i(\"PrivacySentry\", \"结果文件：$filePath\")\n                }\n            })\n\n        PrivacySentry.Privacy.init(this, builder)\n    }\n}\n```\n\n**Java 示例**:\n\n```java\npublic class MyApplication extends Application {\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n\n        \u002F\u002F ⚠️ 重要：尽可能在 attachBaseContext 中第一个调用\n        initPrivacySentry();\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        \u002F\u002F 展示隐私协议弹窗\n        showPrivacyDialog(() -> {\n            \u002F\u002F 用户同意后，必须调用此方法\n            PrivacySentry.Privacy.INSTANCE.updatePrivacyShow();\n        });\n    }\n\n    private void initPrivacySentry() {\n        PrivacySentryBuilder builder = new PrivacySentryBuilder()\n                .configResultFileName(\"privacy_result\")\n                .syncDebug(BuildConfig.DEBUG)\n                .enableFileResult(BuildConfig.DEBUG)\n                .configWatchTime(30 * 60 * 1000)\n                .configResultCallBack(new PrivacyResultCallBack() {\n                    @Override\n                    public void onResultCallBack(@NonNull String filePath) {\n                        Log.i(\"PrivacySentry\", \"结果文件：\" + filePath);\n                    }\n                });\n\n        PrivacySentry.Privacy.INSTANCE.init(this, builder);\n    }\n}\n```\n\n### Step 5: 查看检测结果\n\n#### 方式 1：Logcat 日志\n\n```bash\n# 实时查看日志\nadb logcat | grep \"PrivacyOfficer\"\n```\n\n#### 方式 2：Excel 文件\n\n```bash\n# 拉取结果文件\nadb pull \u002Fstorage\u002Femulated\u002F0\u002FAndroid\u002Fdata\u002F{your.package.name}\u002Ffiles\u002Fprivacy\u002F\n\n# 文件名格式\n# 主进程：privacy_result.xls\n# 子进程：{进程名}_privacy_result.xls\n```\n\n**Excel 文件包含两个 Sheet**:\n\n1. **Sheet 1 - 隐私合规明细**：按时间倒序记录所有敏感 API 调用\n   - 调用时间\n   - 方法别名\n   - 函数名\n   - 完整调用堆栈\n\n2. **Sheet 2 - 调用次数统计**：按堆栈聚合统计调用次数\n   - 方法别名\n   - 函数名\n   - 调用堆栈\n   - 调用次数\n\n## ⚙️ 插件配置详解\n\n### 核心配置项完整说明\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `enablePrivacy` | Boolean | `true` | **插件功能总开关**\u003Cbr>- `true`: 启用插件，执行字节码转换\u003Cbr>- `false`: 禁用插件 |\n| `blackList` | Set\\\u003CString\\> | `null` | **黑名单配置**\u003Cbr>指定不进行字节码修改的包名列表\u003Cbr>- 适用场景：ASM 冲突的三方库\u003Cbr>- 注意：黑名单中的包无法拦截敏感 API |\n| `replaceFileName` | String | `\"privacy_hook.json\"` | **静态扫描结果文件名**\u003Cbr>记录所有被代理的方法和类\u003Cbr>- 文件位置：项目根目录\u003Cbr>- 文件格式：JSON\u003Cbr>- 设置为 `null` 则不生成 |\n| `hookReflex` | Boolean | `false` | **反射方法 Hook 开关**\u003Cbr>是否拦截通过反射调用的敏感方法\u003Cbr>- 需配合 `reflexMap` 使用\u003Cbr>- 适用：三方 SDK 反射获取设备信息 |\n| `reflexMap` | Map\\\u003CString, List\\\u003CString\\>\\> | `null` | **反射拦截配置**\u003Cbr>- Key: 类的全限定名\u003Cbr>- Value: 需要拦截的方法名列表\u003Cbr>- 只有 `hookReflex=true` 时生效 |\n\n### 已废弃配置项\n\n| 配置项 | 类型 | 默认值 | 废弃原因 |\n|--------|------|--------|---------|\n| `hookField` | Boolean | `false` | 几乎没有业务场景，功能不稳定 |\n| `hookConstructor` | Boolean | `false` | 实现复杂，稳定性差，已停止维护 |\n| `enableProcessManifest` | Boolean | `false` | 功能边界不清晰，已停止维护 |\n| `enableReplacePriority` | Boolean | `false` | 针对 MIUI 特殊问题，已停止维护 |\n| `replacePriority` | Int | `0` | 配合 `enableReplacePriority` 使用 |\n| `enableCloseServiceExport` | Boolean | `false` | 可能影响厂商推送，已停止维护 |\n| `serviceExportPkgWhiteList` | Set\\\u003CString\\> | `null` | 配合 `enableCloseServiceExport` 使用 |\n| `enableHookServiceStartCommand` | Boolean | `false` | 针对 MIUI 特殊问题，已停止维护 |\n\n> ⚠️ **重要提示**：所有标记为\"已废弃\"的配置项均不推荐使用，可能在未来版本中移除。\n\n### 黑名单配置说明\n\n#### 什么情况需要配置黑名单？\n\n1. **ASM 版本冲突**\n   - 使用高德地图 SDK（ASM 9.1 vs 其他版本）\n   - 使用其他字节码修改工具的三方库\n\n2. **已知冲突的 SDK**\n   - OpenInstall SDK\n   - 部分混淆工具\n\n3. **不需要监控的库**\n   - 系统库（可选）\n   - 不涉及隐私的三方库\n\n#### 黑名单工作原理\n\n- 插件在 Transform 阶段会跳过黑名单中的包\n- 黑名单采用**前缀匹配**规则\n- 例如：`\"com.loc\"` 会匹配 `com.loc.*` 下的所有类\n\n#### 配置示例\n\n```gradle\nprivacy {\n    blackList = [\n        \u002F\u002F 高德地图（ASM 版本冲突）\n        \"com.loc\",\n        \"com.amap.api\",\n\n        \u002F\u002F OpenInstall SDK\n        \"io.openinstall.sdk\",\n\n        \u002F\u002F Google 服务（可选）\n        \"com.google.android\",\n\n        \u002F\u002F AndroidX 库（可选）\n        \"androidx\",\n\n        \u002F\u002F 自定义不需要监控的包\n        \"com.example.thirdparty\"\n    ]\n}\n```\n\n#### 黑名单注意事项\n\n- ✅ **推荐**：只添加确实冲突的包\n- ❌ **不推荐**：盲目添加大量包到黑名单\n- ⚠️ **影响**：黑名单中的包无法拦截敏感 API 调用\n\n### 反射 Hook 配置详解\n\n#### 使用场景\n\n反射 Hook 用于拦截通过**反射方式**调用的敏感方法，常见场景：\n\n1. **设备标识获取**\n   - 小米设备 OAID\u002FAAID\u002FVAID\n   - 华为设备标识\n   - OPPO\u002FVIVO 设备标识\n\n2. **三方 SDK**\n   - 极光推送（JPush）\n   - 个推（GeTui）\n   - 穿山甲广告 SDK\n   - 友盟统计\n\n3. **自定义反射调用**\n   - 项目中通过反射获取的敏感信息\n\n#### 工作原理\n\n```kotlin\n\u002F\u002F 原始代码（反射调用）\nClass.forName(\"com.android.id.impl.IdProviderImpl\")\n    .getMethod(\"getOAID\")\n    .invoke(obj)\n\n\u002F\u002F 字节码层面\nLDC \"com.android.id.impl.IdProviderImpl\"  \u002F\u002F ← hookReflex 检测这里\nLDC \"getOAID\"                              \u002F\u002F ← reflexMap 匹配方法名\nINVOKEVIRTUAL Method.invoke()              \u002F\u002F ← 替换为代理方法\n```\n\n#### 配置示例\n\n**场景 1：小米设备标识**\n\n```gradle\nprivacy {\n    hookReflex = true\n\n    reflexMap = [\n        \"com.android.id.impl.IdProviderImpl\": [\n            \"getOAID\",   \u002F\u002F 开放匿名设备标识符\n            \"getAAID\",   \u002F\u002F 应用匿名设备标识符\n            \"getVAID\"    \u002F\u002F 开发者匿名设备标识符\n        ]\n    ]\n}\n```\n\n**场景 2：多个 SDK 配置**\n\n```gradle\nprivacy {\n    hookReflex = true\n\n    reflexMap = [\n        \u002F\u002F 小米设备标识\n        \"com.android.id.impl.IdProviderImpl\": [\n            \"getOAID\", \"getAAID\", \"getVAID\"\n        ],\n\n        \u002F\u002F 华为设备标识\n        \"com.huawei.hms.ads.identifier.AdvertisingIdClient\": [\n            \"getAdvertisingIdInfo\"\n        ],\n\n        \u002F\u002F 自定义工具类\n        \"com.example.utils.DeviceUtils\": [\n            \"getDeviceId\",\n            \"getIMEI\",\n            \"getAndroidId\"\n        ]\n    ]\n}\n```\n\n**场景 3：极光推送\u002F个推配置**\n\n```gradle\nprivacy {\n    hookReflex = true\n\n    reflexMap = [\n        \u002F\u002F 极光推送反射获取设备信息\n        \"cn.jpush.android.api.JCoreInterface\": [\n            \"getDeviceId\",\n            \"getRegistrationID\"\n        ],\n\n        \u002F\u002F 个推反射获取设备信息\n        \"com.igexin.sdk.PushManager\": [\n            \"getClientid\"\n        ]\n    ]\n}\n```\n\n#### 反射 Hook 注意事项\n\n- ✅ **精确匹配**：类名和方法名必须完全匹配\n- ✅ **性能影响**：轻微（仅影响 LDC 指令匹配）\n- ⚠️ **必须启用**：`hookReflex = true` 才生效\n- ⚠️ **无法拦截**：动态生成的类名或方法名\n\n### 静态扫描文件说明\n\n#### replaceFileName 配置\n\n```gradle\nprivacy {\n    \u002F\u002F 生成静态扫描文件\n    replaceFileName = \"privacy_hook.json\"\n\n    \u002F\u002F 不生成文件\n    \u002F\u002F replaceFileName = null\n}\n```\n\n#### 文件内容示例\n\n**文件位置**：项目根目录 `\u002Fprivacy_hook.json`\n\n```json\n{\n    \"hookServiceList\": [\n        \"com.example.TestService\",\n        \"com.example.BackgroundService\"\n    ],\n\n    \"replaceMethodMap\": {\n        \"android.app.ActivityManager.getRunningTasks\": {\n            \"count\": 5,\n            \"originMethodList\": [\n                {\n                    \"originClassName\": \"com.example.MainActivity\",\n                    \"originMethodName\": \"checkRunningTasks\"\n                },\n                {\n                    \"originClassName\": \"com.example.utils.AppUtils\",\n                    \"originMethodName\": \"getRunningApps\"\n                }\n            ]\n        },\n        \"android.telephony.TelephonyManager.getDeviceId\": {\n            \"count\": 2,\n            \"originMethodList\": [\n                {\n                    \"originClassName\": \"com.example.DeviceManager\",\n                    \"originMethodName\": \"getIMEI\"\n                }\n            ]\n        }\n    }\n}\n```\n\n#### 文件用途\n\n1. **静态分析**：离线分析敏感 API 调用情况\n2. **合规检查**：提供给安全团队审查\n3. **调试参考**：确认插件是否正确拦截了目标方法\n4. **版本对比**：对比不同版本的 API 调用变化\n\n### 配置优先级建议\n\n| 优先级 | 配置项 | 建议值 | 说明 |\n|--------|--------|--------|------|\n| ⭐⭐⭐ 必须 | `enablePrivacy` | `true` | 插件总开关 |\n| ⭐⭐⭐ 强烈推荐 | `blackList` | 根据实际情况 | 避免 ASM 冲突 |\n| ⭐⭐ 推荐 | `replaceFileName` | `\"privacy_hook.json\"` | 生成静态扫描文件 |\n| ⭐ 按需配置 | `hookReflex` | `false` 或 `true` | 根据是否有反射调用 |\n| ⭐ 按需配置 | `reflexMap` | `[:]` 或配置 | 配合 hookReflex 使用 |\n| ❌ 不推荐 | 所有废弃配置 | - | 已停止维护 |\n\n## 🔧 SDK 初始化\n\n### PrivacySentryBuilder 配置项\n\n| 方法 | 参数类型 | 默认值 | 说明 |\n|------|---------|--------|------|\n| `configResultFileName(String)` | String | 自动生成 | 自定义输出文件名 |\n| `syncDebug(Boolean)` | Boolean | `false` | 开启 debug 日志 |\n| `enableFileResult(Boolean)` | Boolean | `true` | 是否输出到文件 |\n| `configWatchTime(Long)` | Long (毫秒) | 180000 (3分钟) | 监控时长 |\n| `configResultCallBack(PrivacyResultCallBack)` | PrivacyResultCallBack | `null` | 文件输出完成回调 |\n| `enableReadClipBoard(Boolean)` | Boolean | `true` | 是否允许读取剪贴板 |\n\n### 最佳实践\n\n#### 1. 初始化时机\n\n```kotlin\nclass MyApplication : Application() {\n    override fun attachBaseContext(base: Context?) {\n        super.attachBaseContext(base)\n\n        \u002F\u002F ✅ 推荐：第一个调用\n        PrivacySentry.Privacy.init(this, builder)\n\n        \u002F\u002F ❌ 不推荐：在其他初始化之后\n        \u002F\u002F MultiDex.install(this)\n        \u002F\u002F PrivacySentry.Privacy.init(this, builder)\n    }\n}\n```\n\n**原因**：attachBaseContext 之后才能通过反射获取 ActivityThread 的 application，如果初始化太晚，可能无法捕获早期的敏感 API 调用。\n\n#### 2. Debug vs Release 配置\n\n```kotlin\nprivate fun initPrivacySentry() {\n    val builder = PrivacySentryBuilder()\n        .syncDebug(BuildConfig.DEBUG)           \u002F\u002F Debug 开启日志\n        .enableFileResult(BuildConfig.DEBUG)    \u002F\u002F Release 关闭文件输出\n        .configWatchTime(\n            if (BuildConfig.DEBUG) 30 * 60 * 1000  \u002F\u002F Debug: 30分钟\n            else 3 * 60 * 1000                      \u002F\u002F Release: 3分钟（谨慎）\n        )\n\n    PrivacySentry.Privacy.init(this, builder)\n}\n```\n\n#### 3. 隐私协议状态管理\n\n```kotlin\noverride fun onCreate() {\n    super.onCreate()\n\n    \u002F\u002F 检查是否已同意隐私协议\n    if (!hasAgreedPrivacyPolicy()) {\n        showPrivacyDialog {\n            \u002F\u002F 用户同意后保存状态\n            savePrivacyAgreement()\n\n            \u002F\u002F ⚠️ 必须调用，告知 SDK 用户已同意\n            PrivacySentry.Privacy.updatePrivacyShow()\n        }\n    } else {\n        \u002F\u002F 已同意，直接告知 SDK\n        PrivacySentry.Privacy.updatePrivacyShow()\n    }\n}\n```\n\n**重要提示**：\n- ✅ 用户同意隐私协议后，**必须**调用 `updatePrivacyShow()`\n- ✅ 调用前的敏感 API 会返回空数据并标记 `check!!!`\n- ✅ 调用后的敏感 API 返回真实数据并记录日志\n\n#### 4. 手动停止监控\n\n```kotlin\n\u002F\u002F 手动停止监控和文件写入\nPrivacySentry.Privacy.stop()\n```\n\n## 🎯 自定义拦截\n\n### 创建自定义代理类\n\n```kotlin\nimport androidx.annotation.Keep\nimport com.yl.lib.privacy_annotation.MethodInvokeOpcode\nimport com.yl.lib.privacy_annotation.PrivacyClassProxy\nimport com.yl.lib.privacy_annotation.PrivacyMethodProxy\nimport com.yl.lib.sentry.hook.PrivacySentry\nimport com.yl.lib.sentry.hook.util.PrivacyProxyUtil.Util.doFilePrinter\n\n@Keep\n@PrivacyClassProxy\nobject MyCustomProxy {\n\n    \u002F**\n     * 示例 1：拦截实例方法\n     *\u002F\n    @PrivacyMethodProxy(\n        originalClass = YourClass::class,\n        originalMethod = \"getSensitiveData\",\n        originalOpcode = MethodInvokeOpcode.INVOKEVIRTUAL\n    )\n    @JvmStatic\n    fun getSensitiveData(\n        instance: YourClass,  \u002F\u002F 第一个参数是实例对象\n        param1: String\n    ): String {\n        \u002F\u002F 记录日志\n        doFilePrinter(\"getSensitiveData\", \"获取敏感数据: $param1\")\n\n        \u002F\u002F 检查隐私协议状态\n        if (PrivacySentry.Privacy.inDangerousState()) {\n            return \"\"  \u002F\u002F 未同意返回空\n        }\n\n        \u002F\u002F 调用原始方法\n        return instance.getSensitiveData(param1)\n    }\n\n    \u002F**\n     * 示例 2：拦截静态方法\n     *\u002F\n    @PrivacyMethodProxy(\n        originalClass = YourUtilClass::class,\n        originalMethod = \"getDeviceId\",\n        originalOpcode = MethodInvokeOpcode.INVOKESTATIC\n    )\n    @JvmStatic\n    fun getDeviceId(): String {\n        doFilePrinter(\"getDeviceId\", \"获取设备ID\")\n\n        if (PrivacySentry.Privacy.inDangerousState()) {\n            return \"\"\n        }\n\n        return YourUtilClass.getDeviceId()\n    }\n\n    \u002F**\n     * 示例 3：拦截接口方法\n     *\u002F\n    @PrivacyMethodProxy(\n        originalClass = YourInterface::class,\n        originalMethod = \"getData\",\n        originalOpcode = MethodInvokeOpcode.INVOKEINTERFACE\n    )\n    @JvmStatic\n    fun getData(instance: YourInterface): String {\n        doFilePrinter(\"getData\", \"接口方法调用\")\n        return instance.getData()\n    }\n}\n```\n\n### Java 自定义代理示例\n\n```java\nimport androidx.annotation.Keep;\nimport com.yl.lib.privacy_annotation.MethodInvokeOpcode;\nimport com.yl.lib.privacy_annotation.PrivacyClassProxy;\nimport com.yl.lib.privacy_annotation.PrivacyMethodProxy;\nimport com.yl.lib.sentry.hook.PrivacySentry;\nimport static com.yl.lib.sentry.hook.util.PrivacyProxyUtil.Util.doFilePrinter;\n\n@Keep\n@PrivacyClassProxy\npublic class MyCustomProxyJava {\n\n    @PrivacyMethodProxy(\n        originalClass = YourClass.class,\n        originalMethod = \"getSensitiveData\",\n        originalOpcode = MethodInvokeOpcode.INVOKEVIRTUAL\n    )\n    public static String getSensitiveData(YourClass instance, String param) {\n        doFilePrinter(\"getSensitiveData\", \"获取敏感数据: \" + param, false);\n\n        if (PrivacySentry.Privacy.INSTANCE.inDangerousState()) {\n            return \"\";\n        }\n\n        return instance.getSensitiveData(param);\n    }\n}\n```\n\n### 方法签名规则\n\n#### 实例方法 (INVOKEVIRTUAL)\n\n```kotlin\n\u002F\u002F 原始方法\nclass TargetClass {\n    fun method(param1: String, param2: Int): String\n}\n\n\u002F\u002F 代理方法：第一个参数是实例对象\n@JvmStatic\nfun method(\n    instance: TargetClass,  \u002F\u002F ⬅️ 额外的第一个参数\n    param1: String,\n    param2: Int\n): String\n```\n\n#### 静态方法 (INVOKESTATIC)\n\n```kotlin\n\u002F\u002F 原始方法\nobject TargetClass {\n    fun method(param1: String): String\n}\n\n\u002F\u002F 代理方法：参数完全相同\n@JvmStatic\nfun method(param1: String): String\n```\n\n#### 接口方法 (INVOKEINTERFACE)\n\n```kotlin\n\u002F\u002F 原始接口\ninterface TargetInterface {\n    fun method(param: String): String\n}\n\n\u002F\u002F 代理方法：第一个参数是接口实例\n@JvmStatic\nfun method(\n    instance: TargetInterface,  \u002F\u002F ⬅️ 接口实例\n    param: String\n): String\n```\n\n## 📋 支持的敏感 API\n\n### 设备标识\n\n- ✅ IMEI \u002F DeviceId (`TelephonyManager.getDeviceId()`)\n- ✅ IMSI (`TelephonyManager.getSubscriberId()`)\n- ✅ MEID (`TelephonyManager.getMeid()`)\n- ✅ Android ID (`Settings.Secure.getAndroidId()`)\n- ✅ Serial (`Build.getSerial()`, `Build.SERIAL`)\n- ✅ MAC 地址 (`WifiInfo.getMacAddress()`)\n- ✅ ICCID (`TelephonyManager.getSimSerialNumber()`)\n\n### 网络信息\n\n- ✅ WiFi 信息 (`WifiManager.getConnectionInfo()`)\n- ✅ WiFi 扫描结果 (`WifiManager.getScanResults()`)\n- ✅ IP 地址 (`NetworkInterface`, `WifiInfo.getIpAddress()`)\n- ✅ DHCP 信息 (`WifiManager.getDhcpInfo()`)\n\n### 位置信息\n\n- ✅ GPS 定位 (`LocationManager.getLastKnownLocation()`)\n- ✅ 基站信息 (`TelephonyManager.getAllCellInfo()`)\n- ✅ 位置监听 (`LocationManager.requestLocationUpdates()`)\n\n### 应用信息\n\n- ✅ 已安装应用列表 (`PackageManager.getInstalledPackages()`)\n- ✅ 运行中任务 (`ActivityManager.getRunningTasks()`)\n- ✅ 运行中进程 (`ActivityManager.getRunningAppProcesses()`)\n- ✅ 最近任务 (`ActivityManager.getRecentTasks()`)\n\n### 联系人和日历\n\n- ✅ 联系人查询 (`ContentResolver.query()`)\n- ✅ 联系人插入 (`ContentResolver.insert()`)\n- ✅ 日历事件 (Calendar Provider)\n\n### 传感器\n\n- ✅ 传感器列表 (`SensorManager.getSensorList()`)\n- ✅ 传感器注册 (`SensorManager.registerListener()`)\n\n### 其他\n\n- ✅ 剪贴板 (`ClipboardManager.getPrimaryClip()`)\n- ✅ 蓝牙 (`BluetoothAdapter.getAddress()`)\n- ✅ 权限请求 (`requestPermissions()`)\n- ✅ SIM 卡信息 (`TelephonyManager.getSimOperator()`)\n\n> 完整列表请参考 [privacy-proxy](.\u002Fprivacy-proxy) 模块\n\n## 🔍 检测结果说明\n\n### 日志格式\n\n```\n[PrivacyOfficer] getDeviceId-线程名: main | 读取IMEI | com.example.MainActivity.onCreate(MainActivity.kt:42)\n                                                           ↑ 调用堆栈\n```\n\n### 危险状态标记\n\n如果在日志中看到 `check!!!` 标记：\n\n```\ncheck!!! 还未展示隐私协议，Illegal print\n```\n\n**说明**：此时还未同意隐私协议，调用了敏感 API\n\n**解决方法**：\n1. 检查是否在隐私协议同意后调用了 `updatePrivacyShow()`\n2. 优化代码，避免在隐私协议同意前调用敏感 API\n\n## 🛠️ 常见问题\n\n### Q1: 为什么某些 API 没有被拦截？\n\n**可能原因**：\n1. 该 API 未在 privacy-proxy 中实现\n2. 包名在黑名单中\n3. 使用动态加载的代码（热修复、插件化）\n\n**解决方法**：\n1. 查看 `privacy_hook.json` 确认是否包含该 API\n2. 检查黑名单配置\n3. 自定义拦截规则\n\n### Q2: 编译失败或运行时崩溃\n\n**可能原因**：\n1. ASM 版本冲突（特别是高德地图）\n2. AGP 版本不兼容\n\n**解决方法**：\n1. 添加冲突库到黑名单\n2. 升级到 AGP 8.0+\n3. 检查 Gradle 和 Kotlin 版本\n\n### Q3: 如何在线上环境使用？\n\n**建议配置**：\n\n```kotlin\nPrivacySentryBuilder()\n    .syncDebug(false)              \u002F\u002F 关闭 debug 日志\n    .enableFileResult(false)       \u002F\u002F 关闭文件输出\n    .configWatchTime(3 * 60 * 1000) \u002F\u002F 缩短监控时间\n```\n\n**注意**：\n- ❌ 线上版本**不要**开启 `enableFileResult`，避免隐私数据泄露\n- ✅ 可以通过 `configResultCallBack` 上报统计数据\n\n### Q4: 多进程如何处理？\n\nSDK 自动支持多进程，会为不同进程生成独立的日志文件：\n\n```\n主进程：privacy_result.xls\n子进程：com.example.service_privacy_result.xls\n```\n\n### Q5: 性能影响如何？\n\n- **编译时间**：增加 \u003C 2 秒\n- **APK 体积**：增加 ~200KB\n- **运行时性能**：零额外开销（字节码已修改）\n- **内存占用**：缓存数据占用，可通过 watchTime 控制\n\n## 📖 更新日志\n\n### 1.3.7_v820_beta4 (2025-05-18)\n- ✅ 支持 AGP 8.0+\n- ❌ 不兼容 AGP 8.0 以下版本（请使用 1.3.6）\n\n### 1.3.6 (2024-11-01)\n- 修复 `T.(args..):T` 函数 hook 失败的问题\n\n### 1.3.5 (2024-05-08)\n- 修复读取小米系统 OAID 反射代理失败的问题\n- 修复 SHA-256 digest error 问题\n\n### 1.3.4 (2023-09-18)\n- 修复内存缓存数据转换问题\n- 修复 `getSimState` 闪退问题\n\n### 1.3.3 (2023-08-22)\n- 重构 plugin 部分，引入 Booster\n- 适配 AGP 和 Gradle 高版本\n- 支持 AGP 7.0+\n\n> 完整更新日志请查看 [CHANGELOG](.\u002FCHANGELOG.md)\n\n## 🤝 贡献指南\n\n欢迎提交 Issue 和 Pull Request！\n\n- **Bug 报告**：请详细描述问题和复现步骤\n- **功能建议**：欢迎提出改进建议\n- **Pull Request**：请先创建 Issue 讨论\n\n## 📄 许可证\n\n本项目采用 [MIT License](LICENSE)\n\n## 💰 打赏支持\n\n如果这个项目对你有帮助，欢迎打赏支持！\n\n\u003Cimg width=\"290\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F4d966c38-e1cb-44cd-bff3-09efed7b16a6\">\n\n\u003Cimg width=\"290\" alt=\"image\" src=\"https:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F1b7c628f-dc72-45b6-8ff6-161a1c90d463\">\n\n## 🌟 Star History\n\n如果觉得有用，请给个 Star ⭭！\n\n## 🔗 相关资源\n\n- **架构文档**：[docs\u002Farchitecture.md](.\u002Fdocs\u002Farchitecture.md)\n- **开发指南**：[CLAUDE.md](.\u002FCLAUDE.md)\n- **示例项目**：[app](.\u002Fapp)\n- **GitHub Issues**：[提交问题](https:\u002F\u002Fgithub.com\u002Fallenymt\u002FPrivacySentry\u002Fissues)\n\n---\n\n**注意事项**：\n\n1. ⚠️ 线上版本请关闭 `enableFileResult`\n2. ⚠️ 尽可能在 `attachBaseContext` 中第一个调用初始化\n3. ⚠️ 用户同意隐私协议后必须调用 `updatePrivacyShow()`\n4. ⚠️ 使用高德地图等三方库需配置黑名单\n5. ⚠️ 动态加载的代码无法被拦截\n\n---\n\n> 如有问题，请提 [Issue](https:\u002F\u002Fgithub.com\u002Fallenymt\u002FPrivacySentry\u002Fissues)\n>\n> 兄弟们，走过路过请给个 Star ⭭~~~\n","PrivacySentry 是一款针对 Android 应用的隐私合规整改检测工具，通过注解和 ASM 修改字节码的方式实现。其核心功能包括编译期字节码插桩、支持 60 多个敏感 API 的拦截、自动化生成详细的调用记录与统计分析报告等，且具备多进程支持及灵活可扩展性。该工具适用于需要确保应用符合工信部隐私合规要求的开发场景，尤其是在准备上架应用市场前进行隐私问题排查时尤为有用。基于 Kotlin 编写，采用 MIT 许可证开放源代码。",2,"2026-06-11 03:12:45","top_language"]