fix: harden reliability, clean up KMP compliance, and improve code quality (#5023)

This commit is contained in:
James Rich 2026-04-09 13:21:46 -05:00 committed by GitHub
parent 537029a71c
commit 14b381c1eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 370 additions and 409 deletions

View file

@ -18,9 +18,7 @@ package org.meshtastic.core.common
import android.Manifest
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
@ -80,18 +78,3 @@ fun Context.hasLocationPermission(): Boolean {
val perms = listOf(Manifest.permission.ACCESS_FINE_LOCATION)
return perms.all { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED }
}
/**
* Extension for Context to register a BroadcastReceiver in a compatible way across Android versions.
*
* @param receiver The receiver to register.
* @param filter The intent filter.
* @param flag The export flag (defaults to [ContextCompat.RECEIVER_EXPORTED]).
*/
fun Context.registerReceiverCompat(
receiver: BroadcastReceiver,
filter: IntentFilter,
flag: Int = ContextCompat.RECEIVER_EXPORTED,
) {
ContextCompat.registerReceiver(this, receiver, filter, flag)
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.common.util
import java.util.Date
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.time.Duration
import kotlin.time.Instant
/**
* Awaits the latch for the given [Duration].
*
* @param timeout The maximum time to wait.
* @return `true` if the count reached zero and `false` if the waiting time elapsed before the count reached zero.
*/
fun CountDownLatch.await(timeout: Duration): Boolean = this.await(timeout.inWholeMilliseconds, TimeUnit.MILLISECONDS)
/** Converts this [Instant] to a legacy [Date]. */
fun Instant.toDate(): Date = Date(this.toEpochMilliseconds())

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.common.util
/** A deferred execution object (with various possible implementations) */
interface Continuation<in T> {
fun resume(res: Result<T>)
/** Syntactic sugar for resuming with success. */
fun resumeSuccess(res: T) = resume(Result.success(res))
/** Syntactic sugar for resuming with failure. */
fun resumeWithException(ex: Throwable) = resume(Result.failure(ex))
}
/** An async continuation that calls a callback when the result is available. */
class CallbackContinuation<in T>(private val cb: (Result<T>) -> Unit) : Continuation<T> {
override fun resume(res: Result<T>) = cb(res)
}

View file

@ -1,83 +0,0 @@
/*
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.common.util
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
/**
* A blocking version of coroutine Continuation using traditional threading primitives.
*
* This is useful in contexts where coroutine suspension is not desirable or when bridging with legacy threaded code.
*/
class SyncContinuation<T> : Continuation<T> {
private val lock = ReentrantLock()
private val condition = lock.newCondition()
private var result: Result<T>? = null
override fun resume(res: Result<T>) {
lock.lock()
try {
result = res
condition.signal()
} finally {
lock.unlock()
}
}
/**
* Blocks the current thread until the result is available or the timeout expires.
*
* @param timeoutMsecs Maximum time to wait in milliseconds. If 0, waits indefinitely.
* @return The result of the operation.
* @throws IllegalStateException if a timeout occurs or if an internal error happens.
*/
@Suppress("NestedBlockDepth")
fun await(timeoutMsecs: Long = 0): T {
lock.lock()
try {
val startT = nowMillis
while (result == null) {
if (timeoutMsecs > 0) {
val remaining = timeoutMsecs - (nowMillis - startT)
check(remaining > 0) { "SyncContinuation timeout" }
condition.await(remaining, TimeUnit.MILLISECONDS)
} else {
condition.await()
}
}
val r = result
checkNotNull(r) { "Unexpected null result in SyncContinuation" }
return r.getOrThrow()
} finally {
lock.unlock()
}
}
}
/**
* Calls an [initfn] that is responsible for starting an operation and saving the [SyncContinuation]. Then blocks the
* current thread until the operation completes or times out.
*
* Essentially a blocking version of [kotlinx.coroutines.suspendCancellableCoroutine].
*/
fun <T> suspend(timeoutMsecs: Long = -1, initfn: (SyncContinuation<T>) -> Unit): T {
val cont = SyncContinuation<T>()
initfn(cont)
return cont.await(timeoutMsecs)
}