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

Eno*_*Eno 114 android serial-number

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

has*_*man 104

TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
Run Code Online (Sandbox Code Playgroud)

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

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

  • @Hasemam现在在androidManifest.xml文件中添加<uses-permission android:name ="android.permission.READ_PHONE_STATE"> </ uses-permission>权限后运行正常. (23认同)
  • 关于使用此标识符的官方Android开发人员博客上有一些建议:http://android-developers.blogspot.com/2011/03/identifying-app-installations.html (23认同)
  • 应该避免这种方法,这种方法适用于手机,但不适用于没有手机芯片的设备(平板电脑就是一个例子).从2.3开始,您可以使用android.os.Build.SERIAL,但请查看@DavidCaunt建议的开发人员博客. (21认同)
  • 除了Android驱动的微波炉,Android平板电脑怎么样?:) (7认同)

emm*_*mby 71

正如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这是在应用程序重新启动时保留的(但不是应用程序重新安装).

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢发帖.但是,如果只是通过简单地编辑device_id.xml来放弃他们选择的新UUID,那么使用root用户手机阻止什么呢?(即规避"免费试用"检查)如果类只需要使用随机ID方法,那么只将该值存储在首选项文件中会不会更好?否则,没有必要在应用程序运行之间保持它; 重新生成更安全. (3认同)

小智 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) {
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这只是给了我与android.os.Build.SERIAL相同的价值 (7认同)

Ant*_*ney 16

String deviceId = Settings.System.getString(getContentResolver(),
                                Settings.System.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)

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

  • 此ID来自与手机关联的Google帐户.模拟器通常没有.真正的手机可能也没有.此外,它被记录为"可以在出厂重置时更改",并且可以在root电话上随时任意更改.使用风险由您自己承担.没有其他好的选择 - 其他临时设备ID要么不是普遍可用的,要么不是唯一的,或者两者兼而有之.查看其他悲伤故事的其他答案. (4认同)

Dav*_*ebb 14

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

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

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

  • MAC地址
  • 序列号
  • ANDROID_ID

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


Edw*_*rey 12

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

String id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)

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

String serialNumber = Build.SERIAL != Build.UNKNOWN ? Build.SERIAL : Secure.getString(getContentResolver(), Secure.ANDROID_ID);
Run Code Online (Sandbox Code Playgroud)


rad*_*hoo 7

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

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

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


leR*_*bot 6

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

在代码之前,3个事实:

  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同时存在.

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

根据上述事实,通过检查是否存在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;
    }


}
Run Code Online (Sandbox Code Playgroud)

我会建议使用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篡改时才会失败.


Tec*_*ony 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>
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

BackupManager backupManager = new BackupManager(context);
Run Code Online (Sandbox Code Playgroud)

最后创建一个用户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;
}
Run Code Online (Sandbox Code Playgroud)

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