【口算王|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致

【口算王|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致

证据边界:本文依据 D:/huawei/one16-11 当前可读取源码整理;本轮未执行构建、模拟器、真机权限弹窗、通知调度、TTS 网络抓包或 AGC 审核。文中对在线回退和学习提醒的描述均为源码层风险识别,不代表相关能力已经验证或上线。

权限合规不是在 module.json5 里"能少写就少写"。真正的工程目标是让四份事实一致:应用实际功能、调用的系统 API、声明的权限、对用户和审核人员展示的说明。如果页面写着"完全离线",代码却悄悄尝试在线服务;如果设置页提供"每日提醒",却没有通知调度能力;如果清理数据只清界面不清磁盘,都会形成审核和用户信任风险。

口算王当前的权限面非常小:entry 模块 requestPermissions 是空数组,没有 INTERNET、通知、麦克风、相机、位置、联系人等声明,也没有运行时请求权限的代码。题库由本地算法生成,收藏、笔记、错题、进度、挑战历史和设置通过 Preferences 保存在本机。与此同时,PracticePage 的文字转语音先创建离线引擎,失败后会以 online: 1 再试;SettingsPage 的"每日学习提醒"目前只保存时间,没有真正注册通知或提醒任务。

本文基于口算王项目 D:\huawei\one16-11 的真实源码,复核 module.json5EntryAbility.etsPracticePage.etsSettingsPage.etsUserDataManager.etsMockBanks.ets 与题库详情说明。包名 com.jiaweikang.one16 是本文草稿核验使用的唯一标记。项目目标和兼容 SDK 均为 HarmonyOS 6.0 系列,本文方法适用于 HarmonyOS 5.0 及以上 Stage 模型应用。

本文将完成六件事:

  • 从 module.json5 建立实际权限清单;
  • 从源码 API 反查隐含能力与数据流;
  • 区分"不需要权限""权限被拒绝"和"能力不可用";
  • 复核本地存储、清理和隐私说明边界;
  • 找出提醒占位与在线 TTS 回退的一致性风险;
  • 给出上架前可执行的权限隐私核对表。

一、权限审计先以清单事实为准

entry/src/main/module.json5 中:

复制代码
#"requestPermissions": []"requestPermissions": []

这意味着当前安装包没有在该模块声明系统权限。设备类型包含 phone、tablet 和 2in1,但设备类型不等于权限。

源码搜索也没有发现:

  • requestPermissionsFromUser()
  • INTERNET 权限;
  • 相机、麦克风、位置、联系人、日历权限;
  • 通知授权请求;
  • Web 组件或普通 HTTP 客户端;
  • 登录、账号、支付、广告、分析和推送 SDK。

因此当前应用不存在"首次启动弹出权限框"的真实行为。

二、零权限不等于零数据

应用不申请敏感权限,仍然会处理本地业务数据。口算王保存:

数据 用途 存储位置
收藏记录 收藏题目 Preferences
笔记记录 用户题目笔记 Preferences
错题记录 错题复习 Preferences
题库进度 学习统计 Preferences
章节进度 章节完成度 Preferences
挑战历史 分数与统计 Preferences
提醒时间 设置显示 Preferences
挑战时长 练习配置 Preferences
自动下一题 交互偏好 Preferences

这些数据没有因为"无需权限"就不属于隐私说明范围。特别是用户输入的笔记,应明确仅用于本机学习记录,不上传、不共享。

三、题库由本地算法生成

MockBanks 文件明确写着"单机题库,全部题目由本地算法生成"。加减乘除、混合运算、应用题和限时题都由本地函数构造。

题库详情页也向用户说明:

复制代码
#所有题目本地生成,不需要联网;
错题会自动进入错题本,方便反复巩固。所有题目本地生成,不需要联网;
#所有题目本地生成,不需要联网;
错题会自动进入错题本,方便反复巩固。错题会自动进入错题本,方便反复巩固。

这项说明与题目生成源码一致。没有远程题库下载、接口分页或账号同步。

但"题目不需要联网"不应自动扩大为"应用所有能力绝不触网",因为语音引擎还需要单独核查。

四、本地数据入口集中在 UserDataManager

UserDataManager 使用 Preferences 实例 daily_math_drill,将数组序列化为 JSON,并把标量设置直接保存。

页面不直接操作文件系统,也没有数据库或云端 API。数据流是:

复制代码
#用户操作
  -> UserDataManager用户操作
#用户操作
  -> UserDataManager  -> UserDataManager

-> Preferences

复制代码
#-> AppStorage
  -> 页面刷新  -> AppStorage
#-> AppStorage
  -> 页面刷新  -> 页面刷新

这种集中存储边界便于隐私审计:只需核对一个服务中的键、写入和清理逻辑,就能列出主要本地数据类型。

五、当前没有权限拒绝路径

标题中的"拒绝路径"不能机械套用。只有应用请求运行时权限,用户才会选择允许或拒绝。当前 requestPermissions 为空,也没有授权请求,所以不存在真实的系统权限拒绝回调。

当前更准确的是"能力失败路径":

  • TTS 引擎不可用;
  • TTS 播放失败;
  • Preferences 写入失败;
  • 路由参数缺失;
  • 系统避免区读取失败。

这些失败不等于权限拒绝。文章和用户文案应使用正确术语,避免告诉用户"请授权麦克风",因为文本转语音播放不需要录音权限。

六、TTS 不需要麦克风权限

PracticePage 导入:

复制代码
#import textToSpeech from '@hms.ai.textToSpeech'import textToSpeech from '@hms.ai.textToSpeech'

它把题目文本交给语音合成引擎播放。文本转语音是输出能力,不录制用户声音,因此当前没有申请 MICROPHONE 是合理的。

页面点击语音提示后:

  1. 展示文字提示弹窗;
  2. 尝试准备 TTS;
  3. 引擎可用则播放;
  4. 失败则保留文字提示;
  5. 不阻断答题主流程。

这是一条合理的能力降级链路。

七、离线引擎优先是真实实现

ensureTtsEngine 首先执行:

复制代码
#textToSpeech.createEngine({
  language: 'zh-CN',textToSpeech.createEngine({
#textToSpeech.createEngine({
  language: 'zh-CN',  language: 'zh-CN',

person: 0,

复制代码
#online: 0
})  online: 0
#online: 0
})})

online: 0 表示优先使用离线引擎。成功后设置 onStart、onComplete、onStop 和 onError 监听,页面显示"正在播放示例"或失败说明。

如果当前设备缺少对应语音引擎,方法返回 false,页面显示"当前设备语音引擎不可用"。用户仍能在弹窗中阅读 audioHint 或题干。

八、online:1 回退需要合规决策

离线引擎创建失败后,源码会再尝试:

复制代码
#textToSpeech.createEngine({
  language: 'zh-CN',textToSpeech.createEngine({
#textToSpeech.createEngine({
  language: 'zh-CN',  language: 'zh-CN',

person: 0,

复制代码
#online: 1
})  online: 1
#online: 1
})})

这明确表达了在线引擎意图。当前 module.json5 没有 INTERNET 权限,项目也没有普通网络客户端。具体网络由应用还是系统服务承担,需要依据该 TTS API 的官方说明和实机行为确认。

在确认前,不能同时无条件声称"所有功能完全离线"。有两个合规方向:

  • 严格离线:删除 online:1 回退,只保留文字提示;
  • 保留在线语音:确认权限、数据处理和隐私披露,说明发送的文本、服务提供方与网络条件。

九、语音失败不会破坏核心流程

toggleAudioPreview 先打开提示弹窗,再在后台尽力调用 TTS。调用被 try/catch 包围,即使失败也不会关闭弹窗或阻止答题。

这种降级满足三个原则:

  • 用户主动点击后才触发;
  • 失败时有可见状态;
  • 核心功能仍可通过文字完成。

需要补强的是不要空 catch。至少应记录非敏感错误码,便于区分引擎缺失、资源缺失和服务错误;日志不应记录完整题目文本或用户笔记。

十、每日提醒当前只是设置值

SettingsPage 提供"每日学习提醒",可选 09:00、12:00、21:00 或关闭。selectReminder 会:

  • 修改 dailyReminderTime;
  • 更新显示值;
  • 写入 Preferences;
  • 显示"已设置"的提示。

但源码中没有通知权限、提醒代理、定时任务或通知发布 API。当前行为只是保存偏好,并不会在时间到达时真正提醒。

因此产品文案应改为"提醒时间偏好"或暂时隐藏该入口,直到实际提醒能力完成。

十一、不能为占位功能提前申请通知权限

即使未来计划实现提醒,也不应现在先加入通知权限。权限必须与已交付功能和用户场景同步。

正确顺序是:

  1. 明确提醒触发方式;
  2. 查阅 HarmonyOS 官方通知与提醒 API;
  3. 确认是否需要授权;
  4. 设计申请时机和用途说明;
  5. 实现允许、拒绝、不再询问、系统关闭等状态;
  6. 更新隐私材料;
  7. 再添加 module.json5 声明。

当前零权限清单与"没有真实通知调度"是匹配的。

十二、如果未来请求权限,必须按需触发

权限不应在启动页一次性请求。以未来可能的通知为例,合理时机是用户主动开启提醒开关后。

实现时还要把"用户动作、系统授权、界面状态"做成同一事务:只有授权成功才保留开关;拒绝或系统返回异常时立即回滚开关,并让用户继续使用练习、错题和笔记等无权限功能。这样既避免把拒绝误判为功能故障,也不会让设置页显示一个实际上无法兑现的提醒状态。

请求前说明:

  • 为什么需要;
  • 不授权会影响什么;
  • 哪些功能仍可使用;
  • 如何在系统设置中重新开启。

拒绝后页面应保持关闭状态,不能反复弹窗、不能阻断训练,也不能把普通功能包装成"必须授权"。

十三、权限状态需要结构化建模

若未来加入权限,可以定义:

复制代码
#type PermissionState =
  | 'not_requested'type PermissionState =
#type PermissionState =
  | 'not_requested'  | 'not_requested'

| 'granted'

复制代码
#| 'denied'
  | 'blocked'  | 'denied'
#| 'denied'
  | 'blocked'  | 'blocked'

| 'unavailable'

页面据此展示:

  • 未请求:说明用途;
  • 已允许:功能正常;
  • 已拒绝:提供降级;
  • 已禁止再次询问:引导系统设置;
  • 设备不支持:隐藏或说明。

当前项目没有这套模型,因为没有运行时权限;它属于未来扩展建议。

十四、数据保存在本机的说明有源码依据

SettingsPage 的"框架"说明中写着:

复制代码
#页面采用 ArkTS 与 ArkUI 构建,数据保存在本机。页面采用 ArkTS 与 ArkUI 构建,数据保存在本机。

这与 UserDataManager 的 Preferences 实现一致。entry 的 oh-package 依赖只有本地 librarya 和 libraryb,没有第三方分析、广告或网络 SDK。

不过"保存在本机"还应明确生命周期:

  • 数据随应用使用而更新;
  • 用户可在设置页清理;
  • 卸载应用后随应用数据删除;
  • 当前没有账号备份与跨设备同步。

十五、用户笔记是最需要谨慎对待的数据

收藏、错题和进度主要由应用生成,笔记包含用户自由输入。即使只存在本机,也应遵循数据最小化:

口算练习可能由未成年人使用,笔记里还可能出现姓名、班级或学习安排等用户自行填写的内容。因此,默认不导出、不上传、不进入埋点和崩溃日志,比事后猜测文本是否敏感更稳妥;若未来增加备份或分享,应重新设计明确的用户触发、范围预览和删除机制。

  • 不读取与笔记无关的设备信息;
  • 不把笔记写入普通日志;
  • 不上传;
  • 提供删除;
  • 存储异常时不在错误信息中展示全文;
  • 未来增加导出或同步时重新做隐私评估。

当前源码没有上传和分享笔记的能力,这是可复核边界。

十六、清理入口是隐私闭环的一部分

SettingsPage 可以分别清除:

  • 收藏;
  • 笔记;
  • 错题;
  • 学习进度;
  • 挑战历史。

还提供"清除所有学习数据"的二次确认。调用 UserDataManager 的 clear 方法后,把 AppStorage 中的数组设为空。

这让用户无需卸载应用就能删除学习数据。需要继续验证的是持久化失败场景:当前 persist 会吞掉异常,页面可能显示已清空而重启后数据恢复。隐私删除必须以真正落盘成功为准。

十七、隐私删除不能只清当前界面

完整删除验证包括:

  1. 清理前记录各类数据数量;
  2. 执行单项或全部清理;
  3. 当前页面为空;
  4. 返回其他页面仍为空;
  5. 杀进程并重启;
  6. 数据不恢复;
  7. Preferences 对应键已更新;
  8. 失败时向用户反馈而不是假装成功。

如果未来增加缓存、数据库、文件或云端,每个数据副本都要进入删除清单。

十八、模块导出能力也要审计

EntryAbility 的 exported 为 true,并声明 HOME 实体和 home action。这是桌面启动入口的正常配置。

审计时仍要确认:

  • 没有额外隐蔽 Ability;
  • 没有未使用的服务或 Extension;
  • 外部 Want 参数经过校验;
  • 导出入口不能执行敏感操作;
  • 没有后台自启动、悬浮窗或跨应用干预。

当前 module.json5 只列出 EntryAbility,没有其他敏感扩展能力。

十九、零权限有助于降低 2.19 风险

App Review Guideline 2.19 关注对网络、系统机制和其他应用的滥用。当前项目没有:

  • 隐藏后台任务;
  • 自动启动链;
  • 悬浮窗;
  • 无障碍控制;
  • 动态代码加载;
  • 修改其他应用数据;
  • 强制卸载或干扰系统;
  • 未授权设备控制。

这与本地口算工具的产品范围一致。后续若加入推送、网络、账号或后台提醒,必须重新评估。

二十、隐私材料应从代码事实生成

上架材料至少应准确回答:

  • 是否联网;
  • 是否收集个人信息;
  • 保存哪些本地数据;
  • 是否使用第三方 SDK;
  • 是否需要登录;
  • 是否含广告或支付;
  • 是否跨设备同步;
  • 如何清除数据;
  • 语音服务是否可能在线处理文本;
  • 提醒是否真实可用。

当前最稳妥的描述是:题库本地生成,学习记录和设置保存在本机,无账号、广告和支付;语音功能优先使用设备离线引擎,在线回退需在发布前作出明确决策。

二十一、不能把 PV、账号数据或云同步写进材料

源码没有 PV 统计、点赞、收藏量上报、用户账号或云端同步。发布材料和技术文章都不能虚构这些能力。

本地"收藏记录"只是用户自己的题目收藏,不是平台点赞或公开社交数据。挑战历史是设备内记录,也不是排行榜服务。

ExamResultPage 目前展示的排名字符串不是由网络数据计算,不能宣传为真实在线排名。

发布核验时要把"本地功能名"和"平台运营指标"严格分开:本地收藏数量只能说明设备内保存了多少道题,静态排名文案也不能证明存在服务端榜单。PV、点赞、公开收藏、账号规模和云同步状态都必须来自可回读的平台或服务端数据;没有真实数据时就留空。

二十二、第三方依赖审计不能只看 entry

entry/oh-package.json5 只依赖本地 librarya 与 libraryb,但 PracticePage 直接导入系统或 HMS TTS Kit。合规审计应覆盖:

  • 源码 import;
  • HAR/HSP 依赖;
  • oh-package lock;
  • 原生 so;
  • SDK 初始化代码;
  • 网络域名;
  • AGC 服务开关;
  • 隐私政策中的 SDK 清单。

"没有 npm 依赖"不等于没有平台服务依赖。TTS 应作为单独能力说明。

二十三、功能说明要避免绝对化

"所有题目本地生成,不需要联网"是准确的题库说明。若把它改成"应用完全不联网",就会与 online:1 回退产生冲突。

这种按能力拆分的表达也更便于审核复核:审核人员可以分别对照题目生成、Preferences 存储和 TTS 初始化代码,不必依赖一句覆盖全应用的宣传语。以后某个能力发生变化时,只需同步修改对应说明、隐私材料和测试用例,不会让其他离线能力一起失真。

推荐把说明按功能拆开:

  • 题库与训练:本地运行;
  • 学习数据:本机保存;
  • 语音提示:优先使用设备语音引擎,可用性取决于系统服务;
  • 提醒:当前仅保存偏好,未提供系统通知;
  • 同步:当前不支持。

分功能描述比一句绝对承诺更可核验。

二十四、拒绝与失败文案要可行动

当前语音失败文案包括:

  • 当前设备语音引擎不可用;
  • 语音播放失败,请检查系统语音服务。

它们没有错误地要求麦克风权限,方向正确。还可以增强:

失败提示应回答三个问题:哪项能力不可用、当前任务是否还能继续、用户下一步能做什么。对本应用而言,TTS 失败不应中断答题,也不应弹出引导用户授予无关权限的对话框;保留文字展示并提供一次明确重试,比笼统的"请检查权限"更符合真实故障边界。

  • 明确文字提示仍可使用;
  • 提供重试;
  • 避免循环创建引擎;
  • 若仅离线,提示下载系统离线语音资源;
  • 若在线,说明网络条件与隐私处理。

失败文案不能暗示应用拥有实际不存在的修复能力。

二十五、自动化扫描建立权限回归门槛

可以在 CI 或发布脚本中检查:

  • module.json5 的 requestPermissions;
  • 新增 @kit@ohos@hms import;
  • HTTP、Web、Socket、账号和推送 API;
  • Camera、Microphone、Location 等能力;
  • 新增 ExtensionAbility;
  • 第三方依赖和原生库;
  • 隐私材料中的权限、SDK 和数据字段。

任何新增能力都要求开发者更新审计表,而不是等上架被拒后再补材料。

二十六、权限测试矩阵

当前零权限版本也要测试:

场景 预期
首次启动 不弹权限框
离线训练 全部核心题型可用
无语音引擎 文字提示可用,训练不中断
TTS 播放失败 显示失败说明,可继续答题
设置提醒时间 仅保存显示,不宣称通知已调度
清空笔记 当前和重启后均删除
飞行模式 题库、记录和练习可用
系统暗色/多窗口 权限和隐私入口仍可触达
卸载重装 本地学习数据不应保留

二十七、上架前核对表

  • requestPermissions 与实际 API 一致;
  • 不申请未使用权限;
  • 首次启动不批量索权;
  • 题库本地生成说明真实;
  • Preferences 数据类型已列入隐私说明;
  • 用户笔记不上传、不写日志;
  • 清理操作经重启验证;
  • TTS online:1 已确认去留;
  • 提醒入口与真实通知能力一致;
  • 无登录、广告、支付和分析时材料不勾选;
  • 无云同步时不宣传跨设备恢复;
  • 第三方和平台 Kit 清单已复核;
  • 安装、启动、核心流程、退出、卸载正常;
  • 审核说明能快速找到本地题库和清理入口。

二十八、常见问题排查表

症状 可能原因 处理
首次启动出现权限框 新依赖或模块新增声明 对照 requestPermissions
页面要求麦克风 混淆 TTS 与录音 删除错误引导
提醒时间到了无通知 只保存 Preferences 完成真实调度或改文案
宣称完全离线但语音触网 online:1 回退 删除或披露
清空后重启数据回来 持久化失败被吞掉 返回删除结果
隐私政策写"不存储数据" 忽略本地学习记录 按实际键修正
说有跨设备同步 把 AppStorage 当云同步 明确单设备边界
说有在线排名 使用静态展示值 删除能力宣称
无第三方依赖却有 TTS 只看 oh-package 审计平台 Kit import
用户拒绝后反复弹窗 权限状态未建模 按需请求并提供降级

二十九、总结:权限越少,事实越要准确

口算王当前的真实合规基础很好:模块权限清单为空,核心题库本地生成,学习记录保存在 Preferences,没有账号、广告、支付、推送和普通网络客户端;语音引擎失败时保留文字提示,核心训练不被阻断;设置页提供数据清理入口。

同样需要正视两个不一致点:每日提醒目前只是配置值,不能描述为已经发送系统通知;TTS 在离线失败后尝试 online:1,与绝对"完全离线"表述存在冲突,需要在发布前选择纯离线或完成在线服务合规。

权限与隐私不是一张静态表,而是一条持续核验链:

  • 从功能列出真实能力;
  • 从 import 和 API 反查数据流;
  • 与 module.json5 权限逐项比对;
  • 为不可用和拒绝设计降级;
  • 验证本地存储与删除;
  • 同步更新隐私政策、商店材料和审核说明。

只有代码、权限、页面文案和上架材料说的是同一件事,零权限才真正成为产品优势。

本文部分内容由 AI 辅助整理,所有现有权限、数据、TTS、提醒与清理行为均依据上述本地源码复核;运行时权限状态模型、自动审计、通知实现步骤与隐私材料建议属于基于当前边界的工程增强方案。

两个容易被忽略的一致性风险

当前 module.json5requestPermissions 为空,这与工程主体采用本地题库、算法计算和 Preferences 存储的实现是吻合的。但"没有声明权限"不等于可以跳过隐私核验:凡是存在在线回退、系统服务或尚未完成的设置入口,都要继续追踪真实调用链。

第一处是语音播报。现有练习页会优先尝试离线 TTS,并在离线引擎失败后使用 online: 1 重试。这里不能仅凭参数名断言一定产生网络请求,也不能把它写成已经验证的在线服务;正确做法是继续核对所用 TTS Kit 的官方文档、运行条件、数据处理方式和真机网络行为。如果最终确认会联网,应用说明、隐私政策和网络能力配置必须同步更新。

第二处是"每日学习提醒"。当前设置页保存了提醒时间,但现有证据没有显示通知调度或定时任务已经接入。因此文章只把它视为设置占位和后续实现风险,不能宣传为已生效的提醒功能。真正实现时,应重新检查是否需要通知相关能力、用户开关、拒绝后的界面反馈,以及关闭提醒后是否会清理既有任务。

这两个例子说明,权限审计不能只搜索 requestPermissions。更可靠的方式是从用户可见功能出发,沿着页面事件、Service、Kit/API、配置文件和隐私文案逐层回溯,并把"已实现""待验证""规划中"分开记录。

AI 辅助声明

本文在人工复核口算王 ArkTS 源码、配置文件和本地数据实现后,使用 AI 辅助整理结构、润色表达并生成配图;未执行的构建、真机、网络行为和 AGC 审核均未写成已通过。

相关推荐
ChinaDragonDreamer1 小时前
HarmonyOS:6.0 新增和增强特性
harmonyos·鸿蒙
Georgewu1 小时前
【HarmonyOS AI】 通用文字识别详解
harmonyos
哈__2 小时前
Flutter 3.44.9 + OpenHarmony7:home_widget 三方库桌面服务卡片(FormKit)的应用
flutter·华为·harmonyos
贾伟康2 小时前
【句匠|08】HarmonyOS ArkTS 句库搜索实战:支持关键词、分类和无结果反馈
harmonyos·arkts·分类筛选·学习应用·搜索功能
Sunny_G2 小时前
从 DevEco Code 到 Claude Code:一次工具链切换的完整决策
harmonyos
Kevin Coding2 小时前
鸿蒙 emitter/EventHub 没有 Sticky 粘性事件?手写一个轻量级 EmitterManager 解决
前端·华为·前端框架·移动开发·harmonyos
ChinaDragonDreamer3 小时前
HarmonyOS:Web使用Dsbridge与JavaScript完成交互
harmonyos·鸿蒙
贾伟康3 小时前
【句匠|10】HarmonyOS ArkTS 分类句库实战:复用列表结构并保持导航参数类型安全
harmonyos·arkts·router·分类导航·题库列表
体毛旺盛的猿4 小时前
HarmonyOS开发面试题
前端·华为·harmonyos