rea-reverse

Google Play 拒审原因:上传前就能在 APK/AAB 里查出来的那些

发布者 rea-reverse更新于 约 7 分钟读完
本页目录

Google Play 拒审,很多时候跟截图、描述没关系,问题就出在发布包本身:targetSdkVersion 低于当前要求、只有 32 位原生库、打的是可调试包、用调试证书签名,或者申请了 READ_SMS 这类受限权限却没填声明。这些全都能在上传前从 APK/AAB 里读出来,也都能在自己的构建里改掉。

本文先构建一个带着常见问题的 App Bundle,交给本站的 Google Play 上架合规检查 跑一遍,逐条修完再跑一遍。下面的输出都来自 2026-10-14 的实际运行。检查在浏览器里完成,文件不会上传。

拒审分两类

  1. 商店信息和各类声明:隐私政策、数据安全表单、内容分级、广告声明、给审核人员的登录信息。这些从文件里看不到,Google 的 Prepare your app for review 有完整清单。
  2. 发布包里写死的东西:目标 API 级别、ABI、Manifest 标志、权限、服务、签名。这些是 APK/AAB 里的事实,也是本文要讲的。

第 1 步:上传前先体检

我用 aapt2、bundletool 1.18.3 和 NDK r27 构建了 before.aab,再按 AAB 的签名方式用 jarsigner 签名。它集中了实际项目里最常见的几个问题:

  • 包名 com.example.rejectdemo,targetSdkVersion 35
  • application 上写了 android:debuggable="true"
  • 原生库只有 lib/armeabi-v7a/
  • 申请了 READ_SMS、QUERY_ALL_PACKAGES、READ_MEDIA_IMAGES 和 FOREGROUND_SERVICE
  • 一个 foregroundServiceType="dataSync" 的服务,却没有 FOREGROUND_SERVICE_DATA_SYNC
  • 签名证书主题是 CN=Android Debug,O=Android,C=US,也就是 Android SDK 调试密钥的主题

打开 合规检查页(默认已选 Google Play 场景),把文件拖进去。分析核心报出的未通过项如下(输出保持英文原文):

PLAY-01 high    targetSdkVersion=35
                required: >= 36 (phone/tablet, since 2026-08-31)
                note: An extension to 2026-11-01 can be requested in Play Console;
                      without one, submissions are rejected.
PLAY-04 reject  lib/armeabi-v7a/ present, lib/arm64-v8a/ missing
PLAY-06 reject  application android:debuggable="true"
PLAY-07 reject  package="com.example.rejectdemo" (reserved prefix com.example.*)
PLAY-08 reject  uses-permission android.permission.READ_SMS
PLAY-09 high    uses-permission android.permission.QUERY_ALL_PACKAGES
                <queries>: none
PLAY-16 reject  uses-permission android.permission.READ_MEDIA_IMAGES
                targetSdkVersion=35
SIGN-01 reject  v1 signer: CN=Android Debug,O=Android,C=US — Android SDK debug certificate
                (SHA-256 575e39d821c4349d0e7699bdac8a01192e93d38d11506cc9aa34948c0fbf83d4)
PLAY-15 reject  service com.example.rejectdemo.SyncService (dataSync): missing FOREGROUND_SERVICE_DATA_SYNC
                targetSdkVersion=35

中文界面里,这几级分别显示为拒审、高风险和建议。我用 SDK 自带工具做了交叉核对:bundletool dump manifest 列出的是同样四个权限,keytool -printcert -jarfile before.aab 打印的 SHA-256 一致,llvm-readelf -lW 显示 r27 编译的 arm64 库 LOAD 段按 0x1000 对齐。

把检查页里的日期改成 2026-11-01 再查一次,PLAY-01 就变成了拒审,因为延期窗口已经关了。

第 2 步:逐项修掉包里的问题

结果 怎么改
PLAY-01 目标 API 手机和平板把 targetSdk 提到 36(Wear OS 和 Automotive 要 35,TV 和 XR 要 34;检查页根据 required 的 <uses-feature> 判断设备形态)。改这一行很快,真正花时间的是按新版本的行为变更做回归测试。见 目标 API 级别要求。
PLAY-04 只有 32 位 在 abiFilters 里加上 arm64-v8a(有 x86 的话再加 x86_64),并为每个预编译 .so 拿到 64 位版本(64 位要求)。
PLAY-02 / PLAY-03 16 KB 2027-02-01 之前为高风险,之后为拒审(16 KB 页面大小)。NDK r28 起默认按 16 KB 对齐,完整步骤见 检查 APK 是否满足 16 KB 页面大小要求。
PLAY-06 可调试 / testOnly 用 release 构建类型打包,删掉手动加的 android:debuggable 或 android:testOnly。
PLAY-07 com.example 包名 换成自己域名反写的应用 ID。
SIGN-01 / SIGN-02 调试或公开密钥 用自己的上传密钥签名,并启用 Play App Signing。
PLAY-05 传的是 APK 用 ./gradlew bundleRelease 生成 AAB。新应用必须上传 AAB;只有 Android TV 应用的 APK 更新会被直接拒。

表格背后有几个坑:

  • com.example 在文档里并没有写成保留前缀。Google 的 应用 ID 说明 只把 com.example.myapp 当占位示例,但 Play Console 拒收 com.example 包名是开发者普遍遇到的情况。第一次上传前就改掉:同一页面提醒,ID 一改,Play 会当成另一个应用。
  • React Native 模板自带公开密钥。在你配置自己的密钥之前,模板的 release 构建类型用的就是自带的 debug.keystore,SIGN-02 会像识别 AOSP 测试密钥一样识别它。
  • 密钥有效期。Play 要求上传密钥的有效期晚于 2033 年 10 月 22 日(Sign your app),所以生成时直接给 25 年以上。SIGN-05 会标出更早到期的证书:APK 为拒审;AAB 只是建议,因为 AAB 上的是上传密钥。

需要声明的权限

这一类结果并不等于“权限被禁止”,而是说这个权限只允许特定的核心用途,Play Console 会要求你填声明:

规则 级别 触发条件 更窄的替代方案
PLAY-08 拒审 任一短信或通话记录权限 SMS Retriever API
PLAY-09 高风险 QUERY_ALL_PACKAGES 用 <queries> 只声明需要的包
PLAY-10 拒审 MANAGE_EXTERNAL_STORAGE(只有 requestLegacyExternalStorage="true" 时为高风险) SAF、MediaStore
PLAY-12 高风险 REQUEST_INSTALL_PACKAGES 自更新改用 Play In-App Updates
PLAY-13 高风险 ACCESS_BACKGROUND_LOCATION 只用前台位置
PLAY-14 高风险 USE_EXACT_ALARM(只有 SCHEDULE_EXACT_ALARM 时为建议) 非精确闹钟、WorkManager
PLAY-16 拒审 READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO,仅当 targetSdk ≥ 33 系统 Photo Picker
PLAY-17 建议(targetSdk ≥ 37 时为高风险) READ_CONTACTS Android Contact Picker
PLAY-11 高风险 受 BIND_ACCESSIBILITY_SERVICE 保护的服务 填声明并做应用内显著披露

谁能保留:短信和通话记录只给默认短信、电话或助理处理程序(政策);广泛访问照片视频只给以此为核心功能的应用(政策);QUERY_ALL_PACKAGES 给设备搜索、杀毒、文件管理、浏览器这类应用(政策)。不符合的删掉权限;符合的提交声明,检查页仍会报出这条,因为审核这一关还在后面。政策截止日期表 里还有两个日期:2027-01-27 起,“通过电话验证账户”不再是 READ_CALL_LOG 的允许用途;没有广泛读取通讯录需求的应用,要改用 Contact Picker。

前台服务(PLAY-15)

targetSdk ≥ 34 时,服务声明了 foregroundServiceType 却缺少对应的 FOREGROUND_SERVICE_<TYPE> 权限,一启动就会崩溃,所以检查页在所有场景下都判拒审。在 Play 上,每个带类型的前台服务还要在“应用内容”页面填写声明:功能说明、对用户的影响,以及一段演示用户如何触发该功能的视频(前台服务要求)。USE_FULL_SCREEN_INTENT 只对通话和闹钟类应用默认开放。

第 3 步:找出权限是谁带进来的

很多时候 READ_SMS 根本不是你写的,是某个库合并进来的。Android Studio 打开 AndroidManifest.xml,底部的 Merged Manifest 标签会标出每个元素的来源;构建时完整日志写在 build/outputs/logs/manifest-merger-<buildVariant>-report.txt(见 Merge multiple manifest files)。如果需要这个权限的库功能你并没有用到,在自己的 Manifest 里移除它:

<uses-permission android:name="android.permission.READ_SMS" tools:node="remove" />

库如果真的用到了这个权限,删掉后对应功能可能失效,所以要么测一遍这个功能,要么换掉这个库。

第 4 步:修完再跑一遍

after.aab 改用包名 dev.example.rejectdemo、targetSdk 36、正式上传密钥(CN=Example Upload),原生库用 NDK r28 同时编译 armeabi-v7a 和 arm64-v8a,去掉了 debuggable、短信、包查询和媒体权限,并补上 FOREGROUND_SERVICE_DATA_SYNC。只剩一条:

PLAY-15 high    service dev.example.rejectdemo.SyncService: foregroundServiceType=dataSync (Play Console declaration)
                targetSdkVersion=36

这正是预期结果:包本身已经没问题,剩下的是要在 Play Console 里填的前台服务声明。

文件里看不到的部分

报告干净只代表发布包没问题,下面这些仍要自己逐项确认:

  • 数据安全表单与应用里每个 SDK 的数据收集一致
  • 隐私政策链接;用户能注册账号的话,还要提供删除账号的入口
  • 应用有登录墙的话,给审核人员提供登录信息
  • 商店信息不误导、不冒充他人
  • 2023 年 11 月 13 日之后注册的个人开发者账号:上线正式版前,需要至少 12 名测试者连续参与 14 天的封闭测试(测试要求)

Play Console 自己也会在“发布概览”页做 预审检查,覆盖缺失的声明、崩溃率和可能的政策问题;Google 同样说明,通过预审不代表一定能过审,本站检查页的结果也一样。

被拒之后:看状态、重新提交、申诉

  1. 先看原因。在 Play Console 选中应用,左侧菜单打开 Policy status(查看应用的政策状态)。被拒时,上一个成功发布的版本仍在 Google Play 上,邮件里会写明违反的是哪条政策。
  2. 修好后重新提交。Google 的 重新提交步骤 要求除了正式版,还要更新所有发布轨道(内部、封闭、开放测试),并停用不合规的 bundle;原文警告,不停用的话重新提交会失败。权限明明删了还因为它被拒,通常就是某个测试轨道上还有申请该权限的旧 bundle 处于启用状态。
  3. 只有确信判错时才申诉。同一页面写明,每次下架、暂停或其他处置只能申诉一次。如果确实是包里的问题触发了规则,就修包,申诉改变不了 Manifest 里写的内容。
  4. 预留审核时间。Google 说部分账号的审核“在特殊情况下可能需要七天或更久”(Publish your app)。

关于加壳和加固

如果应用用了商业防护运行库,检查结果会给出 PROT-01,级别为建议。加壳和加固是正当的保护手段,这条结果只说明利弊:壳的启动代码在启发式扫描引擎看来可能像恶意软件的打包器;带签名自校验的防护在 Play App Signing 下可能失效,因为 Google 会重新签名。选择明确支持 Play App Signing 的方案,长期使用同一签名证书,不要从 Google Play 以外下载可执行代码。