Android Unique Identifier The Unique Identifier on Android has gone through a few changes due to bugs, so there is a chance you will end up in a situation where you are relying on an ID that has changed between Unity versions. This page tries to document the behavior so you can work around this. Description of the Algorithm The original plan was to use getDeviceId() from TELEPHONY_SERVICE if permitted and available, and fall back to ANDROID_ID if that failed and MAC address as a last resort. But in older versions of Unity (4.5 and back) there was a bug; if the application did not have the READ_PHONE_STATE permission, the ANDROID_ID path was not used either, so it always fell back to the MAC address. This was later "fixed" by checking for the READ_PHONE_STATE permission. But now a new problem was introduced; ANDROID_ID would not be used if we had the permission but getDeviceId() still returned NULL. Rather then making another fix and creating a third way to calculate the identifier, the old behavior was later reintroduced. The "fixed" behavior still ended up in 5.0+ so that is why it is the current algorithm. Another problem is that some Android tablets fail to return MAC address when WiFi is turned off, possibly resulting in a different ID. The current algorithm (5.0+ and possibly some version of 4.6) Code (pseudo): string id; // Needs android.permission.READ_PHONE_STATE and a phone like device (can fail on tablets) if(checkPermission(READ_PHONE_STATE)) id = context.getSystemService(Context.TElEPHONY_SERVICE).getDeviceId() else id = context.getContentResolver().getString(Secure.ANDROID_ID); if(!id) id = getMacAddress(); id = md5hash(id); The original broken algorithm (4.6-) Code (pseudo): string id; // Needs android.permission.READ_PHONE_STATE and a phone like device (can fail on tablets) if(!checkPermission(READ_PHONE_STATE)) id = NULL; else { id = context.getSystemService(Context.TElEPHONY_SERVICE).getDeviceId() if(id == NULL) id = context.getContentResolver().getString(Secure.ANDROID_ID); } if(!id) id = getMacAddress(); id = md5hash(id) Reading the MAC address The current implementation iterates over all interfaces, and gets the hardware address of the first interface that is not the loopback interface. Result is then transformed to a 12 chars long lower case hex string. If the mac could not be read it is set to "00000000000000000000000000000000" (32 zeroes). Simulating the ID in C# The following script is an example on how to generate the ID yourself, to emulate older or newer behavior. It is not fully tested and the way it reads the MAC address is not the same as the native code, but hopefully it is a place to start if you need this functionality. Code (CSharp): // Hash an input string and return the hash as // a 32 character hexadecimal string. static string getMd5Hash(string input) { if (input == "") return ""; MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider(); byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); StringBuilder sBuilder = new StringBuilder(); for (int i = 0; i < data.Length; i++) sBuilder.Append(data[i].ToString("x2")); return sBuilder.ToString(); } static string generateDeviceUniqueIdentifier(bool oldBehavior) { string id = ""; AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>("currentActivity"); AndroidJavaClass contextClass = new AndroidJavaClass("android.content.Context"); string TELEPHONY_SERVICE = contextClass.GetStatic<string>("TELEPHONY_SERVICE"); AndroidJavaObject telephonyService = activity.Call<AndroidJavaObject>("getSystemService", TELEPHONY_SERVICE); bool noPermission = false; try { id = telephonyService.Call<string>("getDeviceId"); } catch (Exception e) { noPermission = true; } if(id == null) id = ""; // <= 4.5 : If there was a permission problem, we would not read Android ID // >= 4.6 : If we had permission, we would not read Android ID, even if null or "" was returned if((noPermission && !oldBehavior) || (!noPermission && id == "" && oldBehavior)) { AndroidJavaClass settingsSecure = new AndroidJavaClass("android.provider.Settings$Secure"); string ANDROID_ID = settingsSecure.GetStatic<string>("ANDROID_ID"); AndroidJavaObject contentResolver = activity.Call<AndroidJavaObject>("getContentResolver"); id = settingsSecure.CallStatic<string>("getString", contentResolver, ANDROID_ID); if(id == null) id = ""; } if(id == "") { string mac = "00000000000000000000000000000000"; try { StreamReader reader = new StreamReader("/sys/class/net/wlan0/address"); mac = reader.ReadLine(); reader.Close(); } catch (Exception e) {} id = mac.Replace(":", ""); } return getMd5Hash(id); }
Thanks, jonas.minnberg -- a further question: in the various posts, I keep seeing that the reported identified will change between 5.3 and 5.4. I can't see from your pseudo-code examples why that is -- won't we receive the hash of either (a) the deviceId() value if available, or (b) the ANDROID_ID? We're trying to determine how much storing of old value => new value we need to do in our code, so if the result *isn't* going to change from 5.3 to 5.4, that'll save us some headaches. Also: for Android 6.0, we want to explicitly TURN OFF the requested READ_PHONE_STATE permission (as it's creating a scary "Allow app to make and manage phone calls?" ... does requesting deviceUniqueIdentifier automatically add this, and do we need to strip out this permission via a post-build script, in order to make sure we're not asking for that permission?
Bump. I, too, would like to know. Unique id is useful, but sometimes not at a price of a rather scary dialog.
Getting a bizarre situation with this using U5.6.3 I have 11 identically coded projects that just use different resources. All 11 have been published before and did not exhibit any unexpected permissions issues with U5.3.8 Now 9 of them are fine and 2 request permissions for the phone. All have been updated to the same plugin version - I only use 1 from Prime31 Before building the manifest files are all identical, just the product name changes I do use SystemInfo.deviceUniqueIdentifier, which apparently is fixed, so why would 2 of them have an issue with that now suddenly??
Ok I found the issue, those 2 projects had another plugin folder in them that I wasn't using, removing that extra plugin fixed the issue. I think it might have been related to the discussion in this thread: https://forum.unity.com/threads/uni...y-how-to-remove-it.333431/page-3#post-2643699 Where a target sdk is required, not sure though, as removing them works for me.
To mask _potentially_ personal information. The lawyers prefer we hash things like this, just in case. For the purposes of an ID, it makes no difference. For the purposes of hacking someone, the hash is useless.
I'm considering using system.deviceUniqueIdentifier in Unity 2017 LTS to uniquely identify players. Is it safe to use it for that purpose? I would like to use it for Android AND iOS. I'm finding all kinds of answers to this with searches on the internet, but from the most recent Unity docs it seems completely safe (I'm fine with the pre-iOS7 device caveat). Hoping a Unity person can reply so I know for sure. According to the Unity docs: iOS: on pre-iOS7 devices it will return hash of MAC address. On iOS7 devices it will be UIDevice identifierForVendor or, if that fails for any reason, ASIdentifierManager advertisingIdentifier. Android: SystemInfo.deviceUniqueIdentifier always returns the md5 of ANDROID_ID. (See https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID). Does it guarantee that it also won't change (OS updates, factory reset, etc.)?
@DKK thanks for the info! Agreed, I had to find a different solution (without uniquely identifying each device).
This has recently started returning different values for different apps on the same phone, but not on all phones, it's ok on my Huawei but not my Samsung S9, has something fundamental changed there?
Is the difference that you're seeing only for Development builds? And did it start with Unity 2019.1? Also, what Android version is on those phones?
Hi all. Our game uses UTNotifications Unity plugin, which tries to read new StreamReader("/sys/class/net/wlan0/address") and it fails massively on multiple android phones. Do you have any solution to this exception? Code (CSharp): FileNotFoundException: Could not find file \"/sys/class/net/wlan0/address\". System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) System.IO.File.OpenRead (System.String path) System.IO.StreamReader..ctor (System.String path, System.Text.Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize) System.IO.StreamReader..ctor (System.String path) UTNotifications.PushNotifications.GenerateDeviceUniqueIdentifier () Targeting Android API level 28. Unity 2018.1.1f1 and latest UTNotifications plugin 1.8.3 (from the asset store)
I am using Unity 2019.2.11 and am getting different values for "SystemInfo.deviceUniqueIdentifier" on Android Device: Redmi Note 7 Pro. Android Version: 9
That is an expected behavior. See https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID "unique to each combination of app-signing key, user, and device".
So each app I make generates a new unique device id. Whereas it used to generate the same id as you'd expect being a device id not an app id. We have a bunch of apps that all work together and having a way to identify they were all running on the same device was important.
@JuliusM Same issue with ios every time I do a fresh install i get different deviceinfo calling SystemInfo.deviceUniqueIdentifier gives following results on first time install: 2BA21064-6018-4820-A7DB-34E68BB3C2B0 on second time install: 8FFF4A85-4C0D-472F-8D07-3E3A43642960 and so on
Same here What is the meaning of a SystemInfo.deviceUniqueIdentifier that changes from app to app? This is crazy, we used this identifier to collect info of the same device for different apps, how should we handle this?
Ok, after a sleepless night I think I've found a solution (at least for Android) The problem here is that https://docs.unity3d.com/ScriptReference/SystemInfo-deviceUniqueIdentifier.html says: Android: SystemInfo.deviceUniqueIdentifier always returns the md5 of ANDROID_ID. (See https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID). Note that since Android 8.0 (API level 26) ANDROID_ID depends on the app signing key. That means "unsigned" builds (which are by default signed with a debug keystore) will have a different value than signed builds (which are signed with a key provided in the player settings). Also when allowing Google Play to sign your app, this value will be different when testing locally built app which is signed with the upload key and app downloaded from the Google Play which will be signed with the "final" key. So, if you allow Google Play to sign your app the deviceUniqueIdentifier will change from app to app even if you sign your apps with the same key in Unity (because it will be re-signed again from Google) The solution is "Upgrade your app signing key for new installs" as stated here https://support.google.com/googleplay/android-developer/answer/7384423#upgrade You can do this only once in the lifetime of the app, so read carefully the doc From the link above follow this procedure Sign in to your Play Console. Select an app. At the left menu, select Release management > App signing. In the 'Upgrade your app signing key for new installs' card, select Request key upgrade. Select an option. Depending on the option that you select, you may need to contact support to complete your request. Get Google to generate a new app signing key (recommended) or upload one. After upgrading your app signing key, if you were using the same key for your app signing and upload key, you can continue using your legacy app signing key as your upload key or generate a new upload key. In step 6 you want to upload your own key, so you can upload the same key for all your apps, choose to do so and follow the instructions (you have to use a jar file called pepk.jar to sign your key and upload it to google. I did a test with one app and now the deviceUniqueIdentifier is the same for my local build and the downloaded version. I really hope this helps!