代码之家  ›  专栏  ›  技术社区  ›  Eno

如何找到安卓设备的序列号?

  •  113
  • Eno  · 技术社区  · 15 年前

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

    16 回复  |  直到 6 年前
        1
  •  102
  •   haseman    15 年前
    TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
    String uid = tManager.getDeviceId();
    

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

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

        2
  •  71
  •   ceph3us    8 年前

    正如戴夫·韦伯所说, Android Developer Blog has an article 包括这个。

    我和谷歌的某个人谈过,想得到一些关于一些项目的补充说明。以下是我在上述博客帖子中没有提到的内容:

    • Android_ID是首选解决方案。Android_ID在Android版本<=2.1或>=2.3上非常可靠。只有2.2存在岗位提到的问题。
    • 多家制造商生产的多个设备受2.2中的Android_id错误的影响。
    • 据我所知,所有受影响的设备 the same ANDROID_ID ,这就是 9774d56d682e549c . 它也是仿真器btw报告的相同设备ID。
    • 谷歌认为,原始设备制造商已经解决了许多或大多数设备的问题,但我能够证实,至少在2011年4月初,找到安卓ID已损坏的设备仍然相当容易。

    根据Google的建议,我实现了一个类,它将为每个设备生成一个唯一的UUID,在适当的情况下使用android作为种子,必要时返回telephonyManager.getdeviceID(),如果失败,则使用随机生成的唯一UUID,该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;
        }
    }
    
        3
  •  32
  •   Wesley Wiser    12 年前
    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返回设备序列号。

        4
  •  16
  •   Anthony Forloney    15 年前
    String deviceId = Settings.System.getString(getContentResolver(),
                                    Settings.System.ANDROID_ID);
    

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

        5
  •  14
  •   Juan Moreno    6 年前

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

    它建议不要使用 TelephonyManager.getDeviceId() 由于它不适用于Android设备,而Android设备不是平板电脑等手机,因此它需要 READ_PHONE_STATE 但在所有手机上都不能可靠地工作。

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

    • MAC地址
    • 序列号
    • 雄蛛科

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

        6
  •  12
  •   Edward Brey    11 年前

    对于设备独有且在其使用寿命内保持不变的简单数字(除非工厂重置或黑客攻击),请使用 Settings.Secure.ANDROID_ID .

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

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

    String serialNumber = Build.SERIAL != Build.UNKNOWN ? Build.SERIAL : Secure.getString(getContentResolver(), Secure.ANDROID_ID);
    
        7
  •  7
  •   radhoo    14 年前

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

    您有一些选择,比如:构建类成员、bt-mac、wlan-mac,甚至更好——所有这些的组合。

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

        8
  •  6
  •   leRobot    11 年前

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

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

    1. TelephonyManager.getDeviceId() (A.K.A.IMEI)在非GSM、3G、LTE等设备上无法正常工作,但 在存在相关硬件时始终返回唯一ID ,即使没有插入SIM卡,甚至没有SIM卡插槽(有些OEM已经这样做了)。

    2. 自姜饼(Android 2.3) android.os.Build.SERIAL 必须存在于任何不提供IMEI的设备上 也就是说,根据Android策略,没有上述硬件。

    3. 由于事实(2.), 这两个唯一标识符中至少有一个始终存在 和系列 可以 与IMEI同时在场。

    注:事实(1)和(2)是 based on Google statements

    解决方案

    根据以上事实,通过检查是否有IMEI绑定的硬件,可以始终拥有唯一的标识符,如果没有,则返回到串行,因为无法检查现有的串行是否有效。下面的静态类提供了两种方法来检查这种存在,并使用IMEI或串行:

    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卡存在的需要。

    附笔。 :请注意,原始设备制造商 设法阻止了针对谷歌政策的系列报道 (多台设备具有相同的序列号)和谷歌(如前所述),在一个大的原始设备制造商中至少有一个已知的案例(我猜三星也不知道它是哪个品牌)。

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

        9
  •  5
  •   Stanley    12 年前

    上述所有方法都存在问题。在Google I/O上,Reto Meier发布了一个强大的解决方案,它可以满足大多数开发人员跟踪用户安装的需求。

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

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

    谷歌会给你一个备份服务密钥,你需要添加到清单中。您还需要告诉应用程序使用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>
    

    然后,您需要创建备份代理并告诉它使用用于共享引用的助手代理:

    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);
        }
    }
    

    要完成备份,您需要在主活动中创建backupManager的实例:

    BackupManager backupManager = new BackupManager(context);
    

    最后,如果用户ID尚不存在,请创建它,并将其存储在sharedreferences中:

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

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

    有关此方法的更多信息,请参阅此处的reto's talk 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 我特别推荐下面关于测试的部分,因为备份不会立即发生,所以为了测试,您必须强制执行备份。

        10
  •  2
  •   insitusec    12 年前

    另一种方法是在没有任何权限的应用程序中使用/sys/class/android_b/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中实现这一点,可以使用FieldPixStand打开ISLID文件并读出字符。请确保将其包装在异常处理程序中,因为并非所有设备都有此文件。

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

    • 银河系
    • 联结S
    • 摩托罗拉Xoom 3G
    • 东芝AT300
    • HTC一V
    • 迷你MK802
    • 三星Galaxy S II

    你也可以在这里看到我的博客帖子: http://insitusec.blogspot.com/2013/01/leaking-android-hardware-serial-number.html 在这里我将讨论哪些其他文件可供参考。

        11
  •  1
  •   cesards    12 年前

    正如@haserman所说:

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

    但必须在清单文件中包含权限:

    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    
        12
  •  1
  •   Jorgesys    11 年前

    作为字符串的Android OS设备的唯一设备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); 
             }
    

    但我极力推荐谷歌建议的这种方法:

    Identifying App Installations

        13
  •  1
  •   Mickäel A.    6 年前

    Build.SERIAL 这是最简单的方法,尽管不是完全可靠 can be empty 或者有时返回不同的值( proof 1 , proof 2 )比你在设备设置中看到的要多。

    根据设备的制造商和安卓版本,有几种方法可以获得这个数字,因此我决定编译在一个单一的解决方案中找到的所有可能的解决方案。 gist . 下面是它的简化版本:

    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;
    }
    
        14
  •  0
  •   MichaelStoddart    10 年前

    我知道这个问题由来已久,但可以用一行代码来解决

    String deviceID = Build.SERIAL;

        15
  •  -1
  •   Carlos P    13 年前

    我发现上面@emmby发布的示例类是一个很好的起点。但它有一些缺陷,正如其他海报所提到的。主要的一点是,它不必要地将UUID保存到XML文件中,然后总是从此文件中检索UUID。这使得类向一个简单的黑客开放:任何拥有根电话的人都可以编辑XML文件,为自己提供一个新的UUID。

    我已经更新了代码,使其仅在绝对必要时(即使用随机生成的UUID时)保持为XML,并根据@brill pappin的答案重新考虑逻辑:

    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;
        }
    
        16
  •  -2
  •   Mahesh Ramchandra Bhatkande    10 年前

    对。它是一个设备硬件序列号,并且是唯一的。因此,在API 2.3及以上级别,您可以使用 android.os.build.android_id版 得到它。对于低于2.3 API等级的应用 TelephoneManager.GetDeviceId()。 .

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