android - how - mobile device unique identifier

Is there a unique Android device ID? (20)

Last Updated: 6/2/15

After reading every Stack Overflow post about creating a unique ID, the Google developer blog and Android documentation, I feel as if the 'Pseudo ID' is the best possible option.

Main Issue: Hardware vs Software


  • Users can change their hardware, Android tablet or phone, so unique IDs based on hardware are not good ideas for TRACKING USERS
  • For TRACKING HARDWARE, this is a great idea


  • Users can wipe/change their ROM if they are rooted
  • You can track users across platforms (iOS, Android, Windows and Web)
  • The best want to TRACK AN INDIVIDUAL USER with their consent is to simply have them login (make this seamless using OAuth)

Overall breakdown with Android

- Guarantee uniqueness (include rooted devices) for API >= 9/10 (99.5% of Android devices)

- No extra permissions

Psuedo code:

if API >= 9/10: (99.5% of devices)

return unique ID containing serial id (rooted devices may be different)


return unique ID of build information (may overlap data - API < 9)

Thanks to @stansult for posting all of our options (in this Stack Overflow question).

List of options - reasons why/ why not to use them:

  • User Email - Software

    • User could change email - HIGHLY unlikely
    • API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" /> or
    • API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> (How to get the Android device's primary e-mail address)
  • User Phone Number - Software

    • Users could change phone numbers - HIGHLY unlikely
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - Hardware (only phones, needs android.permission.READ_PHONE_STATE)

    • Most users hate the fact that it says "Phone Calls" in the permission. Some users give bad ratings, because they believe you are simply stealing their personal information, when all you really want to do is track device installs. It is obvious that you are collecting data.
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • Android ID - Hardware (can be null, can change upon factory reset, can be altered on a rooted device)

    • Since it can be 'null', we can check for 'null' and change its value, but this means it will no longer be unique.
    • If you have a user with a factory reset device, the value may have changed or altered on the rooted device so there may be duplicates entries if you are tracking user installs.
  • WLAN MAC Address - Hardware (needs android.permission.ACCESS_WIFI_STATE)

    • This could be the second best option, but you are still collecting and storing a unique identifier that comes directly from a user. This is obvious that you are collecting data.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Bluetooth MAC Address - Hardware (devices with Bluetooth, needs android.permission.BLUETOOTH)

    • Most applications on the market do not use Bluetooth, and so if your application doesn't use Bluetooth and you are including this, the user could become suspicious.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • Pseudo-Unique ID - Software (for all Android devices)

    • Very possible, may contain collisions - See my method posted below!
    • This allows you to have an 'almost unique' ID from the user without taking anything that is private. You can create you own anonymous ID from device information.

I know there isn't any 'perfect' way of getting a unique ID without using permissions; however, sometimes we only really need to track the device installation. When it comes to creating a unique ID, we can create a 'pseudo unique id' based solely off of information that the Android API gives us without using extra permissions. This way, we can show the user respect and try to offer a good user experience as well.

With a pseudo-unique id, you really only run into the fact that there may be duplicates based on the fact that there are similar devices. You can tweak the combined method to make it more unique; however, some developers need to track device installs and this will do the trick or performance based on similar devices.

API >= 9:

If their Android device is API 9 or over, this is guaranteed to be unique because of the 'Build.SERIAL' field.

REMEMBER, you are technically only missing out on around 0.5% of users who have API < 9. So you can focus on the rest: This is 99.5% of the users!

API < 9:

If the user's Android device is lower than API 9; hopefully, they have not done a factory reset and their 'Secure.ANDROID_ID' will be preserved or not 'null'. (see

If all else fails:

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.


  • Removed 'Android.SECURE_ID' because of factory resets could cause the value to change
  • Edited the code to change on API
  • Changed the Pseudo

Please take a look at the method below:

 * 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!
    // 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!
    // Only devices with API >= 9 have android.os.Build.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!
    // 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();

New (for apps with ads AND Google Play Services):

From the Google Play Developer's console:

Beginning August 1st, 2014, the Google Play Developer Program Policy requires all new app uploads and updates to use the advertising ID in lieu of any other persistent identifiers for any advertising purposes. Learn more



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



// 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 (e.g.,
    // 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();



It is intended that the advertising ID completely replace existing usage of other identifiers for ads purposes (such as use of ANDROID_ID in Settings.Secure) when Google Play Services is available. Cases where Google Play Services is unavailable are indicated by a GooglePlayServicesNotAvailableException being thrown by getAdvertisingIdInfo().

Warning, users can reset:

I have tried to reference every link that I took information from. If you are missing and need to be included, please comment!

Google Player Services InstanceID

Do Android devices have a unique ID, and if so, what is a simple way to access it using Java?

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(

But I strongly recommend a method suggested by Google, see Identifying App Installations.

Google Instance ID

Released at I/O 2015; on Android requires play services 7.5.

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).

TelephonyManger.getDeviceId() Returns the unique device ID, for example, the IMEI for GSM and the MEID or ESN for CDMA phones.

final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);            
String myAndroidDeviceId = mTelephony.getDeviceId(); 

But i recommend to use:

Settings.Secure.ANDROID_ID that returns the Android ID as an unique 64-bit hex string.

    String   myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

Sometimes TelephonyManger.getDeviceId() will return null, so to assure an unique id you will use this method:

public String getUniqueID(){    
    String myAndroidDeviceId = "";
    TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (mTelephony.getDeviceId() != null){
        myAndroidDeviceId = mTelephony.getDeviceId(); 
         myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
    return myAndroidDeviceId;

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.

Add Below code in class file:

final TelephonyManager tm = (TelephonyManager) getBaseContext()
    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(),
    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" />

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"/>

Another way is to use /sys/class/android_usb/android0/iSerial in an app without any permissions whatsoever.

[email protected]:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
[email protected]:~$ adb shell cat /sys/class/android_usb/android0/iSerial

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.

At least the following devices are known to have this file world-readable:

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3G
  • Toshiba AT300
  • HTC One V
  • Mini MK802
  • Samsung 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.

At Google I/O Reto Meier released a robust answer to how to approach this which should meet most developers needs to track users across installations. Anthony Nolan shows the direction in his answer, but I thought I'd write out the full approach so that others can easily see how to do it (it took me a while to figure out the details).

This approach will give you an anonymous, secure user ID which will be persistent for the user across different devices (based on the primary Google account) and across installs. The basic approach is to generate a random user ID and to store this in the apps' shared preferences. You then use Google's backup agent to store the shared preferences linked to the Google account in the cloud.

Let's go through the full approach. First, we need to create a backup for our SharedPreferences using the Android Backup Service. Start by registering your app via

Google will give you a backup service key which you need to add to the manifest. You also need to tell the application to use the BackupAgent as follows:

<application android:label="MyApplication"
    <meta-data android:name=""
        android:value="your_backup_service_key" />

Then you need to create the backup agent and tell it to use the helper agent for sharedpreferences:

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
    public void onCreate() {
        SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this,          PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);

To complete the backup you need to create an instance of BackupManager in your main Activity:

BackupManager backupManager = new BackupManager(context);

Finally create a user ID, if it doesn't already exist, and store it in the 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);

            //backup the changes
            BackupManager mBackupManager = new BackupManager(context);

    return uniqueID;

This User_ID will now be persistent across installations, even if the user moves device.

For more information on this approach see Reto's talk.

And for full details of how to implement the backup agent see Data Backup. I particularly recommend the section at the bottom on testing as the backup does not happen instantaneously and so to test you have to force the backup.

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())
                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()];
        return new String(bytes);

    private static void writeInstallationFile(File installation) throws IOException {
        FileOutputStream out = new FileOutputStream(installation);
        String id = UUID.randomUUID().toString();

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 (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)

    return null;

How about the IMEI. That is unique for Android or other mobile devices.

I think this is sure fire way of building a skeleton for a unique ID... check it out.

Pseudo-Unique ID, that works on all Android devices Some devices don't have a phone (eg. Tablets) or for some reason, you don't want to include the READ_PHONE_STATE permission. You can still read details like ROM Version, Manufacturer name, CPU type, and other hardware details, that will be well suited if you want to use the ID for a serial key check, or other general purposes. The ID computed in this way won't be unique: it is possible to find two devices with the same ID (based on the same hardware and ROM image) but the changes in real-world applications are negligible. For this purpose you can use the Build class:

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

Most of the Build members are strings, what we're doing here is to take their length and transform it via modulo in a digit. We have 13 such digits and we are adding two more in front (35) to have the same size ID as the IMEI (15 digits). There are other possibilities here are well, just have a look at these strings. Returns something like 355715565309247. No special permission is required, making this approach very convenient.

(Extra info: The technique given above was copied from an article on Pocket Magic.)

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.

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.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;

// TODO : hash
public final class DeviceIdentifier {

    private DeviceIdentifier() {}

    /** @see */
    private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
        + "the Android ID bug - its ID is the emulator 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 {

            String getId(Context ctx) {
                // TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
                final TelephonyManager tm = (TelephonyManager) ctx
                if (tm == null) {
                    w("Telephony Manager not available");
                    return null;
                assertPermission(ctx, permission.READ_PHONE_STATE);
                return tm.getDeviceId();
        ANDROID_ID {

            String getId(Context ctx) throws DeviceIDException {
                // no permission needed !
                final String andoidId = Secure.getString(
                if (BUGGY_ANDROID_ID.equals(andoidId)) {
                    throw new DeviceIDNotUniqueException();
                return andoidId;
        WIFI_MAC {

            String getId(Context ctx) {
                WifiManager wm = (WifiManager) ctx
                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();

            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

        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) {

        public DeviceIDException() {

    public static final class DeviceIDNotUniqueException extends
            DeviceIDException {

        private static final long serialVersionUID = -8940090896069484955L;

        public DeviceIDNotUniqueException() {

The following code returns the device serial number using a hidden Android API. But, this code don't works on Samsung Galaxy Tab because "ro.serialno" isn't set on this device.

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) {


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 for Android (see It is easy to integrate and makes use of the ANDROID_ID with fallbacks for those issues mentioned above.

There’s rather useful info here.

It covers five different ID types:

  1. IMEI (only for Android devices with Phone use; needs android.permission.READ_PHONE_STATE)
  2. Pseudo-Unique ID (for all Android devices)
  3. Android ID (can be null, can change upon factory reset, can be altered on rooted phone)
  4. WLAN MAC Address string (needs android.permission.ACCESS_WIFI_STATE)
  5. BT MAC Address string (devices with Bluetooth, needs android.permission.BLUETOOTH)