如何找到Android设备的序列号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2322234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:25:21  来源:igfitidea点击:

How to find serial number of Android device?

androidserial-number

提问by Eno

I need to use a unique ID for an Android app and I thought the serial number for the device would be a good candidate. How do I retrieve the serial number of an Android device in my app ?

我需要为 Android 应用程序使用唯一 ID,我认为设备的序列号将是一个不错的选择。如何在我的应用中检索 Android 设备的序列号?

采纳答案by haseman

TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();

getSystemService is a method from the Activity class. getDeviceID() will return the MDN or MEID of the device depending on which radio the phone uses (GSM or CDMA).

getSystemService 是 Activity 类中的一个方法。getDeviceID() 将根据手机使用的无线电(GSM 或 CDMA)返回设备的 MDN 或 MEID。

Each device MUST return a unique value here (assuming it's a phone). This should work for any Android device with a sim slot or CDMA radio. You're on your own with that Android powered microwave ;-)

每个设备必须在这里返回一个唯一的值(假设它是一部电话)。这应该适用于任何带有 sim 插槽或 CDMA 无线电的 Android 设备。您独自使用 Android 供电的微波炉 ;-)

回答by emmby

As Dave Webb mentions, the Android Developer Blog has an articlethat covers this.

正如 Dave Webb 所提到的,Android Developer Blog 有一篇文章介绍了这一点。

I spoke with someone at Google to get some additional clarification on a few items. Here's what I discovered that's NOT mentioned in the aforementioned blog post:

我与 Google 的某个人进行了交谈,以对一些项目进行进一步说明。这是我发现在上述博客文章中未提及的内容:

  • ANDROID_ID is the preferred solution. ANDROID_ID is perfectly reliable on versions of Android <=2.1 or >=2.3. Only 2.2 has the problems mentioned in the post.
  • Several devices by several manufacturers are affected by the ANDROID_ID bug in 2.2.
  • As far as I've been able to determine, all affected devices have the same ANDROID_ID, which is 9774d56d682e549c. Which is also the same device id reported by the emulator, btw.
  • Google believes that OEMs have patched the issue for many or most of their devices, but I was able to verify that as of the beginning of April 2011, at least, it's still quite easy to find devices that have the broken ANDROID_ID.
  • ANDROID_ID 是首选解决方案。ANDROID_ID 在 Android <=2.1 或 >=2.3 版本上完全可靠。只有2.2有帖子中提到的问题。
  • 多个制造商的多个设备受到 2.2 中 ANDROID_ID 错误的影响。
  • 据我所知,所有受影响的设备都具有相同的 ANDROID_ID,即9774d56d682e549c。这也是模拟器报告的相同设备 ID,顺便说一句。
  • Google 认为 OEM 已经为他们的许多或大部分设备修复了该问题,但我能够验证,至少在 2011 年 4 月初,找到具有损坏 ANDROID_ID 的设备仍然很容易。

Based on Google's recommendations, I implemented a class that will generate a unique UUID for each device, using ANDROID_ID as the seed where appropriate, falling back on TelephonyManager.getDeviceId() as necessary, and if that fails, resorting to a randomly generated unique UUID that is persisted across app restarts (but not app re-installations).

根据谷歌的建议,我实现了一个将为每个设备生成唯一 UUID 的类,在适当的情况下使用 ANDROID_ID 作为种子,必要时回退到 TelephonyManager.getDeviceId(),如果失败,则求助于随机生成的唯一 UUID这在应用程序重新启动(但不是应用程序重新安装)中持续存在。

import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;

import java.io.UnsupportedEncodingException;
import java.util.UUID;

public class DeviceUuidFactory {

    protected static final String PREFS_FILE = "device_id.xml";
    protected static final String PREFS_DEVICE_ID = "device_id";
    protected static volatile UUID uuid;

    public DeviceUuidFactory(Context context) {
        if (uuid == null) {
            synchronized (DeviceUuidFactory.class) {
                if (uuid == null) {
                    final SharedPreferences prefs = context
                            .getSharedPreferences(PREFS_FILE, 0);
                    final String id = prefs.getString(PREFS_DEVICE_ID, null);
                    if (id != null) {
                        // Use the ids previously computed and stored in the
                        // prefs file
                        uuid = UUID.fromString(id);
                    } else {
                        final String androidId = Secure.getString(
                            context.getContentResolver(), Secure.ANDROID_ID);
                        // Use the Android ID unless it's broken, in which case
                        // fallback on deviceId,
                        // unless it's not available, then fallback on a random
                        // number which we store to a prefs file
                        try {
                            if (!"9774d56d682e549c".equals(androidId)) {
                                uuid = UUID.nameUUIDFromBytes(androidId
                                        .getBytes("utf8"));
                            } else {
                                final String deviceId = ((TelephonyManager) 
                                        context.getSystemService(
                                            Context.TELEPHONY_SERVICE))
                                            .getDeviceId();
                                uuid = deviceId != null ? UUID
                                        .nameUUIDFromBytes(deviceId
                                                .getBytes("utf8")) : UUID
                                        .randomUUID();
                            }
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                        // Write the value out to the prefs file
                        prefs.edit()
                                .putString(PREFS_DEVICE_ID, uuid.toString())
                                .commit();
                    }
                }
            }
        }
    }

    /**
     * Returns a unique UUID for the current android device. As with all UUIDs,
     * this unique ID is "very highly likely" to be unique across all Android
     * devices. Much more so than ANDROID_ID is.
     * 
     * The UUID is generated by using ANDROID_ID as the base key if appropriate,
     * falling back on TelephonyManager.getDeviceID() if ANDROID_ID is known to
     * be incorrect, and finally falling back on a random UUID that's persisted
     * to SharedPreferences if getDeviceID() does not return a usable value.
     * 
     * In some rare circumstances, this ID may change. In particular, if the
     * device is factory reset a new device ID may be generated. In addition, if
     * a user upgrades their phone from certain buggy implementations of Android
     * 2.2 to a newer, non-buggy version of Android, the device ID may change.
     * Or, if a user uninstalls your app on a device that has neither a proper
     * Android ID nor a Device ID, this ID may change on reinstallation.
     * 
     * Note that if the code falls back on using TelephonyManager.getDeviceId(),
     * the resulting ID will NOT change after a factory reset. Something to be
     * aware of.
     * 
     * Works around a bug in Android 2.2 for many devices when using ANDROID_ID
     * directly.
     * 
     * @see http://code.google.com/p/android/issues/detail?id=10603
     * 
     * @return a UUID that may be used to uniquely identify your device for most
     *         purposes.
     */
    public UUID getDeviceUuid() {
        return uuid;
    }
}

回答by Roman SL

String serial = null; 

try {
    Class<?> c = Class.forName("android.os.SystemProperties");
    Method get = c.getMethod("get", String.class);
    serial = (String) get.invoke(c, "ro.serialno");
} catch (Exception ignored) {
}

This code returns device serial number using a hidden Android API.

此代码使用隐藏的 Android API 返回设备序列号。

回答by Anthony Forloney

String deviceId = Settings.System.getString(getContentResolver(),
                                Settings.System.ANDROID_ID);

Although, it is not guaranteed that the Android ID will be an unique identifier.

但是,不能保证 Android ID 将是唯一标识符。

回答by Dave Webb

There is an excellent post on the Android Developer's Blog discussing this.

Android Developer's Blog 上一篇很好的文章讨论了这一点

It recommends against using TelephonyManager.getDeviceId()as it doesn't work on Android devices which aren't phones such as tablets, it requires the READ_PHONE_STATEpermission and it doesn't work reliably on all phones.

它建议不要使用,TelephonyManager.getDeviceId()因为它不适用于平板电脑等非手机的 Android 设备,它需要READ_PHONE_STATE许可,并且不能在所有手机上可靠地运行。

Instead you could use one of the following:

相反,您可以使用以下方法之一:

  • Mac Address
  • Serial Number
  • ANDROID_ID
  • MAC地址
  • 序列号
  • ANDROID_ID

The post discusses the pros and cons of each and it's worth reading so you can work out which would be the best for your use.

这篇文章讨论了每种方法的优缺点,值得一读,这样您就可以找出最适合您使用的方法。

回答by Edward Brey

For a simple number that is unique to the device and constant for its lifetime (barring a factory reset or hacking), use Settings.Secure.ANDROID_ID.

对于设备唯一且在其生命周期内保持不变的简单数字(除非恢复出厂设置或黑客攻击),请使用Settings.Secure.ANDROID_ID

String id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

To use the device serial number (the one shown in "System Settings / About / Status") if available and fall back to Android ID:

要使用设备序列号(“系统设置/关于/状态”中显示的序列号)(如果可用)并回退到 Android ID:

String serialNumber = Build.SERIAL != Build.UNKNOWN ? Build.SERIAL : Secure.getString(getContentResolver(), Secure.ANDROID_ID);

回答by radhoo

The IMEI is good but only works on Android devices with phone. You should consider support for Tablets or other Android devices as well, that do not have a phone.

IMEI 很好,但仅适用于带手机的 Android 设备。您还应该考虑支持平板电脑或其他没有手机的 Android 设备。

You have some alternatives like: Build class members, BT MAC, WLAN MAC, or even better - a combination of all these.

您有一些替代方案,例如:构建班级成员、BT MAC、WLAN MAC,或者甚至更好 - 所有这些的组合。

I have explained these details in an article on my blog, see: http://www.pocketmagic.net/?p=1662

我在我博客上的一篇文章中解释了这些细节,请参阅:http: //www.pocketmagic.net/?p=1662

回答by leRobot

Since no answer here mentions a perfect, fail-proof ID that is both PERSISTENT through system updates and exists in ALL devices(mainly due to the fact that there isn't an individual solution from Google), I decided to post a method that is the next best thing by combining two of the available identifiers, and a check to chose between them at run-time.

由于这里没有答案提到一个完美的、防故障的 ID,它既通过系统更新持久存在,又存在于所有设备中(主要是因为 Google 没有单独的解决方案),我决定发布一个方法下一个最好的方法是组合两个可用的标识符,并在运行时检查它们之间的选择。

Before code, 3 facts:

在编写代码之前,有 3 个事实:

  1. TelephonyManager.getDeviceId()(a.k.a.IMEI) will not work well or at all for non-GSM, 3G, LTE, etc. devices, but will always return a unique ID when related hardware is present, even when no SIM is inserted or even when no SIM slot exists (some OEM's have done this).

  2. Since Gingerbread (Android 2.3) android.os.Build.SERIALmust exist on any device that doesn't provide IMEI, i.e., doesn't have the aforementioned hardware present, as per Android policy.

  3. Due to fact (2.), at least one of these two unique identifiers will ALWAYS be present, and SERIAL canbe present at the same time that IMEI is.

  1. TelephonyManager.getDeviceId()(akaIMEI) 对于非 GSM、3G、LTE 等设备将无法正常工作或根本无法运行,但在相关硬件存在时,即使没有插入 SIM 卡或什至没有 SIM 卡插槽,也会始终返回唯一 ID(一些 OEM 已经这样做了)。

  2. 由于 Gingerbread (Android 2.3)android.os.Build.SERIAL必须存在于任何不提供 IMEI 的设备上,即根据 Android 政策,不存在上述硬件。

  3. 由于事实 (2.),这两个唯一标识符中的至少一个将始终存在,并且 SERIAL可以与 IMEI 同时存在。

Note: Fact (1.) and (2.) are based on Google statements

注意:事实 (1.) 和 (2.)基于 Google 声明

SOLUTION

解决方案

With the facts above, one can always have a unique identifier by checking if there is IMEI-bound hardware, and fall back to SERIAL when it isn't, as one cannot check if the existing SERIAL is valid. The following static class presents 2 methods for checking such presence and using either IMEI or SERIAL:

根据上述事实,我们可以通过检查是否有绑定 IMEI 的硬件来获得唯一标识符,如果没有,则返回到 SERIAL,因为人们无法检查现有的 SERIAL 是否有效。以下静态类提供了 2 种方法来检查此类存在并使用 IMEI 或 SERIAL:

import java.lang.reflect.Method;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;

public class IDManagement {

    public static String getCleartextID_SIMCHECK (Context mContext){
        String ret = "";

        TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);

        if(isSIMAvailable(mContext,telMgr)){
            Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId());
            return telMgr.getDeviceId();

        }
        else{
            Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.ANDROID_ID);

//          return Settings.Secure.ANDROID_ID;
            return android.os.Build.SERIAL;
        }
    }


    public static String getCleartextID_HARDCHECK (Context mContext){
        String ret = "";

        TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        if(telMgr != null && hasTelephony(mContext)){           
            Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId() + "");

            return telMgr.getDeviceId();    
        }
        else{
            Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.ANDROID_ID);

//          return Settings.Secure.ANDROID_ID;
            return android.os.Build.SERIAL;
        }
    }


    public static boolean isSIMAvailable(Context mContext, 
            TelephonyManager telMgr){

        int simState = telMgr.getSimState();

        switch (simState) {
        case TelephonyManager.SIM_STATE_ABSENT:
            return false;
        case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
            return false;
        case TelephonyManager.SIM_STATE_PIN_REQUIRED:
            return false;
        case TelephonyManager.SIM_STATE_PUK_REQUIRED:
            return false;
        case TelephonyManager.SIM_STATE_READY:
            return true;
        case TelephonyManager.SIM_STATE_UNKNOWN:
            return false;
        default:
            return false;
        }
    }

    static public boolean hasTelephony(Context mContext)
    {
        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        if (tm == null)
            return false;

        //devices below are phones only
        if (Build.VERSION.SDK_INT < 5)
            return true;

        PackageManager pm = mContext.getPackageManager();

        if (pm == null)
            return false;

        boolean retval = false;
        try
        {
            Class<?> [] parameters = new Class[1];
            parameters[0] = String.class;
            Method method = pm.getClass().getMethod("hasSystemFeature", parameters);
            Object [] parm = new Object[1];
            parm[0] = "android.hardware.telephony";
            Object retValue = method.invoke(pm, parm);
            if (retValue instanceof Boolean)
                retval = ((Boolean) retValue).booleanValue();
            else
                retval = false;
        }
        catch (Exception e)
        {
            retval = false;
        }

        return retval;
    }


}

I would advice on using getCleartextID_HARDCHECK. If the reflection doesn't stick in your environment, use the getCleartextID_SIMCHECKmethod instead, but take in consideration it should be adapted to your specific SIM-presence needs.

我会建议使用getCleartextID_HARDCHECK. 如果反射没有留在您的环境中,请改用该getCleartextID_SIMCHECK方法,但要考虑到它应该适应您特定的 SIM 存在需求。

P.S.: Do please note that OEM's have managed to bug out SERIAL against Google policy(multiple devices with same SERIAL), and Google as stated there is at least one known case in a big OEM (not disclosed and I don't know which brand it is either, I'm guessing Samsung).

PS:请注意,OEM 已设法根据 Google 政策(具有相同 SERIAL 的多个设备)对 SERIAL 进行错误处理,并且 Google 如上所述,在大型 OEM 中至少有一个已知案例(未披露,我不知道是哪个品牌)要么,我猜是三星)。

Disclaimer: This answers the original question of getting a unique device ID, but the OP introduced ambiguity by stating he needs a unique ID for an APP. Even if for such scenarios Android_ID would be better, it WILL NOT WORK after, say, a Titanium Backup of an app through 2 different ROM installs (can even be the same ROM). My solution maintains persistence that is independent of a flash or factory reset, and will only fail when IMEI or SERIAL tampering occurs through hacks/hardware mods.

免责声明:这回答了获取唯一设备 ID 的原始问题,但是 OP 通过声明他需要一个应用程序的唯一 ID 来引入歧义。即使对于这种情况 Android_ID 会更好,但在通过 2 个不同的 ROM 安装(甚至可以是相同的 ROM)对应用程序进行钛备份后,它也将无法工作。我的解决方案保持独立于闪存或出厂重置的持久性,并且仅在 IMEI 或 SERIAL 篡改通过黑客/硬件模块发生时才会失败。

回答by TechnoTony

There are problems with all the above approaches. At Google i/o Reto Meier released a robust answer to how to approach this which should meet most developers needs to track users across installations.

上述所有方法都存在问题。在谷歌 i/o Reto Meier 发布了一个关于如何解决这个问题的可靠答案,它应该满足大多数开发人员在跨安装跟踪用户的需求。

This approach will give you an anonymous, secure user ID which will be persistent for the user across different devices (including tablets, based on primary Google account) and across installs on the same device. The basic approach is to generate a random user ID and to store this in the apps shared preferences. You then use Google's backup agent to store the shared preferences linked to the Google account in the cloud.

这种方法将为您提供一个匿名、安全的用户 ID,该用户 ID 将在不同设备(包括平板电脑,基于主要 Google 帐户)上以及在同一设备上进行安装时保持不变。基本方法是生成一个随机用户 ID 并将其存储在应用程序共享首选项中。然后,您可以使用 Google 的备份代理将链接到 Google 帐户的共享首选项存储在云中。

Lets go through the full approach. First we need to create a backup for our SharedPreferences using the Android Backup Service. Start by registering your app via this link: http://developer.android.com/google/backup/signup.html

让我们来看看完整的方法。首先,我们需要使用 Android 备份服务为 SharedPreferences 创建备份。首先通过此链接注册您的应用程序:http: //developer.android.com/google/backup/signup.html

Google will give you a backup service key which you need to add to the manifest. You also need to tell the application to use the BackupAgent as follows:

Google 会为您提供一个备份服务密钥,您需要将其添加到清单中。您还需要告诉应用程序使用 BackupAgent,如下所示:

<application android:label="MyApplication"
         android:backupAgent="MyBackupAgent">
    ...
    <meta-data android:name="com.google.android.backup.api_key"
        android:value="your_backup_service_key" />
</application>

Then you need to create the backup agent and tell it to use the helper agent for sharedpreferences:

然后,您需要创建备份代理并告诉它使用共享首选项的帮助代理:

public class MyBackupAgent extends BackupAgentHelper {
    // The name of the SharedPreferences file
    static final String PREFS = "user_preferences";

    // A key to uniquely identify the set of backup data
    static final String PREFS_BACKUP_KEY = "prefs";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this,          PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);
    }
}

To complete the backup you need to create an instance of BackupManager in your main Activity:

要完成备份,您需要在主活动中创建一个 BackupManager 实例:

BackupManager backupManager = new BackupManager(context);

Finally create a user ID, if it doesn't already exist, and store it in the SharedPreferences:

最后创建一个用户 ID,如果它不存在,并将其存储在 SharedPreferences 中:

  public static String getUserID(Context context) {
            private static String uniqueID = null;
        private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
    if (uniqueID == null) {
        SharedPreferences sharedPrefs = context.getSharedPreferences(
                MyBackupAgent.PREFS, Context.MODE_PRIVATE);
        uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
        if (uniqueID == null) {
            uniqueID = UUID.randomUUID().toString();
            Editor editor = sharedPrefs.edit();
            editor.putString(PREF_UNIQUE_ID, uniqueID);
            editor.commit();

            //backup the changes
            BackupManager mBackupManager = new BackupManager(context);
            mBackupManager.dataChanged();
        }
    }

    return uniqueID;
}

This User_ID will now be persistent across installations, even if the user switches devices.

现在,即使用户切换设备,此 User_ID 也将在整个安装过程中保持不变。

For more information on this approach see Reto's talk here http://www.google.com/events/io/2011/sessions/android-protips-advanced-topics-for-expert-android-app-developers.html

有关此方法的更多信息,请参阅 Reto 在此处的演讲http://www.google.com/events/io/2011/sessions/android-protips-advanced-topics-for-expert-android-app-developers.html

And for full details of how to implement the backup agent see the developer site here: http://developer.android.com/guide/topics/data/backup.htmlI particularly recommend the section at the bottom on testing as the backup does not happen instantaneously and so to test you have to force the backup.

有关如何实现备份代理的完整详细信息,请参阅此处的开发人员站点:http: //developer.android.com/guide/topics/data/backup.html我特别推荐底部的部分关于备份所做的测试不会立即发生,因此要进行测试,您必须强制备份。

回答by insitusec

Another way is to use /sys/class/android_usb/android0/iSerial in an App with no permissions whatsoever.

另一种方法是在没有任何权限的应用程序中使用 /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

To do this in java one would just use a FileInputStream to open the iSerial file and read out the characters. Just be sure you wrap it in an exception handler because not all devices have this file.

要在 java 中执行此操作,只需使用 FileInputStream 打开 iSerial 文件并读出字符。请确保将其包装在异常处理程序中,因为并非所有设备都有此文件。

At least the following devices are known to have this file world-readable:

至少已知以下设备具有该文件世界可读:

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3g
  • Toshiba AT300
  • HTC One V
  • Mini MK802
  • Samsung Galaxy S II
  • 银河系
  • Nexus S
  • 摩托罗拉 Xoom 3g
  • 东芝AT300
  • 宏达一V
  • 迷你MK802
  • 三星 Galaxy S II

You can also see my blog post here: http://insitusec.blogspot.com/2013/01/leaking-android-hardware-serial-number.htmlwhere I discuss what other files are available for info.

您还可以在此处查看我的博客文章:http: //insitusec.blogspot.com/2013/01/leaking-android-hardware-serial-number.html,我在这里讨论了哪些其他文件可用于信息。