| @@ -0,0 +1,184 @@ |
| +# Feature Dev-Design - 开发设计方案 |
| + |
| +## 特性信息 |
| + |
| +- **特性名称**: notification-switch-fixed-mdm |
| +- **需求描述**: MDM 管控场景下支持应用通知开关固定(Fixed)能力,ANS 根据 MDM 配置的应用列表强制固定通知开关状态,SystemUI 查询固定列表用于设置页开关屏蔽展示 |
| +- **创建时间**: 2026-08-24 |
| +- **文档版本**: v1.0 |
| + |
| +--- |
| + |
| +## 1. 开发概述 |
| + |
| +### 1.1 需求背景 |
| + |
| +MDM kit 需要支持设置应用通知权限静默授权打开。ANS 需要根据 MDM 配置的应用列表: |
| + |
| +1. **强制打开**该应用的通知开关(静默授权) |
| +2. 该应用列表的通知开关**无法关闭**(固定为关的应用同理无法打开) |
| +3. SystemUI 需要从 ANS 查询该应用列表,用于设置页面开关的展示屏蔽操作 |
| + |
| +### 1.2 核心概念 |
| + |
| +**通知开关固定(Notification Switch Fixed)**:应用通知开关被管理员策略固定后,开关值由策略决定(强制开/强制关),任何一方(用户、SystemUI、应用)均不可修改。 |
| + |
| +### 1.3 数据流 |
| + |
| +``` |
| +MDM(EDM服务) ──配置固定列表──▶ ANS(存储固定策略 + 强制生效) |
| + │ |
| +SystemUI ◀──查询固定列表──────────┤(设置页开关置灰/屏蔽) |
| + │ |
| +用户/应用 ◀──SetNotificationsEnabledForSpecifiedBundle 被拒绝(1600030)──┤ |
| +``` |
| + |
| +### 1.4 接口分布 |
| + |
| +| 调用方 | 路径 | 接口 | |
| +|---|---|---| |
| +| MDM/EDM | inner API(C++) | `SetNotificationSwitchFixedForBundles` / `GetNotificationSwitchFixedForBundles` | |
| +| SystemUI | ArkTS systemapi | `getNotificationSwitchFixedBundles()`(仅 Promise) | |
| + |
| +公共 API 只新增 1 个查询函数,无公共设置接口,管控入口收敛在 EDM 侧权限体系内。 |
| + |
| +--- |
| + |
| +## 2. 接口设计 |
| + |
| +### 2.1 Inner API(`interfaces/inner_api/notification_helper.h`) |
| + |
| +```cpp |
| +/** |
| + * @brief Sets the fixed status of application notification switches in batches (full replacement). |
| + * |
| + * The policy is managed by bundle name. Once fixed, the switch value is forcibly applied (silently |
| + * authorized when fixed on) and cannot be modified through SetNotificationsEnabledForSpecifiedBundle. |
| + * An empty vector clears all fixed configs. |
| + * |
| + * @param fixedSwitches Pairs of bundle name and fixed switch status (true: forced on, false: forced off). |
| + * @return Returns ERR_OK on success, others on failure. |
| + */ |
| +static ErrCode SetNotificationSwitchFixedForBundles(const std::vector<std::pair<std::string, bool>> &fixedSwitches); |
| + |
| +/** |
| + * @brief Obtains the fixed status of application notification switches. |
| + * |
| + * @param fixedSwitches Returns pairs of bundle name and fixed switch status. |
| + * @return Returns ERR_OK on success, others on failure. |
| + */ |
| +static ErrCode GetNotificationSwitchFixedForBundles(std::vector<std::pair<std::string, bool>> &fixedSwitches); |
| +``` |
| + |
| +**设计决策**: |
| + |
| +| 决策点 | 结论 | 依据 | |
| +|---|---|---| |
| +| 数据结构 | `vector<pair<string, bool>>`(纯包名) | 与 MDM 先例 `disableNotificationFeature(Array<string>)` 一致;策略为设备级 MDM 管控,无需 uid | |
| +| 设置语义 | 全量替换(空列表 = 清空所有固定配置) | MDM 策略为快照式同步;解除固定无需独立 remove 接口 | |
| +| 生效行为 | 设置固定时 ANS 同步将应用开关强制为固定值(静默授权);解除固定后保持当前值、恢复可修改 | 对应"强制打开通知开关" | |
| +| 同步范围 | 仅本机持久化,不随分布式同步 | MDM 管控为设备级策略 | |
| + |
| +### 2.2 ArkTS 查询接口(SystemUI 用,唯一公共 API) |
| + |
| +interface_sdk-js `api/@ohos.notificationManager.d.ts` 新增: |
| + |
| +```ts |
| +/** |
| + * Obtains the fixed status of application notification switches. This API uses a promise to return the result. |
| + * |
| + * The notification switch of the applications in the returned map is fixed by the administrator policy |
| + * and cannot be modified. The caller (such as SystemUI) can use the result to disable the corresponding |
| + * notification switch entry on the settings page. |
| + * |
| + * @permission ohos.permission.NOTIFICATION_CONTROLLER |
| + * @returns { Promise<Map<string, boolean>> } Promise used to return the key-value pair set of the application |
| + * notification switch fixed status. The key indicates the application bundle name, and the value |
| + * indicates the fixed switch status. The value **true** means that the notification switch is forced on, |
| + * and the value **false** means that the notification switch is forced off. An empty map indicates |
| + * that no application notification switch is fixed. |
| + * @throws { BusinessError } 201 - Permission denied. |
| + * @throws { BusinessError } 202 - Not system application to call the interface. |
| + * @throws { BusinessError } 1600001 - Internal error. |
| + * @throws { BusinessError } 1600002 - Marshalling or unmarshalling error. |
| + * @throws { BusinessError } 1600003 - Failed to connect to the service. |
| + * @syscap SystemCapability.Notification.Notification |
| + * @systemapi |
| + * @stagemodelonly |
| + * @since 27 dynamic&static |
| + */ |
| +function getNotificationSwitchFixedBundles(): Promise<Map<string, boolean>>; |
| +``` |
| + |
| +风格对齐 since 23 的 ByBundles 家族(仅 Promise、`@stagemodelonly`、权限 `NOTIFICATION_CONTROLLER`)。 |
| + |
| +### 2.3 新增错误码 |
| + |
| +`frameworks/core/common/include/ans_inner_errors.h`(当前已用至 1600029): |
| + |
| +```cpp |
| +const int32_t ERROR_NOTIFICATION_SWITCH_FIXED = 1600030; // The notification switch of the application is fixed and cannot be modified. |
| +``` |
| + |
| +对外错误码描述:`1600030 - 应用通知开关已被固定,无法修改。` |
| + |
| +--- |
| + |
| +## 3. 存量接口行为约束(服务端强制,不依赖客户端) |
| + |
| +| 存量接口 | 固定后的行为 | |
| +|---|---| |
| +| `SetNotificationsEnabledForSpecifiedBundle` | bundle 在固定列表中 → 返回 1600030(设置与固定值相同也拒绝,保证语义唯一) | |
| +| `IsNotificationEnabled` | 返回固定状态(固定开 → true,固定关 → false) | |
| +| `GetAllNotificationEnabledBundles` | 包含固定为开的应用 | |
| +| `publish` / 发布链路授权检查 | 固定为开 → 视为已授权,可发布;固定为关 → 维持 1600004 | |
| +| `requestEnableNotification` | 固定为开的应用直接返回成功,不再拉起授权弹窗 | |
| + |
| +--- |
| + |
| +## 4. 实现落点 |
| + |
| +| 层 | 位置 | 变更 | |
| +|---|---|---| |
| +| API 声明 | interface_sdk-js `api/@ohos.notificationManager.d.ts` + 中文文档 | 新增 1 个查询函数 + 1600030 错误码说明 | |
| +| NAPI 绑定 | `frameworks/ets/` + `frameworks/js/`(两套同步) | 模块注册、`Map<string, boolean>` 构造(参考 priority 系列现有实现) | |
| +| IPC | `frameworks/ans/ans_manager.idl` → 重新生成 proxy/stub | 新增 2 方法(set + get),get 同时供 NAPI 与 inner 转发 | |
| +| SDK | `frameworks/ans/src/notification_helper.cpp` + `frameworks/core/src/ans_notification.cpp` | inner API 转发实现 | |
| +| 服务端 | `services/ans/src/advanced_notification_manager/` | 2 个新方法 + `SetNotificationsEnabledForSpecifiedBundle` 固定校验 + `IsNotificationEnabled`/发布链路读取固定状态 | |
| +| 持久化 | `services/ans/src/notification_preferences*.cpp`(或 rdb) | 固定策略按包名存储,跨重启生效(schema 变更需评审) | |
| +| 错误码 | `frameworks/core/common/include/ans_inner_errors.h` + js/ets 错误映射 | 1600030 | |
| +| 版本脚本 | 对应 `*.map` | 新增符号 | |
| +| 测试 | `services/ans/test/unittest/` + napi 单测 | 新接口 + 固定约束行为用例 | |
| + |
| +--- |
| + |
| +## 5. 示例代码 |
| + |
| +```ts |
| +import { notificationManager } from '@kit.NotificationKit'; |
| +import { BusinessError } from '@kit.BasicServicesKit'; |
| + |
| +// SystemUI 查询固定列表,屏蔽设置页开关展示 |
| +notificationManager.getNotificationSwitchFixedBundles().then((data: Map<string, boolean>) => { |
| + console.info(`getNotificationSwitchFixedBundles success, data: ${JSON.stringify(data)}`); |
| + // 对 data 中的应用:设置页通知开关置灰/隐藏 |
| +}).catch((err: BusinessError) => { |
| + console.error(`getNotificationSwitchFixedBundles failed, code is ${err.code}, message is ${err.message}`); |
| +}); |
| +``` |
| + |
| +--- |
| + |
| +## 6. 多用户说明 |
| + |
| +策略按包名全局管理(设备级 MDM 策略);服务端读取固定状态时若需区分用户,在服务端内部结合调用上下文 userId 解析,不暴露到接口。 |
| + |
| +--- |
| + |
| +## 7. 待确认事项 |
| + |
| +1. `@since` 版本号(暂定 27,需与版本火车对齐) |
| +2. inner 设置接口的权限:仅 `NOTIFICATION_CONTROLLER`,还是叠加 EDM 专用校验(如校验调用方是 EDM 进程) |
| +3. Slot 级开关(`setNotificationEnableSlot`)是否同样受固定约束(暂定仅应用级) |
| +4. 与 `disableNotificationFeature` 策略冲突时的优先级(暂定固定策略优先) |
| +5. 固定策略是否区分 userId 存储(暂定设备级统一存储) |
🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)
🟡 未明确固定为关时 requestEnableNotification 的行为
位置:
L135| 严重程度: Medium❓ 问题描述
对于存量接口的约束,仅说明了固定为开时
requestEnableNotification的行为,遗漏了固定为关时的行为描述。如果固定为关,应用调用此接口应当直接返回失败,不应拉起弹窗或无响应。行为不明确会导致实现时产生歧义。💡 修复建议
修改建议:在设计文档中补充固定为关时
requestEnableNotification的明确行为(如直接返回失败或对应错误码)。135: |
requestEnableNotification| 固定为开的应用直接返回成功,不再拉起授权弹窗;固定为关的应用直接返回失败 |