Commit 323852a8 authored by stopcovid@lunabee.com's avatar stopcovid@lunabee.com

Update to 1.0.0

parent 9bba4949
# Authors
* Naos <dev1(dot)stopcovid(at)orange(dot)com>
* Merak <dev2(dot)stopcovid(at)orange(dot)com>
* Wezen <dev3(dot)stopcovid(at)orange(dot)com>
* Kochab <dev4(dot)stopcovid(at)orange(dot)com>
* Régulus <dev5(dot)stopcovid(at)orange(dot)com>
* Eltanin <dev6(dot)stopcovid(at)orange(dot)com>
* Arcturus <dev7(dot)stopcovid(at)orange(dot)com>
* Bételgeuse <dev8(dot)stopcovid(at)orange(dot)com>
* Nunki <dev9(dot)stopcovid(at)orange(dot)com>
* Wei <dev10(dot)stopcovid(at)orange(dot)com>
* Murzim <dev11(dot)stopcovid(at)orange(dot)com>
* Sirius <dev20(dot)stopcovid(at)orange(dot)com>
......@@ -17,8 +17,8 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 6
versionName "0.3.1"
versionCode 8
versionName "1.0.0"
}
compileOptions {
......
......@@ -8,7 +8,8 @@
~ Created by Orange / Date - 2020/04/27 - for the STOP-COVID project
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.orange.proximitynotification">
......@@ -29,5 +30,4 @@
</application>
</manifest>
......@@ -10,7 +10,7 @@
package com.orange.proximitynotification
import java.util.*
import java.util.Date
interface ProximityMetadata
......
......@@ -11,8 +11,12 @@
package com.orange.proximitynotification
import com.orange.proximitynotification.ble.BleProximityMetadata
import java.util.*
import kotlin.math.*
import java.util.Date
import kotlin.math.ceil
import kotlin.math.exp
import kotlin.math.floor
import kotlin.math.ln
import kotlin.math.min
/**
* ProximityInfo risk computer
......@@ -45,7 +49,7 @@ class ProximityInfoRiskComputer {
// Fading compensation
val timestampedRssis = proximityInfos.mapNotNull { proximityInfo ->
val metadata = proximityInfo.metadata as? BleProximityMetadata
val metadata = proximityInfo.metadata as? BleProximityMetadata
return@mapNotNull metadata?.let {
val timestampDelta = (proximityInfo.timestamp.time - from.time) / 1_000
......@@ -67,7 +71,7 @@ class ProximityInfoRiskComputer {
val score = range.fold(0.0) { partialScore, minute ->
val rssis = groupedRssis[minute] + groupedRssis[minute + 1]
return@fold if(rssis.isEmpty()) {
return@fold if (rssis.isEmpty()) {
0.0
} else {
val averageRssi = softmax(rssis)
......
......@@ -12,5 +12,5 @@ package com.orange.proximitynotification
interface ProximityNotificationCallback {
fun onProximity(proximityInfo: ProximityInfo)
fun onError(error : ProximityNotificationError)
fun onError(error: ProximityNotificationError)
}
\ No newline at end of file
......@@ -10,12 +10,12 @@
package com.orange.proximitynotification
data class ProximityNotificationError(val type: Type, val rootErrorCode: Int? = null, val cause : String? = null) {
data class ProximityNotificationError(val type: Type, val rootErrorCode: Int? = null, val cause: String? = null) {
enum class Type {
/**
* BLE advertising error
*/
*/
BLE_ADVERTISER,
/**
......
......@@ -174,7 +174,6 @@ abstract class ProximityNotificationService : Service(),
doStart()
}
/**
* Create the Notification to display once this foreground service is running
*
......
......@@ -38,5 +38,4 @@ data class ProximityPayload(val data: ByteArray) {
return data.contentHashCode()
}
}
\ No newline at end of file
......@@ -29,7 +29,6 @@ data class BlePayload(
private const val SIZE = PROXIMITY_PAYLOAD_SIZE + VERSION_SIZE + TX_POWER_LEVEL_SIZE
fun from(data: ByteArray): BlePayload {
require(data.size >= SIZE) { "Expecting a byte array of $SIZE bytes. Got ${data.size}." }
......
......@@ -11,8 +11,11 @@
package com.orange.proximitynotification.ble
import android.bluetooth.BluetoothDevice
import android.util.Log
import com.orange.proximitynotification.*
import com.orange.proximitynotification.ProximityInfo
import com.orange.proximitynotification.ProximityNotification
import com.orange.proximitynotification.ProximityNotificationCallback
import com.orange.proximitynotification.ProximityNotificationError
import com.orange.proximitynotification.ProximityPayloadProvider
import com.orange.proximitynotification.ble.advertiser.BleAdvertiser
import com.orange.proximitynotification.ble.gatt.BleGattManager
import com.orange.proximitynotification.ble.scanner.BleScannedDevice
......@@ -111,7 +114,7 @@ class BleProximityNotification(
BleScannedDevice(device = device, rssi = rssi)
bleRecordProviderForScanWithoutPayload.fromScan(scannedDevice, payload)
}
}
}
}?.let { notifyProximity(it) }
}
}
......@@ -168,7 +171,6 @@ class BleProximityNotification(
}
private fun notifyProximity(proximityInfo: ProximityInfo) {
Log.d(TAG, "Proximity notification (proximityInfo=$proximityInfo")
callback.onProximity(proximityInfo)
}
......
......@@ -10,7 +10,7 @@
package com.orange.proximitynotification.ble
import java.util.*
import java.util.Date
internal data class BleRecord(
val payload: BlePayload,
......
......@@ -10,7 +10,7 @@
package com.orange.proximitynotification.ble
import java.util.*
import java.util.UUID
/**
* @param serviceUuid Service UUID used by BLE advertiser / scanner
......
......@@ -17,7 +17,7 @@ import com.orange.proximitynotification.tools.ExpiringCache
internal class RecordProviderForScanWithoutPayload(
settings: BleSettings,
private val maxCacheSize: Int = 1000
) : BleRecordProvider(){
) : BleRecordProvider() {
internal val lastPayloadByDeviceId =
ExpiringCache<DeviceId, BlePayload>(
......
......@@ -13,12 +13,12 @@ package com.orange.proximitynotification.ble.advertiser
import com.orange.proximitynotification.ble.BleSettings
interface BleAdvertiser {
val settings : BleSettings
val settings: BleSettings
interface Callback {
fun onError(errorCode: Int)
}
fun start(data : ByteArray, callback: Callback)
fun start(data: ByteArray, callback: Callback)
fun stop()
}
\ No newline at end of file
......@@ -45,19 +45,14 @@ internal class BleGattClientImpl(
private var bluetoothGatt: BluetoothGatt? = null
override suspend fun open() {
Log.d(TAG, "Connecting (device=$bluetoothDevice)")
bluetoothGatt = bluetoothDevice.connectGattCompat(context, Callback())
connectionStateChannel.receive() // suspends until connectionStateChanged is received
check(isConnected)
Log.d(TAG, "Connected (device=$bluetoothDevice)")
}
override suspend fun close() {
if (!isClosed) {
Log.d(TAG, "Closing (device=$bluetoothDevice)")
doClose()
} else {
Log.d(TAG, "Already closed (device=$bluetoothDevice)")
}
}
......
......@@ -10,18 +10,28 @@
package com.orange.proximitynotification.ble.gatt
import android.bluetooth.*
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattServer
import android.bluetooth.BluetoothGattServerCallback
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.content.Context
import android.util.Log
import com.orange.proximitynotification.ble.BleSettings
import com.orange.proximitynotification.tools.CoroutineContextProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import kotlinx.coroutines.channels.receiveOrNull
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
internal class BleGattManagerImpl(
override val settings: BleSettings,
......@@ -150,10 +160,7 @@ internal class BleGattManagerImpl(
value
)
Log.d(
TAG,
"onCharacteristicWriteRequest device=$device, characteristic=${characteristic.uuid}"
)
Log.d(TAG, "onCharacteristicWriteRequest")
val result = when (characteristic.uuid) {
payloadCharacteristic.uuid -> {
......@@ -168,10 +175,7 @@ internal class BleGattManagerImpl(
}
Log.d(
TAG,
"onCharacteristicWriteRequest result=$result device=$device requestId=$requestId characteristic=${characteristic.uuid} preparedWrite=$preparedWrite responseNeeded=$responseNeeded offset=$offset value=$value"
)
Log.d(TAG, "onCharacteristicWriteRequest result=$result")
if (responseNeeded) {
bluetoothGattServer?.sendResponse(device, requestId, result, offset, null)
}
......@@ -179,5 +183,4 @@ internal class BleGattManagerImpl(
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ package com.orange.proximitynotification.ble.scanner
import android.bluetooth.BluetoothDevice
import com.orange.proximitynotification.ble.BleSettings
import java.util.*
import java.util.Date
data class BleScannedDevice(
val device: BluetoothDevice,
......
......@@ -13,7 +13,11 @@ package com.orange.proximitynotification.ble.scanner
import android.os.ParcelUuid
import android.util.Log
import com.orange.proximitynotification.ble.BleSettings
import no.nordicsemi.android.support.v18.scanner.*
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat
import no.nordicsemi.android.support.v18.scanner.ScanCallback
import no.nordicsemi.android.support.v18.scanner.ScanFilter
import no.nordicsemi.android.support.v18.scanner.ScanResult
import no.nordicsemi.android.support.v18.scanner.ScanSettings
class BleScannerImpl(
override val settings: BleSettings,
......@@ -54,7 +58,6 @@ class BleScannerImpl(
scanCallback = null
}
private fun buildScanSettings(): ScanSettings {
return ScanSettings.Builder()
.setLegacy(true)
......@@ -86,8 +89,6 @@ class BleScannerImpl(
override fun onBatchScanResults(results: List<ScanResult>) {
super.onBatchScanResults(results)
Log.d(TAG, "onBatchScanResults results = $results")
results
.distinctBy { it.device }
.toBleScannedDevices(settings.serviceUuid)
......@@ -101,7 +102,6 @@ class BleScannerImpl(
) {
super.onScanResult(callbackType, result)
Log.d(TAG, "onScanResult results = $result")
callback.onResult(listOf(result.toBleScannedDevice(settings.serviceUuid)))
}
......
......@@ -12,7 +12,7 @@ package com.orange.proximitynotification.ble.scanner
import android.os.ParcelUuid
import no.nordicsemi.android.support.v18.scanner.ScanResult
import java.util.*
import java.util.UUID
internal fun ScanResult.toBleScannedDevice(serviceUuid: UUID): BleScannedDevice =
BleScannedDevice(
......
......@@ -20,7 +20,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
import java.util.Date
@RunWith(ZohhakRunner::class)
class ProximityInfoRiskComputerTest {
......@@ -97,7 +97,6 @@ class ProximityInfoRiskComputerTest {
}
}
val score = scores.getOrNull(index)
if (score == null) {
throw Exception("Could not parse score at index $index in risk_computer_dataset.csv")
......
......@@ -84,7 +84,7 @@ class BlePayloadTest {
// Given
val proximityPayload = ProximityPayload((1..16).map { it.toByte() }.toByteArray())
val payload = payload(proximityPayload= proximityPayload, version = 2, txPowerLevel = -1)
val payload = payload(proximityPayload = proximityPayload, version = 2, txPowerLevel = -1)
val expected: ByteArray =
byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, -1)
......
......@@ -13,8 +13,20 @@ package com.orange.proximitynotification.ble
import android.bluetooth.BluetoothDevice
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.*
import com.orange.proximitynotification.*
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.atLeastOnce
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import com.orange.proximitynotification.CoroutineTestRule
import com.orange.proximitynotification.ProximityInfo
import com.orange.proximitynotification.ProximityNotificationCallback
import com.orange.proximitynotification.ProximityNotificationError
import com.orange.proximitynotification.ProximityPayloadProvider
import com.orange.proximitynotification.ble.advertiser.BleAdvertiser
import com.orange.proximitynotification.ble.calibration.BleRssiCalibration
import com.orange.proximitynotification.ble.gatt.BleGattManager
......@@ -25,7 +37,7 @@ import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
import java.util.Date
@RunWith(AndroidJUnit4::class)
class BleProximityNotificationTest {
......@@ -95,7 +107,6 @@ class BleProximityNotificationTest {
assertThat(callbackSucceed).isTrue()
}
@Test
fun scanner_with_bad_result_should_never_call_onProximity() =
testCoroutineRule.runBlockingTest {
......@@ -121,7 +132,6 @@ class BleProximityNotificationTest {
// Then
}
@Test
fun scanner_on_error_should_call_onError() = testCoroutineRule.runBlockingTest {
......@@ -220,7 +230,7 @@ class BleProximityNotificationTest {
bleScannedDevice(device = bluetoothDevice, serviceData = null, rssi = 6)
// Ensure gattCallback is called after scan callback
lateinit var gattCallback : BleGattManager.Callback
lateinit var gattCallback: BleGattManager.Callback
whenever(bleGattManager.start(any())).thenAnswer {
gattCallback = it.arguments[0] as BleGattManager.Callback
null
......
......@@ -11,11 +11,15 @@
package com.orange.proximitynotification.ble
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.*
import com.nhaarman.mockitokotlin2.atLeastOnce
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import com.orange.proximitynotification.ProximityInfo
import com.orange.proximitynotification.ble.calibration.BleRssiCalibration
import org.junit.Test
import java.util.*
import java.util.Date
class BleRecordMapperTest {
......
......@@ -15,7 +15,7 @@ import com.googlecode.zohhak.api.TestWith
import com.googlecode.zohhak.api.runners.ZohhakRunner
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
import java.util.Date
@RunWith(ZohhakRunner::class)
class BleRecordTest {
......
......@@ -17,13 +17,13 @@ import com.orange.proximitynotification.ProximityInfo
import com.orange.proximitynotification.ProximityMetadata
import com.orange.proximitynotification.ProximityPayload
import com.orange.proximitynotification.ble.scanner.BleScannedDevice
import java.util.*
import java.util.Date
internal fun proximityPayload() = ProximityPayload((1..16).map { it.toByte() }.toByteArray())
internal fun record(
payload: BlePayload = payload(),
scannedDevice : BleScannedDevice
scannedDevice: BleScannedDevice
) = record(payload = payload, rssi = scannedDevice.rssi, timestamp = scannedDevice.timestamp)
internal fun record(
......
......@@ -15,7 +15,7 @@ import com.google.common.truth.Truth.assertThat
import com.orange.proximitynotification.ble.scanner.BleScannedDevice
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
import java.util.Date
@RunWith(AndroidJUnit4::class)
class RecordProviderForScanWithPayloadTest {
......
......@@ -20,7 +20,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.shadows.ShadowSystemClock
import java.time.Duration
import java.util.*
import java.util.Date
@RunWith(AndroidJUnit4::class)
class RecordProviderForScanWithoutPayloadTest {
......@@ -32,7 +32,7 @@ class RecordProviderForScanWithoutPayloadTest {
private val bleRecordProvider = RecordProviderForScanWithoutPayload(mock {
on { cacheTimeout } doReturn CACHE_TIMEOUT
}, maxCacheSize = MAX_CACHE_SIZE)
}, maxCacheSize = MAX_CACHE_SIZE)
@Test
fun fromScan_with_payload_should_return_new_record() {
......@@ -257,7 +257,6 @@ class RecordProviderForScanWithoutPayloadTest {
assertThat(bleRecordProvider.lastPayloadByDeviceId.size()).isEqualTo(2)
}
private fun givenScanAndPayload(scannedDevices: BleScannedDevice, payload: BlePayload) {
bleRecordProvider.fromScan(scannedDevices, payload)
}
......
......@@ -26,7 +26,7 @@ class BleGattClientImplTest {
@get:Rule
val testCoroutineRule = CoroutineTestRule()
private val context : Context = mock()
private val context: Context = mock()
@Test
fun close_given_open_not_called_should_not_fail() = testCoroutineRule.runBlockingTest {
......
......@@ -15,7 +15,15 @@ import android.bluetooth.BluetoothManager
import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.*
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.atLeastOnce
import com.nhaarman.mockitokotlin2.doAnswer
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import com.orange.proximitynotification.CoroutineTestRule
import com.orange.proximitynotification.ble.BleSettings
import com.orange.proximitynotification.ble.bluetoothDevice
......@@ -91,7 +99,6 @@ class BleGattManagerImplTest {
verify(bleGattClientProvider, times(3)).fromDevice(any())
}
@Test
fun requestRemoteRssi_given_gattClient_in_success_should_return_remote_rssi() =
testCoroutineRule.runBlockingTest {
......@@ -115,7 +122,6 @@ class BleGattManagerImplTest {
assertThat(result).isEqualTo(rssi)
}
@Test
fun requestRemoteRssi_given_gattClient_with_error_should_return_null() =
testCoroutineRule.runBlockingTest {
......@@ -138,7 +144,6 @@ class BleGattManagerImplTest {
assertThat(result).isNull()
}
private suspend fun <T> onLifecycle(
callback: BleGattManager.Callback,
block: suspend (bleGattManager: BleGattManager) -> T
......
......@@ -21,7 +21,7 @@ import com.nhaarman.mockitokotlin2.whenever
import no.nordicsemi.android.support.v18.scanner.ScanRecord
import no.nordicsemi.android.support.v18.scanner.ScanResult