一、前文
1.1 接入方式
实现源码、接入途径及使用方式,详看:https://github.com/phantomVK/Identifier
1.2 背景介绍
商业化广告分发,强依赖各种用于绑定用户画像的唯一id。但随着Android系统持续迭代,系统层逐步限制读取硬件id的权限。
最终,剩下软uuid这类可行路径,意味着需要不断提高uuid准确度,尽可能达到硬件id的能力水平。
快速扫盲:
-
专业版:通用唯一识别码(英语:Universally Unique Identifier,缩写:UUID)是用于计算机体系中以识别信息的一个128位标识符;
-
简单版:一个类似这种格式的id 4ea478a8-f16d-4ae0-8d37-657dc329d94c
因此,完善uuid有助于实现物料个性化分发、提广告转化率。广告商更精准投放用户喜欢的物料,应用的广告收入也相应增加。
这个uuid需要具备串联 广告投放-分发-曝光&转化-营销归因 整条链路各个节点的能力。
二、技术术语
2.1 硬件标识
-
IMEI 国际移动设备识别码(International Mobile Equipment Identity,IMEI)[1],即通常所说的手机“串号”,用于在移动电话网络中识别每一部独立的手机等移动通信设备[2],相当于移动电话的身份证。序列号共有15位数字,前6位(TAC)是型号核准号码,代表手机类型。接着2位(FAC)是最后装配号,代表产地。后6位(SNR)是串号,代表生产顺序号。最后1位(SP)一般为0,是检验码,备用。
-
IMSI 国际移动用户识别码(英语:IMSI,International Mobile Subscriber Identity),是用于区分蜂窝网络中不同用户的、在所有蜂窝网络中不重复的识别码。手机将IMSI存储于一个64比特的字段发送给网络。IMSI可以用来在归属位置寄存器(HLR,Home Location Register)或拜访位置寄存器(VLR,Visitor Location Register)中查询用户的信息。为了避免被监听者识别并追踪特定的用户,大部分情形下手机和网络之间的通信会使用随机产生的临时移动用户识别码(TMSI,Temporary Mobile Subscriber Identity)代替IMSI。
-
MAC地址(英语:Media Access Control Address),直译为媒体访问控制地址,也称为局域网地址(LAN Address),以太网地址(Ethernet Address)或物理地址(Physical Address),它是一个用来确认网络设备位置的地址。在OSI模型中,第三层网络层负责IP地址,第二层数据链接层则负责MAC地址。MAC地址用于在网络中唯一标示一个网卡,一台设备若有一或多个网卡,则每个网卡都需要并会有一个唯一的MAC地址。
2.2 软件标识
-
GAID 是 Google Play 服务针对广告服务提供的唯一性 ID,可由用户重置和删除。通过广告 ID,用户可以更好地掌控自己的广告体验,开发者则可以借助这个简单的标准化系统持续通过自己的应用创收。它还允许用户重置或删除自己的标识符。
-
OAID 移动安全联盟联合国内手机厂商推出OAID设备识别字段,广告标识符(OAID)是一种非永久性设备标识符,旨在取代原来的IMEI,并逐渐成为广告场景中唯一非永久性设备标识符。
-
IDFA (广告主标识符) 是 Apple 向用户设备随机分配的设备标识符。广告主使用此标识符来跟踪数据,以便提供定制广告。IDFA 不会泄露任何个人信息。相反地,它用于跟踪和识别用户,然后允许广告主访问可用于发现信息的聚合数据 — 例如触发的应用内事件。如果渠道提供 IDFA 跟踪,同时广告主可以跟踪与广告成功互动的用户,那么 IDFA 就可以可以在用户与移动广告推广交互时辨识此类用户。如果满足上述条件,IDFA 可了解特定用户是否出于付款和归因目的而点击广告。
三、调研&目标
3.1 业务背景
Android硬件相关的IMEI、MAC地址信息已被系统禁止获取,退而求其次转向软件标识。需要明确的是,软件uuid不能完全替代硬件标识的能力,尤其是 唯一性 和 不可变性 这两大关键要素。
软件标识是系统通过算法随机生成的id,广义概念上碰撞极低但不为零。用户可以通过系统设置重置为新id、也能阻止系统向任何App提供获取权限。因为软id本质上可能重复,所以 唯一性 特征退化为 相对唯一性;
对于 不可变性,Android的软件标识符分为两大流派:
- OAID:
- 国家推动的背景下,厂商支持力度高、系统覆盖范围广;
- 厂商根据要求各自提供实现能力,国家移动联盟统一封装为MSA-OAID-SDK;
- GAID:
- Google Play Service 为广告商提供获取gaid的能力;
- 国内手机默认不预装该框架,一般无法获取;
同一台设备内任意App获取oaid,系统会保证返回相同值,因此在同一时间内可以实现跨App广告链路的数据流转和营销归因能力。
部分厂商不提供直接更换id能力,只有系统重置才会修改。此外,针对可以修改oaid的设备,App需要周期性获取并上报最新oaid,并结合其他各类id确定该值是否变化。
3.2 联盟SDK
移动联盟SDK,支持获取所有国内设备oaid的能力,并且持续迭代。
- 申请难度
-
需持有公司资质到移动联盟申请AppKey,申请手续烦杂;
-
bugfix和功能升级依赖联盟支持,响应周期长;
-
广告作为累计式收入,尽早上线可持续增收;
-
- 灵活性
-
荣耀从华为分拆后,设备内同时存在 华为oaid 和 荣耀oaid 两种实现,若没有主动重置oaid则值可能不同;
-
各广告sdk依赖不同类型id,但该sdk只能返回oaid,无法同时满足业务诉求;
-
没有支持海外设备自动降级到获取gaid的能力;
-
- 运行性能
- 无内存缓存能力,面对冷启动过程的高频请求,可能会触发魅族手机的oaid调用保护;
- 稳定性
-
该sdk历史版本存在Crash问题,响应及修复速度慢;
-
sdk的native实现令崩溃难以拦截,线上风险不可控;
-
3.3 自研
结合技术和业务角度,总结出以下研发目标:
- 厂商适配:
-
华米OV、三星等主流设备100%支持;
-
其他二三线厂商,覆盖度越大越好;
-
若OAID缺失再尝试GAID,并区分OAID、GAID;
-
- 系统适配:
-
头部厂商新老机型实现代码各异,全部适配;
-
外国品牌设备(Sony、htc、Nokia)尽力而为;
-
- 技术关注:
- 缓存体系
-
获取id是高频操作,若频繁获取会被厂商(魅族)限频,内存缓存是避免大量请求击垮系统的第一道防线;
-
冷启获取oaid速度慢,可考虑从磁盘缓存获取。但是磁盘缓存的TTL设计难度大,因此该模块应由用户自行实现;
-
- 资源优化
-
并发请求、观测优化系统跨进程通讯耗时;
-
部分华为设备限制最多500个线程,可注入线程池让调用方决定线程复用方式;
-
- 方案调研
-
通过公开资料、技术调研等手段,归集各种实现并逐一验证可行性;
-
无法找到设备验证的实现,归纳到 Experimental 作为实验能力,通过开关控制是否启用;
-
- 缓存体系
四、技术实现
4.1 SDK架构
定义并实现获取任务的调度框架,后续可通过增加新Provider的方式,提高兼容性。
4.2 接口设计
-
扩展灵活:避免设计简陋的接口,导致后期无法满足需求而废弃;
-
易于使用:非必要能力,不要求配置、无需用户感知;
-
鲁棒性高:设置参数在执行前做浅克隆,避免运行过程参数被二次变更导致不可预期结果;
4.3 任务调度流程
从App层到系统层,逐层按责任链方式处理任务
4.4 实现选择机制
系统根据Android的 MANUFACTURER 和 BRAND 字段匹配支持的provider。例如:MANUFACTURER==huawei && BRAND==huawei 的设备最多命中3个Provider
方案一、串行执行
-
实现方式:Provider按照顺序查询,有结果则返回;
-
优点:实现、维护难度低,不需要处理并发的结果返回与多任务取消;
-
缺点:暂无,单Provider耗时可控;
方案二、并行执行
-
实现方式:同时请求多个Provider,返回最早的成功结果、同时取消其他任务;
-
优点:执行性能最快,执行过程内存开销极小;
-
缺点:
-
编码复杂度高、增加调试难度;
-
华为设备线程超过500会闪退,而并发任务额外引入额外线程增加稳定性风险;
-
框架已支持内存级缓存,App生命周期内请求可从n次优化到1次,本方案价值不突出。
-
总结
-
同品牌设备不同获取方式,结果相同、但耗时有差异的表现,需要整体权衡利弊;
-
查询失败则降级到下一个实现,有结果则取消后续任务;
-
已经按照耗时从低到高串行查询。耗时最低的方案优先查询,其他方案作为降级;
根据不同厂商的实现,Provider调用耗时为毫秒级。并行任务显著增加编码复杂度,但无法显著降低等待耗时。

4.5 兼容性
从sdk接入方来说,按照优先级如下:
-
接入便捷、编译顺畅。如:依赖库只引入单库、与其他已有依赖库无版本冲突;
-
初始化代码简洁、接口足够灵活,满足业务诉求;
本SDK已达到的要求:
-
语言支撑:依赖kotlin 1.5.0(05/05/21),能符合市面各类App的最低要求(1.7.0 » 1.5.0)。(kotlin 1.5.0是业界使用最广、研发能力较完备的版本)
-
依赖冲突:不依赖Android系统组件外的三方库。接入系统库也是能兼容kotlin 1.5.0的最低版本,覆盖所有依赖场景;
-
接口简单,因为需要考虑ipc异步调用次序,所以仅支持回调这一种实现方式;
-
自建SDK返回值和msa-oaid-sdk一致,例如:荣耀优先返回honor-oaid,若缺失则尝试返回huawei-oaid;
根据SDK嵌入方式也能分为两种:
方式一:直接设置
几乎所有广告sdk都支持接入方提供oaid。因此App直接通过接口提供结果,使用方式简单且可控。以下是快手联盟sdk初始化时获取oaid的接口,其他sdk实现类似不再赘述。
1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class KsCustomController {
@KsAdSdkApi
@Keep
public boolean canUseOaid() {
return true;
}
@KsAdSdkApi
@Keep
public String getOaid() {
return ""; // App实现抽象接口返回oaid结果
}
}
方式二:套包名
不支持直接设置oaid的广告sdk,其内部可能会通过反射的方式,尝试调用外部各类oaid-sdk。因此,通过灵活运用代码包机制,静态代理外部oaid-sdk的包路径、类公共协议为广告sdk提供结果。
原理:外部oaid-sdk = 外部oaid-sdk壳 + 本SDK实现
构造公共协议关注技术点:
-
仿造全部公开类、静态方法且不缺失,并根据反编译的信息,方法要返回正确/异常值;
-
外部oaid-sdk的结果回调在异步线程,App实现时也要对齐。
-
新老版本的静态方法协议基本一致;
-
迁移sdk的混淆规则,实现release包可成功反射获取静态接口;
-
关注外部oaid-sdk回调的线程类型,要支持按需切换;
-
4.6 内存缓存设计
-
设计:保存成功获取的一个结果。对oaid、aaid、vaid算hash-key,通过线程安全的HashMap做分桶存储;
-
优点:简单,基本满足App需求;
-
由于oaid在app生命周期近似不变,缓存行为“读多写少”。应针对读取行为做定向优化,引入CopyOnWrite(写时复制)避免对读取操作做加锁操作;
五、覆盖能力
5.1 支持厂商
-
自测通过13家:华为、小米、Oppo、Vivo、Coolpad、荣耀、魅族、努比亚、三星、ZTE、联想、摩托罗拉、360;
-
接入Google Play Service的外国品牌,如:Pixel、索尼、HTC、诺基亚;
-
暂无测试:PicoVR、小天才、Coosea手机、Freeme系统;
5.2 覆盖范围
| Provider | Manufacturer | oaid | aaid | vaid |
|---|---|---|---|---|
| AsusProvider.kt | ASUS | Y | Y | Y |
| CoolpadServiceProvider.kt | Coolpad | Y | Y | Y |
| CoolpadSettingsProvider.kt | Coolpad | Y | ||
| CooseaProvider.kt | ||||
| FreemeProvider.kt | ||||
| GoogleAdsIdProvider.kt | Y | |||
| HonorSdkProvider.kt | Honor | Y | ||
| HonorServiceProvider.kt | Honor | Y | ||
| HonorSettingsProvider.kt | Honor | Y | ||
| HuaweiContentProvider.kt | Huawei | Y | Y | |
| HuaweiSdkProvider.kt | Huawei | Y | Y | |
| HuaweiServiceProvider.kt | Huawei | Y | Y | |
| HuaweiSettingsProvider.kt | Huawei | Y | Y | |
| MeizuProvider.kt | Meizu | Y | Y | |
| NubiaProvider.kt | Nubia | Y | Y | Y |
| OppoColorOsProvider.kt | Oppo | Y | Y | |
| OppoContentProvider.kt | Oppo | Y | Y | |
| OppoHeyTapProvider.kt | Oppo | Y | Y | |
| OppoIdProvider.kt | Oppo | Y | Y | |
| PicoProvider.kt | Pico | |||
| QikuBinderProvider.kt | Qiku | Y | ||
| QikuServiceProvider.kt | Qiku | |||
| SamsungProvider.kt | Samsung | Y | Y | Y |
| VivoProvider.kt | Vivo | Y | Y | |
| XiaomiProvider.kt | Xiaomi | Y | Y | Y |
| XtcProvider.kt | ||||
| ZteProvider.kt | ZTE | Y | Y | Y |
| ZuiProvider.kt | Lenovo, Motorola | Y | Y | Y |