From 44238f01cc113a90db0be71cb0ab56129b27dcae Mon Sep 17 00:00:00 2001
From: BMItter <46747780+BMItter@users.noreply.github.com>
Date: Tue, 24 Nov 2020 11:08:28 +0100
Subject: [PATCH] Adjust Diagnosis key file provider to prevent deprecation
 (EXPOSUREAPP-3896) (#1700)

* Added isAtLeast to ENFVersion

* Use provideDiagnosisKeys with DiagnosisKeyFileProvider on supported version

* better Version of DefaultENFVersion

* Added tests for DefaultENFVersion

* added missing onSuccessListener

* Adjusted tests for new methods
---
 .../DefaultDiagnosisKeyProvider.kt            | 14 +++++-
 .../modules/version/DefaultENFVersion.kt      | 20 ++++++++
 .../nearby/modules/version/ENFVersion.kt      |  8 +++
 .../DefaultDiagnosisKeyProviderTest.kt        | 25 +++++++++-
 .../modules/version/DefaultENFVersionTest.kt  | 50 +++++++++++++++++++
 5 files changed, 113 insertions(+), 4 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
index 64f2e75d7..2c5924ec9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider
 
 import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.nearby.exposurenotification.DiagnosisKeyFileProvider
 import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
 import de.rki.coronawarnapp.exception.reporting.ReportingConstants
 import de.rki.coronawarnapp.nearby.modules.version.ENFVersion
@@ -35,10 +36,19 @@ class DefaultDiagnosisKeyProvider @Inject constructor(
 //            return false
         }
 
+        val keyFilesList = keyFiles.toList()
+        val provideDiagnosisKeysTask = if (enfVersion.isAtLeast(ENFVersion.V1_7)) {
+            Timber.i("Provide diagnosis keys with DiagnosisKeyFileProvider")
+            val diagnosisKeyFileProvider = DiagnosisKeyFileProvider(keyFilesList)
+            enfClient.provideDiagnosisKeys(diagnosisKeyFileProvider)
+        } else {
+            Timber.i("Provide diagnosis keys as list")
+            enfClient.provideDiagnosisKeys(keyFilesList)
+        }
+
         return suspendCoroutine { cont ->
             Timber.d("Performing key submission.")
-            enfClient
-                .provideDiagnosisKeys(keyFiles.toList())
+            provideDiagnosisKeysTask
                 .addOnSuccessListener { cont.resume(true) }
                 .addOnFailureListener {
                     val wrappedException =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
index 7652fb055..3ce93d482 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
@@ -9,6 +9,7 @@ import javax.inject.Singleton
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 import kotlin.coroutines.suspendCoroutine
+import kotlin.math.abs
 
 @Singleton
 class DefaultENFVersion @Inject constructor(
@@ -39,9 +40,28 @@ class DefaultENFVersion @Inject constructor(
         }
     }
 
+    override suspend fun isAtLeast(compareVersion: Long): Boolean {
+        if (!compareVersion.isCorrectVersionLength) throw IllegalArgumentException("given version has incorrect length")
+
+        getENFClientVersion()?.let { currentENFClientVersion ->
+            Timber.i("Comparing current ENF client version $currentENFClientVersion with $compareVersion")
+            return currentENFClientVersion >= compareVersion
+        }
+
+        return false
+    }
+
     private suspend fun internalGetENFClientVersion(): Long = suspendCoroutine { cont ->
         client.version
             .addOnSuccessListener { cont.resume(it) }
             .addOnFailureListener { cont.resumeWithException(it) }
     }
+
+    // check if a raw long has the correct length to be considered an API version
+    private val Long.isCorrectVersionLength
+        get(): Boolean = abs(this).toString().length == GOOGLE_API_VERSION_FIELD_LENGTH
+
+    companion object {
+        private const val GOOGLE_API_VERSION_FIELD_LENGTH = 8
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
index b7d16994a..7b1b8d830 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
@@ -12,7 +12,15 @@ interface ENFVersion {
      */
     suspend fun requireMinimumVersion(required: Long)
 
+    /**
+     * Indicates if the client runs above a certain version
+     *
+     * @return isAboveVersion, if connected to an old unsupported version, return false
+     */
+    suspend fun isAtLeast(compareVersion: Long): Boolean
+
     companion object {
         const val V1_6 = 16000000L
+        const val V1_7 = 17000000L
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
index da15e5721..d484333fe 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider
 
+import com.google.android.gms.nearby.exposurenotification.DiagnosisKeyFileProvider
 import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
 import de.rki.coronawarnapp.nearby.modules.version.ENFVersion
 import de.rki.coronawarnapp.nearby.modules.version.OutdatedENFVersionException
@@ -36,6 +37,8 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
 
         coEvery { googleENFClient.provideDiagnosisKeys(any<List<File>>()) } returns MockGMSTask.forValue(null)
 
+        coEvery { googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>()) } returns MockGMSTask.forValue(null)
+
         coEvery { enfVersion.requireMinimumVersion(any()) } returns Unit
     }
 
@@ -70,7 +73,23 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
     }
 
     @Test
-    fun `key provision is used on newer ENF versions`() {
+    fun `key provision is used with DiagnosisKeyFileProvider on ENF versions from 1_7 upwards`() {
+        coEvery { enfVersion.isAtLeast(any()) } returns true
+
+        val provider = createProvider()
+
+        runBlocking { provider.provideDiagnosisKeys(exampleKeyFiles) } shouldBe true
+
+        coVerifySequence {
+            submissionQuota.consumeQuota(1)
+            googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>())
+        }
+    }
+
+    @Test
+    fun `key provision is used with key list on ENF versions 1_6`() {
+        coEvery { enfVersion.isAtLeast(any()) } returns false
+
         val provider = createProvider()
 
         runBlocking { provider.provideDiagnosisKeys(exampleKeyFiles) } shouldBe true
@@ -81,9 +100,11 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
         }
     }
 
+
     @Test
     fun `quota is just monitored`() {
         coEvery { submissionQuota.consumeQuota(any()) } returns false
+        coEvery { enfVersion.isAtLeast(any()) } returns true
 
         val provider = createProvider()
 
@@ -91,7 +112,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
 
         coVerifySequence {
             submissionQuota.consumeQuota(1)
-            googleENFClient.provideDiagnosisKeys(exampleKeyFiles)
+            googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>())
         }
     }
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
index 6d9e563b9..48a020e44 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
@@ -109,4 +109,54 @@ internal class DefaultENFVersionTest {
             }
         }
     }
+
+    @Test
+    fun `isAtLeast is true for newer version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_7)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_6) shouldBe true
+        }
+    }
+
+    @Test
+    fun `isAtLeast is true for equal version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_6)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_6) shouldBe true
+        }
+    }
+
+    @Test
+    fun `isAtLeast is false for older version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_6)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_7) shouldBe false
+        }
+    }
+
+    @Test
+    fun `invalid input for isAtLeast throws IllegalArgumentException`() {
+        runBlockingTest {
+            shouldThrow<IllegalArgumentException> {
+                createInstance().isAtLeast(16)
+            }
+        }
+    }
+
+    @Test
+    fun `isAtLeast returns false when client not connected`() {
+        every { client.version } returns MockGMSTask.forError(ApiException(Status(API_NOT_CONNECTED)))
+
+        runBlockingTest {
+            createInstance().apply {
+                shouldNotThrowAny {
+                    isAtLeast(ENFVersion.V1_6) shouldBe false
+                    isAtLeast(ENFVersion.V1_7) shouldBe false
+                }
+            }
+        }
+    }
 }
-- 
GitLab