第一节 MAC


  MAC 地址也叫物理地址、硬件地址,由网络设备制造商生产时烧录在网卡中。
  MAC 地址则是 48 位的(6字节),通常表示为 12 个 16 进制数,每 2 个 16 进制数之间用冒号隔开。如:

08:00:20:0A:8C:6D

  其前 3 字节是 IEEE 的注册管理机构给不同厂家分配的代码,区分不同的厂家,后3字节由厂家自行分配。
  不用担心厂商不够用,每个厂商可以从注册机构那申请多个代码,点击这里查询 。


  范例1:Android 6 以下获取 MAC 地址 。

1
2
3
4
5
6
7
8
9
10
// 必须申请 android.permission.ACCESS_WIFI_STATE 权限

public static String getMac(Context paramContext) {
WifiManager wifiManager = (WifiManager) paramContext.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo != null) {
return wifiInfo.getMacAddress();
}
return null;
}
语句解释:
-  如果不申请权限则会闪退。


  从 Android 6 开始,为给用户提供更严格的数据保护,WifiInfo.getMacAddress()方法和BluetoothAdapter.getAddress()方法现在会返回常量值“ 02:00:00:00:00:00 ”;《官方文档》


  范例2:Android 6 及以上获取 MAC 地址 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 必须申请 android.permission.ACCESS_WIFI_STATE 权限
// 必须申请 android.permission.INTERNET 权限

private static String getMacFromHardware() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase(“wlan0”)) continue;

byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return “”;
}

StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format(“%02X:”, b));
}

if (res1.length() > 0) {
res1.deleteCharAt(res1.length() – 1);
}
return res1.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
return “02:00:00:00:00:00”;
}
语句解释:
-  其中 ACCESS_WIFI_STATE 和 INTERNET 都属于普通权限,在6.0以上不需要动态申请。


  Android 将权限分为三种保护级:普通、签名和危险权限,保护级别影响着是否需要运行时权限请求。

普通权限:
对用户隐私或其他应用的操作带来的风险很小。例如,设置时区的权限就是普通权限。如果应用在清单中声明需要普通权限,系统会在安装时自动向应用授予该权限。系统不会提示用户授予普通权限,用户也无法撤消这些权限。

签名权限:
系统在安装时授予这些应用权限,但仅会在尝试使用某权限的应用签名证书为定义该权限的同一证书时才会授予。注意:有些签名权限不适合第三方应用使用。

危险权限:
应用需要的数据或资源涉及用户隐私信息,或者可能对用户存储的数据或其他应用的操作产生影响。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,必须由用户向应用明确授予该权限。在用户批准该权限之前,应用无法提供依赖于该权限的功能。为了使用危险权限,应用必须在运行时提示用户授予权限。


  更多关于权限的介绍请阅读《官方文档》

  另外,MAC 地址是可以修改的,只要可以修改,就可能存在不唯一的情况。同时网上有人说,在当前没打开WiFi的情况下获取得到的MAC地址值为空,不过这个场景笔者没有重现出来。

  小提示:目前工信部要求App在获取用户明确授权之前,不能私自搜集MAC地址,笔者目前已知友盟、头条相关的SDK在初始化时会搜集,所以推荐将它们的初始化后移。


本节参考阅读:

第二节 UUID


  UUID 是通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准。目前最广泛应用的 UUID ,是微软公司的全局唯一标识符(GUID)。

  UUID 是一个 128 比特( 32 字节)的数值,它是基于当前时间、芯片ID码和硬件标识(通常是MAC地址)、随机数等数据计算生成的。一般来说,可以保证这个值是真正唯一的。

举例来说,每秒产生10亿笔UUID,100年后只产生一次重复的机率是50%。如果地球上每个人都各有6亿笔GUID,发生一次重复的机率是50%。


  范例1:生成 UUID 。

1
2
3
// UUID 大致长这个样子:a79f85e5-7048-4f06-8466-cc6185e70508

UUID.randomUUID().toString()
语句解释:
-  由于UUID会参考当前时间,所以每次调用都会产生不同UUID,因而我们在获取到UUID后应该将它存储起来。

  UUID 的缺点也很明显:它可能随着你的 App 被卸载而消失,也可能被用户手动删除。


本节参考阅读:

第三节 IMEI 、 MEID 和 IMSI

  IMEI 码(国际移动设备识别码),俗称“手机串号”、“手机序列号”,用于在GSM移动网络中识别每一部独立的手机,相当于手机的身份证号码。
  额外的知识点:

1、IMEI码由GSMA协会统一规划,并授权各地区组织进行分配,在中国由工业和信息化部电信终端测试技术协会(TAF)负责国内手机的入网认证。
2、IMEI 码适用于 GSM 、 WCDMA 、 LTE 制式的移动电话和卫星电话。
3、IMEI 码是一个 15 位或 17 位的数字,数字中间可能有/、-、.或空格隔开。
4、双卡双待手机手机有两个IMEI码。
5、在大部分终端设备中都可以通过拨号输入*#06#来查询。

  MEID码 (移动设备识别码),是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。


  范例1:获取设备ID 。

1
2
3
4
5
6
7
8
9
10
11
12
// 注意代码使用需要在 Manifest 配置如下权限(6.0以后需要动态申请)
// <uses-permission android:name=”android.permission.READ_PHONE_STATE” />
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);

/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
* 翻译过来就是:这个方法会返回唯一的设备id,
* 比如在GSM的手机上返回的是IMEI,而在CDMA 手机上返回的是MEID或者ESN。
* 如果设备id不可读取,那么返回null。
*/
String deviceid = tm.getDeviceId();
语句解释:
-  需要注意的是 getDeviceId 有一个重载方法,用来明确指定 SIM 卡槽对应的 DeviceId ,取值从 0 开始。


  上面的方法在 Android 10 中发生了一些变化,点击查看 官方文档 ,原文截图:


  IMSI(国际移动用户识别码),是用于区分蜂窝网络中不同用户的、在所有蜂窝网络中不重复的识别码。在 GSM 、 UMTS 和 LTE 网络中, IMSI 码存储在 SIM 卡,在 CDMA2000 网络中则是直接来自手机,或者 RUIM 。

  IMSI 由一串十进制数字组成,最大长度为 15 位。


  范例2:获取IMSI 。

1
2
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
String imsi =tm.getSubscriberId();


本节参考阅读:

第四节 Android ID

  在设备首次启动时,系统会随机一个16位的字符串,它就是 ANDROID ID ,读取它不需要任何权限。

  范例1:获取ANDROID_ID 。

1
2
// 打印结果:379611317e827a0f
Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID)


  它的不足之处:

1、刷机、root、恢复出厂设置等会使得 Android ID 改变;
2、Android 8.0之后,Android ID 的规则发生了变化,ANDROID ID 的唯一决定于应用签名、用户和设备三者的组合。

  其中第二点意味着不同App获取到的值是不一样的,这可能对于广告联盟之类的有所影响(如果彼此是用Android ID对比数据的话)。


本节参考阅读:

第五节 设备序列号

1Build.getSerial()

  如果厂商比较规范的话,设备序列号 + Build.MANUFACTURER 应该能唯一标识设备,但现实是并非所有厂商都按规范来,不少设备会得到”unknown“。
  所以设备序列号非常不可靠。

第六节 Google ADID

  随着大数据和人工智能时代的到来,数据的价值也逐渐增加,移动终端设备标识码(如IMEI、 MAC、IMSI)等终端设备标识信息的收集和使用成为普遍现象。同时各国对用户隐私保护的要求越来越高,其中 IMEI 等已被部分国家认定为用户隐私的一部分。

  没有了 IMEI 码,上面的那些唯一标识各有各的缺点,比如像 ANDROID_ID 各个应用获取到的是不同值,但总有一些场景需要唯一标识一台设备,尤其是广告,所以 Google 搞了这个 ADID ,其本质是一个设备唯一标识。

  工作原理很简单:你在 A 应用里点了 Google 的广告,那么 Google 就会通过你设备上的 ADID 来记录你的喜好,当你打开 B 应用时,Google 就会按照你的兴趣推送广告,如此反复,这样在提高广告的转化率的同时也不让你那么讨厌广告。下面是它的架构图:

  当然 Google 也提供了“重置ADID”和“停用广告个性化”的功能来实现隐私保护:

前者是重新生成新的ADID,这样一来就需要重新训练推送的广告。
后者是不允许应用向您展示个性化广告。

  如果您不是国内的手机,比如三星,那么在系统设置里依次点击”Google设置 -> 广告“,来进入设置界面。

  点击查看 官方文档 

第七节 OAID

  事实上 OAID 与 GAID 是一个初衷,只不过前者是在国内使用,后者是在国外使用。

  想要实现设备唯一,必须是由厂商支持。根据“移动智能终端补充设备标识体系”技术要求,华为、小米、OPPO、vivo、中兴、努比亚、魅族、联想、三星等设备厂商均将逐步实现本标识体系。

  更多关于 OAID 和 SDK 接入的流程,请点击查看 移动安全联盟 

0
分类: 未分类

bayshier

愿世间每个美好的灵魂都能被温柔以待

0 条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注