鸿蒙6.0应用开发——防窥保护

概述

HarmonyOS自发布以来持续深化安全与隐私能力体系建设:从早期的应用沙箱隔离、权限管控,到数据防泄漏(DLP)框架,再到如今面向物理环境的感知型安全防护能力,安全防护的边界已从系统内部延伸至用户所处的真实空间。

与此同时,移动设备已深度融入日常生活——人们在地铁、咖啡厅、会议室等公共场所查看账户余额、阅读私密消息、浏览个人内容。屏幕上的信息可能暴露在陌生人的视线之下,“肩窥”已成为现实生活中不可忽视的隐私威胁。

正是在这一背景下,HarmonyOS推出了dlpAntiPeep(防窥保护)能力。它依托前置摄像头的实时感知,检测屏幕前是否存在非机主人员,并在检测到窥视时通知应用主动隐藏敏感信息或触发系统防窥蒙层,将安全防护从系统层延伸到用户的物理使用环境。

场景介绍

防窥保护简介

防窥保护能力采用“感知 + 响应”的分层设计,由应用根据自身业务特点灵活决策——检测到窥视时,可以选择遮盖敏感字段、模糊画面、暂停播放,也可以直接拉起系统级防窥蒙层覆盖整个窗口。这种灵活的响应机制意味着防窥保护并非对所有场景一刀切地拦截,而是让不同类型的应用都能以最贴合自身场景的方式接入防窥保护

防窥保护应用场景

以下列举了防窥保护能力的典型应用场景。需要注意的是,这些场景仅为示例,开发者应根据应用的实际业务特点和用户隐私需求,灵活运用防窥保护能力,而不必局限于这些场景。任何涉及敏感信息展示的应用场景都可以考虑接入防窥保护。

  1. 金融类应用汇集了大量的财务敏感信息——账户余额、收益曲线、持仓明细,每一项都是不希望被他人知晓的隐私数据。在地铁、咖啡厅等人流密集的公共场所,用户往往无暇顾及身旁是否有人窥视屏幕。接入dlpAntiPeep能力后,应用可在检测到窥视的瞬间自动将敏感数字替换为 ****,并可选触发系统级防窥蒙层,让用户在任何环境下都能放心查看财务信息,效果如下:

    在这里插入图片描述

  2. 短视频内容往往折射出用户的个人偏好、生活习惯乃至私密时刻,这些信息在公共场所被他人看到,会带来不同程度的隐私不适感。接入dlpAntiPeep能力后,应用可在检测到窥视时暂停定制化推送内容的展示,或对关注列表进行模糊隐藏,待窥视解除后自动恢复,在不打断用户使用节奏的前提下,为内容浏览筑起一道隐形屏障,效果如下:

    在这里插入图片描述

  3. 即时通讯是隐私敏感度最高的应用场景之一——消息列表中的联系人姓名、消息预览,聊天详情中的情感交流、商业沟通,任何一条信息被旁观者读取都可能造成难以挽回的隐私损失。接入dlpAntiPeep能力后,应用可在检测到窥视时将消息内容隐藏或替换为自定义不敏感内容,确保私密对话只属于对话双方,效果如下:

    在这里插入图片描述

案例实现

本节以用户隐私安全的财务场景为例,介绍dlpAntiPeep能力的完整接入方案。

实现原理

dlpAntiPeep能力属于Device Security Kit,通过前置摄像头实时分析屏幕前的人员情况,将结果以DlpAntiPeepStatus枚举值的形式通知应用。核心优势在于系统级的实时感知与应用层的灵活响应,开发者无需自行实现人脸检测算法,只需调用少量API即可为应用添加防窥保护功能。

整体实现流程如下所示:

在这里插入图片描述

开发前置条件

在正式接入防窥保护能力前,开发者需要引导用户授权打开防窥保护开关。防窥保护功能需要用户在系统设置中主动开启,应用才能获取相关权限并启用防窥能力。API23版本开始,可调用dlpAntiPeep.requestAntiPeepOptions(context)拉起弹窗,弹窗中开启系统设置中的防窥保护按钮,此接口避免了用户主动转到设置中开启按钮。

const result = await dlpAntiPeep.requestAntiPeepOptions(context);
// 如用户成功开启或开关已开启,可以开始订阅防窥状态

开发步骤

  1. 检测设备能力并查询开关状态:使用 canIUse()检测设备是否支持防窥能力,再调用isDlpAntiPeepSwitchOn() 确认用户已开启系统开关,两项条件均满足后才进行后续初始化。

    export function canUseAntiPeep(): boolean {
      return canIUse('SystemCapability.Security.DlpAntiPeep');
    }
    
    export async function isAntiPeepOn(): Promise<boolean> {
      try {
        if (canUseAntiPeep()) {
          let result: boolean = await dlpAntiPeep.isDlpAntiPeepSwitchOn();
          Logger.info(TAG, `[isAntiPeepOn] isDlpAntiPeepSwitchOn success. ${result}`);
          return result;
        } else {
          return false;
        }
      } catch (err) {
        Logger.error(TAG, `[isAntiPeepOn] isDlpAntiPeepSwitchOn failed.${JSON.stringify(err)}`);
        return false;
      }
    }
    
  2. 获取当前窥视状态并注册监听:调用getDlpAntiPeepInfo()同步获取页面展示时的初始状态。

    if (canUseAntiPeep()) {
      isAntiPeepOn().then(async (opened) => {
        if (opened) {
          let info = getAntiPeepInfo();
          // ...
        }
      })
      // ...
    }
    
    export function getAntiPeepInfo(): dlpAntiPeep.DlpAntiPeepStatus {
      try {
        if (canUseAntiPeep()) {
          let dlpAntiPeepStatus = dlpAntiPeep.getDlpAntiPeepInfo();
          Logger.info(TAG, `getDlpHideInfo success. ${JSON.stringify(dlpAntiPeepStatus)}`);
          return dlpAntiPeepStatus;
        } else {
          return -1;
        }
      } catch (err) {
        Logger.info(TAG, `getDlpHideInfo failed. ${JSON.stringify(err)}`);
        return -1;
      }
    }
    
  3. 根据窥视状态切换敏感信息显示:在状态回调中,当状态为DlpAntiPeepStatus.HIDE时将@State修饰的金额变量替换为 ****,状态为DlpAntiPeepStatus.PASS时恢复真实数值,ArkUI框架自动触发界面刷新。

    if (canUseAntiPeep()) {
      isAntiPeepOn().then(async (opened) => {
        if (opened) {
          let info = getAntiPeepInfo();
          this.handleAntiPeepStatus(info);
          // ...
        }
      })
      // ...
    }
    
    private async handleAntiPeepStatus(status: dlpAntiPeep.DlpAntiPeepStatus) {
      if (canUseAntiPeep()) {
        switch (status) {
          case dlpAntiPeep.DlpAntiPeepStatus.PASS:
            this.summaryShow(true);
            this.itemsEnableShow(true);
            break;
          case dlpAntiPeep.DlpAntiPeepStatus.HIDE:
            // ...
            this.summaryShow(false);
            this.itemsEnableShow(false);
            break;
          default:
            this.summaryShow(true);
            this.itemsEnableShow(true);
            break;
        }
      } else {
        return
      }
    }
    
  4. 实时监听:通过on(“dlpAntiPeep”)注册监听,确保状态变化时立即响应,更新界面内容。

    private antiPeepCB: AntiPeepCallback = {
      onStatusChanged: async (status: dlpAntiPeep.DlpAntiPeepStatus): Promise<void> => {
        await this.handleAntiPeepStatus(status);
      }
    };
    
    export function listenOnAntiPeepStatus(antiPeepCB: AntiPeepCallback): boolean {
      try {
        if (canUseAntiPeep()) {
          Logger.info(TAG, `start on('dlpAntiPeep')`);
          dlpAntiPeep.on('dlpAntiPeep', (dlpAntiPeepStatus: dlpAntiPeep.DlpAntiPeepStatus) => {
            Logger.info(TAG, `dlpAntiPeep callback: ${JSON.stringify(dlpAntiPeepStatus)}`);
            if (antiPeepCB) {
              antiPeepCB.onStatusChanged(dlpAntiPeepStatus);
            } else {
              Logger.warn(TAG, `antiPeepCB is empty`);
            }
          });
          Logger.info(TAG, `on('dlpAntiPeep') ok`);
          return true;
        } else {
          return false;
        }
      } catch (err) {
        Logger.error(TAG, `dlpAntiPeep.on failed. ${JSON.stringify(err)}`);
        return false;
      }
    }
    
  5. 触发系统防窥蒙层(可选):当用户开启了防窥蒙层开关且检测到窥视时,在切换敏感信息显示函数handleAntiPeepStatus()中调用

    setAntiPeepMaskLayer()

    拉起系统级蒙层覆盖整个窗口。建议开发者在检测到窥视状态后调用一次即可,频繁调用可能对用户体验造成影响。直到用户重新进入页面前不再重复触发。

    case dlpAntiPeep.DlpAntiPeepStatus.HIDE:
      if (this.isShowToggle && this.isToggleOpened) {
        try {
          // The anti-peep overlay has not been triggered yet.
          if (!this.isSystemLayerTriggered && !this.isAlertDialogShow) {
            const id = AppStorage.get(ConstValues.WINDOW_ID_NAME) as number;
            this.isSystemLayerTriggered = await showSystemMaskLayer(id);
            // ...
          }
        } catch (err) {
          Logger.error(TAG, `Show mask layer error:${JSON.stringify(err)}`);
        }
      }
      // ...
      break;
    
    export function showSystemMaskLayer(windowId: number): Promise<boolean> {
      return new Promise((resolve) => {
        try {
          if (canUseAntiPeep()) {
            dlpAntiPeep.setAntiPeepMaskLayer(windowId).catch((err: BusinessError) => {
              Logger.error(TAG, `Execute setAntiPeepMaskLayer failed. error code:${err.code}, error message:${err.message}`);
              resolve(false);
            }).then(() => {
              Logger.info(TAG, `setAntiPeepMaskLayer success`);
              resolve(true);
            })
          } else {
            resolve(false);
          }
        } catch (err) {
          Logger.error(TAG, `Call setAntiPeepMaskLayer failed. ${JSON.stringify(err)}`);
          resolve(false);
        }
      });
    }
    
  6. 页面销毁时取消监听:在aboutToDisappear()生命周期中调用off(“dlpAntiPeep”)取消监听,防止页面销毁后回调仍然触发导致内存泄漏。

aboutToDisappear(): void {
  if (this.isListenOn) {
    listenOffAntiPeepStatus();
    this.isListenOn = false;
  }
  // ...
}
export function listenOffAntiPeepStatus() {
  try {
    if (canUseAntiPeep()) {
      Logger.info(TAG, `start off('dlpAntiPeep')`);
      dlpAntiPeep.off('dlpAntiPeep');
      Logger.info(TAG, `off('dlpAntiPeep') ok`);
    }
  } catch (err) {
    Logger.error(TAG, `dlpAntiPeep.off failed. ${JSON.stringify(err)}`);
  }
}

效果预览

防窥保护效果如下所示:
在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐