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

How to find serial number of Android device?

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


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

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

每个设备必须在这里返回一个唯一值(假设它是一部手机)。这适用于任何带有SIM卡插槽或CDMA无线电的Android设备。你自己使用Android驱动的微波炉;-)


正如Dave Webb所提到的,Android开发人员博客有一篇文章介绍了这一点。

我与Google的某位人员进行了交谈,以便对一些项目进行一些额外的澄清。以下是我在上述博文中未提及的内容:

  • ANDROID_ID是首选解决方案。 ANDROID_ID在Android <= 2.1或> = 2.3的版本上非常可靠。只有2.2有帖子中提到的问题。
  • 几个制造商的几个设备受到2.2中ANDROID_ID错误的影响。
  • 据我所知,所有受影响的设备都具有相同的ANDROID_ID,即9774d56d682e549c。这也是模拟器报告的相同设备ID,顺便说一下。
  • 谷歌相信原始设备制造商已经为他们的许多或大部分设备修补了这个问题,但我能够验证到2011年4月初,至少,找到那些破坏了ANDROID_ID的设备仍然很容易。

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

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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;
    }
}


1
2
3
4
5
6
7
8
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) {
}

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


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

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


Android Developer's Blog上有一篇很好的帖子讨论这个问题。

它建议不要使用TelephonyManager.getDeviceId(),因为它不适用于不是平板电脑等手机的Android设备,它需要READ_PHONE_STATE权限才能在所有手机上无法正常工作。

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

  • MAC地址
  • 序列号
  • ANDROID_ID

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


对于设备唯一且对其生命周期保持不变的简单数字(禁止出厂重置或黑客攻击),请使用Settings.Secure.ANDROID_ID。

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

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

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


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

你有一些选择:构建类成员,BT MAC,WLAN MAC,甚至更好 - 所有这些的组合。

我在博客上的一篇文章中解释了这些细节,请参阅:

Android Unique Device ID


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

在代码之前,3个事实:

  • TelephonyManager.getDeviceId()(akaIMEI)不适用于非GSM,3G,LTE等设备,但在相关硬件存在时始终会返回唯一ID,即使没有插入SIM卡,甚至没有存在SIM插槽(一些OEM已经这样做了)。

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

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

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

    根据上述事实,通过检查是否存在IMEI绑定硬件,可以始终具有唯一标识符,并且当不存在时,可以返回SERIAL,因为无法检查现有SERIAL是否有效。以下静态类提供了两种检查此类状态并使用IMEI或SERIAL的方法:

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    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;
        }


    }

    我建议使用getCleartextID_HARDCHECK。如果反射不会粘附在您的环境中,请改用getCleartextID_SIMCHECK方法,但请注意它应该适应您特定的SIM存在需求。

    PS:请注意,OEM已经成功地违反了SERIAL违反Google政策(多个设备使用相同的SERIAL),谷歌表示至少有一个已知的大型OEM案例(未透露,我不知道哪个品牌)它要么是,我猜三星)。

    免责声明:这回答了获取唯一设备ID的原始问题,但OP通过声明他需要APP的唯一ID来引入歧义。即使对于这样的场景,Android_ID会更好,但是,通过2个不同的ROM安装(甚至可以是相同的ROM),应用程序的Titanium备份后,它将无法工作。我的解决方案保持独立于闪存或恢复出厂设置的持久性,并且只有在通过hacks / hardware mods发生IMEI或SERIAL篡改时才会失败。


    所有上述方法都存在问题。在Google i / o上,Reto Meier发布了一个强有力的解决方案,解决了如何解决这个问题,以满足大多数开发人员在跨安装时跟踪用户的需求。

    这种方法会为您提供一个匿名的,安全的用户ID,该ID对于不同设备(包括平板电脑,基于主要Google帐户)的用户以及同一设备上的安装都是持久的。基本方法是生成随机用户ID并将其存储在apps共享首选项中。然后,您可以使用Google的备用代理存储与云中Google帐户关联的共享偏好设置。

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

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

    1
    2
    3
    4
    5
    6
    <application android:label="MyApplication"
             android:backupAgent="MyBackupAgent">
        ...
        <meta-data android:name="com.google.android.backup.api_key"
            android:value="your_backup_service_key" />
    </application>

    然后,您需要创建备份代理并告诉它使用帮助代理进行共享优先:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
        }
    }

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

    1
    BackupManager backupManager = new BackupManager(context);

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      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;
    }

    即使用户切换设备,此User_ID现在也将在安装期间保持不变。

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

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


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

    1
    2
    3
    4
    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.
    • 摩托罗拉Xoom 3g
    • 东芝AT300
    • HTC One V.
    • 迷你MK802
    • 三星Galaxy S II

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


    正如@haserman所说:

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

    但是必须包括清单文件中的权限:

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


    Build.SERIAL是最简单的方法,虽然不完全可靠,因为它可能是空的,或者有时会返回与您在设备设置中看到的不同的值(校样1,校样2)。

    根据设备的制造商和Android版本,有几种方法可以获得这个数字,所以我决定编译我可以在一个要点中找到的所有可能的解决方案。这是它的简化版本:

    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
    public static String getSerialNumber() {
        String serialNumber;

        try {
            Class< ? > c = Class.forName("android.os.SystemProperties");
            Method get = c.getMethod("get", String.class);

            serialNumber = (String) get.invoke(c,"gsm.sn1");
            if (serialNumber.equals(""))
                serialNumber = (String) get.invoke(c,"ril.serialnumber");
            if (serialNumber.equals(""))
                serialNumber = (String) get.invoke(c,"ro.serialno");
            if (serialNumber.equals(""))
                serialNumber = (String) get.invoke(c,"sys.serialnumber");
            if (serialNumber.equals(""))
                serialNumber = Build.SERIAL;

            // If none of the methods above worked
            if (serialNumber.equals(""))
                serialNumber = null;
        } catch (Exception e) {
            e.printStackTrace();
            serialNumber = null;
        }

        return serialNumber;
    }

    Android OS Device的唯一设备ID为String。

    1
    2
    3
    4
    5
    6
    7
    8
    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建议的这种方法::

    识别应用程序安装


    我知道这个问题很老,但可以在一行代码中完成

    String deviceID = Build.SERIAL;


    我发现@emmby上面发布的示例类是一个很好的起点。但正如其他海报所提到的那样,它有一些缺陷。主要的一点是它不必要地将UUID持久化到XML文件,然后总是从该文件中检索它。这样就可以轻松解决这个问题:任何拥有root权限的人都可以编辑XML文件,为自己提供一个新的UUID。

    我已经更新了代码,以便它只在绝对必要时(即使用随机生成的UUID时)持久保存到XML,并根据@Brill Pappin的答案重新考虑逻辑:

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    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 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) || (androidId == null) ) {
                                    final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();

                                    if (deviceId != null)
                                    {
                                        uuid = UUID.nameUUIDFromBytes(deviceId.getBytes("utf8"));
                                    }
                                    else
                                    {
                                        uuid = UUID.randomUUID();

                                        // Write the value out to the prefs file so it persists
                                        prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit();
                                    }
                                }
                                else
                                {
                                    uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
                                }
                            } catch (UnsupportedEncodingException e) {
                                throw new RuntimeException(e);
                            }



                        }

                    }
                }
            }

        }


        /**
         * 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;
        }


    是。它是设备硬件序列号,它是唯一的。所以在api level 2.3及以上版本中你可以使用android.os.Build.ANDROID_ID来获取它。对于低于2.3 API级别,请使用TelephonyManager.getDeviceID()。

    你可以阅读这个http://android-developers.blogspot.in/2011/03/identifying-app-installations.html