mirror of
https://github.com/home-assistant/android
synced 2024-07-22 10:54:12 +00:00
Send device-preferred Thread dataset if different from core (#3426)
- Expand the automatic Thread dataset sync on Matter commissioning to also send the device-preferred Thread dataset if it's different from core (it will either receive something new or update)
This commit is contained in:
parent
45c4e16074
commit
300edaa26b
|
@ -2,6 +2,7 @@ package io.homeassistant.companion.android.matter
|
|||
|
||||
import android.app.Application
|
||||
import android.content.IntentSender
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -22,6 +23,10 @@ class MatterCommissioningViewModel @Inject constructor(
|
|||
application: Application
|
||||
) : AndroidViewModel(application) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MatterCommissioningView"
|
||||
}
|
||||
|
||||
sealed class CommissioningFlowStep {
|
||||
object NotStarted : CommissioningFlowStep()
|
||||
object NotRegistered : CommissioningFlowStep()
|
||||
|
@ -84,11 +89,16 @@ class MatterCommissioningViewModel @Inject constructor(
|
|||
|
||||
suspend fun syncThreadIfNecessary(): IntentSender? {
|
||||
step = CommissioningFlowStep.Working
|
||||
return threadManager.syncPreferredDataset(
|
||||
getApplication<Application>().applicationContext,
|
||||
serverId,
|
||||
viewModelScope
|
||||
)
|
||||
return try {
|
||||
threadManager.syncPreferredDataset(
|
||||
getApplication<Application>().applicationContext,
|
||||
serverId,
|
||||
viewModelScope
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Unable to sync preferred Thread dataset, continuing", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun onThreadPermissionResult(result: ActivityResult, code: String) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.IntentSender
|
|||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import com.google.android.gms.threadnetwork.IsPreferredCredentialsResult
|
||||
import com.google.android.gms.threadnetwork.ThreadBorderAgent
|
||||
import com.google.android.gms.threadnetwork.ThreadNetwork
|
||||
import com.google.android.gms.threadnetwork.ThreadNetworkCredentials
|
||||
|
@ -40,6 +41,9 @@ class ThreadManagerImpl @Inject constructor(
|
|||
HomeAssistantVersion.fromString(config.version)?.isAtLeast(2023, 3, 0) == true
|
||||
}
|
||||
|
||||
private suspend fun getDatasetsFromServer(serverId: Int): List<ThreadDatasetResponse>? =
|
||||
serverManager.webSocketRepository(serverId).getThreadDatasets()
|
||||
|
||||
override suspend fun syncPreferredDataset(
|
||||
context: Context,
|
||||
serverId: Int,
|
||||
|
@ -48,9 +52,10 @@ class ThreadManagerImpl @Inject constructor(
|
|||
if (!appSupportsThread() || !coreSupportsThread(serverId)) return null
|
||||
|
||||
val getDeviceDataset = scope.async { getPreferredDatasetFromDevice(context) }
|
||||
val getCoreDataset = scope.async { getPreferredDatasetFromServer(serverId) }
|
||||
val getCoreDatasets = scope.async { getDatasetsFromServer(serverId) }
|
||||
val deviceThreadIntent = getDeviceDataset.await()
|
||||
val coreThreadDataset = getCoreDataset.await()
|
||||
val coreThreadDatasets = getCoreDatasets.await()
|
||||
val coreThreadDataset = coreThreadDatasets?.firstOrNull { it.preferred }
|
||||
|
||||
if (deviceThreadIntent == null && coreThreadDataset != null) {
|
||||
try {
|
||||
|
@ -62,15 +67,23 @@ class ThreadManagerImpl @Inject constructor(
|
|||
} else if (deviceThreadIntent != null && coreThreadDataset == null) {
|
||||
Log.d(TAG, "Thread export is ready")
|
||||
return deviceThreadIntent
|
||||
} // else if device and core both have or don't have datasets, continue
|
||||
} else if (deviceThreadIntent != null && coreThreadDataset != null) {
|
||||
try {
|
||||
val coreIsDevicePreferred = isPreferredDatasetByDevice(context, coreThreadDataset.datasetId, serverId)
|
||||
Log.d(TAG, "Thread: device ${if (coreIsDevicePreferred) "prefers" else "doesn't prefer" } core preferred dataset")
|
||||
if (!coreIsDevicePreferred) {
|
||||
return deviceThreadIntent // Import the dataset to core
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Thread device/core preferred comparison failed", e)
|
||||
}
|
||||
} // else if device and core both don't have datasets, continue
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun getPreferredDatasetFromServer(serverId: Int): ThreadDatasetResponse? {
|
||||
val datasets = serverManager.webSocketRepository(serverId).getThreadDatasets()
|
||||
return datasets?.firstOrNull { it.preferred }
|
||||
}
|
||||
override suspend fun getPreferredDatasetFromServer(serverId: Int): ThreadDatasetResponse? =
|
||||
getDatasetsFromServer(serverId)?.firstOrNull { it.preferred }
|
||||
|
||||
override suspend fun importDatasetFromServer(context: Context, datasetId: String, serverId: Int) {
|
||||
val tlv = serverManager.webSocketRepository(serverId).getThreadDatasetTlv(datasetId)?.tlvAsByteArray
|
||||
|
@ -96,6 +109,20 @@ class ThreadManagerImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun isPreferredDatasetByDevice(context: Context, datasetId: String, serverId: Int): Boolean {
|
||||
val tlv = serverManager.webSocketRepository(serverId).getThreadDatasetTlv(datasetId)?.tlvAsByteArray
|
||||
if (tlv != null) {
|
||||
val threadNetworkCredentials = ThreadNetworkCredentials.fromActiveOperationalDataset(tlv)
|
||||
return suspendCoroutine { cont ->
|
||||
ThreadNetwork.getClient(context)
|
||||
.isPreferredCredentials(threadNetworkCredentials)
|
||||
.addOnSuccessListener { cont.resume(it == IsPreferredCredentialsResult.PREFERRED_CREDENTIALS_MATCHED) }
|
||||
.addOnFailureListener { cont.resumeWithException(it) }
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun sendThreadDatasetExportResult(result: ActivityResult, serverId: Int) {
|
||||
if (result.resultCode == Activity.RESULT_OK && coreSupportsThread(serverId)) {
|
||||
val threadNetworkCredentials = ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!)
|
||||
|
|
|
@ -20,8 +20,8 @@ interface ThreadManager {
|
|||
|
||||
/**
|
||||
* Try to sync the preferred Thread dataset with the device and server. If one has a preferred
|
||||
* dataset while the other one doesn't, it will sync. If both have or don't have preferred
|
||||
* datasets, skip syncing.
|
||||
* dataset while the other one doesn't, it will sync. If both have preferred datasets, it will
|
||||
* send updated data to the server if needed. If neither has a preferred dataset, skip syncing.
|
||||
* @return [IntentSender] if permission is required to import the device dataset, `null` if
|
||||
* syncing completed or there is nothing to sync
|
||||
*/
|
||||
|
|
|
@ -332,7 +332,12 @@ class WebViewPresenterImpl @Inject constructor(
|
|||
_matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.REQUESTED)
|
||||
|
||||
mainScope.launch {
|
||||
val deviceThreadIntent = threadUseCase.syncPreferredDataset(context, serverId, this)
|
||||
val deviceThreadIntent = try {
|
||||
threadUseCase.syncPreferredDataset(context, serverId, this)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Unable to sync preferred Thread dataset, continuing", e)
|
||||
null
|
||||
}
|
||||
if (deviceThreadIntent != null) {
|
||||
matterCommissioningIntentSender = deviceThreadIntent
|
||||
_matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.THREAD_EXPORT_TO_SERVER)
|
||||
|
|
Loading…
Reference in a new issue