Android Unique Device ID: History and Updates
Getting a fixed ID is not straightforward anymore since Google wants to protect its users from unwanted tracking and privacy issues. The story of identifying users of an Android application that all Android developers have faced once during their programming days and Android Privacy updates during years from the beginning of the Android era.
Identifying user’s device even after re-install the app or even device reset factory, is always a new challenge for developers/businesses. Because it’s against the user’s privacy. In contrast, businesses want to track the fraud by users and they want to measure the natural behavior of users.
For instance, they want to track installs and they want to pay for it to Marketing agencies and they don’t want to pay for fake installs. Or even referral plans which led to fraud by some users. Marketing tools are all relied on this ID issue and all these changes in the behavior of Android OS are about to stop services from tracking user’s attributes and user’s devices.
Also, all the hardware-based identifiers (SSAID, IMEI, MAC, …) are unreliable for non-google devices (Everything except Pixels and Nexuses), which are more than 50% of active devices worldwide. Also, you should note that hardware identifiers, can easily be modified on rooted or emulated devices, so they’re not reliable indicators of fraud.
Now we know the importance of this issue and besides that, we need to track the users to identify them even after they have re-installed an app. By the way, there are a lot of use cases that led us to identify users based on a unique key that is generated based on user attributes or the device he/she is using (software and hardware attributes). I cover some use cases that led us to find a solution for this unique key.
- Tracking app installs for Marketing Department
- Identify signed-out users in our Server’s Database without a Signup
- Identify same users who are using different devices
- Identify multiple users who are using same device
- Detecting Fraud in one-time use cases
List of all Identifiers in Android
Let’s look at all identifiers that are unique and discuss the uniqueness of them and see whether we can use them based on the Android OS version or User’s Permission or other conditions or not. Here is the list of identifiers suggested by the Android Official document and also by developers in the community:
- Secure ANDROID_ID (SSAID)
- TelephonyManager IMEI
- Device SerialNumber
- Wi-Fi MAC address
- Google Play advertising ID
- Firebase Installation Id
Secure ANDROID_ID (SSAID)
As Google document says, it is a 64-bit number (expressed as a hexadecimal string) and was randomly generated when the user sets up the device for the first time and remains constant for the lifetime of the user’s device. Also, the value would only change if a factory reset is performed by the user. On devices that have multiple users value of this id is unique to each user. So it’s only scoped by user and device.
However in Android 8(API level 26), something changed about this Id. The value of ANDROID_ID now is scoped by signing-key, user, and device. The value still will change if a factory reset is performed on the device plus and also it will change if an APK signing key changes.
- ANDROID_ID doesn’t need any permission in all Android OS versions.
- Note that in all Android Versions, the value of ANDROID_ID does not change on package uninstall or reinstall.
- If you change the sign key of your app, then you would get a new ANDROID_ID in Android 8 and above.
- If your user update the OS from Android <8 to an Android 8 or above, your user will have a new ANDROID_ID.
- Based on a blog post in Google, ANDROID_ID is only reliable for devices of manufacturers shipping with Google Play services and Advertising ID and Other device manufacturers may provide an alternative resettable or even a constant id for all device in old Android Versions.
- ANDROID_ID can be easily changed on a rooted phone.
Based on the facts mentioned above, there is no clear evidence that this approach would give you a unique id in all devices around the world. Also, it seems that it’s not aligned with Google’s claim about user’s privacy. Honestly, if you work at a big company with millions of users, you will realize that this Id is not guaranteed to be unique. But I think it’s still a good way of getting an id if you combine it with other ids and having some fallback plans in case of devices that will return you a null or non-unique key. Here is the code for getting this id:
TelephonyManager IMEI
In the old days of Android development, there was a reliable way to get a unique id for users. This approaches only included the devices that had a SIM inside and it wouldn’t work for Tablets that had no SIM but it covers many Android users. Its implementation was different in the Android version. In a device with an Android OS version lower than Android 8 (API level 26), you should use getDeviceId() and in the newer versions, you should use getImei() from TelephonyManager API. Here was the implementation:
This approach needed READ_PHONE_STATE permission which you should grant from the user. But unfortunately, this approach won’t work anymore since Google started to protect its user from tracking is in newer versions of Android. Starting with Android 10 (API level 29), persistent device identifiers are guarded behind additional restrictions. It means that if you want to have this Id, there are some strict requirements that you should have to access it including having READ_PRIVILEGED_PHONE_STATE permission is privileged permission that can only be granted to system app and apps which were preloaded on the device. There are other alternative requirements that are listed here.
So basically if you go for this approach of getting id, here is two outcome that you might face in new Android versions:
If the calling app’s target SDK is API level 28 or lower and the app has the READ_PHONE_STATE permission then null is returned.
If the calling app’s target SDK is API level 28 or lower and the app does not have the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or higher, then a SecurityException is thrown.
Device SerialNumber
Getting device serial number is the exact same as getting device IMEI which was discussed in the previous section So I skip it here. all the new requirements for getting IMEI are also the same for device serial number too since it’s in TelephonyManager API getSimSerialNumber().
Wi-Fi MAC address
Don’t work with MAC addresses. Google says you shouldn’t use non-resettable identifiers at all. MAC addresses were used to be globally unique in Android devices and they were supposed to survive from factory resets. For protecting users, Google changed MAC addresses access on Android 6 (API level 23) and higher, So the access to MAC addresses is restricted to system apps and third-party apps can’t access them anymore. So this famous block of code won’t work for new Android devices anymore.
Referring to Android 6 release note:
To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00
Even if you can retrieve MAC address you should note that devices running Android 10 (API level 29) and higher report randomized MAC addresses to all apps that aren’t device owner apps. it means that Android devices now are working with randomized MAC addresses. MAC address randomization is the increasing trend of device operating systems using a random, anonymous device identifier instead of the real address when connecting to wireless networks. Google released Android 10 (API level 29) which made MAC randomization the default behavior when both scanning for wireless networks and connecting to them. This was a major change that was intended to prevent tracking across networks. It means that if you connect to a different Wifi, you will have a new randomized MAC address. MAC address availability also changed in Android 11.
Google Play Advertising ID
If you want to track a user (in case of advertising or detect if your marketing strategy is successful) based on their behavior across different apps or sessions on the same device, here is what you are looking for. It’s part of the Google Play service and it means that it only works with devices with Play Service (As you probably know, right now in 2021, we have some devices including Huawei devices that don’t have Play service installed). Let’s see what it is.
based on Google’s claim, the Advertising ID is a user-resettable identifier and is appropriate for ad use cases. Advertising IDs are also configurable in that users can limit the amount of tracking associated with the ID. The Advertising ID library is available on devices running Android 4.0 (API level 14) and higher. Also note that, as part of the Google Play services update in late 2021, Any attempts to access the identifier will receive a string of zeros instead of the identifier when a user opts out of personalization using advertising ID in Android Settings.
When the returned value of isLimitAdTrackingEnabled() is
true
, the returned value ofgetId()
will always be00000000-0000-0000-0000-000000000000
starting with Android 12.Apps with target API level 31 (Android 12) or later must declare the normal permission
com.google.android.gms.AD_ID
as below in the AndroidManifest.xml in order to use this API.This permission will be granted when the app is installed.
If this permission is not declared, the returned value will be
00000000-0000-0000-0000-000000000000
starting early 2022. Until then, to help developers, a warning line is logged if the permission is missing.
So, Because the user can reset their advertising ID after your app starts, you should call AdvertisingIdClient.getAdvertisingIdInfo(context) each time your app needs to check the value of the ID. (Note: This method cannot be called in the main thread and anIllegalStateException
will be thrown if this is called on the main thread). You need to add the play-services-ads-lite library to your project first:
dependencies {
....
implementation "com.google.android.gms:play-services-ads-lite:${playServiceVersion}"
}
Note: I used RxJava since you should fetch this id in another thread instead of main thread.
The advertising Id is a 128-bit format like below:
38400000-8cf0-11bd-b23e-10b96e40000d
Note that on an Android devices that support multiple users, you will get different advertising IDs on a same device.
Firebase Installation Id
Firebase Installation Id is an app instance identifier and its a replacement for Firebase Instance ID which is now deprecated. This id provides a unique identifier for each app install and a mechanism to authenticate and authorize actions (example: sending FCM messages). FID is stable except when:
- App deletes Instance ID programmatically
- User uninstalls the app
- User clears app data or cache
- FID deletion is triggered in the backend due to app inactivity (currently the threshold for this is 270 days of inactivity).
If you have a requirement to identify particular installations of your app, you can do so by retrieving the Firebase installation ID. You should implement firebase-installation in your project:
implementation 'com.google.firebase:firebase-installations:17.0.0'
Take a look at the implementation of Firebase Installation Id and getting id here in official Google’s Github:
In cases where an FID isn’t your option or practical (since it’s only for installation and will be cleared after clear cache), you can also use custom UID to uniquely identify an app instance. The simplest way to do so is by generating your own UID using the following code:
var uniqueID = UUID.randomUUID().toString()
And them. save this key in local storage like SharedPreferences. FID, instead claims that it covers all GDPR issues and is more secure.
Conclusion
Tracking Android devices are not an easy thing, and besides the technical challenges, it has a lot of privacy concerns that you should consider. It’s better to find a solution based on the Scope, Resettability, and Uniqueness of the ID generation for your use cases. Also, you can go for a pseudo Id solution that helps you make a better id for your application by combining all these approaches.
I know that famous Analytics SDK is tracking their users’ devices by storing all of the characteristics of a device that they can retrieve without any permission or sth like that. Also, their approach is to sending these characteristics to their server and then they will generate the Id in Server and return a value in response. So, they can update their instruction of making an Id even if Android changed its behavior or making some of these characteristics null again in the future. So it would be a good option to have this solution of making an Id in your backend based on all characteristics you can find out of your users. Also there are some good but not-free Marketing SDKs around the world like Adjust, AppMetrica and others that they claim they can generate a unique device id.