Skip to content

Commit

Permalink
Contact duration and distance (#123)
Browse files Browse the repository at this point in the history
* Compile Android specifics on desktop build too

* Add duration and distance to DB, add FFI distance parameter

* Revert "Compile Android specifics on desktop build too"

Causing problems with CI, reason unclear as it's supposed to also use
MacOs

This reverts commit b48a649.

* Clear db before each instrumentation test

* Exposure grouping

* Batched updates

* Cleanup

* Fix timer not firing

* Set actual time

* Regenerate iOS headers

* Adjust JNI

* Set timer period to 10s

* Implement TCNs IN query

* Separate exposures in DB, merge TCNs in batch

* Remove not needed Arc

* Add comments

* Fix transaction function error doesn't make operation fail

* Add average distance

* Remove outdated function

* Replace iterators with loop

Should be better for performance

* Add avg distance to Android interface

* Release mutex lock when done accessing tcns

* Remove unnecessary lock

* More idiomatic way to release the lock

* Improve logs
  • Loading branch information
ivnsch authored Jul 22, 2020
1 parent fb566da commit 9bb43af
Show file tree
Hide file tree
Showing 16 changed files with 1,418 additions and 157 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ rand = "0.7"
hex = "0.4.2"
serde-big-array = "0.3.0"
rayon = "1.1"
rusqlite = {version = "0.23.1", features = ["bundled"]}
rusqlite = {version = "0.23.1", features = ["bundled", "vtab", "array"]}
timer = "0.2.0"

[dependencies.reqwest]
default-features = false # do not include the default features, and optionally
Expand Down
6 changes: 6 additions & 0 deletions android/core/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
consumerProguardFiles 'consumer-rules.pro'
}

testOptions {
execution 'androidx_test_orchestrator'
}

buildTypes {
release {
minifyEnabled false
Expand All @@ -42,6 +47,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestUtil 'androidx.test:orchestrator:1.2.0'

androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class JNIInterfaceBootstrappedTests {

@Test
fun recordTcn() {
val value = JniApi().recordTcn("2485a64b57addcaea3ed1b538d07dbce")
val value = JniApi().recordTcn("2485a64b57addcaea3ed1b538d07dbce", 34.03f)
assertEquals(JniVoidResult(1, ""), value)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class JNIInterfaceTests {
runnyNose = true,
other = false,
noSymptoms = true
), 1592567315
), 1592567315, 1592567335, 1.2f, 2.1f
)
),
value
Expand All @@ -75,7 +75,7 @@ class JNIInterfaceTests {
runnyNose = true,
other = false,
noSymptoms = true
), 1592567315
), 1592567315, 1592567335, 1.2f, 2.1f
),
JniAlert(
"343356", JniPublicReport(
Expand All @@ -90,7 +90,7 @@ class JNIInterfaceTests {
runnyNose = true,
other = false,
noSymptoms = true
), 1592567315
), 1592567315, 1592567335, 1.2f, 2.1f
)
)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ data class Alert(
val runnyNose: Boolean,
val other: Boolean,
val noSymptoms: Boolean, // https://github.com/Co-Epi/app-ios/issues/268#issuecomment-645583717
var contactTime: UnixTime
var contactStart: UnixTime,
var contactEnd: UnixTime,
var minDistance: Float,
var avgDistance: Float
) : Parcelable

enum class FeverSeverity {
Expand Down
7 changes: 5 additions & 2 deletions android/core/core/src/main/java/org/coepi/core/jni/JniApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class JniApi {

external fun generateTcn(): String

external fun recordTcn(tcn: String): JniVoidResult
external fun recordTcn(tcn: String, distance: Float): JniVoidResult

// TODO test:
external fun setBreathlessnessCause(cause: String): JniVoidResult
Expand Down Expand Up @@ -133,7 +133,10 @@ data class JniAlertsArrayResult(
data class JniAlert(
var id: String,
var report: JniPublicReport,
var contactTime: Long
var contactStart: Long,
var contactEnd: Long,
var minDistance: Float,
var avgDistance: Float
)

data class JniPublicReport(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,21 @@ class AlertsFetcherImpl(private val api: JniApi) :

private fun JniAlert.toAlert() = Alert(
id = id,
contactTime = when {
contactTime < 0 -> error("Invalid contact time: $contactTime")
else -> UnixTime.fromValue(contactTime)
contactStart = when {
contactStart < 0 -> error("Invalid contact start: $contactStart")
else -> UnixTime.fromValue(contactStart)
},
contactEnd = when {
contactEnd < 0 -> error("Invalid contact end: $contactEnd")
else -> UnixTime.fromValue(contactEnd)
},
minDistance = when {
minDistance < 0 -> error("Invalid min distance: $minDistance")
else -> minDistance
},
avgDistance = when {
avgDistance < 0 -> error("Invalid avg distance: $avgDistance")
else -> avgDistance
},
reportTime = when {
report.reportTime < 0 -> error("Invalid report time: ${report.reportTime}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import org.coepi.core.domain.model.Tcn
import org.coepi.core.domain.common.Result

interface ObservedTcnsRecorder {
fun recordTcn(tcn: Tcn): Result<Unit, Throwable>
fun recordTcn(tcn: Tcn, distance: Float): Result<Unit, Throwable>
}

class ObservedTcnsRecorderImpl(private val api: JniApi) :
ObservedTcnsRecorder {
override fun recordTcn(tcn: Tcn): Result<Unit, Throwable> =
api.recordTcn(tcn.toHex()).asResult()
override fun recordTcn(tcn: Tcn, distance: Float): Result<Unit, Throwable> =
api.recordTcn(tcn.toHex(), distance).asResult()
}
34 changes: 25 additions & 9 deletions src/android/android_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ pub unsafe extern "C" fn Java_org_coepi_core_jni_JniApi_recordTcn(
env: JNIEnv,
_: JClass,
tcn: JString,
distance: jfloat,
) -> jobject {
recordTcn(&env, tcn).to_void_jni(&env)
recordTcn(&env, tcn, distance).to_void_jni(&env)
}

// NOTE: Returns directly success string
Expand Down Expand Up @@ -262,11 +263,13 @@ fn fetch_new_reports(env: &JNIEnv) -> Result<jobjectArray, ServicesError> {
alerts_to_jobject_array(result, &env)
}

fn recordTcn(env: &JNIEnv, tcn: JString) -> Result<(), ServicesError> {
fn recordTcn(env: &JNIEnv, tcn: JString, distance: jfloat) -> Result<(), ServicesError> {
let tcn_java_str = env.get_string(tcn)?;
let tcn_str = tcn_java_str.to_str()?;

let result = dependencies().observed_tcn_processor.save(tcn_str);
let result = dependencies()
.observed_tcn_processor
.save(tcn_str, distance as f32);
info!("Recording TCN result {:?}", result);

result
Expand Down Expand Up @@ -485,8 +488,8 @@ impl LogCallbackWrapper for LogCallbackWrapperImpl {
// Note that if we panic, LogCat will also not show a message, or location.
// TODO consider writing to file. Otherwise it's impossible to notice this.
Err(e) => println!(
"Couldn't get env: Can't send log: level: {}, text: {}",
level, text,
"Couldn't get env: Can't send log: level: {}, text: {}, e: {}",
level, text, e
),
}
}
Expand Down Expand Up @@ -566,7 +569,10 @@ fn placeholder_alert() -> Alert {
Alert {
id: "0".to_owned(),
report,
contact_time: 0,
contact_start: 0,
contact_end: 0,
min_distance: 0.0,
avg_distance: 0.0,
}
}

Expand Down Expand Up @@ -627,16 +633,22 @@ pub fn alert_to_jobject(alert: Alert, env: &JNIEnv) -> Result<jobject, ServicesE
let id_j_string = env.new_string(alert.id)?;
let id_j_value = JValue::from(JObject::from(id_j_string));

let earliest_time_j_value = JValue::from(alert.contact_time as i64);
let contact_start_j_value = JValue::from(alert.contact_start as i64);
let contact_end_j_value = JValue::from(alert.contact_end as i64);
let min_distance_j_value = JValue::from(alert.min_distance);
let avg_distance_j_value = JValue::from(alert.avg_distance);

let result: Result<jobject, jni::errors::Error> = env
.new_object(
jni_alert_class,
"(Ljava/lang/String;Lorg/coepi/core/jni/JniPublicReport;J)V",
"(Ljava/lang/String;Lorg/coepi/core/jni/JniPublicReport;JJFF)V",
&[
id_j_value,
JValue::from(jni_public_report_obj),
earliest_time_j_value,
contact_start_j_value,
contact_end_j_value,
min_distance_j_value,
avg_distance_j_value,
],
)
.map(|o| o.into_inner());
Expand Down Expand Up @@ -682,6 +694,10 @@ impl JniErrorMappable for ServicesError {
status: 5,
message: msg.to_owned(),
},
ServicesError::NotFound => JniError {
status: 6,
message: "Not found".to_owned(),
},
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/android/jni_domain_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ fn create_test_alert(id: &str, report_time: u64) -> Alert {
Alert {
id: id.to_owned(),
report,
contact_time: 1592567315,
contact_start: 1592567315,
contact_end: 1592567335,
min_distance: 1.2,
avg_distance: 2.1,
}
}
13 changes: 8 additions & 5 deletions src/composition_root.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::networking::{TcnApi, TcnApiImpl};
use crate::reports_updater::{
ObservedTcnProcessor, ObservedTcnProcessorImpl, ReportsUpdater, TcnDao, TcnDaoImpl, TcnMatcher,
TcnMatcherRayon,
ExposureGrouper, ObservedTcnProcessor, ObservedTcnProcessorImpl, ReportsUpdater,
TcnBatchesManager, TcnDao, TcnDaoImpl, TcnMatcher, TcnMatcherRayon,
};
use crate::{
errors::ServicesError,
Expand Down Expand Up @@ -152,6 +152,7 @@ fn create_comp_root(
};

let tcn_dao = Arc::new(TcnDaoImpl::new(database.clone()));
let exposure_grouper = ExposureGrouper { threshold: 3600 };

CompositionRoot {
api,
Expand All @@ -161,16 +162,18 @@ fn create_comp_root(
tcn_matcher: TcnMatcherRayon {},
api,
memo_mapper,
exposure_grouper: exposure_grouper.clone(),
},
symptom_inputs_processor: SymptomInputsProcessorImpl {
inputs_manager: SymptomInputsManagerImpl {
inputs: Arc::new(RwLock::new(SymptomInputs::default())),
inputs_submitter: symptom_inputs_submitter,
},
},
observed_tcn_processor: ObservedTcnProcessorImpl {
tcn_dao: tcn_dao.clone(),
},
observed_tcn_processor: ObservedTcnProcessorImpl::new(TcnBatchesManager::new(
tcn_dao.clone(),
exposure_grouper.clone(),
)),
tcn_keys: tcn_keys.clone(),
}
}
13 changes: 9 additions & 4 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::networking::NetworkingError;
use rusqlite::Error::QueryReturnedNoRows;
use std::{error, fmt, io::Error as StdError, io::ErrorKind};
use tcn::Error as TcnError;
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
Expand All @@ -8,6 +9,7 @@ pub enum ServicesError {
Networking(NetworkingError),
Error(Error),
FFIParameters(String),
NotFound,
General(String),
}

Expand Down Expand Up @@ -79,10 +81,13 @@ impl From<std::str::Utf8Error> for ServicesError {

impl From<rusqlite::Error> for ServicesError {
fn from(error: rusqlite::Error) -> Self {
ServicesError::Error(Box::new(StdError::new(
ErrorKind::Other,
format!("{}", error),
)))
match error {
QueryReturnedNoRows => ServicesError::NotFound,
_ => ServicesError::Error(Box::new(StdError::new(
ErrorKind::Other,
format!("{}", error),
))),
}
}
}

Expand Down
Loading

0 comments on commit 9bb43af

Please sign in to comment.