|
|
@ -70,24 +70,30 @@ class StsAgent( |
|
|
/** 动态更新 CMEI(首次初始化时可能尚未获取到) */ |
|
|
/** 动态更新 CMEI(首次初始化时可能尚未获取到) */ |
|
|
fun updateCmei(cmei: String) { |
|
|
fun updateCmei(cmei: String) { |
|
|
if (cmei.isNotEmpty() && cmei != config.cmei) { |
|
|
if (cmei.isNotEmpty() && cmei != config.cmei) { |
|
|
|
|
|
val wasEmpty = config.cmei.isEmpty() |
|
|
config = config.copy(cmei = cmei) |
|
|
config = config.copy(cmei = cmei) |
|
|
Log.i(TAG, "[YIDONG] updateCmei: cmei已更新为 $cmei") |
|
|
Log.i(TAG, "[YIDONG] updateCmei: cmei已更新为 $cmei") |
|
|
|
|
|
if (wasEmpty) reportHeartbeat() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** 动态更新 MAC(首次初始化时可能尚未获取到) */ |
|
|
/** 动态更新 MAC(首次初始化时可能尚未获取到) */ |
|
|
fun updateMac(mac: String) { |
|
|
fun updateMac(mac: String) { |
|
|
if (mac.isNotEmpty() && mac != config.mac) { |
|
|
if (mac.isNotEmpty() && mac != config.mac) { |
|
|
|
|
|
val wasEmpty = config.mac.isEmpty() |
|
|
config = config.copy(mac = mac) |
|
|
config = config.copy(mac = mac) |
|
|
Log.i(TAG, "[YIDONG] updateMac: mac已更新为 $mac") |
|
|
Log.i(TAG, "[YIDONG] updateMac: mac已更新为 $mac") |
|
|
|
|
|
if (wasEmpty) reportHeartbeat() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** 动态更新固件版本(设备连接后由 BLE 上报,首次初始化时通常尚未获取到) */ |
|
|
/** 动态更新固件版本(设备连接后由 BLE 上报,首次初始化时通常尚未获取到) */ |
|
|
fun updateFirmwareVersion(firmwareVersion: String) { |
|
|
fun updateFirmwareVersion(firmwareVersion: String) { |
|
|
if (firmwareVersion.isNotEmpty() && firmwareVersion != config.firmwareVersion) { |
|
|
if (firmwareVersion.isNotEmpty() && firmwareVersion != config.firmwareVersion) { |
|
|
|
|
|
val wasEmpty = config.firmwareVersion.isEmpty() |
|
|
config = config.copy(firmwareVersion = firmwareVersion) |
|
|
config = config.copy(firmwareVersion = firmwareVersion) |
|
|
Log.i(TAG, "[YIDONG] updateFirmwareVersion: firmwareVersion已更新为 $firmwareVersion") |
|
|
Log.i(TAG, "[YIDONG] updateFirmwareVersion: firmwareVersion已更新为 $firmwareVersion") |
|
|
|
|
|
if (wasEmpty) reportHeartbeat() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -181,7 +187,12 @@ class StsAgent( |
|
|
private set |
|
|
private set |
|
|
|
|
|
|
|
|
// ── 心跳冷却 ────────────────────────────────────────────────────────────── |
|
|
// ── 心跳冷却 ────────────────────────────────────────────────────────────── |
|
|
|
|
|
// 同一进程内 StsAgent 会被反复重建(切页面/BLE 重连等),需避免重复上报; |
|
|
|
|
|
// 但进程冷启动必须能再报一次,所以用进程级静态变量而非 SharedPreferences。 |
|
|
private val HEARTBEAT_COOLDOWN_MS = 3_600_000L // 1 小时 |
|
|
private val HEARTBEAT_COOLDOWN_MS = 3_600_000L // 1 小时 |
|
|
|
|
|
companion object { |
|
|
|
|
|
@Volatile private var lastHeartbeatTs: Long = 0L |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// ═════════════════════════════════════════════════════════════════════════ |
|
|
// ═════════════════════════════════════════════════════════════════════════ |
|
|
// 凭证获取 |
|
|
// 凭证获取 |
|
|
@ -246,16 +257,29 @@ class StsAgent( |
|
|
Log.w(TAG, "[YIDONG] reportHeartbeat: 凭证未就绪,跳过上报") |
|
|
Log.w(TAG, "[YIDONG] reportHeartbeat: 凭证未就绪,跳过上报") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
// 1 小时冷却:StsAgent 会被反复重建,init/fetchCredentials 都会调到这里, |
|
|
// 数据完整性校验:deviceId / cmei / mac / firmwareVersion 任一为空都不上报。 |
|
|
// 必须在原生层拦,否则每次重建都会打一发心跳 |
|
|
// 这些字段在 BLE 配对/连接成功后才会透传过来,首次初始化时通常缺失; |
|
|
val prefs = context.getSharedPreferences("sts_agent", Context.MODE_PRIVATE) |
|
|
// 空数据上报既无意义,又会把 1 小时冷却时间戳写死,阻塞 update*() 触发的补报。 |
|
|
val last = prefs.getLong("last_heartbeat_ts", 0L) |
|
|
val missing = buildList { |
|
|
|
|
|
if (config.deviceId.isEmpty()) add("deviceId") |
|
|
|
|
|
if (config.cmei.isEmpty()) add("cmei") |
|
|
|
|
|
if (config.mac.isEmpty()) add("mac") |
|
|
|
|
|
if (config.firmwareVersion.isEmpty()) add("firmwareVersion") |
|
|
|
|
|
} |
|
|
|
|
|
if (missing.isNotEmpty()) { |
|
|
|
|
|
Log.i(TAG, "[YIDONG] reportHeartbeat: 数据不完整,跳过上报 missing=${missing.joinToString(",")} deviceId=${config.deviceId} cmei=${config.cmei} mac=${config.mac} firmwareVersion=${config.firmwareVersion}") |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
// 进程级 1 小时冷却:同一进程内 StsAgent 会被反复重建(切页面/BLE 重连等), |
|
|
|
|
|
// init/fetchCredentials 都会调到这里,必须在原生层拦; |
|
|
|
|
|
// 但冷却仅在进程生命周期内有效,进程冷启动后归零,保证每次启动都能上报一次。 |
|
|
|
|
|
val last = lastHeartbeatTs |
|
|
val now = System.currentTimeMillis() |
|
|
val now = System.currentTimeMillis() |
|
|
if (last > 0L && now - last < HEARTBEAT_COOLDOWN_MS) { |
|
|
if (last > 0L && now - last < HEARTBEAT_COOLDOWN_MS) { |
|
|
Log.i(TAG, "[YIDONG] reportHeartbeat: 距上次 ${(now - last) / 1000}s,冷却中跳过") |
|
|
Log.i(TAG, "[YIDONG] reportHeartbeat: 距上次 ${(now - last) / 1000}s,冷却中跳过") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
prefs.edit().putLong("last_heartbeat_ts", now).apply() |
|
|
lastHeartbeatTs = now |
|
|
Log.i(TAG, "[YIDONG] reportHeartbeat: 开始上报 deviceId=${config.deviceId} deviceNo=${config.deviceNo} cmei=${config.cmei} mac=${config.mac} firmwareVersion=${config.firmwareVersion}") |
|
|
Log.i(TAG, "[YIDONG] reportHeartbeat: 开始上报 deviceId=${config.deviceId} deviceNo=${config.deviceNo} cmei=${config.cmei} mac=${config.mac} firmwareVersion=${config.firmwareVersion}") |
|
|
launch { |
|
|
launch { |
|
|
try { |
|
|
try { |
|
|
@ -303,13 +327,13 @@ class StsAgent( |
|
|
} else { |
|
|
} else { |
|
|
Log.e(TAG, "[YIDONG] reportHeartbeat: 上报失败 code=$code msg=$msg") |
|
|
Log.e(TAG, "[YIDONG] reportHeartbeat: 上报失败 code=$code msg=$msg") |
|
|
// 上报失败回滚时间戳,避免卡 1 小时无法重试 |
|
|
// 上报失败回滚时间戳,避免卡 1 小时无法重试 |
|
|
prefs.edit().putLong("last_heartbeat_ts", last).apply() |
|
|
lastHeartbeatTs = last |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
Log.e(TAG, "[YIDONG] reportHeartbeat: 请求异常 ${e.message}") |
|
|
Log.e(TAG, "[YIDONG] reportHeartbeat: 请求异常 ${e.message}") |
|
|
// 异常同样回滚时间戳 |
|
|
// 异常同样回滚时间戳 |
|
|
prefs.edit().putLong("last_heartbeat_ts", last).apply() |
|
|
lastHeartbeatTs = last |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|