是否有唯一的Android设备ID?
Android设备是否有唯一的ID,如果是,使用Java访问它的简单方法是什么?
Settings.Secure#ANDROID_ID
将Android ID作为每个用户的 64位hexstring唯一返回。
import android.provider.Settings.Secure; private String android_id = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID);
更新 :截至最近的Android版本, ANDROID_ID
许多问题已经解决,我相信这种方法不再是必要的。 请看看安东尼的回答 。
完全公开:我的应用程序,原来使用的方法不再使用这种方法,我们现在使用安博士的 博客条目中概述的方法, emmby的答案链接(即生成和保存一个UUID#randomUUID()
) 。
这个问题有很多答案,其中大部分只会在某些时候起作用,不幸的是这还不够好。
根据我对设备的testing(所有手机,至less其中一个未被激活):
- 所有testing的设备都返回了
TelephonyManager.getDeviceId()
的值 - 所有GSM设备(全部使用SIM卡testing)为
TelephonyManager.getSimSerialNumber()
返回一个值 - 所有CDMA设备为
getSimSerialNumber()
返回null(如预期的那样) - 所有添加了Google帐户的设备都会为
ANDROID_ID
返回一个值 - 所有CDMA设备对于
ANDROID_ID
和TelephonyManager.getDeviceId()
都返回相同的值(或派生相同的值TelephonyManager.getDeviceId()
– 只要在设置期间添加了Google帐户。 - 我还没有机会testing没有SIM卡的GSM设备,没有添加Google帐户的GSM设备,或任何飞机模式的设备。
所以,如果你想要设备本身的独特的东西, TM.getDeviceId()
应该是足够的。 很显然,有些用户比其他用户更偏执,所以散列一个或多个这些标识符可能是有用的,这样string对于设备来说实际上是唯一的,但是并没有明确标识用户的实际设备。 例如,使用String.hashCode()
与UUID结合使用:
final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE); final String tmDevice, tmSerial, androidId; tmDevice = "" + tm.getDeviceId(); tmSerial = "" + tm.getSimSerialNumber(); androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode()); String deviceId = deviceUuid.toString();
可能会导致类似于: 00000000-54b3-e7c7-0000-000046bffd97
它对我来说足够好。
正如Richard在下面提到的,不要忘记您需要读取TelephonyManager
属性的权限,所以将其添加到您的清单:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
导入库
import android.content.Context; import android.telephony.TelephonyManager; import android.view.View;
最后更新:6/2/15
在阅读关于创build唯一ID,Google开发人员博客和Android文档的每个Stack Overflow后,我觉得“伪ID”是最好的select。
主要问题:硬件vs软件
硬件
- 用户可以更改他们的硬件,Android平板电脑或手机,所以基于硬件的唯一ID不是跟踪用户的好主意
- 对于追踪硬件 ,这是一个好主意
软件
- 用户可以擦除/更改自己的ROM
- 您可以跨平台(iOS,Android,Windows和Web)
- 最好要跟踪个人用户的同意是简单地让他们login(使用OAuth无缝)
用Android进行全面细分
– 保证API独特性(包括根植设备)=> 9/10(99.5%的Android设备)
– 没有额外的权限
伪代码:
if API => 9/10: (99.5% of devices) return unique ID containing serial id (rooted devices may be different) else return unique ID of build information (may overlap data - API < 9)
感谢@stansult发布我们所有的选项 (在这个堆栈溢出问题)。
选项清单 – 为什么/为什么不使用它们的原因:
-
用户电子邮件 – 软件
- 用户可以改变电子邮件 – 极不可能
- API 5+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
或 - API 14+
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( 如何获取Android设备的主要电子邮件地址 )
-
用户电话号码 – 软件
- 用户可以更改电话号码 – 极不可能
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
-
IMEI – 硬件 (仅限手机,需要
android.permission.READ_PHONE_STATE
)- 大多数用户不喜欢这个权限中的“电话呼叫”的事实。 有些用户给出的评分不好,因为他们认为你只是在窃取他们的个人信息,当你真正想要做的就是跟踪设备安装。 显然你正在收集数据。
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
-
Android ID – 硬件 (可以为null,可以在出厂设置时更改,可以在根设备上更改)
- 既然它可以是'空',我们可以检查'空'并改变它的值,但这意味着它将不再是唯一的。
- 如果您的用户使用了出厂重置设备,则可能在根设备上更改或更改了该值,因此如果您要跟踪用户安装,则可能会有重复条目。
-
WLAN MAC地址 – 硬件 (需要
android.permission.ACCESS_WIFI_STATE
)- 这可能是第二好的select,但是您仍然在收集和存储直接来自用户的唯一标识符。 这显然是你正在收集数据。
-
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
-
蓝牙MAC地址 – 硬件 (蓝牙设备,需要
android.permission.BLUETOOTH
)- 市场上的大多数应用程序都不使用蓝牙,所以如果您的应用程序不使用蓝牙,并且包含此function,则用户可能会产生怀疑。
-
<uses-permission android:name="android.permission.BLUETOOTH "/>
-
伪唯一ID – 软件 (适用于所有Android设备)
- 很可能,可能包含冲突 – 请参阅下面张贴的方法!
- 这使得用户可以拥有一个“几乎独一无二”的ID,而无需使用任何私密的ID。 您可以从设备信息创build您自己的一致ID。
我知道在没有使用权限的情况下,没有任何“完美”的方式来获得唯一的ID; 但是,有时候我们只需要做的就是跟踪设备的安装。 当涉及到创build一个唯一的ID,我们可以创build一个“伪唯一的ID”完全基于Android API给我们的信息,而不使用额外的权限。 这样,我们可以向用户表示尊重,并尝试提供良好的用户体验。
有了一个伪唯一的ID,你真的只会遇到这样的事实,即可能有重复的事实,有类似的设备。 你可以调整组合的方法,使其更独特; 然而,一些开发人员需要跟踪设备安装,这将基于类似的设备来实现技巧或性能。
API => 9:
如果他们的Android设备是API 9或以上,这是保证是唯一的,因为“Build.SERIAL”字段。
请记住 ,在技术上,只有大约0.5% API <9的用户错过了。 所以你可以把精力集中在其他方面:这是99.5%的用户!
API <9:
如果用户的Android设备低于API 9; 希望他们没有重新设置工厂,他们的'Secure.ANDROID_ID'将被保留或不'空'。 (请参阅http://developer.android.com/about/dashboards/index.html )
如果一切都失败了:
如果一切都失败了,如果用户的API比9低(比姜饼低),重新设置了设备或者'Secure.ANDROID_ID'返回'null',那么返回的ID将仅仅是基于他们的Android设备信息。 这是碰撞可能发生的地方。
变化:
- 由于工厂重置,删除了“Android.SECURE_ID”可能会导致值发生更改
- 编辑代码以更改API
- 改变了伪
请看下面的方法:
/** * Return pseudo unique ID * @return ID */ public static String getUniquePsuedoID() { // If all else fails, if the user does have lower than API 9 (lower // than Gingerbread), has reset their device or 'Secure.ANDROID_ID' // returns 'null', then simply the ID returned will be solely based // off their Android device information. This is where the collisions // can happen. // Thanks http://www.pocketmagic.net/?p=1662! // Try not to use DISPLAY, HOST or ID - these items could change. // If there are collisions, there will be overlapping data String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10); // Thanks to @Roman SL! // https://stackoverflow.com/a/4789483/950427 // Only devices with API >= 9 have android.os.Build.SERIAL // http://developer.android.com/reference/android/os/Build.html#SERIAL // If a user upgrades software or roots their device, there will be a duplicate entry String serial = null; try { serial = android.os.Build.class.getField("SERIAL").get(null).toString(); // Go ahead and return the serial for api => 9 return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); } catch (Exception exception) { // String needs to be initialized serial = "serial"; // some value } // Thanks @Joe! // https://stackoverflow.com/a/2853253/950427 // Finally, combine the values we have found by using the UUID class to create a unique identifier return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); }
新(针对包含广告和Google Play服务的应用):
在Google Play开发者控制台中:
从2014年8月1日起,Google Play开发者计划政策要求所有新的应用程序上传和更新均使用广告ID代替任何其他持久标识符用于任何广告目的。 学到更多
实现 :
允许:
<uses-permission android:name="android.permission.INTERNET" />
码:
import com.google.android.gms.ads.identifier.AdvertisingIdClient; import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info; import com.google.android.gms.common.GooglePlayServicesAvailabilityException; import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import java.io.IOException; ... // Do not call this function from the main thread. Otherwise, // an IllegalStateException will be thrown. public void getIdThread() { Info adInfo = null; try { adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext); } catch (IOException exception) { // Unrecoverable error connecting to Google Play services (eg, // the old version of the service doesn't support getting AdvertisingId). } catch (GooglePlayServicesAvailabilityException exception) { // Encountered a recoverable error connecting to Google Play services. } catch (GooglePlayServicesNotAvailableException exception) { // Google Play services is not available entirely. } final String id = adInfo.getId(); final boolean isLAT = adInfo.isLimitAdTrackingEnabled(); }
来源/文档:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
重要:
当Google Play服务可用时,广告ID完全取代了广告用途的其他标识符的现有使用(例如在Settings.Secure中使用ANDROID_ID)。 Google Play服务不可用的情况由getAdvertisingIdInfo()引发的GooglePlayServicesNotAvailableException指示。
警告,用户可以重置:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
我试图引用我从中获取信息的每一个环节。 如果您缺less,需要包括,请评论!
Google Player服务实例ID
正如戴夫·韦伯(Dave Webb)提到的那样, Android开发人员博客有一篇文章涵盖了这一点。 他们首选的解决scheme是跟踪应用程序安装而不是设备,这对于大多数使用情况来说都是很好的。 博客文章将向您展示必要的代码来完成这项工作,我build议您检查一下。
但是,如果您需要设备标识符而不是应用程序安装标识符,则该博客继续讨论解决scheme。 我和Google的某个人谈过话,以便在需要的时候对几件事情做进一步的说明。 以下是我在上述博客文章中未提及的关于设备标识符的发现:
- ANDROID_ID是首选的设备标识符。 ANDROID_ID在Android <= 2.1或> = 2.3的版本上是完全可靠的。 只有2.2有这个post中提到的问题。
- 2.2中有几个厂商的几个设备受到ANDROID_ID的影响。
- 据我所知,所有受影响的设备都有相同的ANDROID_ID ,即9774d56d682e549c 。 这也是仿真器报告的相同的设备ID,顺便说一句。
- 谷歌认为,原始设备制造商已经为他们的许多或大部分设备修补了这个问题,但是我能够validation到2011年4月初,至lessfind具有破坏的ANDROID_ID的设备还是很容易的。
根据Google的build议,我实现了一个类,它将为每个设备生成一个唯一的UUID,并在适当的时候使用ANDROID_ID作为种子,必要时返回TelephonyManager.getDeviceId(),如果失败,则使用随机生成的唯一UUID这是跨应用程序重新启动(但不是应用程序重新安装)持久。
请注意,对于必须在设备ID上回退的设备,唯一ID 将在工厂重置期间持续存在。 这是要注意的事情。 如果您需要确保出厂重置将重置您的唯一ID,则可能需要考虑直接返回到随机UUID而不是设备ID。
再次,这个代码是为了一个设备ID,而不是一个应用程序的安装ID。 对于大多数情况下,应用程序安装ID可能是你正在寻找的。 但是,如果您确实需要设备ID,那么下面的代码可能适合您。
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 volatile 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)) { 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; } }
以下是Reto Meier今年在Google I / O演示中为用户获取唯一ID的代码:
private static String uniqueID = null; private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID"; public synchronized static String id(Context context) { if (uniqueID == null) { SharedPreferences sharedPrefs = context.getSharedPreferences( PREF_UNIQUE_ID, 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(); } } return uniqueID; }
如果你把它与一个备份策略结合起来发送给云端(在Reto的讲座中也有描述,你应该有一个与用户绑定的id,并且在设备被擦除或者更换之后仍然存在)我打算使用这个在进行分析(换句话说,我还没有做那么一点:)。
你也可以考虑Wi-Fi适配器的MAC地址。 如此检索:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE); return wm.getConnectionInfo().getMacAddress();
在清单中需要权限android.permission.ACCESS_WIFI_STATE
。
据报即使在Wi-Fi没有连接时也可以使用。 如果从上面的答案乔给他尝试了他的许多设备,那会很好。
在某些设备上,当Wi-Ficlosures时不可用。
注意:从Android 6.x开始,它将返回一致的假MAC地址: 02:00:00:00:00:00
这里有相当有用的信息。
它涵盖了五种不同的IDtypes:
- IMEI (仅适用于使用手机的Android设备;需要
android.permission.READ_PHONE_STATE
) - 伪唯一ID (适用于所有Android设备)
- Android ID (可以为空,可以在出厂设置时更改,可以在固定电话上更改)
- WLAN MAC地址string(需要
android.permission.ACCESS_WIFI_STATE
) - BT MAC地址string(蓝牙设备,需要
android.permission.BLUETOOTH
)
官方的Android开发者博客现在有一个完整的文章就是关于这个主题, 识别应用程序安装 。
在Google I / O中, Reto Meier发布了一个强大的解决scheme,可以满足大多数开发人员在安装过程中跟踪用户的需求。 安东尼·诺兰(Anthony Nolan)在他的回答中表明了方向,但我想我会写出完整的方法,以便其他人可以很容易地看到如何去做(我花了一段时间才弄清楚细节)。
这种方法将为您提供一个匿名,安全的用户ID,该用户ID将在不同设备(基于主要Google帐户)和跨安装的情况下持续保留。 基本的方法是生成一个随机的用户ID,并将其存储在应用程序的共享首选项中。 然后,您使用Google的备份代理将链接到Google帐户的共享首选项存储在云中。
我们来看看完整的方法。 首先,我们需要使用Android备份服务为我们的SharedPreferences创build一个备份。 首先通过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>
然后,您需要创build备份代理并告诉它使用助手代理来共享首选项:
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中创build一个BackupManager实例:
BackupManager backupManager = new BackupManager(context);
最后创build一个用户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的演讲 。
有关如何实施备份代理的完整详细信息,请参阅数据备份 。 我特别build议在testing底部的部分,因为备份不会立即发生,所以testing你必须强制备份。
以下代码使用隐藏的Android API返回设备序列号。 但是,这个代码不适用于三星Galaxy Tab,因为“ro.serialno”没有在这个设备上设置。
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) { }
我认为这是build立一个独特的ID骨架火的方式…检查出来。
伪唯一ID,适用于所有Android设备某些设备没有电话(例如,平板电脑)或出于某种原因,您不希望包含READ_PHONE_STATE权限。 您仍然可以阅读ROM版本,制造商名称,CPUtypes以及其他硬件详细信息等详细信息,如果您想要将该ID用于串行密钥检查或其他一般用途,则该信息将非常适合。 以这种方式计算的ID不是唯一的:可以find两个具有相同ID的设备(基于相同的硬件和ROM映像),但是现实应用中的可能性可以忽略不计。 为此,您可以使用Build类:
String m_szDevIDShort = "35" + //we make this look like a valid IMEI Build.BOARD.length()%10+ Build.BRAND.length()%10 + Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 + Build.DISPLAY.length()%10 + Build.HOST.length()%10 + Build.ID.length()%10 + Build.MANUFACTURER.length()%10 + Build.MODEL.length()%10 + Build.PRODUCT.length()%10 + Build.TAGS.length()%10 + Build.TYPE.length()%10 + Build.USER.length()%10 ; //13 digits
大部分的构build成员都是string,我们在这里做的是把他们的长度和模数转换成数字。 我们有13个这样的数字,我们在前面(35)添加了两个像IMEI(15位)一样大小的ID。 这里还有其他的可能性,只要看看这些string。 返回类似于:355715565309247。 没有特别的许可要求,使这种方法非常方便。
(额外的信息:上面给出的技术是从Pocket Magic的一篇文章中复制的。)
使用下面的代码,您可以将Android操作系统设备的唯一设备ID作为string获取。
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
A Serial field was added to the Build
class in API level 9 (Android 2.3 – Gingerbread). Documentation says it represents the hardware serial number. Thus it should be unique, if it exists on the device.
I don't know whether it is actually supported (=not null) by all devices with API level >= 9 though.
One thing I'll add – I have one of those unique situations.
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Turns out that even though my Viewsonic G Tablet reports a DeviceID that is not Null, every single G Tablet reports the same number.
Makes it interesting playing "Pocket Empires" which gives you instant access to someone's account based on the "unique" DeviceID.
My device does not have a cell radio.
For detailed instructions on how to get a unique identifier for each Android device your application is installed from, see the official Android Developers Blog posting Identifying App Installations .
It seems the best way is for you to generate one yourself upon installation and subsequently read it when the application is re-launched.
I personally find this acceptable but not ideal. No one identifier provided by Android works in all instances as most are dependent on the phone's radio states (Wi-Fi on/off, cellular on/off, Bluetooth on/off). The others, like Settings.Secure.ANDROID_ID
must be implemented by the manufacturer and are not guaranteed to be unique.
The following is an example of writing data to an installation file that would be stored along with any other data the application saves locally.
public class Installation { private static String sID = null; private static final String INSTALLATION = "INSTALLATION"; public synchronized static String id(Context context) { if (sID == null) { File installation = new File(context.getFilesDir(), INSTALLATION); try { if (!installation.exists()) writeInstallationFile(installation); sID = readInstallationFile(installation); } catch (Exception e) { throw new RuntimeException(e); } } return sID; } private static String readInstallationFile(File installation) throws IOException { RandomAccessFile f = new RandomAccessFile(installation, "r"); byte[] bytes = new byte[(int) f.length()]; f.readFully(bytes); f.close(); return new String(bytes); } private static void writeInstallationFile(File installation) throws IOException { FileOutputStream out = new FileOutputStream(installation); String id = UUID.randomUUID().toString(); out.write(id.getBytes()); out.close(); } }
Add Below code in class file:
final TelephonyManager tm = (TelephonyManager) getBaseContext() .getSystemService(SplashActivity.TELEPHONY_SERVICE); final String tmDevice, tmSerial, androidId; tmDevice = "" + tm.getDeviceId(); Log.v("DeviceIMEI", "" + tmDevice); tmSerial = "" + tm.getSimSerialNumber(); Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial); androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); Log.v("androidId CDMA devices", "" + androidId); UUID deviceUuid = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode()); String deviceId = deviceUuid.toString(); Log.v("deviceIdUUID universally unique identifier", "" + deviceId); String deviceModelName = android.os.Build.MODEL; Log.v("Model Name", "" + deviceModelName); String deviceUSER = android.os.Build.USER; Log.v("Name USER", "" + deviceUSER); String devicePRODUCT = android.os.Build.PRODUCT; Log.v("PRODUCT", "" + devicePRODUCT); String deviceHARDWARE = android.os.Build.HARDWARE; Log.v("HARDWARE", "" + deviceHARDWARE); String deviceBRAND = android.os.Build.BRAND; Log.v("BRAND", "" + deviceBRAND); String myVersion = android.os.Build.VERSION.RELEASE; Log.v("VERSION.RELEASE", "" + myVersion); int sdkVersion = android.os.Build.VERSION.SDK_INT; Log.v("VERSION.SDK_INT", "" + sdkVersion);
Add in AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
The unique device ID of an Android OS device as String, using TelephonyManager
and ANDROID_ID
, is obtained by:
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); }
But I strongly recommend a method suggested by Google, see Identifying App Installations .
Adding to what others have said, there is a new Best practices for unique identifiers guide in the official Android documentation: http://developer.android.com/training/articles/user-data-ids.html
There are a lot of different approaches to work around those ANDROID_ID
issues (may be null
sometimes or devices of a specific model always return the same ID) with pros and cons:
- Implementing a custom ID generation algorithm (based on device properties that are supposed to be static and won't change -> who knows)
- Abusing other IDs like IMEI , serial number, Wi-Fi/Bluetooth-MAC address (they won't exist on all devices or additional permissions become necessary)
I myself prefer using an existing OpenUDID implementation (see https://github.com/ylechelle/OpenUDID ) for Android (see https://github.com/vieux/OpenUDID ). It is easy to integrate and makes use of the ANDROID_ID
with fallbacks for those issues mentioned above.
How about the IMEI . That is unique for Android or other mobile devices.
My two cents – NB this is for a device (err) unique ID – not the installation one as discussed in the Android developers's blog .
Of note that the solution provided by @emmby falls back in a per application ID as the SharedPreferences are not synchronized across processes (see here and here ). So I avoided this altogether.
Instead, I encapsulated the various strategies for getting a (device) ID in an enum – changing the order of the enum constants affects the priority of the various ways of getting the ID. The first non-null ID is returned or an exception is thrown (as per good Java practices of not giving null a meaning). So for instance I have the TELEPHONY one first – but a good default choice would be the ANDROID_ID beta:
import android.Manifest.permission; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.pm.PackageManager; import android.net.wifi.WifiManager; import android.provider.Settings.Secure; import android.telephony.TelephonyManager; import android.util.Log; // TODO : hash public final class DeviceIdentifier { private DeviceIdentifier() {} /** @see http://code.google.com/p/android/issues/detail?id=10603 */ private static final String ANDROID_ID_BUG_MSG = "The device suffers from " + "the Android ID bug - its ID is the emulator ID : " + IDs.BUGGY_ANDROID_ID; private static volatile String uuid; // volatile needed - see EJ item 71 // need lazy initialization to get a context /** * Returns a unique identifier for this device. The first (in the order the * enums constants as defined in the IDs enum) non null identifier is * returned or a DeviceIDException is thrown. A DeviceIDException is also * thrown if ignoreBuggyAndroidID is false and the device has the Android ID * bug * * @param ctx * an Android constant (to retrieve system services) * @param ignoreBuggyAndroidID * if false, on a device with the android ID bug, the buggy * android ID is not returned instead a DeviceIDException is * thrown * @return a *device* ID - null is never returned, instead a * DeviceIDException is thrown * @throws DeviceIDException * if none of the enum methods manages to return a device ID */ public static String getDeviceIdentifier(Context ctx, boolean ignoreBuggyAndroidID) throws DeviceIDException { String result = uuid; if (result == null) { synchronized (DeviceIdentifier.class) { result = uuid; if (result == null) { for (IDs id : IDs.values()) { try { result = uuid = id.getId(ctx); } catch (DeviceIDNotUniqueException e) { if (!ignoreBuggyAndroidID) throw new DeviceIDException(e); } if (result != null) return result; } throw new DeviceIDException(); } } } return result; } private static enum IDs { TELEPHONY_ID { @Override String getId(Context ctx) { // TODO : add a SIM based mechanism ? tm.getSimSerialNumber(); final TelephonyManager tm = (TelephonyManager) ctx .getSystemService(Context.TELEPHONY_SERVICE); if (tm == null) { w("Telephony Manager not available"); return null; } assertPermission(ctx, permission.READ_PHONE_STATE); return tm.getDeviceId(); } }, ANDROID_ID { @Override String getId(Context ctx) throws DeviceIDException { // no permission needed ! final String andoidId = Secure.getString( ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); if (BUGGY_ANDROID_ID.equals(andoidId)) { e(ANDROID_ID_BUG_MSG); throw new DeviceIDNotUniqueException(); } return andoidId; } }, WIFI_MAC { @Override String getId(Context ctx) { WifiManager wm = (WifiManager) ctx .getSystemService(Context.WIFI_SERVICE); if (wm == null) { w("Wifi Manager not available"); return null; } assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess // getMacAddress() has no java doc !!! return wm.getConnectionInfo().getMacAddress(); } }, BLUETOOTH_MAC { @Override String getId(Context ctx) { BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter(); if (ba == null) { w("Bluetooth Adapter not available"); return null; } assertPermission(ctx, permission.BLUETOOTH); return ba.getAddress(); } } // TODO PSEUDO_ID // http://www.pocketmagic.net/2011/02/android-unique-device-id/ ; static final String BUGGY_ANDROID_ID = "9774d56d682e549c"; private final static String TAG = IDs.class.getSimpleName(); abstract String getId(Context ctx) throws DeviceIDException; private static void w(String msg) { Log.w(TAG, msg); } private static void e(String msg) { Log.e(TAG, msg); } } private static void assertPermission(Context ctx, String perm) { final int checkPermission = ctx.getPackageManager().checkPermission( perm, ctx.getPackageName()); if (checkPermission != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Permission " + perm + " is required"); } } // ========================================================================= // Exceptions // ========================================================================= public static class DeviceIDException extends Exception { private static final long serialVersionUID = -8083699995384519417L; private static final String NO_ANDROID_ID = "Could not retrieve a " + "device ID"; public DeviceIDException(Throwable throwable) { super(NO_ANDROID_ID, throwable); } public DeviceIDException(String detailMessage) { super(detailMessage); } public DeviceIDException() { super(NO_ANDROID_ID); } } public static final class DeviceIDNotUniqueException extends DeviceIDException { private static final long serialVersionUID = -8940090896069484955L; public DeviceIDNotUniqueException() { super(ANDROID_ID_BUG_MSG); } } }
Here is how I am generating the unique id:
public static String getDeviceId(Context ctx) { TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE); String tmDevice = tm.getDeviceId(); String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID); String serial = null; if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL; if(tmDevice != null) return "01" + tmDevice; if(androidId != null) return "02" + androidId; if(serial != null) return "03" + serial; // other alternatives (ie Wi-Fi MAC, Bluetooth MAC, etc.) return null; }
Another way is to use /sys/class/android_usb/android0/iSerial
in an app without any permissions whatsoever.
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
To do this in Java one would just use a FileInputStream to open the iSerial file and read out the characters. Just be sure you wrap it in an exception handler, because not all devices have this file.
至less以下设备已知有这个文件世界可读:
- Galaxy Nexus
- Nexus S
- Motorola Xoom 3G
- 东芝AT300
- HTC One V
- 迷你MK802
- 三星Galaxy S II
You can also see my blog post Leaking Android hardware serial number to unprivileged apps where I discuss what other files are available for information.
For hardware recognition of a specific Android device you could check the MAC Addresses.
you can do it that way:
in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
now in your code:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces()); for (NetworkInterface interface : interfacesList) { // This will give you the interface MAC ADDRESS interface.getHardwareAddress(); }
In every Android device their is at least a "wlan0" Interface witch is the WI-FI chip. This code works even when WI-FI is not turned on.
PS Their are a bunch of other Interfaces you will get from the list containing MACS But this can change between phones.
I found a library on Github that seems to bundle a few of the approaches discussed in this thread: https://github.com/thomashaertel/android-device-identification
Haven't tried it, but maybe it helps.
Google Instance ID
Released at I/O 2015; on Android requires play services 7.5.
https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation
InstanceID iid = InstanceID.getInstance( context ); // Google docs are wrong - this requires context String id = iid.getId(); // blocking call
It seems that Google intends for this ID to be used to identify installations across Android, Chrome, and iOS.
It identifies an installation rather then a device, but then again, ANDROID_ID (which is the accepted answer) now no longer identifies devices either. With the ARC runtime a new ANDROID_ID is generated for every installation ( details here ), just like this new instance ID. Also, I think that identifying installations (not devices) is what most of us are actually looking for.
The advantages of instance ID
It appears to me that Google intends for it to be used for this purpose (identifying your installations), it is cross-platform, and can be used for a number of other purposes (see the links above).
If you use GCM, then you will eventually need to use this instance ID because you need it in order to get the GCM token (which replaces the old GCM registration ID).
The disadvantages/issues
In the current implementation (GPS 7.5) the instance ID is retrieved from a server when your app requests it. This means that the call above is a blocking call – in my unscientific testing it takes 1-3 seconds if the device is online, and 0.5 – 1.0 seconds if off-line (presumably this is how long it waits before giving up and generating a random ID). This was tested in North America on Nexus 5 with Android 5.1.1 and GPS 7.5.
If you use the ID for the purposes they intend – eg. app authentication, app identification, GCM – I think this 1-3 seconds could be a nuisance (depending on your app, of course).
I use the following code to get the IMEI
or use Secure. ANDROID_ID
as an alternative, when the device doesn't have phone capabilities:
String identifier = null; TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)); if (tm != null) identifier = tm.getDeviceId(); if (identifier == null || identifier .length() == 0) identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
More specifically, Settings.Secure.ANDROID_ID
. This is a 64-bit quantity that is generated and stored when the device first boots. It is reset when the device is wiped.
ANDROID_ID
seems a good choice for a unique device identifier. There are downsides: First, it is not 100% reliable on releases of Android prior to 2.2 (“Froyo”).
Also, there has been at least one widely-observed bug in a popular handset from a major manufacturer, where every instance has the same ANDROID_ID.
Android device mac id also a unique id, it won't change suppose if we format the device itself so using the following code to get mac id
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiInfo info = manager.getConnectionInfo(); String address = info.getMacAddress();
Also do not forget to add the appropriate permissions into your AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
There are 30+ answers here and some are same and some are unique. This answer is based on few of those answers. One of them being @Lenn Dolling's answer.
It combines 3 IDs and creates a 32-digit hex string. It has worked very well for me.
3 IDs are:
Pseudo-ID – It is generated based on physical device specifications
ANDROID_ID – Settings.Secure.ANDROID_ID
Bluetooth Address – Bluetooth adapter address
It will return something like this: 551F27C060712A72730B0A0F734064B1
Note: You can always add more IDs to the longId
string. For example, Serial #. wifi adapter address. IMEI. This way you are making it more unique per device.
@SuppressWarnings("deprecation") @SuppressLint("HardwareIds") public static String generateDeviceIdentifier(Context context) { String pseudoId = "35" + Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 + Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + Build.USER.length() % 10; String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); String btId = ""; if (bluetoothAdapter != null) { btId = bluetoothAdapter.getAddress(); } String longId = pseudoId + androidId + btId; try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(longId.getBytes(), 0, longId.length()); // get md5 bytes byte md5Bytes[] = messageDigest.digest(); // creating a hex string String identifier = ""; for (byte md5Byte : md5Bytes) { int b = (0xFF & md5Byte); // if it is a single digit, make sure it have 0 in front (proper padding) if (b <= 0xF) { identifier += "0"; } // add number to string identifier += Integer.toHexString(b); } // hex string to uppercase identifier = identifier.toUpperCase(); return identifier; } catch (Exception e) { Log.e("TAG", e.toString()); } return ""; }