我该如何以编程的方式访问下图中所示的值?
我该如何以编程的方式访问下图中所示的值?
这是硬件序列号,获取方式如下:
Android Q(>= SDK 29)需要使用android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
权限。只有系统应用可以请求此权限。如果调用包是设备或个人资料所有者,则READ_PHONE_STATE
权限就足够了。
Android 8及更高版本(>= SDK 26)使用android.os.Build.getSerial()
方法来获取,需要危险权限READ_PHONE_STATE。使用android.os.Build.SERIAL
将返回android.os.Build.UNKNOWN。
Android 7.1及更早版本(<= SDK 25)和早期的android.os.Build.SERIAL
返回有效的序列号。
每个设备的序列号都是唯一的。如果您正在寻找获取/使用唯一设备 ID 的可能性,可以阅读 此处内容。
对于不需要权限的反射解决方案,请参见此答案。
在Android 7.1之前,您可以使用以下方式获取:
Build.SERIAL
在Android 8(SDK 26)及以上版本中,该字段将返回UNKNOWN
,必须使用以下方式进行访问:
Build.getSerial()
这需要危险权限android.permission.READ_PHONE_STATE
。
自Android Q以来,使用Build.getSerial()
变得更加复杂,需要:
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
(只能由系统应用程序获取),或者调用包是设备或配置文件所有者并具有READ_PHONE_STATE
权限。这意味着大多数应用程序将无法使用此功能。请参见Google的Android Q公告。
如果您只需要一个唯一标识符,最好避免使用硬件标识符,因为出于隐私原因,Google 不断努力使其更难以访问。您可以生成一个 UUID.randomUUID().toString();
并将其保存在例如共享首选项中,第一次需要访问时。或者,您可以使用 ANDROID_ID
,它是一个 8 字节长的十六进制字符串,对于设备、用户和(仅限 Android 8+)应用程序安装是唯一的。有关该主题的更多信息,请参见唯一标识符的最佳实践。
UUID.randomUUID().toString()
总是不同的 -> 这就是为什么你必须持久化,但恢复出厂设置当然会删除它。ANDROID_ID 没有真正定义,但预计也会不同。 - PatrickBuild.SERIAL
可能为空,有时会返回不同于您在设备设置中看到的值(证据1,证据2)。
如果您想要一个更完整和强大的解决方案,我已经将我能找到的每种可能的解决方案编译成了一个单独的代码片段。这是它的简化版本:
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;
}
我会尽可能定期更新代码片段,每当我在新设备或Android版本上进行测试时。欢迎贡献。
null
值? - Jasper de Vries我喜欢我的解决方案:
/**
* 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
}
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
从 Android P 开始,在 AndroidManifest 中定义 READ_PHONE_STATE 权限将不会起作用。我们必须实际请求权限。下面的代码适用于我:
@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);
}
}
<uses-permission android:name = "android.permission.INTERNET"/>
<uses-permission android:name = "android.permission.READ_PHONE_STATE" />
如果你想要在新版本的安卓系统和安卓模拟器上运行,这可能会有帮助:
注意:不要忘记给予必要的权限。(在没有系统级别权限的情况下,可能无法在安卓11和12上运行)
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"
}