0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HarmonyOS开发案例:【应用添加运行时权限】

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-04-22 15:16 次阅读

介绍

通过AbilityAccessCtrl动态向用户申请“允许不同设备间的数据交换”的权限,使用设备管理实例获取周边不可信设备列表。

说明: 查询周边不可信设备之前,请确保本设备与周边设备未进行配对。如果已配对,则恢复出厂设置之后重新查询。

相关概念

  • [访问控制权限申请]:应用的APL(Ability Privilege Level)等级分为normal、system_basic和system_core三个等级,默认情况下,应用的APL等级都为normal等级。权限类型分为system_grant和user_grant两种类型。应用可申请的权限项参见应用权限列表。
  • [权限类型说明]:根据授权方式的不同,权限类型可分为system_grant(系统授权)和user_grant(用户授权)。
  • [应用ALP等级说明]:元能力权限等级APL(Ability Privilege Level)指的是应用的权限申请优先级的定义,不同APL等级的应用能够申请的权限等级不同。
  • [应用权限列表]:在申请目标权限前,建议开发者先阅读访问控制开发概述-权限的工作流程。对权限的工作流程有基本的了解后,再结合以下权限的具体说明,判断应用能否申请目标权限,提高开发效率。
  • [设备管理实例]:用于获取可信设备和本地设备的相关信息。在调用DeviceManager的方法前,需要先通过createDeviceManager构建一个DeviceManager实例dmInstance。

约束与限制

通过DevEco Studio自动下载SDK均为public版本,public-SDK不支持开发者使用系统API。本篇Codelab使用的DeviceManager(设备管理实例)依赖于系统API,需下载full-SDK并替换工具自动下载的public-SDK。

相关权限

本篇Codelab需要在配置文件module.json5里添加允许不同设备间的数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。

环境搭建

软件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:[润和RK3568开发板]。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以3.2 Release版本为例:
  2. 搭建烧录环境。
    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
  3. 搭建开发环境。
    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用[真机进行调测]。
    4. 鸿蒙开发指导文档:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。
搜狗高速浏览器截图20240326151547.png

├──entry/src/main/ets                      // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets            // 公共常量类
│  │  └──util
│  │     ├──DeviceListUtil.ets             // 设备查询工具类
│  │     ├──Logger.ets                     // 日志管理工具类
│  │     └──PermissionUtil.ets             // 权限申请工具类
│  ├──entryability
│  │  └──EntryAbility.ts                   // 程序入口类
│  ├──pages
│  │  └──HomePage.ets                      // 主页面
│  └──view
│     ├──DeviceBean.ets                    // 设备信息类
│     ├──DeviceItem.ets                    // 设备信息对象
│     ├──DeviceListDialog.ets              // 设备列表展示弹框
│     └──NoPermissionDialog.ets            // 无权限弹框
└──entry/src/main/resource                 // 应用静态资源目录

页面设计

页面分为授权图片、授权说明、查询内网设备按钮三部分,效果如图所示:

// HomePage.ets
struct HomePage {
  @State authorizedImage: Resource = $r('app.media.ic_unauthorized');
  @State permissionDescribe: Resource = $r('app.string.no_permission');
  noPermissionDialog: CustomDialogController = new CustomDialogController({
    builder: NoPermissionDialog(),
    autoCancel: false,
    alignment: DialogAlignment.Bottom,
    offset: {
      dx: CommonConstants.DIALOG_OFFSET_X,
      dy: CommonConstants.DIALOG_OFFSET_Y
    }
  });
  deviceListDialog: CustomDialogController = new CustomDialogController({
    builder: DeviceListDialog(),
    autoCancel: false,
    alignment: DialogAlignment.Bottom,
    offset: {
      dx: CommonConstants.DIALOG_OFFSET_X,
      dy: CommonConstants.DIALOG_OFFSET_Y
    }
  })
  ...
  build() {
    Column() {
      Image(this.authorizedImage)
        ...
      Text(this.permissionDescribe)
        ...
      Column() {
        Button($r('app.string.button_text'), { type: ButtonType.Capsule, stateEffect: true })
          ...
      }
      ...
    }
    ...
  }
}

授权状态为未授权时,点击查询内网设备按钮打开未申请权限提示的弹框,效果如图所示:

// NoPermissionDialog.ets
@CustomDialog
export struct NoPermissionDialog {
  controller: CustomDialogController;
 
  build() {
    Column() {
      Text($r('app.string.no_permission_title'))
        ...
      Text($r('app.string.clear_permission'))
        ...
      Text($r('app.string.dialog_confirm'))
        ...
        .onClick(() = > {
          this.controller.close();
        })
    }
    ...
  }
}

授权状态为已授权时,点击查询内网设备按钮打开设备查询列表弹框,效果如图所示:

// DeviceListDialog.ets
@CustomDialog
export struct DeviceListDialog {
  private deviceListUtil: DeviceListUtil = new DeviceListUtil();
  @State deviceList: Array< DeviceBean > = [];
  controller: CustomDialogController;
  ...
  build() {
    Column() {
      Text($r('app.string.device_list'))
        ...
      Column() {
        if (this.deviceList.length === 0) {
          Text($r('app.string.no_device'))
            ...
        } else {
          Column() {
            List() {
              ForEach(this.deviceList, (item: DeviceBean, index: number) = > {
                ListItem() {
                  DeviceItem({item: item, index: index});
                }
              }, (item: DeviceBean) = > JSON.stringify(item))
            }
          }
          ...
        }
      }
      ...
      Text($r('app.string.dialog_confirm'))
        ...
        .onClick(() = > {
          this.deviceListUtil.stopDeviceDiscovery();
          this.controller.close();
        })
    }
    ...
  }
}

权限申请

首次进入主页面弹出授权弹框,点击禁止按钮不会授权,点击允许按钮进行授权,再次进入首页不会出现授权弹框。

// HomePage.ets
struct HomePage {
  ...
  async aboutToAppear() {
    let result = await PermissionUtil.applyPermission();
    if (result === 0) {
      this.permissionDescribe = $r('app.string.has_permission');
      this.authorizedImage = $r('app.media.ic_authorized');
    } else {
      this.permissionDescribe = $r('app.string.no_permission');
    }
  }
  ...
}

// PermissionUtil.ets
async applyPermission() {
  let atManager = abilityAccessCtrl.createAtManager();
  let data = await atManager.requestPermissionsFromUser(getContext(this), [CommonConstants.PERMISSION]);
  let grantStatus: Array< number > = data.authResults;
  let length: number = grantStatus.length;
  if (length === 0) {
    return 1;
  }
  return grantStatus[0];
}

主页面点击查询内网设备按钮时,先查询是否授权,根据授权状态打开对应的弹框(未授权:打开未授权弹框;已授权:打开设备查询列表弹框)。

// HomePage.ets
struct HomePage {
  ...
  build() {
    Column() {
      ...
      Column() {
        Button($r('app.string.button_text'), { type: ButtonType.Capsule, stateEffect: true })
          ...
          .onClick(async () = > {
            let result = await PermissionUtil.checkPermission();
            if(result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
              this.deviceListDialog.open();
            } else {
              this.noPermissionDialog.open();
            }
          })
      }
      ...
    }
    ...
  }
}

// PermissionUtil.ets
async checkPermission() {
  let atManager = abilityAccessCtrl.createAtManager();
  let grantStatus: abilityAccessCtrl.GrantStatus =  abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
  let tokenId: number = 0;
  try {
    let bundleInfo: bundleManager.BundleInfo =
      await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
    tokenId = appInfo.accessTokenId;
  } catch (err) {
    Logger.error(TAG, 'getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}');
  }
  try {
    grantStatus = await atManager.checkAccessToken(tokenId, CommonConstants.PERMISSION);
  } catch (err) {
    Logger.error(TAG, 'checkAccessToken failed, code is ${err.code}, message is ${err.message}');
  }
  return grantStatus;
}

设备查询

先创建dmInstance实例,再注册发现设备(deviceFoundOn)的回调方法,最后调用startDeviceDiscovery方法查询周边设备;当查询到设备之后调用deviceFoundOn方法处理设备信息。

//DeviceListDialog.ets
export struct DeviceListDialog {
  ...
  aboutToAppear() {
    this.deviceListUtil.initDmInstance((data: DeviceInfoInterface) = > {
      ...
    });
  }
  ...
}

// DeviceListUtil.ets
// 创建dmInstance实例
initDmInstance(dealDeviceInfo: Function) {
  this.dealDeviceInfo = dealDeviceInfo;
  try {
    deviceManager.createDeviceManager(getContext(this).applicationInfo.name, (err, data) = > {
      if (err) {
        Logger.error(TAG, 'createDeviceManager errCode:' + err.code + ',errMessage:' + err.message);
        return;
      }
      this.dmInstance = data;
      this.deviceFoundOn();
      this.startDeviceDiscovery();
    });
  } catch (err) {
    Logger.error(TAG, 'createDeviceManager err=' + JSON.stringify(err));
  }
}

// DeviceListUtil.ets
// 注册发现设备回调方法
deviceFoundOn() {
  try {
    if (this.dmInstance !== undefined) {
      this.dmInstance.on('deviceFound', (data) = > {
        if (this.dealDeviceInfo !== undefined) {
          this.dealDeviceInfo(data);
        }
      });
    }
  } catch (err) {
    Logger.error(TAG, 'deviceFoundOn err:' + JSON.stringify(err));
  }
}

// DeviceListUtil.ets
// 发现周边设备方法
startDeviceDiscovery() {
  this.subscribeId = Math.floor(Math.random() * CommonConstants.RANDOM_ONE + CommonConstants.RANDOM_TWO);
  let subscribeInfo: SubscribeInfoInterface = {
    subscribeId: this.subscribeId,
    mode: CommonConstants.MODE,
    medium: 0,
    freq: CommonConstants.FREQ,
    isSameAccount: false,
    isWakeRemote: true,
    capability: 1
  };
  try {
    if (this.dmInstance !== undefined) {
      this.dmInstance.startDeviceDiscovery(subscribeInfo);
    }
  } catch (err) {
    Logger.error(TAG, 'startDeviceDiscovery err:' + JSON.stringify(err));
  }
}

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    57

    文章

    2306

    浏览量

    42731
  • HarmonyOS
    +关注

    关注

    79

    文章

    1967

    浏览量

    30004
  • OpenHarmony
    +关注

    关注

    25

    文章

    3658

    浏览量

    16131
收藏 人收藏

    评论

    相关推荐

    如何缩短Vivado的运行时

    在Vivado Implementation阶段,有时是有必要分析一下什么原因导致运行时间(runtime)过长,从而找到一些方法来缩短运行时间。
    的头像 发表于 05-29 14:37 1.4w次阅读
    如何缩短Vivado的<b class='flag-5'>运行时</b>间

    运行时添加100个项目

    列表小部件受到严重性能问题的影响。如果你试图在运行时添加100个项目,它将永远持续下去!!!!对于你添加的每一项,所需的时间加倍!我测量了这个时间,在一个200兆赫PIC32,需要超过5秒来
    发表于 04-06 16:06

    HarmonyOS应用开发-应用权限安全

    HarmonyOS应用开发安全主要分为两个方面:应用权限和生物特征识别。应用权限严格按照权限分类分级模型进行定义,如图1所示,具体过程可分为
    发表于 09-23 14:26

    如何TouchGFX运行时添加小部件、行等?

    您好,是否可以在运行时在画布上添加一条线?我正在设法做一个 log x 比例尺,为几十年的分隔符提供一个 Line 对象,但我的情节可能超过十年,所以我认为根据需要添加许多行,然后让每一行都可见是一
    发表于 12-02 07:36

    运行时软件故障注入器的设计与实现

    针对实际故障诊断中难以有效获得故障现场信息的问题,提出通过在软件运行时注入故障的方式获取故障现场信息,设计并实现一种运行时软件故障注入器,给出其中的故障脚本描
    发表于 04-06 08:40 19次下载

    FPGA运行时重构的延迟隐藏机制研究与实现

    FPGA运行时重构的延迟隐藏机制研究与实现_刘伟
    发表于 01-07 19:08 0次下载

    紫金桥组态软件新的功能_运行时组态

    运行时组态是组态软件新近提出的新的概念。运行时组态是在运行环境下对已有工程进行修改,添加新的功能。它不同于在线组态,在线组态是在工程运行的同
    发表于 10-13 16:17 2次下载
    紫金桥组态软件新的功能_<b class='flag-5'>运行时</b>组态

    华为开发者分论坛HarmonyOS学生公开课-OpenHarmony Codelabs开发案

    2021华为开发者分论坛HarmonyOS学生公开课-OpenHarmony Codelabs开发案
    的头像 发表于 10-24 11:25 1884次阅读
    华为<b class='flag-5'>开发</b>者分论坛<b class='flag-5'>HarmonyOS</b>学生公开课-OpenHarmony Codelabs<b class='flag-5'>开发案</b>例

    如何高效测量ECU的运行时

    ,最终可能会引起运行时间方面的问题。这在项目后期需要大量的时间和金钱来解决。如果不能掌握系统的运行状态,则很难发现系统内缺陷的根源。 解决方案 将TA软件工具套件与VX1000测量标定硬件相结合,可同步分析 ECU内部运行时序和
    的头像 发表于 10-28 11:05 2167次阅读

    Go运行时:4年之后

    自 2018 年以来,Go GC,以及更广泛的 Go 运行时,一直在稳步改进。近日,Go 社区总结了 4 年来 Go 运行时的一些重要变化。
    的头像 发表于 11-30 16:21 786次阅读

    什么是Kubernetes容器运行时CRI

    起初,Docker是事实上的容器技术标准,Kubernetes v1.5之前的代码中直接调用Docker API,实现容器运行时的相关操作。
    的头像 发表于 02-20 16:22 1465次阅读
    什么是Kubernetes容器<b class='flag-5'>运行时</b>CRI

    ch32v307记录程序运行时

    ch32v307记录程序运行时间 在程序开发中,很重要的一项任务就是对程序的运行时间进行评估。对于大型的程序系统来说,它们通常需要处理大量的数据或进行复杂的计算操作。因此,如果程序的运行时
    的头像 发表于 08-22 15:53 852次阅读

    Xilinx运行时(XRT)发行说明

    电子发烧友网站提供《Xilinx运行时(XRT)发行说明.pdf》资料免费下载
    发表于 09-14 10:01 0次下载
    Xilinx<b class='flag-5'>运行时</b>(XRT)发行说明

    如何保证它们容器运行时的安全?

    紧密耦合的容器运行时继承了主机操作系统的安全态势和攻击面。运行时或主机内核中的任何漏洞及其利用都会成为攻击者的潜在切入点。
    的头像 发表于 11-03 15:24 635次阅读

    jvm运行时内存区域划分

    JVM是Java Virtual Machine(Java虚拟机)的缩写,它是Java编程语言的运行环境。JVM的主要功能是将Java源代码转换为机器代码,并且在运行时管理Java程序的内存。JVM
    的头像 发表于 12-05 14:08 502次阅读