Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Join us on Thursday, June 8, for a Q&A with Unity's Content Pipeline group here on the forum, and on the Unity Discord, and discuss topics around Content Build, Import Workflows, Asset Database, and Addressables!
    Dismiss Notice

Unique Identifier Details

Discussion in 'Android' started by jonas-minnberg, Sep 7, 2015.

  1. jonas-minnberg


    Unity Technologies

    Oct 8, 2014
    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):
    2. string id;
    3. // Needs android.permission.READ_PHONE_STATE and a phone like device (can fail on tablets)
    4. if(checkPermission(READ_PHONE_STATE))
    5.     id = context.getSystemService(Context.TElEPHONY_SERVICE).getDeviceId()
    6. else
    7.     id = context.getContentResolver().getString(Secure.ANDROID_ID);
    8. if(!id)
    9.     id = getMacAddress();
    10. id = md5hash(id);

    The original broken algorithm (4.6-)

    Code (pseudo):
    2.  string id;
    3.  // Needs android.permission.READ_PHONE_STATE and a phone like device (can fail on tablets)
    4.  if(!checkPermission(READ_PHONE_STATE))
    5.     id = NULL;
    6.  else {
    7.     id = context.getSystemService(Context.TElEPHONY_SERVICE).getDeviceId()
    8.     if(id == NULL)
    9.         id = context.getContentResolver().getString(Secure.ANDROID_ID);
    10.  }
    11.  if(!id)
    12.     id = getMacAddress();
    13.  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):
    2. // Hash an input string and return the hash as
    3. // a 32 character hexadecimal string.
    4. static string getMd5Hash(string input)
    5. {
    6.     if (input == "")
    7.         return "";
    8.     MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
    9.     byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
    10.     StringBuilder sBuilder = new StringBuilder();
    11.     for (int i = 0; i < data.Length; i++)
    12.         sBuilder.Append(data[i].ToString("x2"));
    13.     return sBuilder.ToString();
    14. }
    16. static string generateDeviceUniqueIdentifier(bool oldBehavior)
    17. {
    18.     string id = "";
    19.     AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    20.     AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
    21.     AndroidJavaClass contextClass = new AndroidJavaClass("android.content.Context");
    22.     string TELEPHONY_SERVICE = contextClass.GetStatic<string>("TELEPHONY_SERVICE");
    23.     AndroidJavaObject telephonyService = activity.Call<AndroidJavaObject>("getSystemService", TELEPHONY_SERVICE);
    24.     bool noPermission = false;
    25.     try
    26.     {
    27.         id = telephonyService.Call<string>("getDeviceId");
    28.     }
    29.     catch (Exception e) {
    30.         noPermission = true;
    31.     }
    32.     if(id == null)
    33.         id = "";
    34.     // <= 4.5 : If there was a permission problem, we would not read Android ID
    35.     // >= 4.6 : If we had permission, we would not read Android ID, even if null or "" was returned
    36.     if((noPermission && !oldBehavior) || (!noPermission && id == "" && oldBehavior))
    37.     {
    38.         AndroidJavaClass settingsSecure = new AndroidJavaClass("android.provider.Settings$Secure");
    39.         string ANDROID_ID = settingsSecure.GetStatic<string>("ANDROID_ID");
    40.         AndroidJavaObject contentResolver = activity.Call<AndroidJavaObject>("getContentResolver");
    41.         id = settingsSecure.CallStatic<string>("getString", contentResolver, ANDROID_ID);
    42.         if(id == null)
    43.             id = "";
    44.     }
    45.     if(id == "")
    46.     {
    47.         string mac = "00000000000000000000000000000000";
    48.         try
    49.         {
    50.             StreamReader reader = new StreamReader("/sys/class/net/wlan0/address");
    51.             mac = reader.ReadLine();
    52.             reader.Close();
    53.         }
    54.         catch (Exception e) {}
    55.         id = mac.Replace(":", "");
    56.     }
    57.     return getMd5Hash(id);
    58. }
  2. tricket


    Feb 10, 2016
    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?
  3. gwiazdorrr


    Sep 29, 2014
    Bump. I, too, would like to know. Unique id is useful, but sometimes not at a price of a rather scary dialog.
  4. djarcas


    Nov 15, 2012
    Bump and ditto.
  5. monark


    May 2, 2008
    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??
  6. monark


    May 2, 2008
  7. Klik303


    Dec 10, 2016
    Short question. Why hash method is use on "unique identifier"?
    Can-Baycay likes this.
  8. JessChis


    Aug 16, 2018
    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.
  9. breban1


    Jun 7, 2016
    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​

    Does it guarantee that it also won't change (OS updates, factory reset, etc.)?
  10. DKK


    Nov 3, 2014
    Factory reset changes it on my Samsung tablet. So you need to be prepared for that.
  11. breban1


    Jun 7, 2016
    @DKK thanks for the info! Agreed, I had to find a different solution (without uniquely identifying each device).
  12. monark


    May 2, 2008
    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?
    Last edited: Jul 31, 2019
  13. davidro_unity


    Unity Technologies

    Apr 18, 2019
    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?
  14. flashmandv


    Mar 26, 2015
    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):
    1. FileNotFoundException: Could not find file \"/sys/class/net/wlan0/address\".
    2. System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options)
    3. System.IO.File.OpenRead (System.String path)
    4. System.IO.StreamReader..ctor (System.String path, System.Text.Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)
    5. System.IO.StreamReader..ctor (System.String path)
    6. UTNotifications.PushNotifications.GenerateDeviceUniqueIdentifier ()
    Targeting Android API level 28. Unity 2018.1.1f1 and latest UTNotifications plugin 1.8.3 (from the asset store)
  15. monark


    May 2, 2008
    No this is for release builds and I'm using 2018.3.4

    Android versions:
    Samsung S9: v9
    Huawei: v8.1.0
  16. ommzideveloper46


    Jul 3, 2019
    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
  17. JuliusM


    Unity Technologies

    Apr 17, 2013
  18. JuliusM


    Unity Technologies

    Apr 17, 2013
    Different values compared to what? What are you changing to get different values?
  19. monark


    May 2, 2008
    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.
  20. sathya


    Jul 30, 2012
    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
    Last edited: Aug 10, 2020
  21. giggioz


    May 11, 2017
    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?
  22. giggioz


    May 11, 2017
    Ok, after a sleepless night I think I've found a solution (at least for Android)

    The problem here is that says:

    Android: SystemInfo.deviceUniqueIdentifier always returns the md5 of ANDROID_ID. (See 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

    You can do this only once in the lifetime of the app, so read carefully the doc

    From the link above follow this procedure

    1. Sign in to your Play Console.
    2. Select an app.
    3. At the left menu, select Release management > App signing.
    4. In the 'Upgrade your app signing key for new installs' card, select Request key upgrade.
    5. Select an option. Depending on the option that you select, you may need to contact support to complete your request.
    6. 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!
    Elmstrom likes this.