66

How do I programmatically access the value shown in the image below ?

enter image description here

dsolimano
  • 8,870
  • 3
  • 48
  • 63
Heshan Perera
  • 4,592
  • 8
  • 44
  • 57
  • possible duplicate of [How to find serial number of Android device?](http://stackoverflow.com/questions/2322234/how-to-find-serial-number-of-android-device) – George Stocker Jun 14 '12 at 11:32

6 Answers6

118

This is the hardware serial number. To access it on

  • Android Q (>= SDK 29) android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required. Only system apps can require this permission. If the calling package is the device or profile owner then the READ_PHONE_STATE permission suffices.

  • Android 8 and later (>= SDK 26) use android.os.Build.getSerial() which requires the dangerous permission READ_PHONE_STATE. Using android.os.Build.SERIAL returns android.os.Build.UNKNOWN.

  • Android 7.1 and earlier (<= SDK 25) and earlier android.os.Build.SERIAL does return a valid serial.

It's unique for any device. If you are looking for possibilities on how to get/use a unique device id you should read here.

For a solution involving reflection without requiring a permission see this answer.

thaussma
  • 9,756
  • 5
  • 44
  • 46
  • 5
    Why do you say that it changes on factory reset? I know that is true for Settings.Secure.ANDROID_ID, but I hadn't heard about that for Build.Serial. – Tom Jul 10 '12 at 16:50
  • 1
    Tom you are right! I mixed up ANDROID_ID and SERIAL. I edited my answer. – thaussma Jul 11 '12 at 09:22
  • 2
    Is this usually the same serial number that the manufacturer physically prints on the device itself or is it a software-only serial number? – guidod Apr 24 '14 at 13:37
  • 2
    this answer should be edited as with Android 8 (API26) and above the Build.SERIAL will not work. The only way to get the serial number is to call method Build.getSerial() that requires READ_PHONE_STATE permissions – alex_z Jan 28 '19 at 13:44
  • when does the build_serial change ? change hardware ,root device,factory reset does change it? – geosmart Dec 23 '19 at 14:09
68

Up to Android 7.1 (SDK 25)

Until Android 7.1 you will get it with:

Build.SERIAL

From Android 8 (SDK 26)

On Android 8 (SDK 26) and above, this field will return UNKNOWN and must be accessed with:

Build.getSerial()

which requires the dangerous permission android.permission.READ_PHONE_STATE.

From Android Q (SDK 29)

Since Android Q using Build.getSerial() gets a bit more complicated by requiring:

android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE (which can only be acquired by system apps), or for the calling package to be the device or profile owner and have the READ_PHONE_STATE permission. This means most apps won't be able to uses this feature. See the Android Q announcement from Google.

See Android SDK reference


Best Practice for Unique Device Identifier

If you just require a unique identifier, it's best to avoid using hardware identifiers as Google continuously tries to make it harder to access them for privacy reasons. You could just generate a UUID.randomUUID().toString(); and save it the first time it needs to be accessed in e.g. shared preferences. Alternatively you could use ANDROID_ID which is a 8 byte long hex string unique to the device, user and (only Android 8+) app installation. For more info on that topic, see Best practices for unique identifiers.

Patrick
  • 33,984
  • 10
  • 106
  • 126
  • Can the downvoter please explain, why he/she believes this is incorrect? – Patrick Nov 21 '17 at 17:02
  • I voted down probably by mistake, this is clearly the correct answer now. I can not remove my downvote because it is locked until the answer is edited. – peceps Nov 30 '17 at 11:37
  • if the phone is reset to factory, or escape privilege, the ID get from UUID.randomUUID().toString() and ANDROID_ID will change? – TJCLK Jan 20 '20 at 06:55
  • `UUID.randomUUID().toString()` is always differnet -> thats why you have to persist, but factory reset of course deletes it. ANDROID_ID is not really defined, but expect it to be also different. – Patrick Jan 20 '20 at 15:42
42

Build.SERIAL can be empty or sometimes return a different value (proof 1, proof 2) than what you can see in your device's settings.

If you want a more complete and robust solution, I've compiled every possible solution I could found in a single gist. Here's a simplified version of it :

public static String getSerialNumber() {
    String serialNumber;

    try {
        Class<?> c = Class.forName("android.os.SystemProperties");
        Method get = c.getMethod("get", String.class);

        serialNumber = (String) get.invoke(c, "gsm.sn1");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "ril.serialnumber");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "ro.serialno");
        if (serialNumber.equals(""))
            serialNumber = (String) get.invoke(c, "sys.serialnumber");
        if (serialNumber.equals(""))
            serialNumber = Build.SERIAL;

        // If none of the methods above worked
        if (serialNumber.equals(""))
            serialNumber = null;
    } catch (Exception e) {
        e.printStackTrace();
        serialNumber = null;
    }

    return serialNumber;
}

I try to update the gist regularly whenever I can test on a new device or Android version. Contributions are welcome too.

Mickäel A.
  • 9,012
  • 5
  • 54
  • 71
1

From Android P, defining the READ_PHONE_STATE permission in AndroidManifest only, will not work. We have to actually request for the permission. Below code works for me:

@RequiresApi(api = Build.VERSION_CODES.P)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, 101);
    }
}

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void onResume() {
    super.onResume();
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    Log.d(TAG,Build.getSerial());
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case 101:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
        } else {
            //not granted
        }
        break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

Add this permissions in AndroidManifest.xml

<uses-permission android:name = "android.permission.INTERNET"/>
<uses-permission android:name = "android.permission.READ_PHONE_STATE" />
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
MMJ
  • 519
  • 1
  • 9
  • 26
1

I like my solution:

/**
 * Checks some permission
 *
 * @return true - If have any of the permissions sent by parameter
 * @return true - If you do not have any of the permissions sent by parameter
 */
fun Context.hasPermissionSome(vararg permissions: String): Boolean {
    permissions.forEach { permission ->
        if (hasPermission(permission)) {
            return true
        }
    }
    return false
}

@CheckResult
fun validateValue(value: String?, valueToCompare: String? = null): Boolean {
    if (!value.isNullOrEmpty() && value != Build.UNKNOWN && value != valueToCompare)
        return true

    return false
}

/**
 * @see: https://developer.android.com/about/versions/10/privacy/changes#data-ids
 *
 * Retrieve SerialNumber with system commands:
 *   adb shell getprop ro.serialno
 *   adb shell getprop ro.boot.serialno
 *   adb shell getprop ril.serialnumber
 * @return device Serial Number obtained with SystemProperties
 */
@SuppressLint("PrivateApi")
private fun serialNumberBySysProp(): String? {
    var sn: String? = null

    val readPhonePermissions = arrayOf(
        Manifest.permission.READ_PHONE_STATE,
        "android.permission.READ_PRIVILEGED_PHONE_STATE"
    )

    if (!this.context.hasPermissionSome(*readPhonePermissions))
        return sn

    try {
        val systemProps = Class.forName("android.os.SystemProperties")
        val getProp = systemProps.getMethod("get", String::class.java)
        
        // 1ª tentativa
        sn = getProp("ril.serialnumber") as String?
        if (validateValue(sn)) {
            return sn
        }

        // 2ª tentativa
        sn = getProp("ro.serialno") as String?
        if (validateValue(sn)) {
            return sn
        }

        // 3ª tentativa
        sn = getProp("ro.boot.serialno") as String?
        if (validateValue(sn)) {
            return sn
        }

        // 4ª tentativa
        sn = getProp("sys.serialnumber") as String?
        if (validateValue(sn)) {
            return sn
        }

    } catch (e: Exception) { }

    return if (validateValue(sn))
        sn
    else
        null
}

/**
 * @return device Serial Number obtained with Build Class
 */
@SuppressLint("HardwareIds")
private fun serialNumberByBuild(): String? {
    var sn: String? = null
    MediaDrm.PROPERTY_DEVICE_UNIQUE_ID
    if (!this.context.hasPermissionSome(
            Manifest.permission.READ_PHONE_STATE,
            "android.permission.READ_PRIVILEGED_PHONE_STATE"
        ))
        return sn

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        sn = Build.getSerial()
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        sn = Build.getSerial()
    } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
        sn = Build.getSerial()

        if (!validateValue(sn))
            sn = Build.SERIAL
    }

    return if (validateValue(sn))
        sn
    else
        null
}

val serialNumber: String
    @SuppressLint("HardwareIds", "PrivateApi", "MissingPermission", "StaticFieldLeak")
    get() {
        var serial: String = serialNumberByBuild() ?: ""

        if (!validateValue(serial)) {
            serial = serialNumberBySysProp() ?: ""
        }

        return serial
    }

Add this permissions in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
-1

if you want something that works on newer android versions and android emulator, This might help:

Note: don't forget to give necessary permissions. (Might not work in android 11 and 12 without system level permissions)

fun getSerialNumber(): String {
    var serialNumber: String?
    try {
        val c = Class.forName("android.os.SystemProperties")
        val get = c.getMethod("get", String::class.java)
        serialNumber = get.invoke(c, "gsm.sn1") as String
        if (serialNumber == "") serialNumber = get.invoke(c, "ril.serialnumber") as String
        if (serialNumber == "") serialNumber = get.invoke(c, "ro.serialno") as String
        if (serialNumber == "") serialNumber = get.invoke(c, "sys.serialnumber") as String
        if (serialNumber == "") serialNumber = get.invoke(c, "ro.boot.serialno") as String
        if (serialNumber == "") serialNumber = get.invoke(c, "ro.kernel.androidboot.serialno") as String
        if (serialNumber == "") {
            serialNumber = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Build.getSerial()
            } else {
                Build.SERIAL
            }
        }

        // If none of the methods above worked
        if (serialNumber == "") serialNumber = null
    } catch (e: Exception) {
        e.printStackTrace()
        serialNumber = null
    }
    if (!serialNumber.isNullOrEmpty() && serialNumber != Build.UNKNOWN) {
        return serialNumber
    }
    return "TEMP12345678"
}
althafvly
  • 11
  • 3