Android Unique Device ID

There are several occasions when the unique identifier of a device is required. For instance you need it to generate a serial key and unlock a trial version, to generate encryption keys or to have the unique signature of a device.
On Android there are several ways to get such an ID.

1. The IMEI: only for Android devices with Phone use:

  1. TelephonyManager TelephonyMgr = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
  2. String szImei = TelephonyMgr.getDeviceId(); // Requires READ_PHONE_STATE

This requires adding a permission in AndroidManifest.xml, and users will be notified upon installing your software: android.permission.READ_PHONE_STATE. The IMEI is unique for your phone and it looks like this: 359881030314356 (unless you have a pre-production device with an invalid IMEI like 0000000000000).

2. 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 chances in real world applications are negligible. For this purpose you can use the Build class:

  1.  
  2. String m_szDevIDShort = "35" + //we make this look like a valid IMEI
  3. Build.BOARD.length()%10+ Build.BRAND.length()%10 +
  4. Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
  5. Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
  6. Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
  7. Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
  8. Build.TAGS.length()%10 + Build.TYPE.length()%10 +
  9. Build.USER.length()%10 ; //13 digits
  10.  

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 like 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 are required, making this approach very convenient.

3. The Android ID , considered unreliable because it can sometimes be null. The documentation states that it "can change upon factory reset". This string can also be altered on a rooted phone.

  1. String m_szAndroidID = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

Returns: 9774d56d682e549c . No special permissions required.

4. The WLAN MAC Address string, is another unique identifier that you can use as a device id. Before you read it, you will need to make sure that your project has the android.permission.ACCESS_WIFI_STATE permission or the WLAN MAC Address will come up as null.

  1. WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
  2. String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();

Returns: 00:11:22:33:44:55 (not a real address since this is a custom ROM , as you can see the MAC address can easily be faked). WLAN doesn't have to be on, to read this value.

5. The BT MAC Address string, available on Android devices with Bluetooth, can be read if your project has the android.permission.BLUETOOTH permission.

  1. BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth adapter
  2. m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  3. String m_szBTMAC = m_BluetoothAdapter.getAddress();

Returns: 43:25:78:50:93:38 . BT doesn't have to be on, to read it.

Combined Device ID
Above, you have here 5 ways of reading a device unique identifier. Some of them might fail and return null, or you won't be able to use them because of the special permissions or because the hardware is missing (phone, bluetooth, wlan).
Nevertheless on all platforms you will find at least one that works. So a very good idea is to mix these strings, and generate a unique result out of their sum. To mix the strings you can simply concatenate them, and the result can be used to compute a md5 hash:

  1.  
  2. String m_szLongID = m_szImei + m_szDevIDShort + m_szAndroidID+ m_szWLANMAC + m_szBTMAC;
  3. // compute md5
  4. MessageDigest m = null;
  5. try {
  6. m = MessageDigest.getInstance("MD5");
  7. e.printStackTrace();
  8. }
  9. m.update(m_szLongID.getBytes(),0,m_szLongID.length());
  10. // get md5 bytes
  11. byte p_md5Data[] = m.digest();
  12. // create a hex string
  13. String m_szUniqueID = new String();
  14. for (int i=0;i<p_md5Data.length;i++) {
  15. int b = (0xFF & p_md5Data[i]);
  16. // if it is a single digit, make sure it have 0 in front (proper padding)
  17. if (b <= 0xF) m_szUniqueID+="0";
  18. // add number to string
  19. m_szUniqueID+=Integer.toHexString(b);
  20. }
  21. // hex string to uppercase
  22. m_szUniqueID = m_szUniqueID.toUpperCase();
  23.  

The result has 32 hex digits and it looks like this:
9DDDF85AFF0A87974CE4541BD94D5F55
You can use it to generate any size unique string using hex digits or other sets of characters.

Here is the project code:
AndroidIDSample

Related Post

This article has 61 Comments

  1. Thank you Radu..it help me a lot and save my precious time 🙂 cheers..more power and keep on enlightening us..:)

  2. Thank you, exactly what I was looking for. Would have taken quite a while to figure out all possibilities.

  3. Thank you, the pseude unique id was very helpful because i needed the unique id in a class and not in an activity.

  4. the pseude unique id was very helpful because i needed the unique id in a class and not in an activity. thanks for sharing

  5. Hi Jeff,

    The OS version number would change, but how often do you expect this to happen? If you’re using this for a serial key, you can simply supply a new one to the user.

  6. Hi Radu,
    Helpful article, but I’d have one notice: WLAN MAC and BT MAC cannot be read (they’ll be null) if the WIFI is OFF or BT is OFF, on Samsung Galaxy S, for example. However, it works on Nexus.

  7. why all the apps of pocket magic are failed to run is there any changes in need to do in apps

  8. well i import this app into eclipse and when i star running in emulator ON STARTING OF ACTIVITY NO PAGE DISPLAYED AND A MSG “APPLICATION STOPPED UNEXPECTEDLY”

  9. Plz reply Radu its an big issue when ever i download any app from net it happens so is it about the android target version i m using

  10. @Sapna try setting the m_szUniqueID=(some 32 hex value) . Your program might have been crashing due to null value.

  11. Could you put some kind of a license on this code so that others can use it? Ideally Apache 2 or another recognized license?

  12. Kevin, thanks for asking. You can use the code for free, both in non-commercial and commercial applications. It would be nice if you would indicate the source (My name or this page).

  13. I just need to make a comment about approach #4:
    the MAC address DOES change on my phone. When I turn off the WIFI. switch off my phone, then switch it back on. The MAC address reads as null.

  14. Awesome Thanks If it is OK with you I would like to re-post this. Of-course with a link here and your name.

  15. Hi, great article, but can you PLEASE tell me how an ID returned by your algorithm can CHANGE over the lifetime of the device?? For example, we know that doing a factory reset of the device will create a new Secure.ANDROID_ID, and therefore change the output of the Hex digest. So again, how else can the algorithm’s Hex digest change over the lifetime of the device? I hope I’m making sense; thank you for your time.

  16. @HD. Thank you. Yes your question makes sense perfectly, and I should add the answer to the article: This ID will change over the lifetime of device, including when doing a factory reset.
    So take this into account when using it!

  17. Thank you for a great and very helpful article. But of that:

    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

    What can be changed after a factory reset?

  18. @IS, indeed this is the right way to go: using only those identifiers that remain unchanged. I haven’t tested all of them, but you can reduce them to a number you are certain will remain unchanged.

  19. Thanks a lot. Am using the last technique of yours, which is a combination of all. Hope it doesn’t require any special permission.

  20. I am looking also for unique id.
    What I see is that you have excluded the SERIAL (from api 9).
    It should give serial hardware number so it shouldn’t change with firmware upgrade.
    Any particular reason for excluding SERIAL?
    Thanks
    Nenad

  21. with the update to jelly bean 4.2. the unique-id of the device changes from the one of 4.1.. anyone knows why?

  22. Quite a few Build attributes could change as the result of an over-the-air update. Some, however, are less likely to change than others. I’d suggest identifying the ones that would change and using only the ones that would not.

    ANDROID_ID, if available, can be used without any of the other stuff, and will only change on a factory reset. The value given above as its returned value, 9774d56d682e549c, is returned only for a particular, limited set of devices that have implemented this value incorrectly. In other words, the presence of this value indicates a bug with ANDROID_ID on the device which should cause that value to not be used (unless you want to use it in combination with other values because it at least limits the ID to the set of devices having that buggy code). Otherwise, you can use it without the Build or other values, because why contaminate a good ANDROID_ID value with Build values that may change as the result of an OTA update?

    Regarding Build.SERIAL, why not use reflection to detect whether it is available, and use it if it is? This value is required to be present on devices that have no telephony, for Android releases starting at Gingerbread (API Level 9). It should be unique when it is available.

  23. Why not use Build.SERIAL, in try-catch, with fallback to ANDROID_ID, with fallback (in case of null, or buggy value) to anything pseudo unique like SecureRandom uuid?
    Another question, does anyone know which of all that was talked here is usable in BlackBerry android player?

  24. Thank you Radu, this is just great! Do you have anything like this for an iPhone och iPad as well?

  25. String m_szBTMAC = m_BluetoothAdapter.getAddress();
    Returns “” when Bluetooth is OFF
    AND some value (like A0:F4:50:E6:5D:9C) when Bluetooth is ON

    So it affects final m_szUniqueID.

    How to overcome this?

  26. Hi,Radu
    Usually, a mobile device can be characterized as: android id, mac, imei etc. But these features could modified by system upgrade or falsification. How can I distinguish system upgrade or falsification? For example:

    system upgrade:
    android id1 mac1 imei1 ==> device1
    android id2 mac1 imei1 ==> device1

    falsification
    android id1 mac1 imei1 ==> device1
    android id2 mac1 imei1 ==> device2(android id2 mac2 imei2)

    Can you give me some tips?

  27. I would like to know the Pseudo-Unique ID that Build changes to update any version of the device, because when you install an update Pseudo-Unique ID has changed me and has had to be a Build that has changed its length. And if so I would like to know which change with updates and what not, to control it and make it truly Unique.

Leave a Reply