第95篇 | HarmonyOS 权限与隐私声明:从代码反推用户要看到的说明
第95篇 | HarmonyOS 权限与隐私声明:从代码反推用户要看到的说明
权限声明不能靠感觉写。双镜记忆相机用到了相机、定位、生物认证、网络、握姿感知、隐私防窥等能力,每一个权限都应该能在代码里找到使用场景,也应该能在用户文案里解释清楚。
这一篇从 module.json5 的 requestPermissions 开始,反推页面里什么时候申请权限、拒绝后如何降级、用户为什么要授权。发布和社区文章里讲权限时,最好能做到“权限名、触发时机、用户价值、拒绝后表现”四列对齐。
本篇目标
- 从配置文件列出应用声明的所有权限。
- 把权限声明和页面触发函数对应起来。
- 确认拒绝权限后仍有可解释的降级路径。
- 整理隐私声明,说明照片、定位、Key、保险箱分别如何使用。
对应源码位置
superImage/entry/src/main/module.json5superImage/entry/src/main/ets/pages/Index.ets
权限清单要从配置开始
requestPermissions 是最直观的权限清单。这里能看到 CAMERA、ACCESS_BIOMETRIC、INTERNET、GET_NETWORK_INFO、LOCATION、DETECT_GESTURE、DLP_GET_HIDE_STATUS 等能力。
写隐私声明时不要只写“我们会使用必要权限”,而是逐条解释:相机用于双摄拍摄,定位用于地图记忆和旧地提醒,生物认证用于保险箱,网络用于在线 ARK 能力。

权限声明需要和源码触发点、用户可见说明一一对应
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.ACCESS_BIOMETRIC",
"reason": "$string:biometric_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.INTERNET",
"reason": "$string:network_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_NETWORK_INFO",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "always"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.DETECT_GESTURE",
"reason": "$string:gesture_permission_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.DLP_GET_HIDE_STATUS",
"reason": "$string:dlp_antipeep_permission_reason",
"usedScene": {
"abilities": [
页面里保留权限分组
Index 页面把相机、生物认证、定位、握姿权限分别放到独立数组或函数里。这比在业务代码里散写字符串更清楚,也方便后续做权限说明表。
如果后续新增通知、相册保存、蓝牙或传感器权限,也建议沿用这种分组方式,把权限名和业务场景绑定。

权限分组字段让声明、申请和说明更容易对应
private readonly cameraPermissionList: Array<Permissions> = ['ohos.permission.CAMERA'];
private readonly biometricPermissionList: Array<Permissions> = ['ohos.permission.ACCESS_BIOMETRIC'];
private readonly locationPermissionList: Array<Permissions> = [
'ohos.permission.APPROXIMATELY_LOCATION',
'ohos.permission.LOCATION'
];
private readonly gesturePermissionList: Array<Permissions> = ['ohos.permission.DETECT_GESTURE'];
定位权限要区分粗略和精确
定位权限里同时有 APPROXIMATELY_LOCATION 和 LOCATION。项目先检查粗略和精确权限,再决定还需要请求哪些。这样做比一次性请求所有权限更可解释。
用户拒绝定位后,相机主流程仍应可用,只是地图记忆、附近回忆、去年今日等能力会降级。隐私说明里要把这个差异写清楚。

定位权限要区分粗略和精确,并保留拒绝后的业务降级
private async requestLocationPermissions(): Promise<boolean> {
const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const hostContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
try {
let approximateGranted = await this.checkLocationPermissionGrant('ohos.permission.APPROXIMATELY_LOCATION') ===
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
let preciseGranted = await this.checkLocationPermissionGrant('ohos.permission.LOCATION') ===
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
let pendingPermissions: Array<Permissions> = [];
if (!approximateGranted && !preciseGranted) {
pendingPermissions = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
} else if (approximateGranted && !preciseGranted) {
pendingPermissions = ['ohos.permission.LOCATION'];
}
if (pendingPermissions.length > 0) {
const result: PermissionRequestResult = await atManager.requestPermissionsFromUser(hostContext, pendingPermissions);
for (let index = 0; index < result.authResults.length; index++) {
const granted = result.authResults[index] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
const permissionName = pendingPermissions[index];
if (permissionName === 'ohos.permission.APPROXIMATELY_LOCATION' && granted) {
approximateGranted = true;
}
if (permissionName === 'ohos.permission.LOCATION' && granted) {
preciseGranted = true;
approximateGranted = true;
}
}
}
this.locationPermissionReady = approximateGranted || preciseGranted;
this.preciseLocationReady = preciseGranted;
if (this.locationPermissionReady && !this.preciseLocationReady) {
this.currentLocationStatus = '当前为模糊定位,回忆点位可能有偏差。';
}
console.info(`[LocationTrace] permissions approximate=${approximateGranted} precise=${preciseGranted}`);
return this.locationPermissionReady;
} catch (error) {
const err = error as BusinessError;
相机和生物认证要有拒绝路径
相机权限是拍摄入口,生物认证是保险箱入口。两者都要在用户操作时申请,并在拒绝后给出明确状态,而不是让按钮静默失效。
保险箱尤其要注意:权限未授予或认证失败时,不能泄露私密内容;页面应该保留锁定状态,并说明需要认证后查看。

相机和生物认证权限都要有拒绝后的用户可见状态
建议把隐私说明整理成表格:权限、触发按钮、使用数据、是否上传、拒绝后表现。这个表可以直接服务发布材料和社区问答。
工程验收表
| 检查项 | 通过标准 |
|---|---|
| 权限全集 | module.json5 里的每个权限都能找到业务用途。 |
| 触发时机 | 权限在用户触发相关能力时申请,不提前打扰。 |
| 拒绝路径 | 拒绝后页面给出明确文案和可用降级能力。 |
| 隐私说明 | 照片、定位、Key、保险箱的使用边界写清楚。 |
真机复测口令
把 module.json5 的权限逐条映射到用户动作:相机对应拍摄,定位对应地图记忆,生物认证对应保险箱,网络对应在线 ARK 能力,传感器对应握姿或空间感知。每个权限都要能说清“什么时候申请、为什么申请、拒绝后怎么降级”。
复测时至少拒绝一次相机权限、定位权限和生物认证。拒绝后页面不能静默失败,也不能绕过隐私边界。尤其是保险箱,认证失败时必须停留在锁定态,不能为了展示效果提前渲染私密记录。
今日练习
- 为每个权限补一行“触发按钮、使用数据、拒绝后表现”。
- 拒绝相机权限后再点拍摄,检查状态文案是否明确。
- 拒绝生物认证后进入保险箱,确认私密内容仍不可见。
更多推荐



所有评论(0)