安卓设备是否有唯一的设备ID?

3079

Android设备是否有唯一的ID,如果有的话,使用Java访问它的简单方法是什么?


48
如果你正在使用 ANDROID_ID,请务必阅读这个答案这个漏洞 - Dheeraj Vepakomma
54个回答

12

使用TelephonyManagerANDROID_ID,将Android操作系统设备的唯一设备ID作为字符串获取的方法是:

String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
    deviceId = mTelephony.getDeviceId();
}
else {
    deviceId = Secure.getString(
                   getApplicationContext().getContentResolver(),
                   Secure.ANDROID_ID);
}

但我强烈建议使用Google提出的一种方法,参见《识别应用安装》


12

11
我的平板电脑不需要 IMEI 号码,因为它们不连接到我的移动运营商。 - Brill Pappin
3
更不用说CDMA设备使用ESN而非IMEI了。 - David Given
2
它只会这样做,因为它是一部电话 :) 平板电脑可能不行。 - Brill Pappin
4
@ElzoValugi,现在已经是"这些日子"了,但并不是所有的平板电脑都有SIM卡。 - Matthew Quiros

11

以下是我生成唯一标识的方法:

public static String getDeviceId(Context ctx)
{
    TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);

    String tmDevice = tm.getDeviceId();
    String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
    String serial = null;
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;

    if(tmDevice != null) return "01" + tmDevice;
    if(androidId != null) return "02" + androidId;
    if(serial != null) return "03" + serial;
    // other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)

    return null;
}

如果我们在6.0版本中使用ReadPhoneState功能,需要请求运行时权限。 - Harsha

10
我使用以下代码来获取IMEI或者在设备没有电话功能时使用Secure.ANDROID_ID作为替代方案:

我使用以下代码来获取IMEI或者在设备没有电话功能时使用Secure.ANDROID_ID作为替代方案:

String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
      identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
      identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);

10

另一种方法是在没有任何权限的应用程序中使用/sys/class/android_usb/android0/iSerial

user@creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
user@creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5

在Java中,您只需要使用FileInputStream来打开iSerial文件并读取其中的字符。一定要把它包装在异常处理程序中,因为并非所有设备都有这个文件。

以下至少是已知具有此文件且可以被任何应用程序读取的设备:

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3G
  • Toshiba AT300
  • HTC One V
  • Mini MK802
  • Samsung Galaxy S II

您还可以查看我的博客文章“将Android硬件序列号泄露给非特权应用程序”,其中我讨论了其他可用于获取信息的文件。


我刚刚读了你的博客文章。我认为这并不独特:Build.SERIAL也可以在没有任何权限的情况下使用,并且(理论上)是一种独特的硬件序列号。 - Tom
2
你说得对。这只是你的设备可以被追踪的另一种方式,而且正如你所说,这两种方式都不需要应用程序权限。 - insitusec

10

检测特定 Android 设备的硬件识别,您可以检查 MAC 地址。

您可以按照以下方式进行操作:

在 AndroidManifest.xml 文件中添加以下代码:

<uses-permission android:name="android.permission.INTERNET" />

现在,在您的代码中操作:

List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());

for (NetworkInterface interface : interfacesList) {
   // This will give you the interface MAC ADDRESS
   interface.getHardwareAddress();
}

在每个Android设备中,至少有一个“wlan0”接口,它是WI-FI芯片。 即使WI-FI未打开,此代码也可以正常工作。

P.S. 列表中还包含其他接口,其中包含MAC地址,但这可能因手机而异。


9

Google Instance ID(GCM实例ID)

2015年的I/O大会上发布;在Android系统中需要Play服务7.5及以上版本。

https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation

InstanceID iid = InstanceID.getInstance( context );   // Google docs are wrong - this requires context
String id = iid.getId();  // blocking call

看起来 Google 打算将此 ID 用于跨 Android、Chrome 和 iOS 进行安装的标识。它标识的是安装而不是设备,但是 ANDROID_ID(被接受的答案)也不再标识设备。在 ARC 运行时,为每个安装生成一个新的 ANDROID_ID(详情请参见此处),就像这个新的实例 ID 一样。此外,我认为大多数人实际上都是在寻找标识安装(而不是设备)的功能。 实例 ID 的优点 我觉得 Google 打算用它来标识你的安装,它是跨平台的,并且可以用于许多其他目的(请参见上面的链接)。
如果您使用 GCM,则最终需要使用此实例 ID,因为您需要它才能获得 GCM 令牌(替换旧的 GCM 注册 ID)。 缺点/问题 在当前的实现中(GPS7.5),当您的应用程序请求时,实例ID是从服务器检索的。这意味着上面的调用是一个阻塞调用-在我的非科学测试中,如果设备在线,需要1-3秒钟,如果离线,则需要0.5-1.0秒钟(大概是等待多久后放弃并生成随机ID)。这在北美使用具有Android 5.1.1和GPS 7.5的Nexus 5上进行了测试。
如果您将ID用于他们打算的目的-例如应用程序身份验证、应用程序识别、GCM-我认为这1-3秒钟可能会成为麻烦(当然这取决于您的应用程序)。

3
instanceID 的另一个重要缺点是,如果用户清除应用程序的数据,将为您生成一个新的 instanceID。 - idanakav
有趣,但我认为它并没有真正改变潜在的用例:实例ID(例如android_id)不适合用于识别设备。因此,您的服务器将视用户清除数据为用户卸载和重新安装应用程序,这是可以理解的。 - Tom

9

TelephonyManager.getDeviceId() 返回唯一的设备ID,例如GSM手机的IMEI和CDMA手机的MEID或ESN。

final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);            
String myAndroidDeviceId = mTelephony.getDeviceId(); 

但我建议使用:

Settings.Secure.ANDROID_ID,它返回Android ID作为唯一的64位十六进制字符串。

    String   myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

有时候 TelephonyManger.getDeviceId() 方法会返回 null,因此为了获得唯一的 id,您可以使用以下方法:
public String getUniqueID(){    
    String myAndroidDeviceId = "";
    TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (mTelephony.getDeviceId() != null){
        myAndroidDeviceId = mTelephony.getDeviceId(); 
    }else{
         myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
    }
    return myAndroidDeviceId;
}

我最近发现一个客户的设备类型为SM-G928F / Galaxy S6 edge+的Android ID仅提供了15个十六进制数字,而不是16个。 - Holger Jakobs

8

1. 使用电话管理器来获取唯一的设备ID(例如IMEI)。请参考以下示例:

import android.telephony.TelephonyManager;
import android.content.Context;
// ...
TelephonyManager telephonyManager;
telephonyManager = (TelephonyManager) getSystemService(Context.
                TELEPHONY_SERVICE);
/*
* getDeviceId() returns the unique device ID.
* For example,the IMEI for GSM and the MEID or ESN for CDMA phones.
*/
String deviceId = telephonyManager.getDeviceId();
/*
* getSubscriberId() returns the unique subscriber ID,
*/
String subscriberId = telephonyManager.getSubscriberId();

这需要向用户申请 android.permission.READ_PHONE_STATE 权限,这很难在你开发的应用程序中进行合理的解释。
对于没有电话服务的设备(如平板电脑),自Android 2.3 Gingerbread起,必须报告可通过 android.os.Build.SERIAL 获得的唯一设备ID。有些具有电话服务的手机也可以定义序列号。由于不是所有Android设备都有序列号,因此该解决方案并不可靠。
在设备首次启动时,将生成并存储一个随机值。该值可通过 Settings.Secure.ANDROID_ID 获得。它是一个64位数字,应在设备的使用寿命内保持不变。 ANDROID_ID 对于智能手机和平板电脑而言是一个好选择作为唯一设备标识符。要检索该值,您可以使用以下代码,

String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

但是,如果在设备上执行工厂重置,则该值可能会更改。对于来自制造商的流行手持设备存在已知的错误,其中每个实例都具有相同的 ANDROID_ID。显然,该解决方案并不是100%可靠。
使用UUID。由于大多数应用程序的要求是识别特定的安装而不是物理设备,因此用UUID类获取用户的唯一ID是一个很好的解决方案。以下解决方案由谷歌的Reto Meier在Google I/O演示中提出:
SharedPreferences sharedPrefs = context.getSharedPreferences(
         PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
更新:由于Google推出的隐私更新,安卓10版本后选项#1#2不再可用,因为选项2和3需要关键权限。

2
哪个手机是每个实例都具有相同ANDROID_ID的手机? - viper
请参考官方文档 https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID - Kiran Maniya
2
DeviceInfoProvider 不是 Android SDK 的一部分。 - user924
谢谢@user924指出这一点。如果您有更多细节,可以编辑答案以改进它。 - Kiran Maniya
@KiranManiya 编辑您杜撰的答案。人们应该怎么知道如何编辑它?应该是您自己编辑它。请不要用幻想来回答问题。 - stackunderflow
@jerinho 非常感谢。我想你已经看过我的两个答案了。 - Kiran Maniya

8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接