You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter获取Android设备序列号失败,求修正Kotlin方法对接Management API

适配全Android版本获取设备序列号(对应Android Management API映射需求)

修正后的Kotlin函数

fun getSerialNumber(context: Context): String {
    var serialNumber: String? = null

    // 优先使用官方API,按版本分支处理
    try {
        when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
                // Android 8.0+:使用Build.getSerial()
                if (hasRequiredPermission(context)) {
                    serialNumber = Build.getSerial()
                }
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD -> {
                // Android 2.3-7.1:使用Build.SERIAL
                serialNumber = Build.SERIAL
            }
        }
    } catch (e: SecurityException) {
        // 权限不足时,尝试 fallback 到系统属性
        e.printStackTrace()
    }

    // 官方API失败时,尝试读取系统属性(覆盖部分厂商自定义字段)
    if (serialNumber.isNullOrEmpty() || serialNumber == Build.UNKNOWN) {
        serialNumber = try {
            val systemProps = Class.forName("android.os.SystemProperties")
            val getMethod = systemProps.getMethod("get", String::class.java)

            // 按优先级尝试不同的系统属性键
            listOf(
                "ro.serialno",
                "ro.boot.serialno",
                "sys.serialnumber",
                "ril.serialnumber",
                "gsm.sn1",
                "ro.kernel.androidboot.serialno"
            ).firstNotNullOfOrNull { key ->
                val value = getMethod.invoke(systemProps, key) as String
                if (value.isNotEmpty() && value != Build.UNKNOWN) value else null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    // 最终校验,返回有效序列号或临时值
    return if (!serialNumber.isNullOrEmpty() && serialNumber != Build.UNKNOWN) {
        serialNumber
    } else {
        "TEMP12345678"
    }
}

// 检查是否拥有必要权限
private fun hasRequiredPermission(context: Context): Boolean {
    return when {
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
            // Android 11+:设备所有者/配置文件所有者无需额外权限,或检查READ_PHONE_STATE
            context.packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) ||
                    ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
        }
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
            // Android 6.0-10:检查READ_PHONE_STATE权限
            ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
        }
        else -> {
            // Android 5.1及以下:无需动态权限
            true
        }
    }
}

关键适配要点

  • 权限逻辑优化:
    • Android 6.0(API23)及以上:必须动态申请READ_PHONE_STATE权限,仅在Manifest声明无效。
    • Android 11(API30)及以上:如果你的应用是设备所有者/配置文件所有者(Android Management API管理的设备默认满足此条件),调用Build.getSerial()无需额外权限;普通应用受系统限制无法获取真实序列号。
  • API调用优先级:优先使用官方标准API,减少对系统属性的依赖(部分属性在高版本会被系统限制访问)。
  • 异常处理增强:单独捕获权限异常,避免反射或API调用失败导致函数崩溃。
  • 系统属性 fallback:按优先级尝试常见厂商自定义序列号字段,覆盖更多设备场景。

额外注意事项

  • 确保应用已通过Android Management API注册为设备所有者/配置文件所有者,这是Android 10+获取真实序列号的必要前提。
  • 移除Manifest中READ_PRIVILEGED_PHONE_STATE权限,该权限仅授予系统应用或预装应用,第三方应用无法获取。

内容的提问来源于stack exchange,提问作者Shreyansh Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 01:43:11