Introduction
Many apps need to recognise a device: to tie a trial or a licence to it, to limit the number of devices per account, to register it for push messages, or to find it again after the app was reinstalled. KlugDeviceId reads the device identifier that the operating system provides for this purpose and returns it as a string.
- Android: the system's ANDROID_ID. It stays the same after the app is uninstalled and installed again.
- iOS: the system's identifier for vendor (identifierForVendor). It is shared by all your apps on the device and is reset when all of them have been removed.
No permission is needed and no prompt is shown. The getters never throw: where no identifier is available they return an empty string.
| Extension ID | com.klug.deviceid |
| Version | 1.1.0 |
| Platforms | Android, iOS |
| ActionScript package | com.klug.deviceid, one class: DeviceId |
| Delivery | The extension file com.klug.deviceid.ane, the ActionScript library KlugDeviceId.swc and this documentation |
Requirements
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 17 or later, iPhone and iPad |
| Android | Android 6.0 (API 23) or later. The identifier survives a reinstall from Android 8.0 on (see Privacy). |
| Permissions | None |
| Other platforms | macOS, Windows and the AIR simulator: the extension can stay in the build; isSupported returns false and every getter returns an empty string. |
Add the extension
1 · Copy the files
Copy com.klug.deviceid.ane into your project's extension folder, for example extensions/. In your IDE add the .ane as a native extension of the project. When you build on the command line, compile against KlugDeviceId.swc as an external library and give adt the extension folder:
amxmlc -external-library-path+=lib/KlugDeviceId.swc -output Main.swf src/Main.as adt -package -target ipa-app-store ... -extdir extensions adt -package -target aab ... -extdir extensions
2 · Declare it in the application descriptor
<extensions>
<extensionID>com.klug.deviceid</extensionID>
</extensions>
3 · Platform settings
KlugDeviceId needs no further entries in the application descriptor:
- iOS: no InfoAdditions keys and no entitlements. The identifier for vendor needs no permission and no App Tracking Transparency prompt.
- Android: no permissions, features, activities or services in <manifestAdditions>. Reading ANDROID_ID needs no permission.
- macOS and desktop: nothing to add.
What you do need is a privacy declaration in the stores if the identifier leaves the device. See Privacy and store policies.
Getting started
DeviceId is a singleton: always use DeviceId.getInstance(). The getters are synchronous and return the value straight away.
import com.klug.deviceid.DeviceId; var dev:DeviceId = DeviceId.getInstance(); if ( dev.isSupported ) { var id:String = dev.uniqueId; // ANDROID_ID on Android, identifierForVendor on iOS if ( id != "" ) trace( "device id: " + id ); }
Each read asks the system again. Read the value once at start, keep it in a variable and send it to your server where you need it.
Guides
Read the identifier on both platforms
uniqueId is the cross-platform property: it returns the platform's device identifier. androidId returns the Android identifier only and is always empty on iOS. Use uniqueId unless you need to know that you have the reinstall-proof Android value.
| Property | Android · iOS · other |
| isSupported | true · true · false |
| uniqueId | ANDROID_ID · identifier for vendor · "" |
| androidId | ANDROID_ID · "" · "" |
import com.klug.deviceid.DeviceId; var dev:DeviceId = DeviceId.getInstance(); var deviceId:String = dev.uniqueId; // Android or iOS identifier, "" elsewhere var reinstallProof:Boolean = dev.androidId != ""; // true only on Android
Bind a licence, an account or push messages to the device
Send the identifier with the login or registration request. Your server stores it with the account, for example to limit the number of devices or to know which device receives push messages.
import com.klug.deviceid.DeviceId; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.URLRequestMethod; import flash.net.URLVariables; var deviceId:String = DeviceId.getInstance().uniqueId; // read once at start function login( user:String, password:String ):void { var vars:URLVariables = new URLVariables(); vars.user = user; vars.password = password; vars.device = deviceId; var req:URLRequest = new URLRequest( "https://your.server/login" ); req.method = URLRequestMethod.POST; // never put the identifier into the URL req.data = vars; var loader:URLLoader = new URLLoader(); loader.addEventListener( Event.COMPLETE, onLoginResult ); // your app loader.load( req ); }
Treat the identifier like other account data: send it over HTTPS in the request body, not in the URL, and do not show it in the user interface or in logs.
Keep a trial across reinstalls on Android
On Android 8.0 and later ANDROID_ID stays the same when your app is uninstalled and installed again, as long as it is signed with the same key. That makes it a good anchor for a free trial: your server remembers which devices have already used it.
var anchor:String = DeviceId.getInstance().androidId; if ( anchor != "" ) checkTrial( anchor ); // your server: has this device used the trial before? else checkTrialOtherwise(); // iOS and other platforms, see the next guide
iOS: what survives a reinstall
On iOS the identifier for vendor is the same for all apps of your developer account on the device. It changes when the user removes all of these apps and then installs one again; with a single app that means every reinstall. It is therefore a good device identity while the app is installed, but not a reinstall-proof anchor.
For a value that survives a reinstall on iOS and macOS, store your own identifier in the keychain with KlugKeychain, and use KlugDeviceId for the device identity.
function trialAnchor():String { var dev:DeviceId = DeviceId.getInstance(); if ( dev.androidId != "" ) return dev.androidId; // Android: survives reinstall return readOrCreateKeychainAnchor(); // your app, for example with KlugKeychain }
Empty values and cross-platform code
Every getter returns a string and never throws. Expect an empty string in these cases:
- The platform is not supported (desktop, simulator): isSupported is false.
- androidId on iOS.
- iOS: the system can briefly have no identifier, for example after a restart before the device was unlocked for the first time. Ask again a little later.
- Android: the system did not return a value (rare).
import com.klug.deviceid.DeviceId; import flash.utils.setTimeout; var attempts:int = 0; function readDeviceId():void { var dev:DeviceId = DeviceId.getInstance(); if ( !dev.isSupported ) { useOwnDesktopId(); // your app: desktop, simulator return; } var id:String = dev.uniqueId; if ( id != "" ) onDeviceId( id ); // your app else if ( ++attempts < 5 ) setTimeout( readDeviceId, 1000 ); // ask again a little later else continueWithoutDeviceId(); // your app }
Lifecycle
The identifier does not change while your app runs, in the background or after returning. There is no event to listen for. Read it once at start; if it was empty, read it again when the app returns to the foreground.
import flash.desktop.NativeApplication; import flash.events.Event; NativeApplication.nativeApplication.addEventListener( Event.ACTIVATE, function( e:Event ):void { if ( deviceId == "" ) deviceId = DeviceId.getInstance().uniqueId; });
Privacy and store policies
What the value is
| Android | ANDROID_ID: a random 64-bit number as a hexadecimal string, created by the system. From Android 8.0 it is different for every combination of app signing key, user and device, so apps of other developers see a different value. |
| iOS | The identifier for vendor: a random UUID string such as E621E1F8-C36C-495A-93FC-0C247A3E6E5F, the same for all apps of one developer on one device. |
| What it is not | Not the advertising identifier, not a hardware serial number or IMEI, and not linked to the user's name or account by the system. |
When it changes
| Android 8.0 and later | Stays the same across uninstall and reinstall of your app. Changes after a factory reset, when the app is signed with a different key (debug, release and store-signed builds can differ), and for a different user or work profile on the same device. |
| Android 6 and 7 | One value shared by all apps of the user on the device. Changes after a factory reset. |
| iOS | Changes when all apps of your developer account are removed from the device and one is installed again, and on a new or reset device. Builds installed outside the App Store, for example during development, can have a different value than the store build. |
Store declarations, in general terms
A device identifier counts as personal data in many jurisdictions once it is stored together with an account. Keep it to the purpose you state and check the current policies of each store before you submit:
- Declare it if it leaves the device: in the App Store privacy details as a device identifier, and in the Google Play Data safety form as a device or other ID, with the purpose (for example app functionality, account management or fraud prevention).
- Describe it in your privacy policy: what you store, why, and how long.
- Do not use it for advertising or cross-app tracking. Advertising has its own identifiers and consent rules on both platforms; combining a persistent device identifier with advertising data is not allowed without the required consent.
- Delete it together with the account when the user deletes the account.
API reference
Package com.klug.deviceid.
class DeviceId
| Member | Description |
| static getInstance():DeviceId | Returns the single instance and creates it on the first call. Use this instead of the constructor. All platforms. |
| DeviceId() | Constructor. Do not call it directly: a second instance throws an Error ("DeviceId is a singleton, use DeviceId.getInstance()"). |
| isSupported:Boolean | Read-only. True when the extension is available: Android and iOS. False on other platforms. |
| uniqueId:String | Read-only. The platform's device identifier: ANDROID_ID on Android, the identifier for vendor on iOS. An empty string where not available. Never throws, never null. |
| androidId:String | Read-only. The Android identifier ANDROID_ID only. Always an empty string on iOS and other platforms. Never throws, never null. |
Events
KlugDeviceId dispatches no events. All results arrive as return values of the getters, synchronously.
Troubleshooting
uniqueId is an empty string on iOS.
The system can briefly have no identifier, for example right after a restart before the device was unlocked. Read it again a second later or when the app returns to the foreground (see Empty values).
The iOS value changed after reinstalling the app.
Expected when no other app of your developer account was installed on the device: removing the last one resets the identifier for vendor. Use KlugKeychain for a value that survives a reinstall.
The Android value differs between my test build and the store build.
From Android 8.0 the value depends on the key the app is signed with. A debug build, a locally signed release and the build installed from the store can be signed with different keys and then see different values. Test reinstall behaviour with builds signed by the same key.
Two users on one Android tablet get different values.
Correct: the value is per user and per work profile. Each profile counts as its own device for your server.
androidId is always empty.
On iOS and desktop that is by design. Use uniqueId for cross-platform code.
isSupported is false.
You are running on desktop or in the AIR simulator, or the <extensionID> is missing from the application descriptor or the .ane was not packaged into the build.
Can I use it to identify the user for advertising?
No. Use it for your app's own functions such as licences, device limits, push registration or fraud prevention, and declare it in the stores. See Privacy and store policies.
Changelog
1.1.0
- iOS support: uniqueId returns the identifier for vendor.
- New cross-platform property uniqueId; androidId stays for the Android value.
- Extension ID is now com.klug.deviceid; update the <extensionID> in your application descriptor when upgrading from 1.0.0.
1.0.0 · first release
- Android: read ANDROID_ID, which stays the same across uninstall and reinstall. No permission needed.