Android: How to programmatically access the device serial number shown in the AVD manager (API Version 8)
Asked Answered
G

6

68

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

enter image description here

Griqua answered 14/6, 2012 at 8:25 Comment(1)
possible duplicate of How to find serial number of Android device?Edyth
B
121

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.

Baram answered 14/6, 2012 at 8:30 Comment(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.Leggat
Tom you are right! I mixed up ANDROID_ID and SERIAL. I edited my answer.Baram
Is this usually the same serial number that the manufacturer physically prints on the device itself or is it a software-only serial number?Karyolysis
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 permissionsZins
when does the build_serial change ? change hardware ,root device,factory reset does change it?Rigid
S
72

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.

Sought answered 30/10, 2017 at 18:47 Comment(4)
Can the downvoter please explain, why he/she believes this is incorrect?Sought
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.Harwill
if the phone is reset to factory, or escape privilege, the ID get from UUID.randomUUID().toString() and ANDROID_ID will change?Sistrunk
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.Sought
A
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.

Abingdon answered 2/10, 2018 at 10:17 Comment(3)
it finally solves my problem, the accepted answer should be thisAlluring
I think this is the best answer here. As note, if I am using this code in my android studio and ran 2 instances of Simulator, it will return same ID.Artis
Are you supposed to get null with this using the emulator?Zola
M
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" />
Magnificent answered 22/6, 2020 at 20:57 Comment(0)
A
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" />
Apollyon answered 30/3, 2021 at 2:6 Comment(0)
F
0

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"
}
Frank answered 3/6, 2022 at 5:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.