Most visited

Recently visited

Best Practices for Unique Identifiers

While there are valid reasons why your application may need to identify a device rather than an instance of the application or an authenticated user on the device, for the vast majority of applications, the ultimate goal is to identify a particular installation of your app (not the actual physical device).

Fortunately, identifying an installation on Android is straightforward using an Instance ID or by creating your own GUID at install time. This document provides guidance for selecting appropriate identifiers for your application, based on your use-case.

For a general look at Android permissions, please see Permissions and User Data. For specific best practices for working with Android permissions, please see Best Practices for App Permissions.

Tenets of Working with Android Identifiers

We recommend that you follow these tenets when working with Android identifiers:

#1: Avoid using hardware identifiers. Hardware identifiers such as SSAID (Android ID) and IMEI can be avoided in most use-cases without limiting required functionality.

#2: Only use Advertising ID for user profiling or ads use-cases. When using an Advertising ID, always respect the Limit Ad Tracking flag, ensure the identifier cannot be connected to personally identifiable information (PII) and avoid bridging Advertising ID resets.

#3: Use an Instance ID or a privately stored GUID whenever possible for all other use-cases except payment fraud prevention and telephony. For the vast majority of non-ads use-cases, an instance ID or GUID should be sufficient.

#4: Use APIs that are appropriate to your use-case to minimize privacy risk. Use the DRM API API for high value content protection and the SafetyNet API for abuse prevention. The Safetynet API is the easiest way to determine whether a device is genuine without incurring privacy risk.

The remaining sections of this guide elaborate on these rules in the context of developing Android applications.

Identifiers in Android 6.0+

MAC addresses are globally unique, not user-resettable and survive factory reset. It is generally not recommended to use MAC address for any form of user identification. As a result, as of Android M, local device MAC addresses (for example, Wifi and Bluetooth) are not available via third party APIs. The WifiInfo.getMacAddress() method and the BluetoothAdapter.getDefaultAdapter().getAddress() method will both return 02:00:00:00:00:00..

Additionally, you must hold the following permissions to access MAC addresses of nearby external devices available via Bluetooth and Wifi scans:

Method/Property Permissions Required
WifiManager.getScanResults() ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
BluetoothDevice.ACTION_FOUND ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
BluetoothLeScanner.startScan(ScanCallback) ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION

Working with Advertising IDs

Advertising ID is a user-resettable identifier and is appropriate for Ads use-cases, but there are some key points to bear in mind when using it:

Always respect the user’s intention in resetting the advertising ID. Do not bridge user resets by using a more persistent device identifier or fingerprint to link subsequent Advertising IDs together without the user’s consent. The Google Play Developer Content Policy states:

...if reset, a new advertising identifier must not be connected to a previous advertising identifier or data derived from a previous advertising identifier without the explicit consent of the user.

Always respect the associated Personalized Ads flag. Advertising IDs are configurable in that users can limit the amount of tracking associated with the ID. Always use the AdvertisingIdClient.Info.isLimitAdTrackingEnabled() method to ensure you are not circumventing your users' wishes. The Google Play Developer Content Policy states:

...you must abide by a user’s ‘Opt out of interest-based advertising’ or 'Opt out of Ads Personalization' setting. If a user has enabled this setting, you may not use the advertising identifier for creating user profiles for advertising purposes or for targeting users with personalized advertising. Allowed activities include contextual advertising, frequency capping, conversion tracking, reporting and security and fraud detection.

Be aware of any privacy or security policies associated with SDKs you use that are related to Advertising ID use. For example, if you are using the Google Analytics SDK mTracker.enableAdvertisingIdCollection(true) method, make sure to review and adhere to all applicable Analytics SDK policies.

Also, be aware that the Google Play Developer Content Policy requires that the Advertising ID “must not be connected to personally-identifiable information or associated with any persistent device identifier (for example: SSAID, MAC address, IMEI, etc.,) without the explicit consent of the user.”

As an example, suppose you want to collect information to populate database tables with the following columns:

timestamp ad_id account_id clickid

TABLE-01

account_id name dob country

TABLE-02

In this example, the ad_id column could be joined to PII via the account_id column in both tables, which would be a violation of the Google Play Developer Content Policy, if you did not get explicit permission from your users.

Keep in mind that links between Advertiser ID and PII aren't always this explicit. It's possible to have “quasi-identifiers” that appear in both PII and Ad ID keyed tables, which also cause problems. For example, assume we change TABLE-01 and TABLE-02 as follows:

timestamp ad_id clickid dev_model

TABLE-01

timestamp demo account_id dev_model name

TABLE-02

In this case, with sufficiently rare click events, it's still possible to join between the Advertiser ID TABLE-01 and the PII contained in TABLE-2 using the timestamp of the event and the device model.

While it is often difficult to guarantee that no such quasi-identifiers exist in a dataset, the most obvious join risks can be prevented by generalizing unique data where possible. In the example, this would mean reducing the accuracy of the timestamp so that multiple devices with the same model appear for every timestamp.

Other solutions include:

  • Not designing tables that explicitly link PII with Advertising IDs. In the first example above this would mean not including the account_id column in TABLE-01.
  • Segregating and monitoring access control lists for users or roles that have access to both the Advertising ID keyed data and PII. If the ability to access both sources simultaneously (for example, to perform a join between two tables) is tightly controlled and audited, it reduces the risk of association between the Advertising ID and PII. Generally speaking, controlling access means:
    1. Keeping access control lists (ACLs) for Advertiser ID keyed data and PII disjoint to minimize the number of individuals or roles that are in both ACLs, and
    2. Implementing access logging and auditing to detect and manage any exceptions to this rule.

For more information on working responsibly with Advertising IDs, please see the Advertising ID help center article.

Working with Instance IDs and GUIDs

The most straightforward solution to identifying an application instance running on a device is to use an Instance ID, and this is the recommended solution in the majority of non-ads use-cases. Only the app instance for which it was provisioned can access this identifier, and it's (relatively) easily resettable because it only persists as long as the app is installed.

As a result, Instance IDs provide better privacy properties compared to non-resettable, device-scoped hardware IDs. They also come with a key-pair for message signing (and similar actions) and are available on Android, iOS and Chrome. Please see the What is Instance ID? help center document for more information.

In cases where an Instance ID isn't practical, custom globally unique IDs (GUIDs) can also be used to uniquely identify an app instance. The simplest way to do so is by generating your own GUID using the following code.

String uniqueID = UUID.randomUUID().toString();

Because the identifier is globally unique, it can be used to identify a specific app instance. To avoid concerns related to linking the identifier across applications, GUIDs should be stored in internal storage rather than external (shared) storage. Please see Storage Options guide for more information.

Understanding Identifier Characteristics

The Android Operating system offers a number of IDs with different behavior characteristics and which ID you should use depends on how those following characteristics work with your use-case. But these characteristics also come with privacy implications so it's important to understand how these characteristics play together.

Scope

Identifier scope explains which systems can access the identifier. Android identifier scope generally comes in three flavors:

  • Single app. the ID is internal to the app and not accessible to other apps.
  • Group of apps - the ID is accessible to a pre-defined group of related apps.
  • Device - the ID is accessible to all apps installed on the device.

The wider the scope granted to an identifier, the greater the risk of it being used for tracking purposes. Conversely, if an identifier can only be accessed by a single app instance, it can’t be used to track a device across transactions in different apps.

Resettability and persistence

Resettability and persistence define the lifespan of the identifier and explain how it can be reset. Common reset triggers are: in-app resets, resets via System Settings, resets on launch, and resets on installation. Android Identifiers can have varying lifespans, but the lifespan is usually related to how the ID is reset:

  • Session-only - a new ID is used every time the user restarts the app.
  • Install-reset - a new ID is used every time user uninstalls and reinstalls the app.
  • FDR-reset - a new ID is used every time the user factory-resets the device.
  • FDR-persistent - the ID survives factory reset.

Resettability gives users the ability to create a new ID that is disassociated from any existing profile information. This is important because the longer, and more reliably, an identifier persists (e.g. across factory resets etc.), the greater the risk that the user may be subjected to long-term tracking. If the identifier is reset upon app reinstall, this reduces the persistence and provides a means for the ID to be reset, even if there is no explicit user control to reset it from within the app or the System Settings.

Uniqueness

Uniqueness establishes the likelihood that identical identifiers exist within the associated scope. At the highest level, a globally unique identifier will never have a collision - even on other devices/apps. Otherwise, the level of uniqueness depends on the size of the identifier and the source of randomness used to create it. For example, the chance of a collision is much higher for random identifiers seeded with the calendar date of installation (e.g., 2015-01-05) than for identifiers seeded with the Unix timestamp of installation (e.g., 1445530977).

In general, user account identifiers can be considered unique (i.e., each device/account combo has a unique ID). On the other hand, the less unique an identifier is within a population (e.g. of devices), the greater the privacy protection because it's less useful for tracking an individual user.

Integrity protection and non-repudiability

An identifier that is difficult to spoof or replay can be used to prove that the associated device or account has certain properties (e.g. it’s not a virtual device used by a spammer). Difficult to spoof identifiers also provide non-repudiability. If the device signs a message with a secret key, it is difficult to claim someone else’s device sent the message. Non-repudiability could be something a user wants (e.g. authenticating a payment) or it could be an undesirable property (e.g. sending a message they regret).

Common Use Cases and the Identifier to Use

This section provides alternatives to using hardware IDs such as IMEI or SSAID for the majority of use-cases. Relying on hardware IDs is discouraged because the user cannot reset them and generally has limited control over their collection.

Tracking signed-out user preferences

In this case, you are saving per-device state on the server side.

We Recommend: Instance ID or a GUID.

Why this Recommendation?

Persisting information through reinstalls is not recommended because users may want to reset their preferences by reinstalling the app.

Tracking signed-out user behavior

In this case, you have created a profile of a user based on their behavior across different apps/sessions on the same device.

We Recommend: Advertising ID.

Why this Recommendation?

Use of the Advertising ID is mandatory for Advertising use-cases per the Google Play Developer Content Policy because the user can reset it.

Generating signed-out/anonymous user analytics

In this case, you are measuring usage statistics and analytics for signed-out or anonymous users.

We Recommend: Instance ID; if an Instance ID is insufficient, you can also use a GUID.

Why this Recommendation?

An Instance ID or a GUID is scoped to the app that creates it, which prevents it from being used to track users across apps. It is also easily reset by clearing app data or reinstalling the app. Creating Instance IDs and GUIDs is straightforward:

  • Creating an Instance ID: String iid = InstanceID.getInstance(context).getId()
  • Creating a GUID: String uniqueID = UUID.randomUUID().toString

Be aware that if you have told the user that the data you are collecting is anonymous, you should ensure you are not connecting the identifier to PII or other identifiers that may be linked to PII.

You can also use Google Analytics for Mobile Apps, which offers a solution for per-app analytics.

Tracking signed-out user conversion

In this case, you are tracking conversions to detect if your marketing strategy was successful.

We Recommend: Advertising ID.

Why this Recommendation?

This is an ads-related use-case which may require an ID that is available across different apps so using an Advertising ID is the most appropriate solution.

Handling multiple installations

In this case, you need to identify the correct instance of the app when it's installed on multiple devices for the same user.

We Recommend: Instance ID or GUID.

Why this Recommendation?

Instance ID is designed explicitly for this purpose; its scope is limited to the app so that it cannot be used to track users across different apps and it is reset upon app reinstall. In the rare cases where an Instance ID is insufficient, you can also use a GUID.

Anti-fraud: Enforcing free content limits / detecting Sybil attacks

In this case, you want to limit the number of free content (e.g. articles) a user can see on a device.

We Recommend: Instance ID or GUID.

Why this Recommendation?

Using a GUID or Instance ID forces the user to reinstall the app in order to overcome the content limits, which is a sufficient burden to deter most people. If this is not sufficient protection, Android provides a DRM API which can be used to limit access to content.

Managing telephony and carrier functionality

In this case, your app is interacting with the device's phone and texting functionality.

We Recommend: IMEI, IMSI, and Line1 (requires PHONE permission group in Android 6.0 (API level 23) and higher).

Why this Recommendation?

Leveraging hardware identifiers is acceptable if it is required for telephony/carrier related functionality; for example, switching between cellular carriers/SIM slots or delivering SMS messages over IP (for Line1) - SIM-based user accounts. But it's important to note that in Android 6.0+ these identifiers can only be used via a runtime permission and that users may toggle off this permission so your app should handle these exceptions gracefully.

Abuse detection: Identifying bots and DDoS attacks

In this case, you are trying to detect multiple fake devices attacking your backend services.

We Recommend: The Safetynet API.

Why this Recommendation?

An identifier in isolation does little to indicate that a device is genuine. You can verify that a request comes from a genuine Android device (as opposed to an emulator or other code spoofing another device) using the Safetynet API's SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) method to verify the integrity of a device making a request. For more detailed information, please see Safetynet's API documentation.

Abuse detection: Detecting high value stolen credentials

In this case, you are trying to detect if a single device is being used multiple times with high-value, stolen credentials (e.g. to make fraudulent payments).

We Recommend: IMEI/IMSI (requires PHONE permission group in Android 6.0 (API level 23) and higher.)

Why this Recommendation?

With stolen credentials, devices can be used to monetize multiple high value stolen credentials (such as tokenized credit cards). In these scenarios, software IDs can be reset to avoid detection, so hardware identifiers may be used.

Hooray!