feat: 统一 deviceId 来源,所有 SDK 模块使用 XuqmSDK.deviceId

- DeviceUtils.getDeviceId() 统一委托 XuqmSDK.deviceId
- sdk-license: DeviceInfoProvider 改用 DeviceUtils
- sdk-push: 所有 getDeviceId(context) 改为 getDeviceId()
- sdk-update: checkUpdate 增加 deviceId/model/osVersion/vendor 参数
- DeviceUtils 新增 getDeviceInfo() 返回完整设备信息 Map
这个提交包含在:
XuqmGroup 2026-06-18 23:37:59 +08:00
父节点 fd37f57fbf
当前提交 e3d03c0ea7
共有 6 个文件被更改,包括 45 次插入49 次删除

查看文件

@ -1,14 +1,15 @@
package com.xuqm.sdk.utils
import android.content.Context
import android.os.Build
import android.provider.Settings
import com.xuqm.sdk.XuqmSDK
object DeviceUtils {
fun getDeviceId(context: Context): String =
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
?: Build.FINGERPRINT
/**
* 获取统一设备 ID来源XuqmSDK.deviceId
* 所有 SDK 模块应使用此方法获取 deviceId确保同一设备在所有服务中标识一致
*/
fun getDeviceId(): String = XuqmSDK.deviceId
fun getDeviceModel(): String = "${Build.MANUFACTURER} ${Build.MODEL}"
@ -21,4 +22,13 @@ object DeviceUtils {
"vivo", "iqoo" -> "VIVO"
else -> "FCM"
}
/** 获取完整设备信息 Map,用于 API 上报。 */
fun getDeviceInfo(): Map<String, String> = mapOf(
"deviceId" to getDeviceId(),
"model" to getDeviceModel(),
"osVersion" to getOsVersion(),
"vendor" to getVendor(),
"platform" to "ANDROID",
)
}

查看文件

@ -226,7 +226,7 @@ object LicenseSDK {
private fun getOrCreateDeviceId(): String {
store.deviceId?.let { return it }
val newId = DeviceInfoProvider.getDeviceId(appContext)
val newId = DeviceInfoProvider.getDeviceId()
store.deviceId = newId
return newId
}

查看文件

@ -1,50 +1,22 @@
package com.xuqm.sdk.license.internal
import android.content.Context
import android.os.Build
import android.provider.Settings
import java.util.UUID
import com.xuqm.sdk.utils.DeviceUtils
/**
* Provides stable device information for license checks.
* deviceId 统一使用 XuqmSDK.deviceId通过 DeviceUtils
*/
internal object DeviceInfoProvider {
private const val FALLBACK_PREFS = "xuqm_license_device_fallback"
private const val KEY_FALLBACK_ID = "fallback_device_id"
/** 统一设备 ID,来源XuqmSDK.deviceId */
fun getDeviceId(): String = DeviceUtils.getDeviceId()
/**
* Get stable device ID.
* Priority:
* 1. ANDROID_ID (stable per app signing key + device + user, survives reinstall with same signer)
* 2. Persisted fallback UUID
*
* Note: ANDROID_ID changes on factory reset or if app is reinstalled with different signing key.
*/
fun getDeviceId(context: Context): String {
val androidId = try {
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
} catch (_: Exception) { null }
if (!androidId.isNullOrBlank() && androidId != "9774d56d682e549c") {
return androidId
}
// Fallback to persisted UUID
val prefs = context.getSharedPreferences(FALLBACK_PREFS, Context.MODE_PRIVATE)
var fallbackId = prefs.getString(KEY_FALLBACK_ID, null)
if (fallbackId == null) {
fallbackId = UUID.randomUUID().toString()
prefs.edit().putString(KEY_FALLBACK_ID, fallbackId).apply()
}
return fallbackId
}
fun getDeviceModel(): String = "${Build.MANUFACTURER} ${Build.MODEL}"
fun getDeviceModel(): String = DeviceUtils.getDeviceModel()
fun getDeviceVendor(): String = Build.MANUFACTURER
fun getOsVersion(): String = "Android ${Build.VERSION.RELEASE}"
fun getOsVersion(): String = DeviceUtils.getOsVersion()
fun getDeviceName(): String = getDeviceModel()
}

查看文件

@ -54,7 +54,7 @@ object PushSDK {
api.setReceivePush(
appKey = XuqmSDK.appKey,
userId = userId,
deviceId = DeviceUtils.getDeviceId(context),
deviceId = DeviceUtils.getDeviceId(),
enabled = enabled,
)
if (enabled) {
@ -127,7 +127,7 @@ object PushSDK {
fun currentRegistration(context: Context): PushRegistrationSnapshot? {
XuqmSDK.requireInit()
val deviceId = DeviceUtils.getDeviceId(context)
val deviceId = DeviceUtils.getDeviceId()
val detectedVendor = detectVendor()
val registration = store(context).load(deviceId = deviceId, fallbackVendor = detectedVendor)
?: return null
@ -190,7 +190,7 @@ object PushSDK {
val registration = currentRegistration(context)
val vendor = registration?.vendor ?: detectVendor()
val pushToken = registration?.pushToken?.takeIf { it.isNotBlank() }
val deviceId = DeviceUtils.getDeviceId(context)
val deviceId = DeviceUtils.getDeviceId()
if (pushToken.isNullOrBlank()) {
Log.w("XuqmPushSDK", "Native push token not ready yet, waiting for onNewToken()")
ensureNativePushTokenInternal(context)
@ -215,8 +215,8 @@ object PushSDK {
vendor = vendor.name,
token = pushToken,
deviceId = deviceId,
brand = Build.MANUFACTURER.orEmpty(),
model = Build.MODEL.orEmpty(),
brand = DeviceUtils.getVendor(),
model = DeviceUtils.getDeviceModel(),
osVersion = DeviceUtils.getOsVersion(),
appVersion = appVersion(context),
)
@ -235,14 +235,14 @@ object PushSDK {
scope.launch {
runCatching {
val reg = store(XuqmSDK.appContext).load(
deviceId = DeviceUtils.getDeviceId(XuqmSDK.appContext),
deviceId = DeviceUtils.getDeviceId(),
fallbackVendor = detectVendor(),
)
api.unregisterDevice(
XuqmSDK.appKey,
userId,
reg?.vendor?.name ?: detectVendor().name,
DeviceUtils.getDeviceId(XuqmSDK.appContext),
DeviceUtils.getDeviceId(),
)
registeredUserId.compareAndSet(userId, null)
store(XuqmSDK.appContext).updateLastUserId(null)
@ -272,7 +272,7 @@ object PushSDK {
private fun isReceivePushEnabled(context: Context): Boolean =
store(context).load(
deviceId = DeviceUtils.getDeviceId(context),
deviceId = DeviceUtils.getDeviceId(),
fallbackVendor = detectVendor(),
)?.receivePush ?: true

查看文件

@ -184,7 +184,17 @@ object UpdateSDK {
}
runCatching {
val response = api.checkUpdate(XuqmSDK.appKey, "ANDROID", versionCode, userId)
val deviceInfo = com.xuqm.sdk.utils.DeviceUtils.getDeviceInfo()
val response = api.checkUpdate(
appKey = XuqmSDK.appKey,
platform = "ANDROID",
currentVersionCode = versionCode,
userId = userId,
deviceId = deviceInfo["deviceId"],
model = deviceInfo["model"],
osVersion = deviceInfo["osVersion"],
vendor = deviceInfo["vendor"],
)
if (response.code == 40404) {
throw IllegalStateException("更新服务未开通 (code=40404) [appKey=${XuqmSDK.appKey}]")
}

查看文件

@ -40,5 +40,9 @@ internal interface UpdateApi {
@Query("platform") platform: String,
@Query("currentVersionCode") currentVersionCode: Int,
@Query("userId") userId: String? = null,
@Query("deviceId") deviceId: String? = null,
@Query("model") model: String? = null,
@Query("osVersion") osVersion: String? = null,
@Query("vendor") vendor: String? = null,
): ApiResponse<UpdateInfoDto>
}