已开启
feat(ans): add dev design for notification switch fixed by mdm policy #5008
feat(ans): add dev design for notification switch fixed by mdm policy #5008
已开启
wangsen1994创建于 27 天前
1 个文件变更+184-0
@@ -0,0 +1,184 @@
1+# Feature Dev-Design - 开发设计方案
2+ 
3+## 特性信息
4+ 
5+- **特性名称**: notification-switch-fixed-mdm
6+- **需求描述**: MDM 管控场景下支持应用通知开关固定(Fixed)能力,ANS 根据 MDM 配置的应用列表强制固定通知开关状态,SystemUI 查询固定列表用于设置页开关屏蔽展示
7+- **创建时间**: 2026-08-24
8+- **文档版本**: v1.0
9+ 
10+---
11+ 
12+## 1. 开发概述
13+ 
14+### 1.1 需求背景
15+ 
16+MDM kit 需要支持设置应用通知权限静默授权打开。ANS 需要根据 MDM 配置的应用列表:
17+ 
18+1. **强制打开**该应用的通知开关(静默授权)
19+2. 该应用列表的通知开关**无法关闭**(固定为关的应用同理无法打开)
20+3. SystemUI 需要从 ANS 查询该应用列表,用于设置页面开关的展示屏蔽操作
21+ 
22+### 1.2 核心概念
23+ 
24+**通知开关固定(Notification Switch Fixed)**:应用通知开关被管理员策略固定后,开关值由策略决定(强制开/强制关),任何一方(用户、SystemUI、应用)均不可修改。
25+ 
26+### 1.3 数据流
27+ 
28+```
29+MDM(EDM服务) ──配置固定列表──▶ ANS(存储固定策略 + 强制生效)
30+
31+SystemUI ◀──查询固定列表──────────┤(设置页开关置灰/屏蔽)
32+
33+用户/应用 ◀──SetNotificationsEnabledForSpecifiedBundle 被拒绝(1600030)──┤
34+```
35+ 
36+### 1.4 接口分布
37+ 
38+| 调用方 | 路径 | 接口 |
39+|---|---|---|
40+| MDM/EDM | inner API(C++) | `SetNotificationSwitchFixedForBundles` / `GetNotificationSwitchFixedForBundles` |
41+| SystemUI | ArkTS systemapi | `getNotificationSwitchFixedBundles()`(仅 Promise) |
42+ 
43+公共 API 只新增 1 个查询函数,无公共设置接口,管控入口收敛在 EDM 侧权限体系内。
44+ 
45+---
46+ 
47+## 2. 接口设计
48+ 
49+### 2.1 Inner API(`interfaces/inner_api/notification_helper.h`)
50+ 
51+```cpp
52+/**
53+ * @brief Sets the fixed status of application notification switches in batches (full replacement).
54+ *
55+ * The policy is managed by bundle name. Once fixed, the switch value is forcibly applied (silently
56+ * authorized when fixed on) and cannot be modified through SetNotificationsEnabledForSpecifiedBundle.
57+ * An empty vector clears all fixed configs.
58+ *
59+ * @param fixedSwitches Pairs of bundle name and fixed switch status (true: forced on, false: forced off).
60+ * @return Returns ERR_OK on success, others on failure.
61+ */
62+static ErrCode SetNotificationSwitchFixedForBundles(const std::vector<std::pair<std::string, bool>> &fixedSwitches);
63+ 
64+/**
65+ * @brief Obtains the fixed status of application notification switches.
66+ *
67+ * @param fixedSwitches Returns pairs of bundle name and fixed switch status.
68+ * @return Returns ERR_OK on success, others on failure.
69+ */
70+static ErrCode GetNotificationSwitchFixedForBundles(std::vector<std::pair<std::string, bool>> &fixedSwitches);
71+```
72+ 
73+**设计决策**
74+ 
75+| 决策点 | 结论 | 依据 |
76+|---|---|---|
77+| 数据结构 | `vector<pair<string, bool>>`(纯包名) | 与 MDM 先例 `disableNotificationFeature(Array<string>)` 一致;策略为设备级 MDM 管控,无需 uid |
78+| 设置语义 | 全量替换(空列表 = 清空所有固定配置) | MDM 策略为快照式同步;解除固定无需独立 remove 接口 |
79+| 生效行为 | 设置固定时 ANS 同步将应用开关强制为固定值(静默授权);解除固定后保持当前值、恢复可修改 | 对应"强制打开通知开关" |
80+| 同步范围 | 仅本机持久化,不随分布式同步 | MDM 管控为设备级策略 |
81+ 
82+### 2.2 ArkTS 查询接口(SystemUI 用,唯一公共 API)
83+ 
84+interface_sdk-js `api/@ohos.notificationManager.d.ts` 新增:
85+ 
86+```ts
87+/**
88+ * Obtains the fixed status of application notification switches. This API uses a promise to return the result.
89+ *
90+ * The notification switch of the applications in the returned map is fixed by the administrator policy
91+ * and cannot be modified. The caller (such as SystemUI) can use the result to disable the corresponding
92+ * notification switch entry on the settings page.
93+ *
94+ * @permission ohos.permission.NOTIFICATION_CONTROLLER
95+ * @returns { Promise<Map<string, boolean>> } Promise used to return the key-value pair set of the application
96+ * notification switch fixed status. The key indicates the application bundle name, and the value
97+ * indicates the fixed switch status. The value **true** means that the notification switch is forced on,
98+ * and the value **false** means that the notification switch is forced off. An empty map indicates
99+ * that no application notification switch is fixed.
100+ * @throws { BusinessError } 201 - Permission denied.
101+ * @throws { BusinessError } 202 - Not system application to call the interface.
102+ * @throws { BusinessError } 1600001 - Internal error.
103+ * @throws { BusinessError } 1600002 - Marshalling or unmarshalling error.
104+ * @throws { BusinessError } 1600003 - Failed to connect to the service.
105+ * @syscap SystemCapability.Notification.Notification
106+ * @systemapi
107+ * @stagemodelonly
108+ * @since 27 dynamic&static
109+ */
110+function getNotificationSwitchFixedBundles(): Promise<Map<string, boolean>>;
111+```
112+ 
113+风格对齐 since 23 的 ByBundles 家族(仅 Promise、`@stagemodelonly`、权限 `NOTIFICATION_CONTROLLER`)。
114+ 
115+### 2.3 新增错误码
116+ 
117+`frameworks/core/common/include/ans_inner_errors.h`(当前已用至 1600029):
118+ 
119+```cpp
120+const int32_t ERROR_NOTIFICATION_SWITCH_FIXED = 1600030; // The notification switch of the application is fixed and cannot be modified.
121+```
122+ 
123+对外错误码描述:`1600030 - 应用通知开关已被固定,无法修改。`
124+ 
125+---
126+ 
127+## 3. 存量接口行为约束(服务端强制,不依赖客户端)
128+ 
129+| 存量接口 | 固定后的行为 |
130+|---|---|
131+| `SetNotificationsEnabledForSpecifiedBundle` | bundle 在固定列表中 → 返回 1600030(设置与固定值相同也拒绝,保证语义唯一) |
132+| `IsNotificationEnabled` | 返回固定状态(固定开 → true,固定关 → false) |
133+| `GetAllNotificationEnabledBundles` | 包含固定为开的应用 |
134+| `publish` / 发布链路授权检查 | 固定为开 → 视为已授权,可发布;固定为关 → 维持 1600004 |
135+| `requestEnableNotification` | 固定为开的应用直接返回成功,不再拉起授权弹窗 |
A
Aafwk_helper27 天前

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟡 未明确固定为关时 requestEnableNotification 的行为

位置: L135 | 严重程度: Medium

❓ 问题描述

对于存量接口的约束,仅说明了固定为开时 requestEnableNotification 的行为,遗漏了固定为关时的行为描述。如果固定为关,应用调用此接口应当直接返回失败,不应拉起弹窗或无响应。行为不明确会导致实现时产生歧义。

💡 修复建议

修改建议:在设计文档中补充固定为关时 requestEnableNotification 的明确行为(如直接返回失败或对应错误码)。

135: | requestEnableNotification | 固定为开的应用直接返回成功,不再拉起授权弹窗;固定为关的应用直接返回失败 |


likedislike
136+ 
137+---
138+ 
139+## 4. 实现落点
140+ 
141+| 层 | 位置 | 变更 |
142+|---|---|---|
143+| API 声明 | interface_sdk-js `api/@ohos.notificationManager.d.ts` + 中文文档 | 新增 1 个查询函数 + 1600030 错误码说明 |
144+| NAPI 绑定 | `frameworks/ets/` + `frameworks/js/`(两套同步) | 模块注册、`Map<string, boolean>` 构造(参考 priority 系列现有实现) |
145+| IPC | `frameworks/ans/ans_manager.idl` → 重新生成 proxy/stub | 新增 2 方法(set + get),get 同时供 NAPI 与 inner 转发 |
146+| SDK | `frameworks/ans/src/notification_helper.cpp` + `frameworks/core/src/ans_notification.cpp` | inner API 转发实现 |
147+| 服务端 | `services/ans/src/advanced_notification_manager/` | 2 个新方法 + `SetNotificationsEnabledForSpecifiedBundle` 固定校验 + `IsNotificationEnabled`/发布链路读取固定状态 |
148+| 持久化 | `services/ans/src/notification_preferences*.cpp`(或 rdb) | 固定策略按包名存储,跨重启生效(schema 变更需评审) |
149+| 错误码 | `frameworks/core/common/include/ans_inner_errors.h` + js/ets 错误映射 | 1600030 |
150+| 版本脚本 | 对应 `*.map` | 新增符号 |
151+| 测试 | `services/ans/test/unittest/` + napi 单测 | 新接口 + 固定约束行为用例 |
152+ 
153+---
154+ 
155+## 5. 示例代码
156+ 
157+```ts
158+import { notificationManager } from '@kit.NotificationKit';
159+import { BusinessError } from '@kit.BasicServicesKit';
160+ 
161+// SystemUI 查询固定列表,屏蔽设置页开关展示
162+notificationManager.getNotificationSwitchFixedBundles().then((data: Map<string, boolean>) => {
163+ console.info(`getNotificationSwitchFixedBundles success, data: ${JSON.stringify(data)}`);
164+ // 对 data 中的应用:设置页通知开关置灰/隐藏
165+}).catch((err: BusinessError) => {
166+ console.error(`getNotificationSwitchFixedBundles failed, code is ${err.code}, message is ${err.message}`);
167+});
168+```
169+ 
170+---
171+ 
172+## 6. 多用户说明
173+ 
174+策略按包名全局管理(设备级 MDM 策略);服务端读取固定状态时若需区分用户,在服务端内部结合调用上下文 userId 解析,不暴露到接口。
A
Aafwk_helper27 天前

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟡 全局包名策略可能导致多用户越权管控

位置: L174 | 严重程度: Medium

❓ 问题描述

设计文档中规定“策略按包名全局管理”。在 OpenHarmony 多用户场景下,不同 userId 可安装相同包名的应用。设备级全局包名管控会导致 A 用户的 MDM 固定策略波及到 B 用户的同名应用,这可能不符合 MDM 针对特定用户管控的预期。

💡 修复建议

修改建议:明确管控策略的生效范围。如果确实为设备级全局生效,需说明副作用;若 MDM 意图仅管控当前用户,建议按 userId + bundleName 维度存储策略。

174: 策略存储建议按 userId + bundleName 维度管理,或明确说明全局策略对所有用户的同名应用均生效的副作用。


likedislike
175+ 
176+---
177+ 
178+## 7. 待确认事项
179+ 
180+1. `@since` 版本号(暂定 27,需与版本火车对齐)
181+2. inner 设置接口的权限:仅 `NOTIFICATION_CONTROLLER`,还是叠加 EDM 专用校验(如校验调用方是 EDM 进程)
A
Aafwk_helper27 天前

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟠 Inner API 缺失 EDM 进程级权限校验设计

位置: L181 | 严重程度: High

❓ 问题描述

在“待确认事项”中,Inner 设置接口的权限被标记为待确认。如果该接口仅校验 NOTIFICATION_CONTROLLER 权限,则任何拥有此权限的系统应用均可修改通知开关固定状态,这绕过了 MDM 管控入口,会导致严重的越权管控漏洞。管控类接口必须严格限制调用源。

💡 修复建议

修改建议:在设计阶段明确该 Inner API 必须校验调用方为 EDM 进程,并叠加 EDM 专用权限,而不能仅凭 NOTIFICATION_CONTROLLER 放行。

181: 2. inner 设置接口的权限:必须校验调用方为 EDM 进程并叠加 EDM 专用校验,不得仅凭 NOTIFICATION_CONTROLLER 放行


likedislike
182+3. Slot 级开关(`setNotificationEnableSlot`)是否同样受固定约束(暂定仅应用级)
A
Aafwk_helper27 天前

🤖 AI 代码检视意见(回复本评论可解决检视意见,点击被检视代码行左侧的小头像可收起检视意见)


🟡 Slot 级开关不受约束可能导致管控失效

位置: L182 | 严重程度: Medium

❓ 问题描述

如果应用级开关被 MDM 固定为开,但 Slot 级开关仍可被用户或应用关闭,将导致应用实际上无法发布通知,破坏了 MDM “强制授权”的管控意图。这属于策略层级不一致的架构风险。

💡 修复建议

修改建议:在设计文档中明确固定策略对 Slot 级开关的影响,建议固定为开时强制所有 Slot 开关为开且不可关闭,以保障管控策略生效。

182: 3. Slot 级开关(setNotificationEnableSlot)同样受固定约束(固定为开时强制开启,固定为关时强制关闭)


likedislike
183+4.`disableNotificationFeature` 策略冲突时的优先级(暂定固定策略优先)
184+5. 固定策略是否区分 userId 存储(暂定设备级统一存储)