HarmonyOS 鸿蒙Next中静默的获取用户的手机唯一标识
HarmonyOS 鸿蒙Next中静默的获取用户的手机唯一标识 需要静默的获取用户的手机唯一标识 ,有什么实现方案推荐吗?
【背景知识】
常见设备的标识有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}`)
}
}
}
- 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、序列号等)的静默方式。推荐采用以下合规方案:
-
使用OAID(匿名设备标识符)
通过[@ohos](/user/ohos).identifier模块获取OAID,该标识符在用户重置设备时可变更,需用户授权。调用示例:import { deviceId } from '[@ohos](/user/ohos).identifier'; let oaid = await deviceId.getOAID(); -
生成应用专属标识
通过[@ohos](/user/ohos).data.preferences持久化生成GUID/UUID,卸载应用后失效:import { preferences } from '[@ohos](/user/ohos).data.preferences'; // 生成并存储UUID -
结合系统能力生成标识
使用[@ohos](/user/ohos).deviceInfo获取设备类型、型号等非敏感信息,结合时间戳等生成复合标识。
注意:所有方案均需在应用清单中声明对应权限(如ohos.permission.APPROXIMATELY_LOCATION用于OAID),且部分能力需通过用户显式授权。建议根据业务场景选择平衡标识稳定性与隐私合规的方案。

