Android设备唯一码的获取

UTDID是集团无线设备统一ID方案,目的是给每一台设备一个ID,作为唯一标识。UTDID由客户端生成,并在设备中各个客户端之间共享。UTDID的生成中包含时间戳和随机数等,因此重新生成的UTDID值一定是会改变的,UTDID的稳定性强依赖于手机存储,UTDID方案是一个重在持久化存储的方案。

1.老UTDID方案面临的问题

1.1 权限问题

utdid开发手册中是强制需要下面3个权限的:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

但是随着Android6.0的发布和targetSDK使用23编译,很多权限问题都暴露了出来:
1.WRITE_SETTINGS
targetSDK使用23以后,即使申请了WRITE_SETTINGS权限,再想写settings中的数据会抛出了如下异常。 
IllegalArgumentException: You cannot keep your settings in the secure settings. 
想写Setting.System中的数据也已经没有权限了,即使想修改Settings.Global也需要是系统应用。

2.READ_PHONE_STATE
targetSDK使用23以后,需要手动授权。imei作为utdid生成字段的其中一部分,在生成utdid时,如果无法获取就使用随机值代替。这个即使不授权问题也不大。

3.WRITE_EXTERNAL_STORAGE
targetSDK使用23以后,需要手动授权。在settings中没有写入utdid的情况下,如果没有WRITE_EXTERNAL_STORAGE权限,应用外部的utdid是无法获得的,内部也没有的情况下,那么utdid势必会重新生成了。

1.2 内、外存储可靠性

如果settings不能保存,那么应用外就需要寄希望于sdcard的存储。除了权限问题会导致sdcard中的数据无法取得外,三方的手机管理工具也会对sdcard中的数据做清除。(utdid外存储目录用AAA,BBB表示)
以360手机卫士为例:
360深度空间清理时,在可安全清理项->广告垃圾->将AAA和BBB识别为淘宝应用缓存和阿里网页广告,导致被删除掉。AAA和BBB文件夹下的所以文件和文件夹被会被删除。
个人感觉这些文件的识别是依赖360某些配置的下发,因为在断网并清除360缓存的情况下,是识别不到AAA和BBB的。(360安装包文件被混淆,网络数据被加密,很难知道真正的实现。)
应用内的存储可靠性就更不用说了,卸载应用或清除应用缓存就直接没有了。

2.Android中一些其它的唯一标识

Android系统中并没有可靠获取所有厂商设备唯一ID的方法,各个方法都有自己的使用范围和局限性,这也是目前流行的Android系统版本过多,设备也是来自不同厂商,且没有统一标准等原因造成的。

2.1 通过Android SDk获取标识

DEVICE_ID

假设我们确实需要用到真实设备的标识,可能就需要用到DEVICE_ID。
在以前,我们的Android设备是手机,这个DEVICE_ID可以同通过getSystemService(Context.TELEPHONY_SERVICE).getDeviceId()获取,它根据不同的手机设备返回IMEI,MEID或者ESN码,但它在使用的过程中会遇到很多问题:

非手机设备:最开始搭载Android系统都手机设备,而现在也出现了非手机设备:如平板电脑、电子书、电视、音乐播放器等。这些设备没有通话的硬件功能,系统中也就没有TELEPHONY_SERVICE,自然也就无法通过上面的方法获得DEVICE_ID。当设备为手机时,返回设备的唯一ID。手机制式为 GSM 时,返回手机的 IMEI 。手机制式为 CDMA 时,返回手机的 MEID 或 ESN 。非电话设备或者 Device ID 不可用时,返回 null .
权限问题:获取DEVICE_ID需要READ_PHONE_STATE权限,如果只是为了获取DEVICE_ID而没有用到其他的通话功能,申请这个权限一来大才小用,二来部分用户会怀疑软件的安全性。 (Android 6.0 以上需要用户手动赋予该权限)
厂商定制系统中的Bug:少数手机设备上,由于该实现有漏洞,会返回垃圾,如:zeros或者asterisks

MAC ADDRESS

wifi mac获取方法:

WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
String mac=wifiInfo.getMacAddress();

这种方法比较通用,但是最近在Android 6.0系统上,这个方法失效了,返回了”02:00:00:00:00:00”的常量。这并不是一个BUG,在google的博客中找到如下一段话:

Most notably, Local WiFi and Bluetooth MAC addresses are no longer available. The getMacAddress() method of a WifiInfo object and the BluetoothAdapter.getDefaultAdapter().getAddress() method will both return 02:00:00:00:00:00 from now on.

可以考虑使用NetworkInterface.getHardwareAddress。其原理和cat /sys/class/net/wlan0/address是一模一样的,但是这个是上层API,不需要自己处理底层数据,在Android 6.0上测试通过。

NetworkInterface networkInterface = NetworkInterface.getByName("wlan0");
byte[] mac = networkInterface.getHardwareAddress();

问题:
1.如果重启手机后,Wifi没有打开过,是无法获取其Mac地址的。(可以考虑授予CHANGE_WIFI_STATE权限,开关一次wifi刷一下。)
2.有一些定制系统的目录并不一样。 例如三星的目录为"cat /sys/class/net/eth0/address",所以是否对所以机型都有效有待验证。(需要适配)
3.网上也有反映mac变更问题,是不是刷mac或者wifi故障导致,也不确定。
4.并不是所有的设备都有Wifi硬件,硬件不存在自然也就得不到这一信息。(这个还好)
5.需要 ACCESS_WIFI_STATE 权限。(这个还好)

设备序列号(Serial Number, SN)

获取办法:
String serialNum = android.os.Build.SERIAL;
装有SIM卡的设备获取办法:getSystemService(Context.TELEPHONY_SERVIEC).getSimSerialNumber();

注意对CDMA设备,返回的是一个空值。
在Android 2.3可以通过android.os.Build.SERIAL获取,非手机设备可以通过该接口获取。
在少数的一些设备上,会返回垃圾数据。对于没有通话功能的设备,它可能会返回一个固定的值。

ANDROID_ID

在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被恢复出厂设置后该值会被重置。可以通过下面的方法获取:

import android.provider.Settings;
String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);

ANDROID_ID可以作为设备标识,但需要注意:
它在Android <=2.1 or Android >=2.3的版本是可靠、稳定的,但在2.2的版本并不是100%可靠的

厂商定制系统的Bug:不同的设备可能会产生相同的ANDROID_ID:9774d56d682e549c。(摩托罗拉好像出现过这个问题)
厂商定制系统的Bug:有些设备返回的值为null。
设备差异:对于CDMA设备,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
并且,如果某个Andorid手机被Root过的话,这个ID也可以被改变。

2.2 通过Linux命令获取标识

cpu号

文件路径:/proc/cpuinfo
通过Adb shell 查看:adb shell cat /proc/cpuinfo
但是在Nexus4的Android6.0手机上Serial为0000000000000000

rickydeMacBook-Pro:bin ricky$ adb shell cat /proc/cpuinfo
Processor   : ARMv7 Processor rev 2 (v7l)
processor   : 0
BogoMIPS    : 13.53processor   : 1
BogoMIPS    : 13.53processor   : 2
BogoMIPS    : 13.53processor   : 3
BogoMIPS    : 13.53Features    : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
CPU implementer : 0x51
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0x06f
CPU revision    : 2Hardware    : QCT APQ8064 MAKO
Revision    : 000b
Serial      : 0000000000000000

mac地址

文件路径: /sys/class/net/wlan0/address
通过Adb shell 查看:adb shell cat /sys/class/net/wlan0/address

rickydeMacBook-Pro:bin ricky$ adb shell  cat /sys/class/net/wlan0/address
10:68:3f:49:93:7d

3.Android获取手机制作商,系统版本等

我们有时候会需要获取当前手机的系统版本来进行判断,或者需要获取一些当前手机的硬件信息。

3.1 android.os.Build类

android.os.Build类中,包括了这样的一些信息。我们可以直接调用 而不需要添加任何的权限和方法。

android.os.Build.BOARD:获取设备基板名称
android.os.Build.BOOTLOADER:获取设备引导程序版本号
android.os.Build.BRAND:获取设备品牌
android.os.Build.CPU_ABI:获取设备指令集名称(CPU的类型)
android.os.Build.CPU_ABI2:获取第二个指令集名称
android.os.Build.DEVICE:获取设备驱动名称
android.os.Build.DISPLAY:获取设备显示的版本包(在系统设置中显示为版本号)和ID一样
android.os.Build.FINGERPRINT:设备的唯一标识。由设备的多个信息拼接合成。
android.os.Build.HARDWARE:设备硬件名称,一般和基板名称一样(BOARD)
android.os.Build.HOST:设备主机地址
android.os.Build.ID:设备版本号。
android.os.Build.MODEL :获取手机的型号 设备名称。
android.os.Build.MANUFACTURER:获取设备制造商
android:os.Build.PRODUCT:整个产品的名称
android:os.Build.RADIO:无线电固件版本号,通常是不可用的 显示unknown
android.os.Build.TAGS:设备标签。如release-keys 或测试的 test-keys
android.os.Build.TIME:时间
android.os.Build.TYPE:设备版本类型  主要为"user" 或"eng".
android.os.Build.USER:设备用户名 基本上都为android-build
android.os.Build.VERSION.RELEASE:获取系统版本字符串。如4.1.2 或2.2 或2.3等
android.os.Build.VERSION.CODENAME:设备当前的系统开发代号,一般使用REL代替
android.os.Build.VERSION.INCREMENTAL:系统源代码控制值,一个数字或者git hash值
android.os.Build.VERSION.SDK:系统的API级别 一般使用下面大的SDK_INT 来查看
android.os.Build.VERSION.SDK_INT:系统的API级别 数字表示

3.2 build.prop中获取当前系统属性

在Android系统中,/system/build.prop中含有大量系统相关的信息:

rickydeMacBook-Pro:bin ricky$ adb shell cat /system/build.prop# begin build properties
# autogenerated by buildinfo.sh
ro.build.id=MDB08M
ro.build.display.id=cm_mako-userdebug 6.0 MDB08M c28ecd9956 test-keys
ro.build.version.incremental=c28ecd9956
ro.build.version.sdk=23
ro.build.version.preview_sdk=0
ro.build.version.codename=REL
ro.build.version.all_codenames=REL
ro.build.version.release=6.0
ro.build.version.security_patch=2015-11-01
ro.build.version.base_os=
ro.build.date=2015年 11月 23日 星期一 17:03:23 CST
ro.build.date.utc=1448269403
ro.build.type=userdebug
ro.build.user=moonlight
ro.build.host=moonlight-roms
ro.build.tags=test-keys
ro.build.flavor=cm_mako-userdebug
ro.product.brand=google
ro.product.name=occam
ro.product.board=MAKO
... ...

如果有root权限,修改/system/build.prop文件内容,系统相关的信息就会被改变(总会有那么些无聊的人)。因此设备信息的安全级别不高,但是可以作为参考。此外,可以上传一些手机root相关信息做参考(虽然也不是100%有效)。

3.3 关于手机root相关信息采集

可以提供手机root相关信息,作为build.prop有效性的一个参考值。

3.3.1 build.prop中的字段描述

在/system/build.prop中的Build.TAGS字段设备标签。如release-keys 或测试的 test-keys。
test-keys为root手机或第三方ROM
ro.build.type字段设备版本类型。如:user或userdebug等。

3.3.2 su文件是否存在

查看su文件是否存在,可以参考下面代码的检索路径:

"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su","/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"

3.3.3 apk检查

查看/system/app/ 下是否存root后常用软件。Kinguser.apk、Superuser.apk等,如:

private boolean hasSuperuserApk() {return new File("/system/app/Superuser.apk").exists();
}

3.3.4 执行su命令

执行su命令,推荐使用new ProcessBuilder().command("su").start() 代替Runtime.getRuntime().exec()实现。Runtime.getRuntime().exec()的执行会有些bug,可以参考(Determine if running on a rooted device),记得要Process.destroy()。执行su命令,会唤起root授权对话框,在数据上报的场景是不建议使用,体验非常不好。

4.总结

设备唯一标识码还是以utdid做标识,但是在Android6.0+系统上,外存储权限越来越难获取和越来越不可靠的情况下,除考虑加入LocalSocket和Broadcast等机制做多应用间的utdid同步(问题也很明显)外,必须依赖网络,构建设备ID库来提升设备标识的可靠性。
因此需要考虑在服务器上建立utdid与各设备数据间的对应关系,通过做大规模的适配和数据上报,来解决问题。通过可获得手机参数做服务器请求,服务器的utdid与各设备数据间的对应关系表来寻找最匹配utdid值。
可以考虑的数据关系体系是以Wifi Mac地址、设备序列号、ANDROID_ID为主要基准,配合android.os.Build中手机基本信息为参考(用手机root相关信息采集做修正),DEVICE_ID(用READ_PHONE_STATE权限做修正),常用ip地址等。如果可以的话,还可以参考手机号码、业务登录账号等。目前只是一些初步想法,可行性还有待实际数据验证,方案还在探索阶段。

参考文档:

获取Android设备唯一标识码
Android 手机上获取物理唯一标识码
Getting Unique Device ID of an Android Smartphone 
android 利用反射获取当前系统sdk版本等属性
Android6.0 动态权限申请步骤以及需要注意的一些坑
分享一些Android设备root检测的思路
Android设备硬件层次跟踪

Android设备唯一码的获取相关推荐

  1. android设备唯一码的获取之二

    2019独角兽企业重金招聘Python工程师标准>>> 此篇文章对比android设备唯一码的获取之一看比较好,地址 http://blog.csdn.net/fastthinkin ...

  2. android p随机mac,android设备唯一码的获取,cpu号,mac地址

    抄自http://blog.csdn.net/hpccn/article/details/7872141 开发Android应用中,我们常常需要设备的唯一码来确定客户端. Android 中的几中方法 ...

  3. android动态mac地址,android 设备唯一码的获取,Cpu号,Mac地址

    赞0 开发Android应用中,我们常常需要设备的唯一码来确定客户端. Android 中的几中方法,使用中常常不可靠 1. DEVICE_ID 假设我们确实需要用到真实设备的标识,可能就需要用到DE ...

  4. android设备唯一码的获取之一手机串号IMEI和网络识别号imsi等

    1.IMEI IMEI(International Mobile Equipment Identity)是 国际移动设备身份码 的缩写,国际移动装备辨识码,是由 15位数字组成的"电子串号& ...

  5. 稳定获取Android设备唯一码(UUID)的解决方案

    最近做的一个项目中需要用到Android设备唯一码(UUID)来标识一台设备, Android中设备唯一码有很多,如:MAC地址.IMEI号(DeviceId).IMSI号.ANDROID_ID.序列 ...

  6. 获取android设备唯一编号_android获取设备唯一标识完美解决方案的思考以及实现方式...

    关于Android设备唯一标识符号 前言 由于在开发中需要开发游客模式,在用户没有登录的情况下必须确保设备的唯一性,于是惯性思维想到的肯定是使用DevicesId 来作为设备的唯一标识,用以代替用户登 ...

  7. 获取android设备唯一编号_android 获取手机设备的唯一码(IMIE )

    获取手机设备的唯一码(IMIE ) public void fetch_status(){ TelephonyManager tm = (TelephonyManager) this .getSyst ...

  8. Android设备唯一识别码的获取

    应用设备唯一识别码的解决方案 唯一标识必须满足两个特性才能完美解决定位唯一设备的问题,但这个问题的解决却注定只能极限接近完美 唯一性:标识必须在所有使用该应用的设备上保持唯一性 不变性:标识必须在同一 ...

  9. 获取android设备唯一编号_获取android设备的唯一ID

    在Android开发者官方blog上已经有一篇文章对此做了总结(参考链接1), 这里结合自已查询的资料再总结一下, 并给出最终符合要求的解决方案. 1. ANDROID_ID, Secure.ANDR ...

最新文章

  1. C++实现九九乘法表
  2. 安全攻防技能——Web安全——SQL注入
  3. DDos***种类,原理,简单预防
  4. javaScript设计模式---(单例模式学习)
  5. 算术编码例题详解_百分数与百分点区别详解
  6. guava之cache
  7. (转)Hibernate框架基础——映射集合属性
  8. 内河港口首次实现区块链无纸化进口放货
  9. 以前的(山寨机)手机是怎么逐渐消失的?
  10. STM32硬件编程_学习思路
  11. ZXing源码解析四:如何识别图片中的二维码
  12. 个税计算、税基的处理
  13. Android ViewPager放入多个XML如何监听其的控件
  14. iOS 获取设备类型和系统信息
  15. 给一些想要学习Java同学的一些建议
  16. Python中文转数字
  17. 使用MATLAB提取图片中特定颜色
  18. 淘宝天猫婴儿产品销量数据分析报告
  19. 安卓角色扮演游戏源码_角色扮演类安卓手游排行榜 热门手游推荐
  20. 使用vuex保存singer每个歌星的基本信息

热门文章

  1. 教师是唯一无法被人工智能取代的职业
  2. 新必应(new bing)申请
  3. TokenInsight 与 BitUniverse 达成战略合作,用数据提高收益降低风险
  4. proguard学习
  5. 安利7款高质量的Mac软件,每一款都让人相见恨晚!
  6. python之getattr()函数和importlib库的使用
  7. 有宠商城Android端
  8. SVG排版点击可以输入内容
  9. C# Obsolete、Contional等等一些特性的介绍
  10. 阿里企业云邮箱 报错 526 Authentication failure[0]