HarmonyOS 鸿蒙Next中静默的获取用户的手机唯一标识

HarmonyOS 鸿蒙Next中静默的获取用户的手机唯一标识 需要静默的获取用户的手机唯一标识 ,有什么实现方案推荐吗?

3 回复

【背景知识】

常见设备的标识有OAID、ODID、AAID、UDID等,定义和用途如下:

  • OAID(开放匿名设备标识符)一种非永久性设备标识符,基于OAID,可在保护用户个人数据隐私安全的前提下,媒体App、广告平台、三方监测平台等开发者,可获取设备上的OAID,进行个性化广告推荐或广告转化归因分析。
  • ODID(开发者匿名设备标识符):用于识别同一设备上运行的同一个开发者的应用,标识应用身份。帮助开发者更好地理解用户在不同应用间的行为,从而提供更个性化的服务和推荐。
  • AAID(应用匿名标识符):标识应用的身份,主要用于应用的消息推送。
  • UDID(设备唯一标识符):标识设备的属性,可作为设备唯一识别码。

开发者需要根据具体需要去使用合适的标识符。以下分别介绍上述四种标识符:

1. OAID主要用于个性化广告推荐或广告转化归因分析,可通过identifier.getOAID()获取OAID,

注意:获取OAID需要申请权限ohos.permission.APP_TRACKING_CONSENT权限,该权限是user_grant权限,需要弹窗授权用户允许后才可以获取。如果未定义该权限或者“跨应用关联访问权限”为“禁止”,获取的OAID将为全0。具体可参考广告标识服务
OAID是设备级标识符同一台设备上不同的App获取到的OAID值一样,且在应用卸载后不会变化,但在以下场景会发生改变

  • 用户手机恢复出厂设置。
  • 用户手动操作重置OAID:打开系统设置–隐私和安全–跨应用关联页面,把所有应用的跟踪权限关了,再任意打开一个,即可触发OAID的重置。

示例:

import { abilityAccessCtrl, common, Want } from '@kit.AbilityKit';
import { identifier } from '@kit.AdsKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  @State oaid: string = '';

  build() {
    Column() {
      Button('手动开启允许跨应用关联访问权限').onClick(()=>{
        let want: Want = {
          bundleName: 'com.huawei.hmos.settings',
          abilityName: 'com.huawei.hmos.settings.MainAbility',
          uri: 'privacy_settings',
          parameters: {
            // 传对应应用的包名
            pushParams: 'com.example.myapplication'
          }
        };
        this.context.startAbility(want);
      })

      Button('获取设备OAID')
        .margin(20)
        .onClick(() => {
          let atManager = abilityAccessCtrl.createAtManager();
          atManager.requestPermissionsFromUser(this.context, ['ohos.permission.APP_TRACKING_CONSENT']).then((data) => {
            console.info(`data: ${data}`);
            console.info(`data permissions: ${data.permissions}`);
            console.info(`data authResults: ${data.authResults}`);
            identifier.getOAID().then((data) => {
              this.oaid = data;
              console.info(`Succeeded in getting oaid. Oaid is ${this.oaid}`);
            }).catch((err: BusinessError) => {
              console.error(`Failed to get oaid. Code is ${err.code}, message is ${err.message}`);
            });
          }).catch((err: BusinessError) => {
            console.info(`data: ${err}`);
          });
        });

      Text(`${this.oaid}`)
    }
    .height('100%')
    .width('100%')
  }
}

2. ODID的获取方法:通过Basic Services Kit的deviceInfo.ODID接口获取ODID,

注意:ODID是用于标识同一设备上同一开发者的应用,是开发者级的标识符。同一台设备上运行的同一个开发者的应用,ODID相同。同一台设备上不同开发者的应用,ODID不同。ODID在以下场景重新生成:

  • 设备恢复出厂设置。
  • 同一设备上同一个开发者的应用全部卸载后重新安装时。

示例:

import { deviceInfo } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State Odid:string = '';

  build() {
    Column() {
      Button('获取设备ODID').onClick(async () => {
        this.Odid = deviceInfo.ODID;
      })
        .height(100)
        .width('100%')
        .margin(20)

      Text(`${this.Odid}`)
    }
  }
}

3. AAID的获取方法:通过getAAID()获取AAID,

注意:AAID和已有的任何标识符都不关联,并且每个应用只能访问自己的AAID,是应用级的标识符。只有该应用实例才能访问该标识符,它只存在于应用的安装期,适用于应用的消息推送。AAID会在以下场景中发生变化:

  • 应用卸载重装。
  • 应用调用删除AAID接口。
  • 用户恢复出厂设置。
  • 用户清除应用数据。

示例:

import { AAID } from '@kit.PushKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State Aaid:string = '';

  build() {
    Column() {
      Button('获取设备AAID')
        .onClick(()=>{
          try {
            AAID.getAAID().then((data: string) => {
              console.info('Succeeded in getting AAID');
              this.Aaid = data;
            }).catch((err: BusinessError) => {
              console.error('Failed to get AAID: %{public}d %{public}s', err.code, err.message);
            });
          } catch (err) {
            let e: BusinessError = err as BusinessError;
            console.error('Failed to get AAID: %{public}d %{public}s', e.code, e.message);
          };
        })
        .height(100)
        .width('100%')
        .margin(20)

      Text(`${this.Aaid}`)
    }
  }
}
  1. UDID的获取方法:通过Basic Services Kit的deviceInfo.udid接口获取UDID,

注意:UDID需要申请ohos.permission.sec.ACCESS_UDID权限才能获取,该权限当前只允许系统应用及企业定制应用才能申请。因此,UDID虽然是设备级的唯一标识符,但对三方应用不开放,一般用于本地HAP包的调试

示例:

import { deviceInfo } from '@kit.BasicServicesKit';

@Entry
@Component
struct Index {
  @State uuid:string = '';

  build() {
    Column() {
      Button('获取设备UUID').onClick(async () => {
        this.uuid = deviceInfo.ODID;
      })
        .height(100)
        .width('100%')
        .margin(20)

      Text(`${this.uuid}`)
    }
  }
}

在实际开发中,会使用ODID+关键资产存储服务来实现在应用卸载后仍能有效的确保ODID不发生变化。实现思路:将ODID存放于asset中,并设置IS_PERSISTENT为true。示例代码如下:

import { asset } from '@kit.AssetStoreKit';
import util from '@ohos.util';
import { deviceInfo } from '@kit.BasicServicesKit';

function stringToArray(str: string): Uint8Array {
  let textEncoder = new util.TextEncoder();
  return textEncoder.encodeInto(str);
}

function setAttr(id: string) {
  let attr: asset.AssetMap = new Map();
  attr.set(asset.Tag.SECRET, stringToArray(id));
  attr.set(asset.Tag.ALIAS, stringToArray('demo_device_id'));
  attr.set(asset.Tag.IS_PERSISTENT, true);
  try {
    asset.add(attr).then(() => {
      console.info(`Asset added successfully.`);
    }).catch(() => {
      console.error(`Failed to add Asset.`);
    });
  } catch (error) {
    console.error(`Failed to add Asset.`);
  };
}

function arrayToString(arr: Uint8Array): string {
  let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true });
  let str = textDecoder.decodeToString(new Uint8Array(arr));
  return str;
}

async function getAttr(): Promise<string> {
  let query: asset.AssetMap = new Map();
  query.set(asset.Tag.ALIAS, stringToArray('demo_device_id')); // 指定了关键资产别名,最多查询到一条满足条件的关键资产
  query.set(asset.Tag.RETURN_TYPE, asset.ReturnType.ALL); // 此处表示需要返回关键资产的所有信息,即属性+明文
  try {
    const res: Array<asset.AssetMap> = await asset.query(query);
    // 解析密钥
    let secret: Uint8Array = res[0].get(asset.Tag.SECRET) as Uint8Array;
    // 将uint8array解析为字符串
    let secretStr: string = arrayToString(secret);
    return secretStr;
  } catch (error) {
    console.error(`Failed to query Asset.`);
    return '';
  }
}

@Entry
@Component
struct AttrTest {
  build() {
    Column() {
      Button('获取设备ID').onClick(async () => {
        let deviceId: string = await getAttr();
        if (deviceId === undefined || deviceId === null || deviceId.length === 0) {
          deviceId = deviceInfo.ODID;
          setAttr(deviceId);
        }
        console.log(`设备ID为:${deviceId}`);
      })
        .height(100)
        .width('100%')
    }
  }
}

更多关于HarmonyOS 鸿蒙Next中静默的获取用户的手机唯一标识的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,可通过@ohos.deviceInfo模块的getDeviceId方法获取设备唯一标识。该标识符由系统生成,具有唯一性和稳定性,适用于用户识别。使用时需在module.json5中声明ohos.permission.DISTRIBUTED_DATASYNC权限。示例代码:import deviceInfo from '@ohos.deviceInfo'; let deviceId = deviceInfo.deviceId; 注意,该标识符与设备绑定,不会因应用卸载而改变。

在HarmonyOS Next中,出于用户隐私保护要求,系统不再提供直接获取设备唯一标识(如IMEI、序列号等)的静默方式。推荐采用以下合规方案:

  1. 使用OAID(匿名设备标识符)
    通过[@ohos](/user/ohos).identifier模块获取OAID,该标识符在用户重置设备时可变更,需用户授权。调用示例:

    import { deviceId } from '[@ohos](/user/ohos).identifier';
    let oaid = await deviceId.getOAID();
    
  2. 生成应用专属标识
    通过[@ohos](/user/ohos).data.preferences持久化生成GUID/UUID,卸载应用后失效:

    import { preferences } from '[@ohos](/user/ohos).data.preferences';
    // 生成并存储UUID
    
  3. 结合系统能力生成标识
    使用[@ohos](/user/ohos).deviceInfo获取设备类型、型号等非敏感信息,结合时间戳等生成复合标识。

注意:所有方案均需在应用清单中声明对应权限(如ohos.permission.APPROXIMATELY_LOCATION用于OAID),且部分能力需通过用户显式授权。建议根据业务场景选择平衡标识稳定性与隐私合规的方案。

回到顶部