Skip to content

fix(mobile): remove install permission from Play AAB - #5187

Open
xd-bobo wants to merge 1 commit into
mainfrom
fix/google-play-aab-install-permission
Open

xd-bobo wants to merge 1 commit into
mainfrom
fix/google-play-aab-install-permission

Conversation

@xd-bobo

@xd-bobo xd-bobo commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

这次改了什么

摘要

Global Android 冷更从同一次 Expo prebuild 生成官网 APK 与 Google Play AAB 两个 Gradle 变体。官网 APK 保留 REQUEST_INSTALL_PACKAGES 供应用内安装更新;Play AAB 在 Manifest 合并时移除该权限,避免 Play Console 要求声明不适用的安装包权限。

变更类型

  • fix 缺陷修复
  • feat 新功能
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联需求:Google Play AAB 不包含 REQUEST_INSTALL_PACKAGES。
  • 本 PR 包含:构建期 website / play 变体、Play Manifest overlay、产物路径与最终权限校验、回归测试。
  • 明确不包含:Play 安装来源的更新入口路由(见 fix(mobile): route Google Play installs away from website APK updates #5181)、签名配置及实际上传发布。
  • 配套私仓 MR:https://git.xindong.com/smash/cindy-build-scripts/-/merge_requests/2 。两个仓库改动须配套使用。
  • 用户可见变化:新构建的 Play 安装包不再声明应用内安装 APK 的权限;官网 APK 行为保持一致。
  • 是否存在 breaking change:无;发布脚本须同时使用配套 MR 的新产物路径。

UI 变化

  • 引用的设计规范:不涉及;本 PR 只修改 Android 构建脚本与测试。

怎么验证的

自动验证

pnpm test:unit:related:通过(mobile related)。
pnpm --filter mobile run typecheck:通过。
pnpm check:dco:通过,提交带 Signed-off-by。
git diff --check:通过。

手工验证

在隔离 worktree 的本地 Expo 预构建工程中,模拟 Global 自建主 Manifest 的安装权限,实际执行 :app:bundlePlayRelease 和 :app:assembleWebsiteRelease。Play AAB 内嵌 Manifest 不含 REQUEST_INSTALL_PACKAGES;官网 APK 经 aapt2 dump permissions 确认包含该权限;两份产物回读的 runtimeVersion 相同。AGP 产物与 metadata 路径符合脚本预期。

未执行的验证

未用正式 Global 区域配置与 release keystore 运行完整发布任务,也未上传 Play Console。该验证需在两仓改动集成后的发布环境执行。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:依赖私仓发布脚本的新产物路径

影响与回滚

  • 影响范围:仅新构建的 Global Android 包。变体 patch 发生在 Expo prebuild 后,不修改仓库中的原生配置或 runtime fingerprint 输入;本地实包验证的 APK/AAB runtimeVersion 相同。Manifest 权限变化必须由新的冷更包交付,既有安装包不会追溯改变。Play 安装包的商店更新入口还依赖 fix(mobile): route Google Play installs away from website APK updates #5181 合入。
  • 回滚 / 降级方式:回滚本 PR 和配套私仓 MR,重新使用新的 versionCode 构建;不要复用已上传或归档的 AAB。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s)
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI)
  • 未提交凭证、令牌或授权文件
  • 已核对受影响的文档;现有 Mobile 文档已说明商店构建不声明该权限
  • 已确认测试结果或说明未执行原因

Signed-off-by: xd-bobo <caojianbo@xd.com>
@xd-bobo
xd-bobo requested a review from a team as a code owner September 28, 2026 07:43
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Splits Android build variants for Play Store distribution.

Play AAB 发布前需要配套完成商店更新入口路由,否则强更用户仍会被引向 APK 下载路径。

Findings

  1. P1 Play 强更入口未分流 ▶
  2. P2 缺少实际产物回归测试 ▶
Fix with agent prompt
### Issue 1
apps/mobile/scripts/lib/android-local.mjs:68-71
Play 变体移除安装包权限后,原生安装器会判定它不支持应用内安装。但 Global 的强更页面仍使用同一 Android 发布记录中的安装地址,没有转到 Play 商店的分流。若该地址是官网 APK,Play 用户点击“去更新”会打开 APK 下载地址,无法通过预期的 Play 更新路径解除强更阻断。发布此 AAB 前,需要让商店更新入口的配套改动生效。

### Issue 2
apps/mobile/src/__tests__/androidLocal.test.ts:61-79
新测试只用手写的 Gradle 片段和普通 UTF-8 Buffer 检查变体及权限逻辑,没有自动检查 Expo prebuild 后的 Gradle 配置、Manifest 合并结果或 AAB 内嵌 Manifest。后续构建配置变化即使使 Play 权限移除失效,这些测试仍可能通过,问题要到发布构建时才会暴露。建议增加可重复执行的最终产物权限测试。

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Summary

本 PR 在一次 Global Android prebuild 后增加官网与 Play 两个构建变体,以 Manifest overlay 移除 Play AAB 的安装包权限,并校验最终产物。官网 APK 继续保留该权限,CN/Dev 的 APK 构建路径保持不变。

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  P[Expo prebuild] --> G[配置 website / play 变体]
  G --> W[官网 APK:保留安装权限]
  G --> A[Play AAB:overlay 移除安装权限]
  W --> V[最终产物权限校验]
  A --> V
Loading

Reviews (1) · Last reviewed commit: "fix(mobile): exclude package install per..."

Comment on lines +68 to +71
export const GOOGLE_PLAY_MANIFEST_OVERLAY = `<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove" />
</manifest>\n`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Play 强更入口未分流 Play 变体移除安装包权限后,原生安装器会判定它不支持应用内安装。但 Global 的强更页面仍使用同一 Android 发布记录中的安装地址,没有转到 Play 商店的分流。若该地址是官网 APK,Play 用户点击“去更新”会打开 APK 下载地址,无法通过预期的 Play 更新路径解除强更阻断。发布此 AAB 前,需要让商店更新入口的配套改动生效。

Knowledge Base Used: Mobile application architecture

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/mobile/scripts/lib/android-local.mjs
Line: 68-71

Comment:
**Play 强更入口未分流** Play 变体移除安装包权限后,原生安装器会判定它不支持应用内安装。但 Global 的强更页面仍使用同一 Android 发布记录中的安装地址,没有转到 Play 商店的分流。若该地址是官网 APK,Play 用户点击“去更新”会打开 APK 下载地址,无法通过预期的 Play 更新路径解除强更阻断。发布此 AAB 前,需要让商店更新入口的配套改动生效。

**Knowledge Base Used:** [Mobile application architecture](https://app.greptile.com/xindong/-/custom-context/knowledge-base/makecindy/cindy/-/docs/mobile-application.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +61 to +79
describe('Global Android distribution manifests', () => {
it('官网和 Play 使用独立变体,Play overlay 移除安装权限', () => {
const source = 'android {\n signingConfigs {\n }\n}\n';
const patched = patchBuildGradleDistributionFlavors(source);
expect(patched).toContain('website { dimension "distribution" }');
expect(patched).toContain('play { dimension "distribution" }');
expect(patchBuildGradleDistributionFlavors(patched)).toBe(patched);
expect(GOOGLE_PLAY_MANIFEST_OVERLAY).toContain('android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove"');
expect(() => patchBuildGradleDistributionFlavors('missing')).toThrow(/android/);
});

it('验证最终 APK 有权限,AAB 无权限', () => {
expect(() => assertAndroidInstallPermissionPresent('android.permission.REQUEST_INSTALL_PACKAGES'))
.not.toThrow();
expect(() => assertAndroidInstallPermissionPresent('android.permission.INTERNET'))
.toThrow(/缺少/);
expect(() => assertGooglePlayInstallPermissionAbsent(Buffer.from('manifest'))).not.toThrow();
expect(() => assertGooglePlayInstallPermissionAbsent(Buffer.from('android.permission.REQUEST_INSTALL_PACKAGES')))
.toThrow(/仍包含/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 缺少实际产物回归测试 新测试只用手写的 Gradle 片段和普通 UTF-8 Buffer 检查变体及权限逻辑,没有自动检查 Expo prebuild 后的 Gradle 配置、Manifest 合并结果或 AAB 内嵌 Manifest。后续构建配置变化即使使 Play 权限移除失效,这些测试仍可能通过,问题要到发布构建时才会暴露。建议增加可重复执行的最终产物权限测试。

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/mobile/src/__tests__/androidLocal.test.ts
Line: 61-79

Comment:
**缺少实际产物回归测试** 新测试只用手写的 Gradle 片段和普通 UTF-8 Buffer 检查变体及权限逻辑,没有自动检查 Expo prebuild 后的 Gradle 配置、Manifest 合并结果或 AAB 内嵌 Manifest。后续构建配置变化即使使 Play 权限移除失效,这些测试仍可能通过,问题要到发布构建时才会暴露。建议增加可重复执行的最终产物权限测试。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants