我需要在Android应用程序中使用唯一标识符,而我认为设备的序列号是一个好的选择。如何在我的应用程序中检索Android设备的序列号?
我需要在Android应用程序中使用唯一标识符,而我认为设备的序列号是一个好的选择。如何在我的应用程序中检索Android设备的序列号?
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开发者博客有一篇文章介绍了这个问题。
我与谷歌的一位员工交流,以获取一些额外的澄清。这是我发现的,在上述博客文章中没有提到的内容:
根据谷歌的建议,我实现了一个类,它将使用ANDROID_ID作为必要的种子为每个设备生成唯一的UUID,在必要时退回到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;
}
}
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返回设备序列号。
String deviceId = Settings.System.getString(getContentResolver(),
Settings.System.ANDROID_ID);
尽管如此,Android ID并不保证是唯一的标识符。
getContentResolver
返回了null
。但值得一提的是,开个问题并发布你的代码可能会有所帮助。 - Anthony ForloneyAndroid开发者博客上有一篇关于此问题的优秀文章。
它建议不要使用TelephonyManager.getDeviceId()
,因为它不能在非手机设备(如平板电脑)上工作,需要READ_PHONE_STATE
权限,并且在所有手机上都不可靠。
相反,您可以使用以下之一:
这篇文章讨论了每个选项的利弊,值得一读,以便确定哪个选项最适合您的用途。
如果需要一个简单的、与设备唯一且终身不变(除非进行出厂设置或破解)的数字,请使用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);
IMEI码很好,但仅适用于带有电话的Android设备。您应该考虑支持平板电脑或其他没有电话的Android设备。
您有一些替代方案,例如:构建类成员、蓝牙MAC地址、 WLAN MAC地址,或者更好的方法是将所有这些组合起来。
我在我的博客上解释了这些细节,请参见: http://www.pocketmagic.net/?p=1662
由于没有一个完美、经过系统更新后仍然持久存在于所有设备中的ID,这里没有提到任何答案(主要是因为谷歌没有一个独立的解决方案),因此我决定发布一种方法,通过组合两个可用的标识符和在运行时选择它们之间的检查来实现最佳效果。
在代码之前,有三个事实:
TelephonyManager.getDeviceId()
(即IMEI)对于非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是否有效。以下静态类提供了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;
}
}
getCleartextID_HARDCHECK
。如果反射在您的环境中不起作用,请改用getCleartextID_SIMCHECK
方法,但要考虑适应您特定的SIM出现需求。<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 = new BackupManager(context);
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@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