EN: Make service more robust against exceptions while processing app input

This commit is contained in:
Marvin W 2020-12-27 17:09:51 +01:00
parent 45d4dffb88
commit 91c8e43ab9
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A

View File

@ -12,6 +12,7 @@ import android.content.*
import android.os.*
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.common.api.Status
@ -19,6 +20,8 @@ import com.google.android.gms.nearby.exposurenotification.*
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.*
import com.google.android.gms.nearby.exposurenotification.internal.*
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.withTimeout
import org.json.JSONArray
import org.json.JSONObject
@ -37,6 +40,8 @@ import kotlin.random.Random
class ExposureNotificationServiceImpl(private val context: Context, private val lifecycle: Lifecycle, private val packageName: String) : INearbyExposureNotificationService.Stub(), LifecycleOwner {
private fun LifecycleCoroutineScope.launchSafely(block: suspend CoroutineScope.() -> Unit): Job = launchWhenStarted { try { block() } catch (e: Exception) { Log.w(TAG, "Error in coroutine", e) } }
override fun getLifecycle(): Lifecycle = lifecycle
private fun pendingConfirm(permission: String): PendingIntent {
@ -102,7 +107,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun start(params: StartParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val isAuthorized = ExposureDatabase.with(context) { it.isAppAuthorized(packageName) }
val adapter = BluetoothAdapter.getDefaultAdapter()
val status = if (isAuthorized && ExposurePreferences(context).enabled) {
@ -131,7 +136,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun stop(params: StopParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val isAuthorized = ExposureDatabase.with(context) { database ->
database.isAppAuthorized(packageName).also {
if (it) database.noteAppAction(packageName, "stop")
@ -149,7 +154,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun isEnabled(params: IsEnabledParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val isAuthorized = ExposureDatabase.with(context) { database ->
database.isAppAuthorized(packageName)
}
@ -162,7 +167,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getTemporaryExposureKeyHistory(params: GetTemporaryExposureKeyHistoryParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val status = confirmPermission(CONFIRM_ACTION_KEYS)
val response = when {
status.isSuccess -> ExposureDatabase.with(context) { database ->
@ -243,7 +248,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
override fun provideDiagnosisKeys(params: ProvideDiagnosisKeysParams) {
val token = params.token ?: TOKEN_A
Log.w(TAG, "provideDiagnosisKeys() with $packageName/$token")
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val tid = ExposureDatabase.with(context) { database ->
val configuration = params.configuration
if (configuration != null) {
@ -259,7 +264,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
} catch (e: Exception) {
Log.w(TAG, "Callback failed", e)
}
return@launchWhenStarted
return@launchSafely
}
ExposureDatabase.with(context) { database ->
val start = System.currentTimeMillis()
@ -289,21 +294,25 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
params.keyFileSupplier?.let { keyFileSupplier ->
Log.d(TAG, "Using key file supplier")
while (keyFileSupplier.isAvailable && keyFileSupplier.hasNext()) {
try {
val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextInt()}.zip")
ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) }
val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile)
val storedKeys = database.storeDiagnosisFileUsed(tid, hash)
if (storedKeys != null) {
keys += storedKeys.toInt()
cacheFile.delete()
} else {
todoKeyFiles.add(cacheFile to hash)
try {
while (keyFileSupplier.isAvailable && keyFileSupplier.hasNext()) {
try {
val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextInt()}.zip")
ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) }
val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile)
val storedKeys = database.storeDiagnosisFileUsed(tid, hash)
if (storedKeys != null) {
keys += storedKeys.toInt()
cacheFile.delete()
} else {
todoKeyFiles.add(cacheFile to hash)
}
} catch (e: Exception) {
Log.w(TAG, "Failed parsing file", e)
}
} catch (e: Exception) {
Log.w(TAG, "Failed parsing file", e)
}
} catch (e: Exception) {
Log.w(TAG, "Disconnected from key file supplier", e)
}
}
@ -389,7 +398,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getExposureSummary(params: GetExposureSummaryParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val response = buildExposureSummary(params.token)
ExposureDatabase.with(context) { database ->
@ -413,7 +422,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getExposureInformation(params: GetExposureInformationParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
ExposureDatabase.with(context) { database ->
val pair = database.loadConfiguration(packageName, params.token)
val response = if (pair != null && database.isAppAuthorized(packageName)) {
@ -494,7 +503,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getExposureWindows(params: GetExposureWindowsParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val response = getExposureWindowsInternal(params.token ?: TOKEN_A)
ExposureDatabase.with(context) { database ->
@ -529,7 +538,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getDailySummaries(params: GetDailySummariesParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val response = getExposureWindowsInternal().groupBy { it.dateMillisSinceEpoch }.map {
val map = arrayListOf<DailySummary.ExposureSummaryData>()
for (i in 0 until ReportType.VALUES) {
@ -564,7 +573,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun setDiagnosisKeysDataMapping(params: SetDiagnosisKeysDataMappingParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
ExposureDatabase.with(context) { database ->
database.storeConfiguration(packageName, TOKEN_A, params.mapping)
database.noteAppAction(packageName, "setDiagnosisKeysDataMapping")
@ -578,7 +587,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
}
override fun getDiagnosisKeysDataMapping(params: GetDiagnosisKeysDataMappingParams) {
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
val mapping = ExposureDatabase.with(context) { database ->
val triple = database.loadConfiguration(packageName, TOKEN_A)
database.noteAppAction(packageName, "getDiagnosisKeysDataMapping")
@ -594,7 +603,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
override fun getPackageConfiguration(params: GetPackageConfigurationParams) {
Log.w(TAG, "Not yet implemented: getPackageConfiguration")
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
ExposureDatabase.with(context) { database ->
database.noteAppAction(packageName, "getPackageConfiguration")
}
@ -608,7 +617,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
override fun getStatus(params: GetStatusParams) {
Log.w(TAG, "Not yet implemented: getStatus")
lifecycleScope.launchWhenStarted {
lifecycleScope.launchSafely {
ExposureDatabase.with(context) { database ->
database.noteAppAction(packageName, "getStatus")
}