前言

因为项目中经常会遇到要上传一系列设备信息的功能,为了方便使用,所以就拆分成以下系列文章来单独介绍如何获取各类设备信息

  • 手机运营商获取

  • AndroidID、IMEI、OAID获取

  • 地理位置信息经纬度获取

  • 公网IP地址获取:移动网络IP、Wifi IP

  • Build类获取相关设备信息

  • 屏幕相关信息:密度、物理尺寸获取

  • BuildConfig获取的一系列基础信息

  • UA、网络状态…等持续更新

1. AndroidID获取

1.1 所需权限

不需要任何权限

1.2 获取方法

private fun getAndroidID() {val androidID = Settings.System.getString(contentResolver, Settings.Secure.ANDROID_ID)Log.i(TAG, "AndroidID为:$androidID")
}

注意:手机在恢复出厂设置后,这个AndroidID会发生改变,所以用它作为设备的唯一标识不太保险

2. IMEI、MEID获取

2.1 概念了解

讲具体的获取方法前,先大概了解下这是什么东西。

IMEI和MEID其实都是用来标识设备的识别码,不同的是IMEI标识的是支持GSM网络制式的设备MEID标识的是支持CDMA网络制式的设备

什么是GSM和CDMA呢? CDMA和GSM简单点说其实就是使用了不同的通信技术,以下表格显示了各大运营商所使用的通信技术

2G 3G 4G
中国移动 GSM TD-SCDMA TD-LTE
中国联通 GSM WCDMA TD-LTE/FDD-LTE
中国电信 CDMA1X 有时直接写CDMA CDMA2000 EVDO是中国电信的CDMA2000的3G网络的无线上网模式 TD-LTE/FDD-LTE

而现在我们的手机大都是双卡双待,所以这些手机IMEI和MEID号码都有。

大白话讲其实IMEI、MEID就相当于我们手机的身份证号码,唯一标识它,不同的是,有的手机双卡双待,可以同时插入两张支持GSM网络的卡(移动联通、移动移动、联通联通),或一张支持GSM网络的卡一张支持CDMA网络的卡,所以就出现一部手机有两个IMEI号,一个MEID号码

注意:IMEI和MEID是标识手机设备,跟我们手机插什么卡没有关系。

2.2 通过getDeviceId()获取

2.2.1 所需权限

需要动态申请READ_PHONE_STATE权限

注意:Android10以上禁止获取IMEI,因为需要READ_PRIVILEGED_PHONE_STATE权限,而该权限只能是系统应用才可以获取到。

2.2.2 获取方法

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerval IMEI = telManager.deviceIdLog.i(TAG, "IMEI为:${IMEI}")
} else {Log.i(TAG, "Android10及以上版本禁止获取IMEI")
}

2.2.3 方法解释

从2.2.2可以看到,我们主要是通过TelephonyManager的getDeviceId()方法来获取的

我们来看看该方法的注解:

/*** 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.
** <p>Starting with API level 29, persistent device identifiers are guarded behind additional* restrictions, and apps are recommended to use resettable identifiers (see <a* href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of* the following requirements is met:* <ul>*     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this*     is a privileged permission that can only be granted to apps preloaded on the device.*     <li>If the calling app is the device or profile owner and has been granted the*     {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that*     owns a managed profile on the device; for more details see <a*     href="https://developer.android.com/work/managed-profiles">Work profiles</a>.*     Profile owner access is deprecated and will be removed in a future release.*     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any*     active subscription.*     <li>If the calling app is the default SMS role holder (see {@link*     RoleManager#isRoleHeld(String)}).* </ul>
** <p>If the calling app does not meet one of these requirements then this method will behave* as follows:
** <ul>*     <li>If the calling app's target SDK is API level 28 or lower and the app has the*     READ_PHONE_STATE permission then null is returned.</li>*     <li>If the calling app's target SDK is API level 28 or lower and the app does not have*     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or*     higher, then a SecurityException is thrown.</li>* </ul>
** @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns* MEID for CDMA.
*/
翻译:
返回唯一的设备 ID,例如,GSM 手机的 IMEI 和 CDMA 手机的 MEID 或 ESN。 如果设备ID不可用,则返回 null。 从Android10 API29开始,就不允许调用该方法,建议使用可重置的标志符,如果满足以下条件,可调用此方法
1、授予了READ_PRIVILEGED_PHONE_STATE该权限,但是该权限仅系统应用才可获得
2、如果应用的API为28或者更低,且具备READ_PHONE_STATE权限,则返回null
3、如果应用API为29或者更高,则会抛出SecurityException异常建议我们通过getImei方法获取GSM手机的IMEI或者getMeid()方法获取CDMA手机的MEID

总结:

  • 可以看出该方法得到的并不一定是IMEI。

  • 对于只有GSM制式的手机是得到的是IMEI,对于只有CDMA制式的手机,返回的是ESN或MEID。

  • 所以对于我们的双卡双待手机,有可能返回IMEI,也有可能返回MEID。这里推荐我们使用getImei()来获取GSM网络制式的IMEI或者getMeid()方法来获取CDMA网络制式的MEID。

2.3 通过getImei()和getMeid()获取

2.3.1 所需权限

需要动态申请READ_PHONE_STATE权限

注意:Android10以上禁止获取IMEI,因为需要READ_PRIVILEGED_PHONE_STATE权限,而该权限只能是系统应用才可以获取到。

2.3.2 获取方法

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&Build.VERSION.SDK_INT>Build.VERSION_CODES.O){val telManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManagerval imei1 = telManager.getImei(0)val imei2 = telManager.getImei(1)Log.i(TAG, "IMEI卡槽1:$imei1")Log.i(TAG, "IMEI卡槽2:$imei2")val meid1 = telManager.getMeid(0)val meid2 = telManager.getMeid(1)Log.i(TAG, "MEID卡槽1为: $meid1")Log.i(TAG, "MEID卡槽2为:$meid2 ")
}

2.3.3 特殊说明

在写文章之前,看到网上关于获取IMEI的文章都是见getDeviceId()获取不到就一股脑的反射调用getImei(),我当时还在想人家这方法本身就能直接调用,你还反射个锤子。

反射是调用@hide标识的class或者是一些方法没有编到SDK里的,也就是我们的隐藏接口,这些才用到反射。后来通过查看5.1、7.1、8、9的源码发现,到7.1,该方法还是隐藏的,从8.0开始,getImei()方法就可以直接调用了,所以傻孩子别再不看版本就一股脑反射调用了。

5.1

10.0

2.4 测试结果-总结

通过云端的测试机测试,小米9SE和OPPOR15的数据如下:

小米9SE
IMEI手机卡1:862427041314834
IMEI手机卡2:862427041314842
MEID1为: 99001279065741
MEID2为: 99001279065741
deviceId为:862427041314834
OPPOR15
IMEI手机卡1:865741048560315
IMEI手机卡2:865741048560307
MEID1为: A0000088576EC0
MEID2为: A0000088576EC0
deviceId为:A0000088576EC0

⭐从数据再次验证了2.2和2.3中的说法

  • getDeviceId()返回的不一定是IMEI,也有可能是MEID

  • 一个双卡双待的手机一般IMEI号码有两个,MEID号码有一个

  • 如果想准确的获取手机的IMEI,就使用getImei()方法

3. OAID获取

在2中说道,在Android10以上,安卓是禁止我们获取IMEI的,上面的方法都无法获取到,那如果想要唯一标识一部手机,那我们可以使用OAID

因传统的移动终端设备标识如国际移动设备识别码(IMEI)等已被部分国家认定为用户隐私的一部分,并存在被篡改和冒用的风险,所以在Android 10及后续版本中非厂商系统应用将无法获取IMEI、MAC等设备信息。无法获取IMEI会在用户行为统计过程中对设备识别产生一定影响。

近日移动安全联盟针对该问题联合国内手机厂商推出补充设备标准体系方案,选择OAID字段作为IMEI等的替代字段。OAID字段是由中国信通院联合华为、小米、OPPO、VIVO等厂商共同推出的设备识别字段,具有一定的权威性,可满足用户行为统计的使用场景。

3.1 所需权限

不需要任何权限

3.2 获取方法

3.2.1 下载aar文件

移动安全联盟MSA (msa-alliance.cn) 官网下载如下文件,但是需要登录,注册的账号还需要公司信息等…晕死,嫌麻烦的在该项目Github上自取

该lib仅支持以下设备获取OAID

3.2.2 添加依赖

将aar文件添加到lib中,并添加依赖

implementation files('libs/oaid_sdk_1.0.25.aar')

3.2.3 设置gradle编译选项

defaultConfig{ndk {//设置支持的SO库架构abiFilters  'armeabi-v7a', 'arm64-v8a','x86','x86_64','armeabi'}
}

3.2.4 assets目录添加supplierconfig.json文件

{"supplier":{"vivo":{},"xiaomi": {},"huawei":{},"oppo":{}}
}

注意:一定要添加该文件,否则会报加载配置文件出错

3.2.5 代码实现

class OAIDActivity : AppCompatActivity(), IIdentifierListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_o_a_i_d)getOAID()}private fun getOAID() {//初始化var error = MdidSdkHelper.InitSdk(this, true, this)when (error) {ErrorCode.INIT_ERROR_DEVICE_NOSUPPORT -> {//不支持的设备Log.i(TAG, "getOAID: 不支持的设备")}ErrorCode.INIT_ERROR_LOAD_CONFIGFILE -> {//加载配置文件出错Log.i(TAG, "getOAID: 加载配置文件出错")}ErrorCode.INIT_ERROR_MANUFACTURER_NOSUPPORT -> {//不支持的设备厂商Log.i(TAG, "getOAID: 不支持的设备厂商")}ErrorCode.INIT_ERROR_RESULT_DELAY -> {//获取接口是异步的,结果会在回调中返回,回调执行的回调可能在工作线程Log.i(TAG, "getOAID: 获取接口是异步的,结果会在回调中返回,回调执行的回调可能在工作线程")}ErrorCode.INIT_HELPER_CALL_ERROR -> {//反射调用出错Log.i(TAG, "getOAID: 反射调用出错")}}}//注意,该回调方法在非主线程中override fun OnSupport(isSupport: Boolean, _supplier: IdSupplier?) {if (_supplier == null) {runOnUiThread {tvInfo.text = "获取到的配置信息为空"}Log.i(TAG, "OnSupport: 获取到的信息为空")return}//关键用这个val oaid: String = _supplier.oaidval vaid: String = _supplier.vaidval aaid: String = _supplier.aaidval builder = StringBuilder()               }
}

注意:OnSupport()回调方法在非主线程中

4. 总结

  • AndroidID能够直接获取,不需要权限

  • IMEI和MEID只与设备有关,getDeviceId()返回的不一定是IMEI,也有可能是MEID

  • 一个双卡双待的手机一般IMEI号码有两个,MEID号码有一个

  • 如果想准确的获取手机的IMEI,就使用getImei()方法

  • Android10以上无法获取IMEI,可通过获取OAID来作为设备唯一码

在第一次查询如何获取IMEI的相关文章时,真是惊呆我了,有的文章竟然在区分插入移动联通卡和插入电信卡获取方法的区别,拜托,IMEI是跟设备相关的,跟你插什么卡有半毛线关系!!!还有的毛线不看,直接照抄前几年的代码,各种反射来获取,拜托,都可以直接获取了好不好,看不惯那些,所以总结了这篇文章。

项目地址

如果本文对你有帮助,请别忘记点赞start,如果有不恰当的地方也请提出来,下篇文章见。

AndroidID、IMEI、OAID获取相关推荐

  1. Android Q(10.0)上IMEI获取不到;Android Q(10.0)上OAID替代IMEI;OAID获取方式

    最近公司研发的 Android Q (android 10)的5G手机,然后就开始适配Android Q:发现无系统权限的应用无法获取到可作为唯一标识的IMEI,MAC等等:由于项目(负一屏/浏览器/ ...

  2. android读取imei原理,IMEI 的获取原理追踪

    imei 是怎么生成及获取的,原理是什么,如果改码的话,怎样才是最简单的最有效的? framework 层的接口获取 首先掉到这里 android.telephony.TelephonyManager ...

  3. Android设备唯一标识(AndroidID,OAID等 )

    一.ID 体系:你只是一串代码 想要了解 OAID,我们首先需要明白 ID 体系:想要追踪一个用户就必须先找到用户,在这个过程中,标识符(ID)就像我们的另一张身份证,它们就代表了数字化之后的你和我. ...

  4. Android 获取手机系统版本号、获取手机型号、获取手机厂商、获取手机IMEI、获取手机CPU_ABI、获取手机唯一识别码

    1.先申请权限,关于如何申请权限请查看RxPermissions的使用(简单实用)_ErwinNakajima的博客-CSDN博客 2.手機唯一識別碼管理類. package com.phone.co ...

  5. Android多个imei如何获取,如何在Android 10中获取IMEI号,这是获取在Android 10及以下Android 10中获取IMEI号的代码...

    如何在android 10中获取imei编号,这是获取在android 10及以下android 10中获取imei编号的代码. if (android.os.Build.VERSION.SDK_IN ...

  6. android获取imei(android获取wifi密码)

    android系统如何获取imei号码,获取手机型号和系统版本号 /*****创建日期2010-4-29下午05:02:47**/packagenet.sunniwell.app;importandr ...

  7. java获取imei_Android10 获取IMEI,获取UUID,唯一ID

    Andorid10无法获取IMEI,读写文件也被限制. 获取设备唯一ID逻辑. 如果Android10以上 -> 在设备的外部目录创建UUID,只要用户没有手动删除该文件UUID一直存在. 如果 ...

  8. android系统如何获取imei号码,获取手机型号和系统版本号

    2.tm.CALL_STATE_RINGING=1 响铃 3.tm.CALL_STATE_OFFHOOK=2 摘机 */ tm.getCallState();//int /* 电话方位: */ tm. ...

  9. android_ 模拟器检测 手机IMEI号 获取

    1 模拟器检测 private boolean isEmulator() {return (Build.MODEL.equals("sdk")) || (Build.MODEL.e ...

最新文章

  1. 2022-03-18 今日工作
  2. java中字符串分割器_java简易文本分割器实现代码
  3. MATLAB批量实现dicom转换为bmp格式
  4. 程序员该面向运维,还是面向开发?
  5. mysql explain ref const_MySQL EXPLAIN 详解
  6. 船舶网络搭建项目案例
  7. 你必须知道的.NET
  8. 【AR优秀开源项目】ARCore项目工程汇总
  9. 使用 JDBC 连接不同版本 DB2 数据库的兼容性问题
  10. 操作系统与裸机的区别
  11. 小武与FasterRCNN
  12. 计算机基础知识教程excel试题,计算机基础知识试题
  13. oracle误删除数据,恢复删除的数据,删除数据后提交恢复
  14. mysql 校对规则作用_讲讲Mysql中的校对规则究竟是怎么一回事
  15. Mocha Pro 平面跟踪插件
  16. 传统网络变压器和电容试片式性能区别
  17. Linux下C语言串口应用编程
  18. 英语自我介绍——应聘初中英语老师
  19. 生物学-脑:脑(动物中枢神经系统主要部分)
  20. inspinia admin 最新版 inspinia 2.7.1 一套非常优秀的bootstrap后台管理模板

热门文章

  1. .md文件转.pdf文件
  2. vscode远程连接虚拟机/云服务器
  3. 语雀三个月会员,兑换码领取
  4. 去掉搜狗拼音烦人的x+;进入搜狗搜索
  5. 主流的企业级报表工具,国内报表工具排名前列
  6. 综合布线系统带宽与计算机网络带宽计算题,计算机网络思考与练习题.doc
  7. 微信公众号(一) --- 开启微信公众号服务
  8. 【概率论与数理统计】p1-4 前言、随机试验、样本空间、事件间的关系、事件的运算及运算法则
  9. 中邮网院/邮e联下载
  10. 2022款联想小新Pro16,联想小新air15,thinkbook16+选哪个好