1# 使用AppServiceExtensionAbility组件实现后台服务 2<!--Kit: Ability Kit--> 3<!--Subsystem: Ability--> 4<!--Owner: @yewei0794--> 5<!--Designer: @jsjzju--> 6<!--Tester: @lixueqing513--> 7<!--Adviser: @huipeizi--> 8 9## 概述 10 11从API version 20开始,支持开发者使用[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件,为应用提供后台服务能力,其他三方应用可通过启动或连接该AppServiceExtensionAbility组件获取相应的服务。 12例如,企业部署的数据防泄漏 (DLP) 软件需要能够长期无界面长期运行,持续监听文件操作、网络流量,并拦截违规行为,可以使用AppServiceExtensionAbility组件来实现其核心的后台监控服务。 13> **说明** 14> 15> 本文将被启动或被连接的AppServiceExtensionAbility组件称为服务端,将启动或连接AppServiceExtensionAbility组件的应用组件(当前仅支持UIAbility)称为客户端。 16 17## 约束与限制 18 19### 设备限制 20 21AppServiceExtensionAbility组件当前仅支持2in1设备。 22 23### 规格限制 24 25- 应用集成AppServiceExtensionAbility组件需要申请ACL权限(ohos.permission.SUPPORT_APP_SERVICE_EXTENSION)。该ACL权限当前只对企业普通应用开放申请。 26 27- AppServiceExtensionAbility组件内不支持调用[window](../reference/apis-arkui/arkts-apis-window.md)相关API。 28 29## 运作机制 30 31开发者可以在[UIAbility](../reference/apis-ability-kit/js-apis-app-ability-uiAbility.md)中以[启动](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)或[连接](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)的方式来拉起AppServiceExtensionAbility组件。 32 33- **启动:** 客户端必须为AppServiceExtensionAbility所属应用或者在AppServiceExtensionAbility支持的应用清单(即[extensionAbilities标签](../quick-start/module-configuration-file.md#extensionabilities标签)的appIdentifierAllowList属性)中的应用才能调用[startAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)接口。 34- **连接:** 如果[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)实例未启动,客户端必须为AppServiceExtensionAbility所属应用或者在AppServiceExtensionAbility支持的应用清单(即[extensionAbilities标签](../quick-start/module-configuration-file.md#extensionabilities标签)的appIdentifierAllowList属性)中的应用才能调用[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)接口。如果实例已启动,则没有上述限制。 35 36下表展示了拉起和连接的几种场景: 37 38 39> **说明** 40> 41> “客户端是否可信”为是时,表示客户端属于服务端所属应用或已配置在appIdentifierAllowList中。为否时,表示客户端不属于服务端所属应用且未配置在appIdentifierAllowList中。 42 43| 客户端操作 | 服务端状态 | 客户端是否可信 | 结果说明 | 44| --------- | --------- | -------------------------------------------- | ---- | 45| startAppServiceExtensionAbility | 未启动 | 是 | 成功,服务端通过start方式启动,服务端状态变为已启动。 | 46| startAppServiceExtensionAbility | 未启动 | 否 | 失败,客户端不在允许列表中,无法调用启动服务。 | 47| startAppServiceExtensionAbility | 已启动 | 是 | 成功,服务端已经启动,start操作直接返回成功。 | 48| startAppServiceExtensionAbility | 已启动 | 否 | 失败,客户端不在允许列表中,无法调用启动服务。 | 49| connectAppServiceExtensionAbility | 未启动 | 是 | 成功,服务端通过connect方式启动,并建立连接。 | 50| connectAppServiceExtensionAbility | 未启动 | 否 | 失败,客户端不在允许列表中,无法启动服务端。 | 51| connectAppServiceExtensionAbility | 已启动 | 是 | 成功,服务端已启动,直接建立连接。 | 52| connectAppServiceExtensionAbility | 已启动 | 否 | 成功,服务端已启动,直接建立连接。 | 53 54 55## 实现一个后台服务 56 57在DevEco Studio工程中手动新建一个AppServiceExtensionAbility组件,具体步骤如下: 58 591. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为MyAppServiceExtAbility。 60 612. 在MyAppServiceExtAbility目录,右键选择“New > ArkTS File”,新建一个文件并命名为MyAppServiceExtAbility.ets。 62 63 64 其目录结构如下所示: 65 66 ``` 67 ├── ets 68 │ ├── MyAppServiceExtAbility 69 │ │ ├── MyAppServiceExtAbility.ets 70 └ 71 ``` 72 733. 在MyAppServiceExtAbility.ets文件中,增加导入[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)的依赖包,自定义类继承AppServiceExtensionAbility组件并实现生命周期回调。 74 75 ```ts 76 import { AppServiceExtensionAbility, Want } from '@kit.AbilityKit'; 77 import { rpc } from '@kit.IPCKit'; 78 import { hilog } from '@kit.PerformanceAnalysisKit'; 79 80 const TAG: string = '[MyAppServiceExtAbility]'; 81 const DOMAIN_NUMBER: number = 0xFF00; 82 83 class StubTest extends rpc.RemoteObject { 84 constructor(des: string) { 85 super(des); 86 } 87 88 onRemoteMessageRequest(code: number, 89 data: rpc.MessageSequence, 90 reply: rpc.MessageSequence, 91 options: rpc.MessageOption): boolean | Promise<boolean> { 92 // 处理客户端发送的消息 93 return true; 94 } 95 } 96 97 export default class MyAppServiceExtAbility extends AppServiceExtensionAbility { 98 onCreate(want: Want): void { 99 let appServiceExtensionContext = this.context; 100 hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`); 101 } 102 103 onRequest(want: Want, startId: number): void { 104 hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`); 105 } 106 107 onConnect(want: Want): rpc.RemoteObject { 108 hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`); 109 return new StubTest("test"); 110 } 111 112 onDisconnect(want: Want): void { 113 hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`); 114 } 115 116 onDestroy(): void { 117 hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy'); 118 } 119 }; 120 ``` 121 1224. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册AppServiceExtensionAbility组件,type标签需要设置为“appService”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。 123 124 ```json 125 { 126 "module": { 127 // ... 128 "extensionAbilities": [ 129 { 130 "name": "MyAppServiceExtAbility", 131 "description": "appService", 132 "type": "appService", 133 "exported": true, 134 "srcEntry": "./ets/MyAppServiceExtAbility/MyAppServiceExtAbility.ets", 135 "appIdentifierAllowList": [ 136 // 此处填写允许启动该后台服务的客户端应用的appIdentifier列表 137 ], 138 } 139 ] 140 } 141 } 142 ``` 143 144## 启动一个后台服务 145 146应用通过[startAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#startappserviceextensionability20)方法启动一个后台服务,服务的[onRequest()](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md#onrequest)回调就会被调用,并在该回调方法中接收到调用者传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用[AppServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-appServiceExtensionContext.md)的[terminateSelf()](../reference/apis-ability-kit/js-apis-inner-application-appServiceExtensionContext.md#terminateself)来自行停止,或者由另一个组件调用[stopAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#stopappserviceextensionability20)来将其停止。 147 148> **说明:** 149> 150> AppServiceExtensionAbility组件以start方式启动,并且没有连接的时候,AppServiceExtensionAbility组件进程可能被挂起(请参考[Background Tasks Kit简介](../task-management/background-task-overview.md))。 151 152- 在应用中启动一个新的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。 153 154 ```ts 155 import { common, Want } from '@kit.AbilityKit'; 156 import { hilog } from '@kit.PerformanceAnalysisKit'; 157 import { BusinessError } from '@kit.BasicServicesKit'; 158 159 const TAG: string = '[Page_AppServiceExtensionAbility]'; 160 const DOMAIN_NUMBER: number = 0xFF00; 161 162 @Entry 163 @Component 164 struct Page_AppServiceExtensionAbility { 165 build() { 166 Column() { 167 //... 168 List({ initialIndex: 0 }) { 169 ListItem() { 170 Row() { 171 //... 172 } 173 .onClick(() => { 174 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 175 let want: Want = { 176 deviceId: '', 177 bundleName: 'com.samples.stagemodelabilitydevelop', 178 abilityName: 'MyAppServiceExtAbility' 179 }; 180 context.startAppServiceExtensionAbility(want).then(() => { 181 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting AppServiceExtensionAbility.'); 182 // 成功启动后台服务 183 this.getUIContext().getPromptAction().showToast({ 184 message: 'SuccessfullyStartBackendService' 185 }); 186 }).catch((err: BusinessError) => { 187 hilog.error(DOMAIN_NUMBER, TAG, 188 `Failed to start AppServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 189 }); 190 }) 191 } 192 193 //... 194 } 195 196 //... 197 } 198 199 //... 200 } 201 } 202 ``` 203 204- 在应用中停止一个已启动的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件。 205 206 ```ts 207 import { common, Want } from '@kit.AbilityKit'; 208 import { hilog } from '@kit.PerformanceAnalysisKit'; 209 import { BusinessError } from '@kit.BasicServicesKit'; 210 211 const TAG: string = '[Page_AppServiceExtensionAbility]'; 212 const DOMAIN_NUMBER: number = 0xFF00; 213 214 @Entry 215 @Component 216 struct Page_AppServiceExtensionAbility { 217 build() { 218 Column() { 219 //... 220 List({ initialIndex: 0 }) { 221 ListItem() { 222 Row() { 223 //... 224 } 225 .onClick(() => { 226 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 227 let want: Want = { 228 deviceId: '', 229 bundleName: 'com.samples.stagemodelabilitydevelop', 230 abilityName: 'MyAppServiceExtAbility' 231 }; 232 context.stopAppServiceExtensionAbility(want).then(() => { 233 hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping AppServiceExtensionAbility.'); 234 this.getUIContext().getPromptAction().showToast({ 235 message: 'SuccessfullyStoppedAStartedBackendService' 236 }); 237 }).catch((err: BusinessError) => { 238 hilog.error(DOMAIN_NUMBER, TAG, 239 `Failed to stop AppServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); 240 }); 241 }) 242 } 243 244 //... 245 } 246 247 //... 248 } 249 250 //... 251 } 252 } 253 ``` 254 255- 已启动的[AppServiceExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md)组件停止自身。 256 257 ```ts 258 import { AppServiceExtensionAbility } from '@kit.AbilityKit'; 259 import { BusinessError } from '@kit.BasicServicesKit'; 260 import { hilog } from '@kit.PerformanceAnalysisKit'; 261 262 const TAG: string = '[MyAppServiceExtAbility]'; 263 264 export default class MyAppServiceExtAbility extends AppServiceExtensionAbility { 265 onCreate(want: Want) { 266 // 执行业务逻辑 267 this.context.terminateSelf().then(() => { 268 hilog.info(0x0000, TAG, '----------- terminateSelf succeed -----------'); 269 }).catch((error: BusinessError) => { 270 hilog.error(0x0000, TAG, `terminateSelf failed, error.code: ${error.code}, error.message: $ {error.message}`); 271 }); 272 } 273 } 274 ``` 275 276## 连接一个后台服务 277 278### 客户端连接服务端 279 280客户端可以通过[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)连接服务端(在Want对象中指定连接的目标服务),服务端的[onConnect()](../reference/apis-ability-kit/js-apis-app-ability-appServiceExtensionAbility.md#onconnect)就会被调用,并在该回调方法中接收到客户端传递过来的[Want](../reference/apis-ability-kit/js-apis-app-ability-want.md)对象。 281 282服务端的AppServiceExtensionAbility组件会在onConnect()中返回[IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象给客户端[ConnectOptions](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md)的[onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect)方法。开发者通过该IRemoteObject定义通信接口,实现客户端与服务端的RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务端的交互后,客户端需要通过调用[disconnectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectappserviceextensionability20)来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。 283 284- 使用[connectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#connectappserviceextensionability20)建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)。 285 286 ```ts 287 import { common, Want } from '@kit.AbilityKit'; 288 import { rpc } from '@kit.IPCKit'; 289 import { hilog } from '@kit.PerformanceAnalysisKit'; 290 291 const TAG: string = '[Page_AppServiceExtensionAbility]'; 292 const DOMAIN_NUMBER: number = 0xFF00; 293 294 let connectionId: number; 295 let want: Want = { 296 deviceId: '', 297 bundleName: 'com.samples.stagemodelabilitydevelop', 298 abilityName: 'MyAppServiceExtAbility' 299 }; 300 301 let options: common.ConnectOptions = { 302 onConnect(elementName, remote: rpc.IRemoteObject): void { 303 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 304 if (remote === null) { 305 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 306 return; 307 } 308 // 通过remote进行通信 309 }, 310 onDisconnect(elementName): void { 311 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 312 }, 313 onFailed(code: number): void { 314 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code)); 315 } 316 }; 317 318 @Entry 319 @Component 320 struct Page_AppServiceExtensionAbility { 321 build() { 322 Column() { 323 //... 324 List({ initialIndex: 0 }) { 325 ListItem() { 326 Row() { 327 //... 328 } 329 .onClick(() => { 330 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 331 // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入 332 connectionId = context.connectAppServiceExtensionAbility(want, options); 333 // 成功连接后台服务 334 this.getUIContext().getPromptAction().showToast({ 335 message: 'SuccessfullyConnectBackendService' 336 }); 337 hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`); 338 }) 339 } 340 341 //... 342 } 343 344 //... 345 } 346 347 //... 348 } 349 } 350 ``` 351 352- 使用[disconnectAppServiceExtensionAbility()](../reference/apis-ability-kit/js-apis-inner-application-uiAbilityContext.md#disconnectappserviceextensionability20)断开与后台服务的连接。 353 354 ```ts 355 import { common } from '@kit.AbilityKit'; 356 import { hilog } from '@kit.PerformanceAnalysisKit'; 357 import { BusinessError } from '@kit.BasicServicesKit'; 358 359 const TAG: string = '[Page_AppServiceExtensionAbility]'; 360 const DOMAIN_NUMBER: number = 0xFF00; 361 362 let connectionId: number; 363 364 @Entry 365 @Component 366 struct Page_AppServiceExtensionAbility { 367 build() { 368 Column() { 369 //... 370 List({ initialIndex: 0 }) { 371 ListItem() { 372 Row() { 373 //... 374 } 375 .onClick(() => { 376 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 377 // connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护 378 context.disconnectAppServiceExtensionAbility(connectionId).then(() => { 379 hilog.info(DOMAIN_NUMBER, TAG, 'disconnectAppServiceExtensionAbility success'); 380 // 成功断连后台服务 381 this.getUIContext().getPromptAction().showToast({ 382 message: 'SuccessfullyDisconnectBackendService' 383 }); 384 }).catch((error: BusinessError) => { 385 hilog.error(DOMAIN_NUMBER, TAG, 'disconnectAppServiceExtensionAbility failed'); 386 }); 387 }) 388 } 389 390 //... 391 } 392 393 //... 394 } 395 396 //... 397 } 398 } 399 ``` 400 401### 客户端与服务端通信 402 403客户端在[onConnect()](../reference/apis-ability-kit/js-apis-inner-ability-connectOptions.md#onconnect)中获取到[rpc.IRemoteObject](../reference/apis-ipc-kit/js-apis-rpc.md#iremoteobject)对象后便可与服务端进行通信。 404 405**客户端**:使用[sendMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#sendmessagerequest9)接口向服务端发送消息。 406 407```ts 408import { common } from '@kit.AbilityKit'; 409import { rpc } from '@kit.IPCKit'; 410import { hilog } from '@kit.PerformanceAnalysisKit'; 411import { BusinessError } from '@kit.BasicServicesKit'; 412 413const TAG: string = '[Page_AppServiceExtensionAbility]'; 414const DOMAIN_NUMBER: number = 0xFF00; 415const REQUEST_CODE = 1; 416let connectionId: number; 417let want: Want = { 418 deviceId: '', 419 bundleName: 'com.samples.stagemodelabilitydevelop', 420 abilityName: 'MyAppServiceExtAbility' 421}; 422let options: common.ConnectOptions = { 423 onConnect(elementName, remote): void { 424 hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback'); 425 if (remote === null) { 426 hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`); 427 return; 428 } 429 let option = new rpc.MessageOption(); 430 let data = new rpc.MessageSequence(); 431 let reply = new rpc.MessageSequence(); 432 433 // 写入请求数据 434 data.writeInt(1); 435 data.writeInt(2); 436 437 remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => { 438 if (ret.errCode === 0) { 439 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest got result`); 440 let sum = ret.reply.readInt(); 441 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest success, sum:${sum}`); 442 } else { 443 hilog.error(DOMAIN_NUMBER, TAG, `sendRequest failed`); 444 } 445 }).catch((error: BusinessError) => { 446 hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`); 447 }); 448 }, 449 onDisconnect(elementName): void { 450 hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback'); 451 }, 452 onFailed(code): void { 453 hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback'); 454 } 455}; 456 457// 调用connectAppServiceExtensionAbility相关代码 458//... 459 460@Entry 461@Component 462struct Page_AppServiceExtensionAbility { 463 build() { 464 Column() { 465 //... 466 List({ initialIndex: 0 }) { 467 ListItem() { 468 Row() { 469 //... 470 } 471 .onClick(() => { 472 let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext 473 connectionId = context.connectAppServiceExtensionAbility(want, options); 474 hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`); 475 }) 476 } 477 } 478 //... 479 } 480 } 481} 482``` 483 484**服务端**:使用[onRemoteMessageRequest](../reference/apis-ipc-kit/js-apis-rpc.md#onremotemessagerequest9)接口接收客户端发送的消息。 485 486```ts 487import { AppServiceExtensionAbility } from '@kit.AbilityKit'; 488import { rpc } from '@kit.IPCKit'; 489import { hilog } from '@kit.PerformanceAnalysisKit'; 490 491const TAG: string = '[MyAppServiceExtAbility]'; 492const DOMAIN_NUMBER: number = 0xFF00; 493 494// 开发者需要在这个类型里对接口进行实现 495class Stub extends rpc.RemoteObject { 496 onRemoteMessageRequest(code: number, 497 data: rpc.MessageSequence, 498 reply: rpc.MessageSequence, 499 options: rpc.MessageOption): boolean | Promise<boolean> { 500 hilog.info(DOMAIN_NUMBER, TAG, 'onRemoteMessageRequest'); 501 let sum = data.readInt() + data.readInt(); 502 reply.writeInt(sum); 503 return true; 504 } 505} 506 507// 服务端实现 508export default class MyAppServiceExtAbility extends AppServiceExtensionAbility { 509 onCreate(want: Want): void { 510 hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onCreate'); 511 } 512 513 onDestroy(): void { 514 hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onDestroy'); 515 } 516 517 onConnect(want: Want): rpc.RemoteObject { 518 hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onConnect'); 519 return new Stub('test'); 520 } 521 522 onDisconnect(): void { 523 hilog.info(DOMAIN_NUMBER, TAG, 'MyAppServiceExtAbility onDisconnect'); 524 } 525} 526``` 527 528### 服务端对客户端身份校验 529 530部分开发者需要使用AppServiceExtensionAbility组件提供一些较为敏感的服务,可以通过如下方式对客户端身份进行校验。 531 532<!--Del--> 533**通过callerUid识别客户端应用** 534 535通过调用[getCallingUid()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallinguid)接口获取客户端的uid,再调用[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid14)接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()](../reference/apis-ability-kit/js-apis-bundleManager-sys.md#bundlemanagergetbundlenamebyuid14)是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下: 536 537```ts 538import { AppServiceExtensionAbility } from '@kit.AbilityKit'; 539import { bundleManager } from '@kit.AbilityKit'; 540import { rpc } from '@kit.IPCKit'; 541import { osAccount, BusinessError } from '@kit.BasicServicesKit'; 542 543class Stub extends rpc.RemoteObject { 544 private validAppIdentifier: string = "your_valid_app_identifier_here"; 545 546 onRemoteMessageRequest( 547 code: number, 548 data: rpc.MessageSequence, 549 reply: rpc.MessageSequence, 550 options: rpc.MessageOption): boolean | Promise<boolean> { 551 this.verifyClientIdentity().then((isValid: boolean) => { 552 if (isValid) { 553 console.info('Client authentication PASSED'); 554 } else { 555 console.error('Client authentication FAILED'); 556 } 557 }).catch((err: BusinessError) => { 558 console.error(`Authentication error: ${err.code}, ${err.message}`); 559 }); 560 return true; 561 } 562 563 private async verifyClientIdentity(): Promise<boolean> { 564 try { 565 const callerUid: number = rpc.IPCSkeleton.getCallingUid(); 566 console.info(`Caller UID: ${callerUid}`); 567 568 const userId: number = await this.getUserIdByUid(callerUid); 569 console.info(`User ID: ${userId}`); 570 571 const bundleName: string = await bundleManager.getBundleNameByUid(callerUid); 572 console.info(`Bundle Name: ${bundleName}`); 573 574 const bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_SIGNATURE_INFO; 575 const bundleInfo: bundleManager.BundleInfo = await bundleManager.getBundleInfo(bundleName, bundleFlags, userId); 576 577 if (bundleInfo.signatureInfo && bundleInfo.signatureInfo.appIdentifier) { 578 const appIdentifier: string = bundleInfo.signatureInfo.appIdentifier; 579 console.info(`App Identifier: ${appIdentifier}`); 580 return appIdentifier === this.validAppIdentifier; 581 } 582 return false; 583 } catch (err) { 584 if (err instanceof Error) { 585 console.error(`Verification failed: ${err.message}`); 586 } else { 587 console.error(`Verification failed: ${String(err)}`); 588 } 589 return false; 590 } 591 } 592 593 private async getUserIdByUid(uid: number): Promise<number> { 594 try { 595 const accountManager = osAccount.getAccountManager(); 596 const userId: number = await accountManager.getOsAccountLocalIdForUid(uid); 597 return userId; 598 } catch (err) { 599 if (err instanceof Error) { 600 console.error(`Get userId failed: ${err.message}`); 601 throw err; 602 } else { 603 const error = new Error(String(err)); 604 console.error(`Get userId failed: ${error.message}`); 605 throw error; 606 } 607 } 608 } 609} 610 611export default class MyAppServiceExtAbility extends AppServiceExtensionAbility { 612 onConnect(want: Want): rpc.RemoteObject { 613 return new Stub('test'); 614 } 615 // 其他生命周期 616} 617``` 618<!--DelEnd--> 619 620**通过callerTokenId对客户端进行鉴权** 621 622通过调用[getCallingTokenId()](../reference/apis-ipc-kit/js-apis-rpc.md#getcallingtokenid8)接口获取客户端的tokenID,再调用[verifyAccessTokenSync()](../reference/apis-ability-kit/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)接口判断客户端是否有某个具体权限,由于当前不支持自定义权限,因此只能校验当前[系统所定义的权限](../security/AccessToken/app-permissions.md)。示例代码如下: 623 624```ts 625import { AppServiceExtensionAbility } from '@kit.AbilityKit'; 626import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit'; 627import { rpc } from '@kit.IPCKit'; 628import { hilog } from '@kit.PerformanceAnalysisKit'; 629import { BusinessError } from '@kit.BasicServicesKit'; 630 631const TAG: string = '[AppServiceExtImpl]'; 632const DOMAIN_NUMBER: number = 0xFF00; 633 634// 开发者需要在这个类里进行实现 635 636class Stub extends rpc.RemoteObject { 637 onRemoteMessageRequest( 638 code: number, 639 data: rpc.MessageSequence, 640 reply: rpc.MessageSequence, 641 options: rpc.MessageOption): boolean | Promise<boolean> { 642 // 开发者自行实现业务逻辑 643 hilog.info(DOMAIN_NUMBER, TAG, `onRemoteMessageRequest: ${data}`); 644 let callerUid = rpc.IPCSkeleton.getCallingUid(); 645 bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { 646 hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName); 647 // 对客户端包名进行识别 648 if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过 649 hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject'); 650 return; 651 } 652 // 识别通过,执行正常业务逻辑 653 }).catch((err: BusinessError) => { 654 hilog.error(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message); 655 }); 656 657 let callerTokenId = rpc.IPCSkeleton.getCallingTokenId(); 658 let accessManager = abilityAccessCtrl.createAtManager(); 659 // 所校验的具体权限由开发者自行选择,此处ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作为示例 660 let grantStatus = accessManager.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED'); 661 if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { 662 hilog.error(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED'); 663 return false; 664 } 665 hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.'); 666 return true; 667 } 668} 669 670export default class MyAppServiceExtAbility extends AppServiceExtensionAbility { 671 onConnect(want: Want): rpc.RemoteObject { 672 return new Stub('test'); 673 } 674 // 其他生命周期 675} 676``` 677 678