原文地址:https://juejin.cn/post/7072741392178479140
补充设备标识体系
中文缩写 | 中文名称 | 描述 | 重置性 | 场景 |
---|---|---|---|---|
UUID | 设备唯一标识符 | 设备唯一硬件标识,由设备出厂时的硬件信息生成,如IMEI | 出厂后无法重置 | 设备唯一标识,如广告业务在广告投放时进行效果归因 |
OAID | 匿名设备标识符 | OAID是UUID的替代品,是设备维度的ID。在终端首次启动时生成,同一设备的OAID相同 | 恢复出厂设置后,OAID会重置 | 设备唯一标识,如广告业务在广告投放时进行效果归因 |
VAID | 开发者匿名设备标识符 | VAID是开发者维度的ID。在应用安装时生成,同一设备上且同一开发者的所有应用VAID相同,其他情况VAID不同 | 恢复出厂设置、重新安装应用后,VAID会重置(例外情况:卸载应用时如果设备中另有相同开发者的应用且读取过VAID则不会重置) | 开发者唯一标识,如果统一开发者不同莹莹之间的推荐 |
AAID | 应用匿名设备标识符 | AAID是应用沙盒维度的ID。在应用安装时生成,即使是统一设备且同一个开发者的应用,AAID也不同 | 恢复出厂设置、重新安装、清除用户数据,AAID会重置例外情况:卸载应用时如果设备中另有相同开发者的应用且读取过AAID,则不会重置) | 应用唯一标识,可用于用户统计 |
集成与封装
interface IOAIDApi {
/**
* 1. 初始化,确保用户同意《隐私政策》之后,再初始化 OAID SDK
* 2. 加固版本在调用前必须载入SDK安全库,因为加载有延迟,推荐在 Application 中调用 loadLibrary 方法
*
* @param debug 是否调试,debug 状态会开启 SDK 日志输出
*/
fun init(debug: Boolean)
/**
* 获取 ID,回调可能是同步的,也可能是异步的
*/
@AnyThread
fun fetchDeviceIds(callback: (OAIDResult) -> Unit)
}
internal class OAID private constructor(
context: Context
) : IOAIDApi {
// ApplicationContext
private val context: Context = context.applicationContext
/**
* 证书初始化标记,true:已经初始化
*/
@Volatile
private var isCertInit: Boolean = false
/**
* 证书是否有效,true:有效
*/
private var isCertValid: Boolean = false
/**
* 证书过期时间,null:证书无效
*/
private var certExpDate: Date? = null
/**
* 调试开关
*/
private var debug: Boolean = false
companion object {
@Volatile
private var _oaid: IOAIDApi? = null
@AnyThread
fun oaid(context: Context): IOAIDApi {
if (null == _oaid) {
synchronized(IOAIDApi::class.java) {
if (null == _oaid) {
_oaid = OAIDImpl(context)
}
}
}
return _oaid!!
}
}
override fun init(debug: Boolean) {
System.loadLibrary("msaoaidsec")
this.debug = debug
}
override fun fetchDeviceIds(callback: (OAIDResult) -> Unit) {
// 1. 验证与初始化证书
checkCertValidity()
// 2.1 提供空信息的 ID 提供器
val unsupportedIdSupplier = IdSupplierImpl()
// 2.2 统一的回调接收器
val listener = IIdentifierListener { supplier: IdSupplier? ->
supplier?.let {
// 回调
callback(
OAIDResult(
supplier.isSupported, supplier.isLimited, supplier.oaid, supplier.vaid, supplier.aaid
)
)
}
}
if (!isCertValid) {
// 证书无效,直接回调空信息
listener.onSupport(unsupportedIdSupplier)
return
}
// 3. 调用 SDK 接口获取 OAID
val code = try {
MdidSdkHelper.InitSdk(context, debug, listener)
} catch (error: Error) {
error.printStackTrace()
-1
}
// 4. 处理异常情况
when (code) {
InfoCode.INIT_ERROR_CERT_ERROR, // 证书未初始化或证书无效,SDK 内部不会回调 onSupport
InfoCode.INIT_ERROR_DEVICE_NOSUPPORT, // 不支持的设备, SDK 内部不会回调 onSupport
InfoCode.INIT_ERROR_LOAD_CONFIGFILE, // 加载配置文件出错, SDK 内部不会回调 onSupport
InfoCode.INIT_ERROR_MANUFACTURER_NOSUPPORT, // 不支持的设备厂商, SDK 内部不会回调 onSupport
InfoCode.INIT_ERROR_SDK_CALL_ERROR // SDK 调用出错, SDK 内部不会回调 onSupport
-> {
// 异常情况,直接回调空信息
listener.onSupport(unsupportedIdSupplier)
}
InfoCode.INIT_INFO_RESULT_DELAY, // 获取接口是异步的,SDK 内部会回调 onSupport
InfoCode.INIT_INFO_RESULT_OK // 获取接口是同步的,SDK 内部会回调 onSupport
-> {
// do nothing
}
else -> {
// do nothing
}
}
}
/**
* 验证与初始化证书
* @return true:证书初始化成功;false:证书初始化失败
*/
private fun checkCertValidity() {
/**
* 从asset文件读取证书内容
*
* @return 证书字符串
*/
fun loadPemFromAssetFile(): String {
return try {
// 证书文件名
val certFileName = context.packageName + ".cert.pem"
val inputStream = context.assets.open(certFileName)
val bufferReader = BufferedReader(InputStreamReader(inputStream))
val builder = StringBuilder()
var line: String?
while (bufferReader.readLine().also { line = it } != null) {
builder.append(line)
builder.append('\n')
}
builder.toString()
} catch (e: IOException) {
""
}
}
/**
* 解析证书过期时间
* @return 过期时间,证书不合法时返回 null
*/
fun getCertExpDate(certStr: String): Date? {
return try {
// 证书实体
val cert = CertificateFactory.getInstance("X.509").generateCertificate(certStr.byteInputStream()) as X509Certificate
// 验证证书有效性,如果证书过期会抛出异常
cert.checkValidity()
cert.notAfter
} catch (ex: Exception) {
// 证书无效
null
}
}
// DCL
if (isCertInit) {
// 初始化只需要进行一次,返回上一次的结果
return
}
synchronized(IOAID::class) {
if (isCertInit) {
return
}
// 证书文件名
val certStr = loadPemFromAssetFile()
// 证书过期时间
certExpDate = getCertExpDate(certStr)
// TODO 如果你的应用场景对证书有效性要求非常高,可以在这个时机提前下载更新证书
// 初始化证书,证书只需要初始化一次
isCertValid = MdidSdkHelper.InitCert(context, certStr)
isCertInit = true
}
}
}
网友评论