鸿蒙Ability Kit(程序框架服务)【ServiceExtensionAbility】

ServiceExtensionAbility

概述

[ServiceExtensionAbility]是SERVICE类型的ExtensionAbility组件,提供后台服务能力,其内部持有了一个[ServiceExtensionContext],通过[ServiceExtensionContext]提供了丰富的接口供外部使用。

本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。

[ServiceExtensionAbility]可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。[ServiceExtensionAbility]支持以启动和连接两种形式运行,系统应用可以调用[startServiceExtensionAbility()]方法启动后台服务,也可以调用[connectServiceExtensionAbility()]方法连接后台服务,而三方应用只能调用[connectServiceExtensionAbility()]方法连接后台服务。启动和连接后台服务的差别:

  • 启动:AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。
  • 连接:AbilityA连接ServiceB,连接后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。

此处有如下细节需要注意:

  • 若Service只通过connect的方式被拉起,那么该Service的生命周期将受客户端控制,当客户端调用一次[connectServiceExtensionAbility()]方法,将建立一个连接,当客户端退出或者调用[disconnectServiceExtensionAbility()]方法,该连接将断开。当所有连接都断开后,Service将自动退出。
  • Service一旦通过start的方式被拉起,将不会自动退出,系统应用可以调用[stopServiceExtensionAbility()]方法将Service退出。
  • 只能在主线程线程中执行connect/disconnect操作,不要在Worker、TaskPool等子线程中执行connect/disconnect操作。

说明:

  1. 当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见[后台任务]。
  2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
  3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。

生命周期

[ServiceExtensionAbility]提供了onCreate()、onRequest()、onConnect()、onDisconnect()和onDestroy()生命周期回调,根据需要重写对应的回调方法。下图展示了ServiceExtensionAbility的生命周期。

图1 ServiceExtensionAbility生命周期
ServiceExtensionAbility-lifecycle

  • onCreate 服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。

    说明:  如果服务已创建,再次启动该ServiceExtensionAbility不会触发onCreate()回调。

  • onRequest 当另一个组件调用[startServiceExtensionAbility()]方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。每调用一次[startServiceExtensionAbility()]方法均会触发该回调。

  • onConnect 当另一个组件调用[connectServiceExtensionAbility()]方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象(IRemoteObject),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信,同时系统侧也会将该远端代理对象(IRemoteObject)储存。后续若有组件再调用[connectServiceExtensionAbility()]方法,系统侧会直接将所保存的远端代理对象(IRemoteObject)返回,而不再触发该回调。

  • onDisconnect 当最后一个连接断开时,将触发该回调。客户端死亡或者调用[disconnectServiceExtensionAbility()]方法可以使连接断开。

  • onDestroy 当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。

实现一个后台服务(仅对系统应用开放)

开发准备

只有系统应用才允许实现ServiceExtensionAbility,因此开发者在开发之前需做如下准备:

  • 替换Full SDK:ServiceExtensionAbility相关接口都被标记为System-API,默认对开发者隐藏,因此需要手动从镜像站点获取Full SDK,并在DevEco Studio中替换,具体操作可参考[替换指南]。
  • 申请AllowAppUsePrivilegeExtension特权:只有具有AllowAppUsePrivilegeExtension特权的应用才允许开发ServiceExtensionAbility,具体申请方式可参考[应用特权配置指南])。

定义IDL接口

ServiceExtensionAbility作为后台服务,需要向外部提供可调用的接口,开发者可将接口定义在idl文件中,并使用[IDL工具]生成对应的proxy、stub文件。此处定义一个名为IIdlServiceExt.idl的文件作为示例:

interface OHOS.IIdlServiceExt {int ProcessData([in] int data);void InsertDataToMap([in] String key, [in] int val);
}

在DevEco Studio工程Module对应的ets目录下手动新建名为IdlServiceExt的目录,将[IDL工具]生成的文件复制到该目录下,并创建一个名为idl_service_ext_impl.ts的文件,作为idl接口的实现:

├── ets
│ ├── IdlServiceExt
│ │   ├── i_idl_service_ext.ts      # 生成文件
│ │   ├── idl_service_ext_proxy.ts  # 生成文件
│ │   ├── idl_service_ext_stub.ts   # 生成文件
│ │   ├── idl_service_ext_impl.ts   # 开发者自定义文件,对idl接口的具体实现
│ └
└

idl_service_ext_impl.ts实现如下:

import IdlServiceExtStub from './idl_service_ext_stub';
import hilog from '@ohos.hilog';
import type { insertDataToMapCallback } from './i_idl_service_ext';
import type { processDataCallback } from './i_idl_service_ext';const ERR_OK = 0;
const TAG: string = "[IdlServiceExtImpl]";
const DOMAIN_NUMBER: number = 0xFF00;// 开发者需要在这个类型里对接口进行实现
export default class ServiceExtImpl extends IdlServiceExtStub {processData(data: number, callback: processDataCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑}insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);callback(ERR_OK);}
}

创建ServiceExtensionAbility

在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下:

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为ServiceExtAbility。

  2. 在ServiceExtAbility目录,右键选择“New > ArkTS File”,新建一个文件并命名为ServiceExtAbility.ets。

    ├── ets
    │ ├── IdlServiceExt
    │ │   ├── i_idl_service_ext.ets      # 生成文件
    │ │   ├── idl_service_ext_proxy.ets  # 生成文件
    │ │   ├── idl_service_ext_stub.ets   # 生成文件
    │ │   ├── idl_service_ext_impl.ets   # 开发者自定义文件,对idl接口的具体实现
    │ ├── ServiceExtAbility
    │ │   ├── ServiceExtAbility.ets
    └
    
  3. 在ServiceExtAbility.ets文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并实现生命周期回调,在onConnect生命周期回调里,需要将之前定义的ServiceExtImpl对象返回。

    import hilog from '@ohos.hilog';
    import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
    import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl';
    import type Want from '@ohos.app.ability.Want';
    import type rpc from '@ohos.rpc';const TAG: string = '[ServiceExtAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;export default class ServiceExtAbility extends ServiceExtensionAbility {serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl');onCreate(want: Want): void {let serviceExtensionContext = this.context;hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`);};onRequest(want: Want, startId: number): void {hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`);};onConnect(want: Want): rpc.RemoteObject {hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`);// 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信return this.serviceExtImpl as rpc.RemoteObject;};onDisconnect(want: Want): void {hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`);};onDestroy(): void {hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');};
    };
    
  4. 在工程Module对应的[module.json5配置文件]中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。

    {"module": {..."extensionAbilities": [{"name": "ServiceExtAbility","icon": "$media:icon","description": "service","type": "service","exported": true,"srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets"}]}
    }
    

启动一个后台服务(仅对系统应用开放)

系统应用通过[startServiceExtensionAbility()]方法启动一个后台服务,服务的[onRequest()]回调就会被调用,并在该回调方法中接收到调用者传递过来的want对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用ServiceExtensionContext的[terminateSelf()]来自行停止,或者由另一个组件调用[stopServiceExtensionAbility()]来将其停止。

说明:  ServiceExtensionContext的[startServiceExtensionAbility()]、[stopServiceExtensionAbility()]和[terminateSelf()]为系统接口,三方应用不支持调用。

  1. 在系统应用中启动一个新的ServiceExtensionAbility。示例中的context的获取方式请参见[获取UIAbility的上下文信息]。

    import common from '@ohos.app.ability.common';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;@Entry
    @Component
    struct Page_ServiceExtensionAbility {build() {Column() {//...List({ initialIndex: 0 }) {ListItem() {Row() {//...}.onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '',bundleName: 'com.samples.stagemodelabilitydevelop',abilityName: 'ServiceExtAbility'};context.startServiceExtensionAbility(want).then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.');// 成功启动后台服务promptAction.showToast({message: $r('app.string.SuccessfullyStartBackendService')});}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);});})}//...}//...}//...}
    }
    
  2. 在系统应用中停止一个已启动的ServiceExtensionAbility。

    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;@Entry
    @Component
    struct Page_ServiceExtensionAbility {build() {Column() {//...List({ initialIndex: 0 }) {ListItem() {Row() {//...}.onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '',bundleName: 'com.samples.stagemodelabilitydevelop',abilityName: 'ServiceExtAbility'};context.stopServiceExtensionAbility(want).then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.');promptAction.showToast({message: $r('app.string.SuccessfullyStoppedAStartedBackendService')});}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);});})}//...}//...}//...}
    }
    
  3. 已启动的ServiceExtensionAbility停止自身。

    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';
    import Want from '@ohos.app.ability.Want';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;@Entry
    @Component
    struct Page_ServiceExtensionAbility {build() {Column() {//...List({ initialIndex: 0 }) {ListItem() {Row() {//...}.onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextcontext.terminateSelf().then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.');// 成功停止当前后台服务promptAction.showToast({message: $r('app.string.SuccessfullyStopStartedBackendService')});}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);});})}//...}//...}//...}
    }
    

说明:  后台服务可以在后台长期运行,为了避免资源浪费,需要对后台服务的生命周期进行管理。即一个后台服务完成了请求方的任务,需要及时销毁。销毁已启动的后台服务有两种方式:

  • 后台服务自身调用[terminateSelf()]方法来自行停止。
  • 由其他组件调用[stopServiceExtensionAbility()]方法来停止。 调用[terminateSelf()]或[stopServiceExtensionAbility()]方法之后,系统将销毁后台服务。

连接一个后台服务

系统应用或者三方应用可以通过[connectServiceExtensionAbility()]连接一个服务(在Want对象中指定启动的目标服务),服务的[onConnect()]就会被调用,并在该回调方法中接收到调用者传递过来的Want对象,从而建立长连接。

ServiceExtensionAbility服务组件在[onConnect()]中返回IRemoteObject对象,开发者通过该IRemoteObject定义通信接口,用于客户端与服务端进行RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务的交互后,客户端需要通过调用[disconnectServiceExtensionAbility()]来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。

  • 使用connectServiceExtensionAbility()建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息]。

    import common from '@ohos.app.ability.common';
    import deviceManager from '@ohos.distributedDeviceManager';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import Want from '@ohos.app.ability.Want';
    // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;let connectionId: number;
    let want: Want = {deviceId: '',bundleName: 'com.samples.stagemodelabilitydevelop',abilityName: 'ServiceExtAbility'
    };let options: common.ConnectOptions = {onConnect(elementName, remote: rpc.IRemoteObject): void {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');if (remote === null) {hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);return;}let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);// 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);});serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);})},onDisconnect(elementName): void {hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');},onFailed(code: number): void {hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));}
    };
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {build() {Column() {//...List({ initialIndex: 0 }) {ListItem() {Row() {//...}.onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入connectionId = context.connectServiceExtensionAbility(want, options);// 成功连接后台服务promptAction.showToast({message: $r('app.string.SuccessfullyConnectBackendService')});// connectionId = context.connectAbility(want, options);hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);})}//...}//...}//...}
    }
    
  • 使用disconnectServiceExtensionAbility()断开与后台服务的连接。

    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;let connectionId: number;
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {build() {Column() {//...List({ initialIndex: 0 }) {ListItem() {Row() {//...}.onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext// connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护context.disconnectServiceExtensionAbility(connectionId).then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');// 成功断连后台服务promptAction.showToast({message: $r('app.string.SuccessfullyDisconnectBackendService')});}).catch((error: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');});})}//...}//...}//...}
    }
    

客户端与服务端通信

客户端在onConnect()中获取到[rpc.RemoteObject]对象后便可与Service进行通信,有如下两种方式:

  • 使用服务端提供的IDL接口进行通信(推荐)

    // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;let options: common.ConnectOptions = {onConnect(elementName, remote: rpc.IRemoteObject): void {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');if (remote === null) {hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);return;}let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);// 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);});serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) => {hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);})},onDisconnect(elementName): void {hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');},onFailed(code: number): void {hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));}
    };
    
  • 直接使用[sendMessageRequest]接口向服务端发送消息(不推荐)

    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';const TAG: string = '[Page_CollaborateAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    const REQUEST_CODE = 1;
    let options: common.ConnectOptions = {onConnect(elementName, remote): void {hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');if (remote === null) {hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);return;}let option = new rpc.MessageOption();let data = new rpc.MessageSequence();let reply = new rpc.MessageSequence();data.writeInt(99);// 开发者可发送data到目标端应用进行相应操作// @param code 表示客户端发送的服务请求代码。// @param data 表示客户端发送的{@link MessageSequence}对象。// @param reply 表示远程服务发送的响应消息对象。// @param options 指示操作是同步的还是异步的。// @return 如果操作成功返回{@code true}; 否则返回 {@code false}。remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => {let errCode = reply.readInt(); // 在成功连接的情况下,会收到来自目标端返回的信息(100)let msg: number = 0;if (errCode === 0) {msg = reply.readInt();}hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);// 成功连接后台服务promptAction.showToast({message: `sendRequest msg:${msg}`});}).catch((error: BusinessError) => {hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);});},onDisconnect(elementName): void {hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');},onFailed(code): void {hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');}
    };
    //...
    

服务端对客户端身份校验

部分开发者需要使用ServiceExtension提供一些较为敏感的服务,因此需要对客户端身份进行校验,开发者可在IDL接口的stub端进行校验,IDL接口实现详见上文[定义IDL接口],此处推荐两种校验方式:

  • 通过callerUid识别客户端应用

    通过调用[getCallingUid()]接口获取客户端的uid,再调用[getBundleNameByUid()]接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()]是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下:

    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = "[IdlServiceExtImpl]";
    const DOMAIN_NUMBER: number = 0xFF00;// 开发者需要在这个类型里对接口进行实现
    export default class ServiceExtImpl extends IdlServiceExtStub {processData(data: number, callback: ProcessDataCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);let callerUid = rpc.IPCSkeleton.getCallingUid();bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);// 对客户端包名进行识别if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');return;}// 识别通过,执行正常业务逻辑}).catch((err: BusinessError) => {hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);});//...};insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);callback(ERR_OK);};
    };
    
  • 通过callerTokenId对客户端进行鉴权

    通过调用[getCallingTokenId()]接口获取客户端的tokenID,再调用[verifyAccessTokenSync()]接口判断客户端是否有某个具体权限,由于当前不支持自定义权限,因此只能校验当前[系统所定义的权限]。示例代码如下:

    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = '[IdlServiceExtImpl]';
    const DOMAIN_NUMBER: number = 0xFF00;// 开发者需要在这个类型里对接口进行实现
    export default class ServiceExtImpl extends IdlServiceExtStub {processData(data: number, callback: ProcessDataCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);let callerUid = rpc.IPCSkeleton.getCallingUid();bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);// 对客户端包名进行识别if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');return;}// 识别通过,执行正常业务逻辑}).catch((err: BusinessError) => {hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);});let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();let accessManger = abilityAccessCtrl.createAtManager();// 所校验的具体权限由开发者自行选择,此处ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作为示例let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED');if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED');callback(ERR_DENY, data); // 鉴权失败,返回错误return;}hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.');callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑};insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {// 开发者自行实现业务逻辑hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);callback(ERR_OK);};
    };
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340524.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE5 刺客信条动态地面复刻】实现无界地面01:动态生成

2024.6.4更新 昨天半夜意识到生成Cube的方案不合适,又开始到处找动态地面的方法,发现了我想要的效果直接可以用nigara实现!!!! 于是这个部分就暂时告一段落,今季开始新的方向的学习。 为了快速…

动态规划——打家劫舍问题

198. 打家劫舍 问题描述 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一…

中国青年网投稿三下乡社会实践稿件方法

文稿要求 实践纪实类文稿 (1)标题:10-30个汉字为宜,要用一句话标题,清楚叙述开展社会实践的学校、内容等基本信息。 (2)正文:表述要流畅,不可写宣传稿,要注…

文件中找TOPK问题

一.TOP—K问题简介: TOP-K 问题:即求数据结合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大 。 比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。 对于 Top-K 问题,能想到的最简…

c# - 运算符 << 不能应用于 long 和 long 类型的操作数

Compiler Error CS0019 c# - 运算符 << 不能应用于 long 和 long 类型的操作数 处理方法 特此记录 anlog 2024年5月30日

个人笔记-随意记录

常见问题&#xff1f; 1.linux重启服务 端口被占用如何解决&#xff1f; 查看某个端口被占用的进程 netstat -tulnp | grep :23454 强制杀死进程 kill -9 1776 重启服务即可

无人机、机器人10公里WiFi远距离图传模块,实时高清视频传输,飞睿CV5200模组方案,支持mesh自组网模块

在快速发展的物联网时代&#xff0c;远距离无线通信技术已成为连接各种智能设备的关键。无人机、安防监控、机器人等领域对数据传输的距离和速度要求越来越高。 公里级远距离WiFi模组方案可以通过多种技术和策略的结合来实现无人机和机器人之间的高效通信传输。 飞睿智能CV52…

Java排序算法汇总篇,八种排序算法

排序算法汇总: Java排序算法(一)&#xff1a;冒泡排序 Java排序算法(二)&#xff1a;选择排序 Java排序算法(三)&#xff1a;插入排序 Java排序算法(四)&#xff1a;快速排序 Java排序算法(五)&#xff1a;归并排序 Java排序算法(六)&#xff1a;希尔排序 Java排序算法(…

PPT 隐藏开启对象图层

目录预览 一、问题描述二、解决方案三、参考链接 一、问题描述 制作PPT的时候&#xff0c;有时候需要在一张PPT放置多个依次出现的内容&#xff0c;然后设置对应的动画&#xff0c;要是需要对某个内容进行修改的话&#xff0c;就会很不方便&#xff0c;这个时候就需要使用&…

代码随想录第24天|回溯part4 寻找切割点

93.复原ip地址 寻找切割点&#xff0c;但是需要注意的是&#xff0c;切割点&#xff08;即.号&#xff09;只有三个 将需要拼凑的值先放进一个数组里&#xff0c;等满足条件后再将其拼成字符串 class Solution { public:vector<string> res;vector<int> path;int …

opencv-python(五)

opencv的颜色通道中顺序是B&#xff0c;G&#xff0c;R。 图像属性 import cv2img cv2.imread(jk.jpg) print(fshape{img.shape}) print(fsize{img.size}) print(fdtype{img.dtype}) shape&#xff1a;图像像素的行&#xff0c;列&#xff0c;通道 size&#xff1a;行数 X …

用贪心算法计算十进制数转二进制数(整数部分)

十进制整数转二进制数用什么方法&#xff1f;网上一搜&#xff0c;大部分答案都是用短除法&#xff0c;也就是除2反向取余法。这种方法是最基本最常用的&#xff0c;但是计算步骤多&#xff0c;还容易出错&#xff0c;那么还有没有其他更好的方法吗&#xff1f; 一、短除反向取…

WMS仓储管理系统高效驱动制造企业物料管理

在现代制造业的快速发展中&#xff0c;仓储管理作为供应链的核心环节&#xff0c;其效率直接影响到企业的生产力和市场竞争力。随着科技的进步&#xff0c;实施WMS仓储管理系统逐渐成为推动仓储管理向智能化转型的关键力量。本文将深入探讨WMS仓储管理系统如何以创新的方式驱动…

软件系统测试的定义和测试内容介绍

一、什么是软件系统测试? 软件系统测试是指对软件系统的功能、性能、可靠性、稳定性等方面进行全面检查和验证的过程。其目的是发现潜在的问题、缺陷和风险&#xff0c;并确保软件系统的质量和稳定性。 软件系统测试可以分为多个阶段&#xff0c;包括单元测试、集成测试、系…

OBS插件--Spout输入与输出

Spout是什么&#xff1f; Spout是用于Microsoft Windows的视频帧共享系统,它允许应用程序以类似于Mac上的Syphon的方式共享OpenGL纹理。 其主要目的是允许应用程序实时共享帧&#xff0c;而无需显著的性能开销。这一功能在多个领域有着广泛的应用&#xff0c;尤其是在实时视频…

【python】成功解决“TypeError: not enough arguments for format string”错误的全面指南

成功解决“TypeError: not enough arguments for format string”错误的全面指南 一、引言 在Python编程中&#xff0c;TypeError: not enough arguments for format string错误是一个常见的字符串格式化问题。这个错误通常发生在使用str.format()方法时&#xff0c;提供的参数…

【DMG80480T070_05WTR】文本显示、数据变量显示、基本图形显示、实时曲线功能及串口下载流程(串口屏)

这篇文章写给自己看的&#xff0c;要不然明天就忘完了。 首先新建一个工程&#xff0c;名称路径自拟。 导入一张图片&#xff0c;名字从00开始&#xff0c;图片放到本工程的DWIN_SET下面就行&#xff0c;后面如果没有特殊说明&#xff0c;生成的配置或者放入的图片全都放在该文…

三种常见的报表模板,省时又方便

前言 在业务应用和数据分析中&#xff0c;报表是一种常见的数据展示形式&#xff0c;可以帮助用户更直观地理解和解读数据。然而&#xff0c;每次创建和设计一款报表都需要花费大量的时间和精力。为了提高报表设计的效率&#xff0c;本文小编以葡萄城公司的嵌入式BI工具——Wy…

什么样的人适合成为产品经理

产品经理就好比是大楼的设计师&#xff0c;如果没有好的设计理念&#xff0c;好的洞察力&#xff0c;很难设计出让住户心满意足的房子。产品经理也是如此。 01要有创新思维和敏锐商业洞察力 做了很长时间的产品经理了&#xff0c;发现大部分产品经理基本上都是墨守成规&#…

AI写代码:我用kimi生成了一个设备节点监控网站,完美实现功能

更多精彩内容在公众号。 这一次继续用kimi来完成一个网站的初步搭建。这次是用来搭建一个节点监控网站。需求是通过输入节点Ip地址&#xff0c;用户名&#xff0c;密码得到远端节点的IP&#xff0c;CPU信息&#xff0c;内存信息&#xff0c;硬盘信息&#xff0c;网络收发包信息…