如何获取移动设备唯一标识
在移动广告领域,设备唯一标识是用来追踪用户的最重要的标识。
对于精准广告和个性化推荐而言,可以通过设备唯一标识,进行千人千面的精准投放。
既然移动设备唯一标识如此的重要,那我们今天就来说一说如何获取设备的唯一标识。
IMEI/MEID
IMEI (International Mobile Equipment Identity) 是国际移动设备识别码,即通常所说的手机“串号”,用于在移动电话网络中识别每一部独立的手机等移动通信设备,相当于移动电话的身份证。IMEI 适用于 GSM、WCDMA、LTE 制式的移动设备。
MEID (Mobile Equipment IDentifier) 是移动设备识别码,它也是一个全球唯一识别移动设备的号码。但它适用于 CDMA 制式手机。
对于全网通的移动设备 IMEI 和 MEID 标识都存在,对于支持双卡的设备,还会有 2 个 IMEI/MEID。
因为这两个标识的作用相同,所以对于移动广告行业来说,一般不对 IMEI/MEID 做严格区分,在广告请求中一般作为同一个字段(比如:DeviceID
或缩写成 did
)进行传输。
对于 iOS 设备来说,苹果官方在 iOS 5.0 之后就屏蔽了获取 IMEI/MEID 的接口,因此现在的苹果设备都无法获取到 IMEI/MEID。
对于 Android 设备来说,可以通过下面的方法来获取 IMEI/MEID:
static String getDeviceId(Context context) { |
对于非手机设备,如 Android 平板电脑,电视等,这些设备没有通话的硬件功能,系统中也就没有 TELEPHONY_SERVICE
,所以这里要判断一下 tm
这个返回值是否为空。
在 Android 8.0 之后,TelephonyManager
上的这个 getDeviceId
方法尽管被标记为已过时,并提供了 getImei
和 getMeid
来取代它。但 getDeviceId
又不是不能用,而且我们也不需要区分获取到的究竟是 Imei
还是 Meid
,所以这里我们不需要通过判断版本号的方式来替换这个方法。
获取 IMEI/MEID 的需要 READ_PHONE_STATE
权限,否则会发生 SecurityException
异常。如果应用以 Android 10 或更高版本为目标平台,在应用没有 READ_PRIVILEGED_PHONE_STATE
权限时,也会发生 SecurityException
异常,而且普通开发者开发的应用是不可能获取到该权限的。所以,调用 tm.getDeviceId()
时,我们加了 try/catch
语句,避免崩溃。
IMSI
IMSI (International Mobile Subscriber Identity) 是国际移动用户识别码。通俗的讲,它是相对手机卡而言的唯一识别码。
在 iOS 设备上,跟 IMEI/MEID 一样,是无法获取到 IMSI 的。
对于 Android 设备来说,获取 IMSI 的方法跟获取 IMEI/MEID 的方法类似:
static String getIMSI(Context context) { |
在 Android 上获取 IMSI 的权限要求跟 IMEI/MEID 一样,这里就不在重复。
Android ID
Android ID 是 Android 设备里不依赖于硬件的一种「半永久标识符」,在系统生命周期内不会改变,但系统重置或刷机后可能会发生变化,其作用域为一组有关联的应用。
Android ID 获取的方式很简单:
static String getAndroidID(Context context) { |
但是在 Android 8.0 以后,签名不同的 App 所获取的 Android ID 是不一样的,但同一个开发者可以根据自己的数字签名,将所开发的不同 App 进行关联。
Android AdID
Android AdID (简称 AAID) 是 Android 平台专为广告跟踪提供的唯一标识。但是 AAID 依赖 Google 服务框架,但国内手机基本上都没有内置 Google 服务框架,这种情况下,就无法获取 AAID。因此,这里就不做讨论了。
OAID
上面在介绍 IMEI/MEID 时谈到过,在 Android 10 以后,IMEI/MEID、IMSI 这些设备标识都被限制读取了。没有了 IMEI/MEID、IMSI 这些设备唯一标识,对于国内广告行业来说,简直是灭顶之灾。不过正所谓道高一尺魔高一丈,为了拯救国内移动广告市场,移动安全联盟(MSA) 推出了一套“移动智能终端补充设备标识体系”,并根据这套体系的技术要求,联盟开发并发布了一套支持多厂商的统一的补充设备标识调用 SDK。
这套体系中包含了四种标识符:UDID(设备唯一标识符), OAID(匿名设备标识符), VAID(开发者匿名设备标识符), AAID(应用匿名设备标识符)。
这四种标识符中,UDID 相当于 IMEI/MEID,它具有无法重置,始终不变的特性。但是它不是提供给广告业务使用的,并且在统一 SDK v1.0.10 版本之后,这个标识符的获取接口被移除了。
OAID(匿名设备标识符)是用于广告业务的标识符,它在系统首次启动后立即生成,它虽然允许用户手动重置,在刷机、恢复出厂设置等特殊情况下也会重置,但是对于同一台设备上的不同 App,只要没有被重置的情况下,获取到的值都是一致的。因此,虽然它的跟踪特性跟 IMEI/MEID 相比稍微差了点,但是比 Android ID 还是要好一些的。
关于如何获取 OAID,在 移动安全联盟(MSA) 官网的文档里有详细说明,在我们公司的广告 SDK 演示实例中也给出了具体的代码实例,由于代码较长,这里就不再单独贴出来了。
另外多说一句,如果想要在 Flutter 开发的 Android 应用中获取 OAID 的话,可以使用 flutter_msa_sdk 这个插件,这个插件也是我们公司开发的,不过没有什么技术难度,只是对移动安全联盟(MSA) 的统一 SDK 做了一下封装,不过用起来要比 Android 原版容易的多。
IDFA/IDFV
前面我们说了,iOS 设备虽然也有 IMEI、IMSI,但是苹果说要保护用户隐私,于是在 iOS 5.0 之后,就把这些接口取消了,不再允许开发者来获取它们了。
可是隐私归隐私,广告还是要做的嘛,于是苹果在 iOS 6.0 之后,推出了 IDFA 这样一个专门用于广告的标识符。同时还提供了一个 IDFV,它是同一个组织下的唯一标识符。
IDFA 允许用户在设置中进行重置,刷机或重置设备时,该标识符也会重置,但是在不同的 App 之间它是同一个值。它相当于上面谈到的 OAID 或 Android AdID。
IDFV 是给 Vendor 标识用户用的,对于隶属于同一个组织的 App,在同一台设备上获取到的 IDFV 都是同一个值。假设有两个应用,它们的 BundleID 分别是 com.adtalos.demo1 和 com.adtalos.demo2,那么它们的就是同一个 Vendor,这两个应用在同一台设备中获取到的 IDFV 就是相同的。它相当于 Android ID,只是表示组织的方式不同而已。
IDFA 和 IDFV 的获取方式非常简单:
NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; |
IDFA 和 IDFV 都可能获取不到值,获取不到时,返回值为 nil
。
Mac
除了以上这些 ID 标识符以外,Mac 地址通常也会用于广告追踪。
但是很不幸,从 iOS 7.0 之后,苹果就禁止开发者获取 Mac 地址了。
对于 Android 来说,情况稍微好一点,虽然从 Android 6.0 开始,通过 WifiInfo
获取到的 Mac 地址永远是 02:00:00:00:00:00
这样一个固定值。但是通过下面这个方法还是可以在大多数情况下获取到 Mac 地址的:
static String getMac() { |
原理是扫描各个网络接口,当网络接口是 wlan0
或者 eth0
时,返回它的 Mac 地址。