1# DRM解决方案开发指导 2<!--Kit: Drm Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @qin_wei_jie--> 5<!--Designer: @chris2981--> 6<!--Tester: @xdlinc--> 7<!--Adviser: @zengyawen--> 8DRM 解决方案插件实现 DRM HDI 接口(链接),DRM Kit的DRM框架将通过HDI接口加载DRM解决方案插件。 9 10插件由DRM解决方案集成方开发,放置在设备的 /vendor 分区中。 11 12OpenHarmony HDI 插件驱动服务开发流程参考[HDF驱动开发流程](../../../device-dev/driver/driver-hdf-manage.md),DRM HDI API 的 IDL 在 ohos/drivers/interface/drm/v1_0 目录中定义,其中 v1_0 对应不同版本的 HDI API 版本号,需根据实际调用的 HDI API 版本进行修改。 13 14DRM HDI API 的 IDL 构建完成后,可以在`//ohos/out/产品型号/gen/drivers/interface/drm/v1_0/`中找到生成的相应版本的 .h 和 .cpp文件。 15实现 DRM 解决方案插件的步骤如下(以clearplay为例): 16 171. [开发插件](#开发插件) 18 - 模块添加 19 - 驱动入口实现 20 - HDI接口实现 21 - 编译配置 22 - 部件配置 23 - 部件编译入口配置 24 - 服务代码编译 25 262. [DRM解决方案插件服务配置](#drm解决方案插件服务配置) 27 - hcs配置 28 - host用户与组配置 29 - 动态加载 30 313. [添加SELinux权限](#添加selinux权限) 32 33## 开发插件 34 35### 模块添加 36 37创建插件目录,参考如下: 38``` 39//drivers/peripheral/clearplay 40. 41├── BUILD.gn # 模块编译BUILD.gn。 42├── bundle.json # 部件配置。 43├── hdi_service # DRM解决方案HDI服务代码。 44│ ├── BUILD.gn # DRM解决方案HDI服务代码编译BUILD.gn。 45│ ├── common # DRM解决方案HDI服务依赖的工具类代码,包含json解析、base64编解码。 46│ ├── include # RM解决方案HDI服务实现头文件。 47│ └── src # DRM解决方案HDI服务实现代码。 48├── interfaces # DRM解决方案HDI服务能力接口。 49│ ├── BUILD.gn # DRM解决方案HDI服务能力接口编译BUILD.gn。 50│ ├── include # DRM解决方案HDI服务能力接口文件。 51│ └── src # DRM解决方案HDI服务能力接口实现。 52└── README_zh.md # DRM解决方案HDI服务组件说明。 53``` 54 55### 驱动入口实现 56 57驱动入口实现可以参考`//ohos/out/产品型号/gen/drivers/interface/drm/v1_0/media_key_system_factory_driver.cpp`,需要驱动入口实现中修改以下几点,并手动配置编译: 58 59``` 60using namespace OHOS::HDI::Drm::V1_0; // 1. 本文中 V1_0 为 HDI API 版本号,需根据不同版本进行变更。 61 62struct HdfMediaKeySystemFactoryHost { 63 struct IDeviceIoService ioService; 64 OHOS::sptr<OHOS::IRemoteObject> stub; 65}; 66static int HdfMediaKeySystemFactoryDriverBind(struct HdfDeviceObject *deviceObject) 67{ 68 auto *hdfMediaKeySystemFactoryHost = new (std::nothrow) HdfMediaKeySystemFactoryHost; 69 if (hdfMediaKeySystemFactoryHost == nullptr) { 70 HDF_LOGE("%{public}s: failed to create create HdfMediaKeySystemFactoryHost object", __func__); 71 return HDF_FAILURE; 72 } 73 int ret = HdfDeviceObjectSetInterfaceDesc(deviceObject, "ohos.hdi.drm.v1_0.IMediaKeySystemFactory"); // 2. 服务绑定接口描述符,便于DRM框架服务通过接口描述符获取到DRM解决方案HDI服务,根据不同的HDI API版本号调整。 74 if (ret != HDF_SUCCESS) { 75 HDF_LOGE("%{public}s: failed to HdfDeviceObjectSetInterfaceDesc", __func__); 76 } 77 78 hdfMediaKeySystemFactoryHost->ioService.Dispatch = MediaKeySystemFactoryDriverDispatch; 79 hdfMediaKeySystemFactoryHost->ioService.Open = NULL; 80 hdfMediaKeySystemFactoryHost->ioService.Release = NULL; 81 82 auto serviceImpl = OHOS::HDI::Drm::V1_0::IMediaKeySystemFactory::Get("clearplay_service", true); // 3. 获取DRM解决方案HDI服务实例。 83 if (serviceImpl == nullptr) { 84 HDF_LOGE("%{public}s: failed to get of implement service", __func__); 85 delete hdfMediaKeySystemFactoryHost; 86 return HDF_FAILURE; 87 } 88 89 hdfMediaKeySystemFactoryHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, 90 OHOS::HDI::Drm::V1_0::IMediaKeySystemFactory::GetDescriptor()); // 4. 获取DRM解决方案HDI服务实现对应的Stub对象。 91 if (hdfMediaKeySystemFactoryHost->stub == nullptr) { 92 HDF_LOGE("%{public}s: failed to get stub object", __func__); 93 delete hdfMediaKeySystemFactoryHost; 94 return HDF_FAILURE; 95 } 96 97 deviceObject->service = &hdfMediaKeySystemFactoryHost->ioService; 98 return HDF_SUCCESS; 99} 100 101static int32_t MediaKeySystemFactoryDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, 102 struct HdfSBuf *reply) 103{ 104 auto *hdfMediaKeySystemFactoryHost = 105 CONTAINER_OF(client->device->service, struct HdfMediaKeySystemFactoryHost, ioService); 106 107 OHOS::MessageParcel *dataParcel = nullptr; 108 OHOS::MessageParcel *replyParcel = nullptr; 109 OHOS::MessageOption option; 110 111 if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) { 112 HDF_LOGE("%{public}s: invalid data sbuf object to dispatch", __func__); 113 return HDF_ERR_INVALID_PARAM; 114 } 115 if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) { 116 HDF_LOGE("%{public}s: invalid reply sbuf object to dispatch", __func__); 117 return HDF_ERR_INVALID_PARAM; 118 } 119 120 return hdfMediaKeySystemFactoryHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); 121} 122``` 123 124### HDI接口实现 125 126实现可以参考`//ohos/out/产品型号/gen/drivers/interface/drm/v1_0/`中自动生成的.cpp文件,可以按照业务需要进行定制化修改或新增文件,如`media_key_system_factory_service.cpp`: 127 128``` 129extern "C" IMediaKeySystemFactory *MediaKeySystemFactoryImplGetInstance(void) 130{ 131 // 请新增实现。 132 return new (std::nothrow) MediaKeySystemFactoryService(); 133} 134 135int32_t MediaKeySystemFactoryService::IsMediaKeySystemSupported(const std::string& name, const std::string& mimeType, 136 OHOS::HDI::Drm::V1_0::ContentProtectionLevel level, bool& isSupported) 137{ 138 // 请新增实现。 139 return HDF_SUCCESS; 140} 141 142int32_t MediaKeySystemFactoryService::CreateMediaKeySystem(sptr<OHOS::HDI::Drm::V1_0::IMediaKeySystem>& mediaKeySystem) 143{ 144 // 请新增实现。 145 return HDF_SUCCESS; 146} 147 148int32_t MediaKeySystemFactoryService::GetMediaKeySystemDescription(std::string& name, std::string& uuid) 149{ 150 // 请新增实现。 151 return HDF_SUCCESS; 152} 153 154``` 155 156### 编译配置 157//drivers/peripheral/clearplay/BUILD.gn 158 159``` 160if (defined(ohos_lite)) { 161 group("clearplay_entry") { 162 deps = [] 163 } 164} else { 165 group("clearplay_entry") { 166 deps = [ 167 "./hdi_service:hdf_clearplay_service", 168 "./interfaces:hdf_clearplay_interfaces", 169 ] 170 } 171} 172``` 173 174//drivers/peripheral/clearplay/hdi_service/BUILD.gn 175``` 176import("//build/ohos.gni") 177 178ohos_shared_library("libmedia_key_system_factory_clearplay_service_1.0") { 179 include_dirs = [ 180 "./include", 181 "./include/drm/v1_0", 182 "./include/drm", 183 "./../interfaces/include", 184 "./../interfaces/include/drm/v1_0", 185 "./../interfaces/include/drm", 186 "./common", 187 ] 188 sources = [ 189 "./common/base64_utils.cpp", 190 "./src/media_decrypt_module_service.cpp", 191 "./src/media_key_session_callback_service.cpp", 192 "./src/media_key_session_service.cpp", 193 "./src/media_key_system_callback_service.cpp", 194 "./src/media_key_system_factory_service.cpp", 195 "./src/media_key_system_service.cpp", 196 "./src/oem_certificate_service.cpp", 197 ] 198 199 deps = [ ] 200 201 cflags = [ 202 "-Wall", 203 "-Wextra", 204 "-Werror", 205 "-fsigned-char", 206 "-fno-common", 207 "-fno-strict-aliasing", 208 ] 209 210 external_deps = [ 211 "c_utils:utils", 212 "hdf_core:libhdf_utils", 213 "hilog:libhilog", 214 "ipc:ipc_single", 215 ] 216 217 install_images = [ chipset_base_dir ] 218 subsystem_name = "hdf" 219 part_name = "drivers_peripheral_clearplay" 220} 221 222group("hdf_clearplay_service") { 223 deps = [ ":libmedia_key_system_factory_clearplay_service_1.0" ] 224} 225``` 226 227//drivers/peripheral/clearplay/interfaces/BUILD.gn 228 229``` 230import("//build/ohos.gni") 231 232ohos_shared_library("libclearplay_driver") { 233 include_dirs = [ 234 "./include", 235 "./include/drm", 236 ] 237 238 public_configs = [ ":clearplay_imp_external_library_config" ] 239 sources = [ "./src/media_key_system_factory_driver.cpp" ] 240 241 external_deps = [ 242 "c_utils:utils", 243 "drivers_interface_drm:libdrm_stub_1.0", 244 "hdf_core:libhdf_host", 245 "hdf_core:libhdf_ipc_adapter", 246 "hdf_core:libhdf_utils", 247 "hdf_core:libhdi", 248 "hilog:libhilog", 249 "ipc:ipc_single", 250 ] 251 cflags = [ 252 "-Wall", 253 "-Wextra", 254 "-Werror", 255 "-fsigned-char", 256 "-fno-common", 257 "-fno-strict-aliasing", 258 ] 259 shlib_type = "hdi" 260 install_images = [ chipset_base_dir ] 261 subsystem_name = "hdf" 262 part_name = "drivers_peripheral_clearplay" 263} 264 265config("clearplay_imp_external_library_config") { 266 include_dirs = 267 [ "//drivers/peripheral/clearplay/interfaces/include/drm/v1_0" ] 268} 269group("hdf_clearplay_interfaces") { 270 deps = [ ":libclearplay_driver" ] 271} 272``` 273 274### 部件配置 275 276新建drivers/peripheral/clearplay/build.json用于定义新增的drivers_peripheral_clearplay部件: 277 278``` 279{ 280 "name": "@ohos/drivers_peripheral_clearplay", 281 "description": "clearplay drm device driver", 282 "version": "4.0", 283 "license": "Apache License 2.0", 284 "publishAs": "code-segment", 285 "segment": { 286 "destPath": "drivers/peripheral/clearplay" 287 }, 288 "dirs": {}, 289 "scripts": {}, 290 "component": { 291 "name": "drivers_peripheral_clearplay", 292 "subsystem": "hdf", 293 "syscap": [], 294 "adapted_system_type": ["standard"], 295 "rom": "", 296 "ram": "", 297 "deps": { 298 "components": [ 299 "bounds_checking_function", 300 "drivers_interface_drm", 301 "c_utils", 302 "hdf_core", 303 "hilog", 304 "ipc" 305 ], 306 "third_party": [] 307 }, 308 "build": { 309 "sub_component": [ 310 "//drivers/peripheral/clearplay:clearplay_entry" 311 ], 312 "inner_kits": [ 313 { 314 "type":"so", 315 "name": "//drivers/peripheral/clearplay/hdi_service:libmedia_key_system_factory_clearplay_service_1.0", 316 "header": { 317 "header_files": [ 318 "imedia_decrypt_module.h", 319 "imedia_key_session_callback.h", 320 "imedia_key_session.h", 321 "imedia_key_system_callback.h", 322 "imedia_key_system_factory.h", 323 "imedia_key_system.h", 324 "ioem_certificate.h", 325 "media_decrypt_module_proxy.h", 326 "media_decrypt_module_stub.h", 327 "media_key_session_callback_proxy.h", 328 "media_key_session_callback_stub.h", 329 "media_key_session_proxy.h", 330 "media_key_session_stub.h", 331 "media_key_system_callback_proxy.h", 332 "media_key_system_callback_stub.h", 333 "media_key_system_factory_proxy.h", 334 "media_key_system_factory_stub.h", 335 "media_key_system_proxy.h", 336 "media_key_system_stub.h", 337 "media_key_system_types.h", 338 "oem_certificate_proxy.h", 339 "oem_certificate_stub.h" 340 ], 341 "header_base": "//drivers/peripheral/clearplay/interfaces/include/drm/v1_0" 342 } 343 }, 344 { 345 "type":"so", 346 "name": "//drivers/peripheral/clearplay/interfaces:libclearplay_driver", 347 "header": { 348 "header_files": [], 349 "header_base": "//drivers/peripheral/clearplay/interfaces/include/drm/v1_0" 350 } 351 } 352 ] 353 } 354 } 355} 356``` 357### 部件编译入口配置 358 359以rk3568产品为例:`//productdefine/common/inherit/chipset_common.json` 360 361``` 362{ 363 "component": "drivers_peripheral_clearplay", 364 "features": [] 365} 366``` 367 368### 服务代码编译 369 370与编译系统部件编译类似: 371`./build.sh --product-name rk3568 --ccache --build-target drivers_peripheral_clearplay` 372编译生成的二进制文件如下: 373//ohos/out/rk3568/hdf/drivers_peripheral_clearplay/libclearplay_driver.z.so 374//ohos/out/rk3568/hdf/drivers_peripheral_clearplay/libmedia_key_system_factory_clearplay_service_1.0.z.so 375 376## DRM解决方案插件服务配置 377 378### hcs配置 379 380以rk3568产品为例,在`vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs`添加驱动服务配置 381 382``` 383clearplay :: host { 384 hostName = "clearplay_host"; // 进程名。 385 priority = 50; 386 uid = ""; // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。 387 gid = ""; // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。 388 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。 389 clearplay_device :: device { 390 device0 :: deviceNode { 391 policy = 2; 392 priority = 100; 393 moduleName = "libclearplay_driver.z.so"; // 驱动加载入口。 394 serviceName = "clearplay_service"; // 服务名称。 395 } 396 } 397} 398``` 399 400### host用户与组配置 401 402对于在hcs中新增加的host节点,需要新增配置对应进程的uid(用户ID)和gid(组ID) 403passwd文件为系统用户配置文件,存储了系统中所有用户的基本信息,这里以此为例: 404 405``` 406//base/startup/init/services/etc/passwd 407clearplay_host:x:1089:1089::/bin/false 408``` 409 410`//base/startup/init/services/etc/passwd`中每行用户信息使用“:”作为分隔符,划分为7个字段,每个字段所表示的含义如下: 411用户名:密码:UID(用户ID):GID(组ID):描述信息:主目录:默认shell 412 413group为用户组配置文件,存储了所有用户组的信息,以下为例: 414 415``` 416base/startup/init/services/etc/group 417clearplay_host:x:1089: 418``` 419 420base/startup/init/services/etc/group中每行代表一个用户组,用户组中以“:”作为分隔符,分为4个字段,每个字段的含义如下: 421组名:密码:GID(组ID):该用户组中的用户列表 422注意: 423- passwd中clearplay_host对应device_info.hcs中的uid,若device_info.hcs中uid缺省,则默认为hostName 424- group中clearplay_host对应device_info.hcs中的gid,若device_info.hcs中gid缺省,则默认为hostName 425 426### 动态加载 427 428为节约 RAM 内存占用,DRM 框架服务支持动态加载 DRM 解决方案插件,DRM 框架服务调用完解决方案插件后,及时卸载 DRM 解决方案插件,释放 RAM 内存占用,插件需通过修改服务启动属性将自身服务配置成懒加载,并加入到设备上的 DRM 框架服务懒加载列表配置文件中;HDI服务提供动态加载能力,系统启动过程中默认不加载,支持动态加载,以下为示例: 429`device_info.hcs`配置preload为2 430``` 431clearplay :: host { 432 hostName = "clearplay_host"; 433 priority = 50; 434 clearplay_device :: device { 435 device0 :: deviceNode { 436 policy = 2; 437 priority = 100; 438 preload = 2; // 设置preload为2,则系统启动过程中默认不加载,后续可手动加载。 439 moduleName = "libclearplay_driver.z.so"; 440 serviceName = "clearplay_service"; 441 } 442 } 443} 444``` 445设备上`/etc/drm/drm_plugin_lazyloding.cfg`为 DRM 框架服务懒加载列表配置文件,键值对形式,DRM 解决方案插件解决方案名为键,DRM解决方案服务名为值: 446``` 447{ 448 "plugin_services": { 449 "lazy_load_service": [ 450 "com.clearplay.drm:clearplay_service" 451 ] 452 } 453} 454``` 455 456## 添加SELinux权限 457 458selinux用于限制服务进程可访问的资源,以下给定基础的selinux配置,在此基础上按业务添加所需规则。 459 460注意:以下示例中,clearplay_host表示hcs中的hostName值,clearplay_service表示服务名称 461//base/security/selinux_adapter/sepolicy/ohos_policy/drivers/adapter/public/hdf_service_contexts 462`clearplay_service u:object_r:hdf_clearplay_service:s0` 463 464//base/security/selinux_adapter/sepolicy/ohos_policy/drivers/adapter/public/hdf_service.te 465`type hdf_clearplay_service, hdf_service_attr;` 466 467//base/security/selinux_adapter/sepolicy/ohos_policy/startup/init/public/chipset_init.te 468`allow init clearplay_host:process { rlimitinh siginh transition };` 469 470//base/security/selinux_adapter/sepolicy/ohos_policy/drivers/peripheral/clearplay/vendor/hdf_devmgr.te 471``` 472allow hdf_devmgr clearplay_host:binder { call transfer }; 473allow hdf_devmgr clearplay_host:dir { search }; 474allow hdf_devmgr clearplay_host:file { open read }; 475allow hdf_devmgr clearplay_host:process { getattr }; 476``` 477 478//base/security/selinux_adapter/sepolicy/ohos_policy/drivers/adapter/public/type.te 479`type clearplay_host, hdfdomain, domain;` 480 481//base/security/selinux_adapter/sepolicy/ohos_policy/drivers/peripheral/clearplay/vendor/clearplay_host.te(新建此目录) 482``` 483allow clearplay_host chip_prod_file:dir { search }; 484allow clearplay_host dev_console_file:chr_file { read write }; 485allow clearplay_host dev_hdf_kevent:chr_file { open read write ioctl getattr }; 486allow clearplay_host dev_unix_socket:dir { search }; 487allow clearplay_host hdf_device_manager:hdf_devmgr_class { get }; 488allow clearplay_host hdf_devmgr:binder { call transfer }; 489allow clearplay_host hdf_clearplay_service:hdf_devmgr_class { add }; 490allow clearplay_host hilog_param:file { open read map }; 491allow clearplay_host musl_param:file { open read map }; 492allow clearplay_host sa_device_service_manager:samgr_class { get }; 493allow clearplay_host samgr:binder { call }; 494allow clearplay_host sh:binder { call }; 495allow clearplay_host vendor_etc_file:dir { open read getattr search }; 496allow clearplay_host vendor_etc_file:file { open read getattr }; 497allowxperm clearplay_host dev_hdf_kevent:chr_file ioctl { 0x6202 0x6203 }; 498```