kvnnj.m_av
kvnnj.m_av

Reputation: 341

Android 10: IMEI no longer available on API 29. Looking for alternatives

Our client's app main feature is heavily relaying on tracking their clients' devices, they offer products that are bound to the specific phone(not its owner). This was possible using the device imei, but with the privacy changes in Android 10, they made it unreachable. (https://developer.android.com/about/versions/10/privacy/changes).

Android has a documentation about what identifier to use on specific user cases, but non matches our case since we need it to be unique, constant and bound to the device(or at least difficult to change). https://developer.android.com/training/articles/user-data-ids. I'm considering Android ID to be a possible solution, or using the mac address knowing they aren't 100% reliable.

Any thoughts? recommendations? experiences? at this point anything could be an option

Upvotes: 34

Views: 22854

Answers (5)

MathMax
MathMax

Reputation: 607

For Java users that are interested in Sofien's solution, I have:

  1. Converted Sofien's code to Java and further simplified;
  2. Extensively tested on Android 10 (API 29), Android 11 (API 30) and previous versions.

1. Code and discussion

@Nullable
String getUniqueID() {
   UUID wideVineUuid = new UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L);
   try {
      MediaDrm wvDrm = new MediaDrm(wideVineUuid);
      byte[] wideVineId = wvDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID);
      return Arrays.toString(wideVineId);
   } catch (Exception e) {
      // Inspect exception
      return null;
   }
   // Close resources with close() or release() depending on platform API
   // Use ARM on Android P platform or higher, where MediaDrm has the close() method
}

There are two key differences w.r.t. Sofien's code.

  • I am not using the MessageDigest, which results in a simpler code. Moreover, the MessageDigest.update() method applies the SHA-256 hash function to its argument, which introduces an extremely low probability of losing UUID uniqueness. The only drawback of not hashing the UUID is that you don't have a fixed length UUID, which I don't care about in my application.
  • Instead of the Kotlin function toHexString (which has no one-line counterpart in Java) I am using Arrays.toString. This choice is safe because (A) It throws no Exception and (B) it retains a one-to-one correspondence between the wideVineId and its String representation. If you prefer to stick to hex conversion, the Apache Commons Codec library offers a one-line solution.

Of course, these changes result in a different UUID, needless to say that other choices are possible. Notice also that an UUID generated with Arrays.toString takes the form

[92, -72, 76, -100, 26, -86, 121, -57, 81, -83, -81, -26, -26, 3, -49, 97, -24, -86, 17, -106, 25, 102, 55, 37, 47, -5, 33, -78, 34, 121, -58, 109]

So, if you don't want special characters in your UUID you can remove them with String.replaceAll().

2. Tests

I have tested the persistence of the UUID

  • over reinstallation
  • over reinstallation AND reboot

on the following device/OS combinations:

  • Google Pixel 4A / API 30
  • Samsung Galaxy S10 / API 29
  • Samsung Galaxy S9 / API 29
  • Huawei Nexus 6P / API 27 (tested also factory reset)
  • LG V20 / API 27 (tested also factory reset)
  • Asus ZenFone 2 / API 23
  • Samsung Galaxy J5 / API 23
  • LG Nexus 5 / API 23
  • LG K4 / API 22
  • Samsung Galaxy J3 / API 22
  • Samsung Galaxy S4 / API 21

In all of the tests, the targetSdkVersion is 30. More tests (especially on API 29 and 30) are welcome.

Upvotes: 21

Yaser Yousef
Yaser Yousef

Reputation: 11

I have tested it in Nokia phone "the identifier is changed when I reset my phone on factory reset". Did you test it on factory reset?

Upvotes: -4

Vinayakkumar
Vinayakkumar

Reputation: 6480

MEDIADRM API is one can use

//From Exo player

val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
    val id = MediaDrm(WIDEVINE_UUID)
        .getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)
    var encodedString: String = Base64.encodeToString(id,Base64.DEFAULT)
    Log.i("Uniqueid","Uniqueid"+encodedString)

Upvotes: 0

Kiran Maniya
Kiran Maniya

Reputation: 8979

  1. On a device first boot, a random value is generated and stored. This value is available via Settings.Secure.ANDROID_ID. It’s a 64-bit number that should remain constant for the lifetime of a device. ANDROID_ID seems a good choice for a unique device identifier because it’s available for smartphones and tablets. To retrieve the value, you can use the following code,
String androidId = Settings.Secure.getString(getContentResolver(),
                                             Settings.Secure.ANDROID_ID);

However, the value may change if a factory reset is performed on the device. There is also a known bug with a popular handset from a manufacturer where every instance has the same ANDROID_ID. Clearly, the solution is not 100% reliable.

  1. Use UUID. As the requirement for most of the applications is to identify a particular installation and not a physical device, a good solution to get the unique id for a user if to use UUID class. The following solution has been presented by Reto Meier from Google in a Google I/O presentation,
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);

Upvotes: 3

Sofien Rahmouni
Sofien Rahmouni

Reputation: 4917

I advice you to read the official blog of the best practice of google to see what the use case match with your specification : https://developer.android.com/training/articles/user-data-ids.html

For me i occcured the same problem about the unicity of android identifiers and i found the only solution is to use the MediaDrm API ( https://android.googlesource.com/platform/frameworks/base/+/android-cts-4.4_r1/media/java/android/media/MediaDrm.java#539 ) which contains a unique device id and can survive even on the factory reset and doesn't need any additional permission on your manifest file.

Here is the couple of code how can we retreive the unique identifier on Android 10 :

import android.media.MediaDrm
import java.security.MessageDigest
import java.util.*

object UniqueDeviceID {

    /**
     * UUID for the Widevine DRM scheme.
     * <p>
     * Widevine is supported on Android devices running Android 4.3 (API Level 18) and up.
     */
    fun getUniqueId(): String? {

        val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
        var wvDrm: MediaDrm? = null
        try {
            wvDrm = MediaDrm(WIDEVINE_UUID)
            val widevineId = wvDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)
            val md = MessageDigest.getInstance("SHA-256")
            md.update(widevineId)
            return  md.digest().toHexString()
        } catch (e: Exception) {
            //WIDEVINE is not available
            return null
        } finally {
            if (AndroidPlatformUtils.isAndroidTargetPieAndHigher()) {
                wvDrm?.close()
            } else {
                wvDrm?.release()
            }
        }
    }


    fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
}

Upvotes: 26

Related Questions