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

时间:2010-02-23 22:26:39

标签: android serial-number

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

18 个答案:

答案 0 :(得分:102)

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

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

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

答案 1 :(得分:71)

正如Dave Webb所提到的,Android Developer Blog has an article涵盖了这一点。

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

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

根据Google的建议,我实现了一个类,它将为每个设备生成一个唯一的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;
    }
}

答案 2 :(得分:32)

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返回设备序列号。

答案 3 :(得分:16)

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

虽然不能保证Android ID是唯一标识符。

答案 4 :(得分:14)

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

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

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

  • Mac地址
  • 序列号
  • ANDROID_ID

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

答案 5 :(得分:12)

对于设备唯一的简单数字及其生命周期的常量(禁止恢复出厂设置或黑客攻击),请使用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);

答案 6 :(得分:7)

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

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

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

答案 7 :(得分:6)

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

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

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

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

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

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

    <强>解

    根据上述事实,通过检查是否存在IMEI绑定硬件,可以始终具有唯一标识符,并且当不存在时,可以返回SERIAL,因为无法检查现有SERIAL是否有效。以下静态类提供了两种方法来检查这种存在并使用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;
        }
    
    
    }
    

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

    PS :请注意,OEM已经设法违反了针对Google政策的SERIAL (具有相同SERIAL的多个设备),而Google表示至少有一个一个大型OEM的已知案例(未透露,我不知道它是哪个品牌,我猜三星)。

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

答案 8 :(得分:5)

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

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

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

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>

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

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实例
BackupManager backupManager = new BackupManager(context);

最后创建一个用户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;
}

即使用户切换设备,此User_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 我特别推荐测试底部的部分,因为备份不会立即发生,所以要测试你必须强制备份。

答案 9 :(得分:2)

另一种方法是在没有任何权限的应用程序中使用/ 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
  • 东芝AT300
  • HTC One V
  • Mini MK802
  • 三星Galaxy S II

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

答案 10 :(得分:1)

正如@haserman所说:

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

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

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

答案 11 :(得分:1)

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

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 ::

建议的这种方法

Identifying App Installations

答案 12 :(得分:1)

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

有多种方法可以获取该数字,具体取决于设备的制造商和Android版本,因此我决定在一个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;
}

答案 13 :(得分:0)

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

String deviceID = Build.SERIAL;

答案 14 :(得分:0)

从Android 10开始,应用必须具有READ_PRIVILEGED_PHONE_STATE特权,才能访问设备的不可重置标识符,包括IMEI和序列号。

受影响的方法包括:

构建 getSerial() 电话经理 getImei() getDeviceId() getMeid() getSimSerialNumber() getSubscriberId()

READ_PRIVILEGED_PHONE_STATE仅适用于平台

答案 15 :(得分:0)

public static String getManufacturerSerialNumber() {
  String serial = null; 
  try {
      Class<?> c = Class.forName("android.os.SystemProperties");
      Method get = c.getMethod("get", String.class, String.class);
      serial = (String) get.invoke(c, "ril.serialnumber", "unknown");
  } catch (Exception ignored) {}
  return serial;
}

适用于 API 29 和 30,在三星 Galaxy s7 s9 Xcover 上测试

答案 16 :(得分:-1)

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

答案 17 :(得分:-2)

是。它是设备硬件序列号,它是唯一的。所以在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