Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class ThreadManagerImpl @Inject constructor(
return if (getDeviceDataset == null) {
ThreadManager.SyncResult.NoneHaveCredentials
} else {
val appIsDevicePreferred = appAddedIsPreferredCredentials(context)
val appIsDevicePreferred = appAddedPreferredCredential(context) != null
Timber.d("Thread: device ${if (appIsDevicePreferred) "prefers" else "doesn't prefer" } dataset from app")

return if (appIsDevicePreferred) {
Expand Down Expand Up @@ -128,13 +128,16 @@ class ThreadManagerImpl @Inject constructor(
Timber.d(
"Thread: device ${if (coreIsDevicePreferred) "prefers" else "doesn't prefer" } core preferred dataset",
)
val appIsDevicePreferred = coreIsDevicePreferred || appAddedIsPreferredCredentials(context)
val appPreferredCredential = if (coreIsDevicePreferred) null else appAddedPreferredCredential(context)
val appIsDevicePreferred = coreIsDevicePreferred || appPreferredCredential != null
Timber.d(
"Thread: device ${if (appIsDevicePreferred) "prefers" else "doesn't prefer" } dataset from app",
)

var exportFromDevice = false
var updated: Boolean? = null
var deviceNowPrefersCore: Boolean? = null
var devicePreferredNetworkName: String? = null
if (!coreIsDevicePreferred) {
if (appIsDevicePreferred) {
// Update or remove the device preferred credential to match core state.
Expand Down Expand Up @@ -169,6 +172,27 @@ class ThreadManagerImpl @Inject constructor(
)
}
Timber.d("Thread update device completed: deleted ${localIds.size} datasets, updated 1")
// ThreadNetworkClient.addCredentials does not promote the new
// credential to preferred. Verify what Play Services chose so the
// caller can report honestly instead of assuming success.
deviceNowPrefersCore = try {
isPreferredDatasetByDevice(context, coreThreadDataset.datasetId, serverId)
} catch (e: Exception) {
Timber.w(e, "Unable to verify preferred dataset after import")
null
Comment on lines +178 to +182
}
Timber.d(
"Thread: after import device ${
when (deviceNowPrefersCore) {
true -> "now prefers"
false -> "still doesn't prefer"
null -> "preference state unknown for"
}
} core preferred dataset",
)
if (deviceNowPrefersCore == false) {
devicePreferredNetworkName = appAddedPreferredCredential(context)?.networkName
}
true
} else { // Core prefers imported from other app, this shouldn't be managed by HA
localIds.forEach { baId ->
Expand Down Expand Up @@ -197,6 +221,8 @@ class ThreadManagerImpl @Inject constructor(
matches = coreIsDevicePreferred,
fromApp = appIsDevicePreferred,
updated = updated,
deviceNowPrefersCore = deviceNowPrefersCore,
devicePreferredNetworkName = devicePreferredNetworkName,
exportIntent = if (exportFromDevice) deviceThreadIntent else null,
)
} catch (e: Exception) {
Expand All @@ -205,6 +231,8 @@ class ThreadManagerImpl @Inject constructor(
matches = null,
fromApp = null,
updated = null,
deviceNowPrefersCore = null,
devicePreferredNetworkName = null,
exportIntent = null,
)
}
Expand Down Expand Up @@ -262,28 +290,26 @@ class ThreadManagerImpl @Inject constructor(
}
}

private suspend fun appAddedIsPreferredCredentials(context: Context): Boolean {
@OptIn(ExperimentalStdlibApi::class)
private suspend fun appAddedPreferredCredential(context: Context): ThreadNetworkCredentials? {
val appCredentials = suspendCoroutine { cont ->
ThreadNetwork.getNetworkClient(context)
.allCredentials
.addOnSuccessListener { cont.resume(it) }
.addOnFailureListener { cont.resume(null) }
}
return try {
appCredentials?.any {
val isPreferred = isPreferredCredentials(context, it)
if (isPreferred) {
Timber.d(
"Thread device prefers app added dataset: ${it.networkName} (PAN ${it.panId}, EXTPAN ${String(
it.extendedPanId,
)})",
)
}
isPreferred
} ?: false
appCredentials?.firstOrNull { isPreferredCredentials(context, it) }?.also {
Timber.d(
"Thread device prefers app added dataset: %s (PAN %s, EXTPAN %s)",
it.networkName,
it.panId,
it.extendedPanId.toHexString(HexFormat.UpperCase),
)
}
} catch (e: Exception) {
Timber.e(e, "Thread app added credentials preferred check failed")
false
null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,21 @@ class DeveloperSettingsPresenterImpl @Inject constructor(
} else if (syncResult.matches == true) {
view.onThreadDebugResult(context.getString(commonR.string.thread_debug_result_match), true)
} else if (syncResult.fromApp == true && syncResult.updated == true) {
view.onThreadDebugResult(
context.getString(commonR.string.thread_debug_result_updated),
true,
)
val message = if (syncResult.deviceNowPrefersCore == false) {
val base = context.getString(commonR.string.thread_debug_result_updated_not_preferred)
val name = syncResult.devicePreferredNetworkName
if (name != null) {
"$base ${context.getString(
commonR.string.thread_debug_result_mismatch_detail,
name,
)}"
} else {
base
}
} else {
context.getString(commonR.string.thread_debug_result_updated)
}
view.onThreadDebugResult(message, syncResult.deviceNowPrefersCore != false)
} else if (syncResult.fromApp == true && syncResult.updated == false) {
view.onThreadDebugResult(
context.getString(commonR.string.thread_debug_result_removed),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface ThreadManager {
val matches: Boolean?,
val fromApp: Boolean?,
val updated: Boolean?,
val deviceNowPrefersCore: Boolean?,
val devicePreferredNetworkName: String?,
val exportIntent: IntentSender?,
) : SyncResult()
object NoneHaveCredentials : SyncResult()
Expand Down
1 change: 1 addition & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,7 @@
<string name="thread_debug_result_removed">Removed old network from Home Assistant on this device</string>
<string name="thread_debug_result_unsupported_server">The Home Assistant server does not support Thread</string>
<string name="thread_debug_result_updated">Updated network from Home Assistant on this device</string>
<string name="thread_debug_result_updated_not_preferred">Updated network from Home Assistant on this device, but the device still prefers a different network</string>
<string name="thread_debug_summary">Manually update device and server Thread credentials and verify results</string>
<string name="thread_export_success">Imported credential</string>
<string name="thread_export_none">You don\'t have any credentials to import.</string>
Expand Down
Loading