Skip to content

Commit

Permalink
Merge pull request #196 from DP-3T/develop
Browse files Browse the repository at this point in the history
Version 1.0.4
  • Loading branch information
simonroesch authored Aug 25, 2020
2 parents 739bf16 + b6b92e1 commit a47fdad
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 21 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog for DP3T-SDK Android

## version 1.0.4 (25.8.2020)

- exposed days are now deleted 14 days after reporting the exposureDay (before they were deleted 10 days after the exposure which could be only 1 day after reporting)
- handle same day release of TEK: if we receive the current days TEK directly, we do a fake request the next day. Otherwise, the current days TEK is uploaded the next day as before.
- the number of uploaded keys has been increased to 30, because with same day release of keys it is possible to have more than 14 TEKs for the past 14 days

## version 1.0.3 (24.7.2020)

- upgraded to play-services-nearby-18.0.3-eap (adds a service for Google to restart our application after force stop starting v1.5)
Expand Down
4 changes: 2 additions & 2 deletions dp3t-sdk/sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ android {
defaultConfig {
minSdkVersion 23
targetSdkVersion 29
versionCode 103
versionName "1.0.3"
versionCode 104
versionName "1.0.4"
testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'EMULATOR,LOW-BATTERY,ACTIVITY-MISSING,DEBUGGABLE,UNLOCKED,UNSUSTAINED-ACTIVITY-MISSING'
testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"

Expand Down
7 changes: 7 additions & 0 deletions dp3t-sdk/sdk/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
tools:ignore="HardcodedDebugMode"
tools:replace="android:debuggable">

<activity android:name=".TestActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,16 @@ public void testResetAddNewDay() {
assertEquals(1, eds.getExposureDays().size());
}

@Test
public void testKeepTestsFor14DaysAfterReport() {
ExposureDayStorage eds = ExposureDayStorage.getInstance(context);
eds.clear();
//should be returned in getExposureDays()
eds.addExposureDay(context, new ExposureDay(-1, new DayDate().subtractDays(11), System.currentTimeMillis() - 10));
//should not be considered because the report date is more than 14 days in the past
eds.addExposureDay(context,
new ExposureDay(-2, new DayDate().subtractDays(16), System.currentTimeMillis() - 15 * 24 * 60 * 60 * 1000));
assertEquals(1, eds.getExposureDays().size());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicLong;

import org.dpppt.android.sdk.BuildConfig;
import org.dpppt.android.sdk.DP3T;
import org.dpppt.android.sdk.InfectionStatus;
import org.dpppt.android.sdk.TracingStatus;
Expand Down Expand Up @@ -74,6 +75,9 @@ public void setup() throws IOException {

@Test
public void testSyncStartAtMorning() {
if (!BuildConfig.FLAVOR.equals("production")) {
throw new IllegalStateException("Wrong Build Variant. Make sure to run this Test with the production build variant.");
}
AtomicLong time = new AtomicLong(yesterdayAt3am());

server.setDispatcher(new Dispatcher() {
Expand Down Expand Up @@ -109,6 +113,9 @@ public MockResponse dispatch(RecordedRequest request) {

@Test
public void testSyncStartEvening() throws Exception {
if (!BuildConfig.FLAVOR.equals("production")) {
throw new IllegalStateException("Wrong Build Variant. Make sure to run this Test with the production build variant.");
}
AtomicLong time = new AtomicLong(yesterdayAt8pm());

server.setDispatcher(new Dispatcher() {
Expand All @@ -131,6 +138,9 @@ public MockResponse dispatch(RecordedRequest request) {

@Test
public void testSyncDelayedInEvening() throws Exception {
if (!BuildConfig.FLAVOR.equals("production")) {
throw new IllegalStateException("Wrong Build Variant. Make sure to run this Test with the production build variant.");
}
AtomicLong time = new AtomicLong(yesterdayAt8am());

server.setDispatcher(new Dispatcher() {
Expand Down Expand Up @@ -201,19 +211,6 @@ public void testExposureNotLongEnough() {
assertEquals(InfectionStatus.HEALTHY, status.getInfectionStatus());
}

@Test
public void testExposureTooLongAgo() {
TestGoogleExposureClient.ExposureTestParameters params = new TestGoogleExposureClient.ExposureTestParameters();
params.attenuationDurations = new int[] { 20, 0, 0 };
params.daysSinceLastExposure = 11;
params.matchedKeyCount = 1;

testExposure(params);

TracingStatus status = DP3T.getStatus(context);
assertEquals(InfectionStatus.HEALTHY, status.getInfectionStatus());
}

private void testExposure(TestGoogleExposureClient.ExposureTestParameters params) {
AtomicLong time = new AtomicLong(yesterdayAt8am());
server.setDispatcher(new Dispatcher() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright (c) 2020 Ubique Innovation AG <https://www.ubique.ch>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* SPDX-License-Identifier: MPL-2.0
*/
package org.dpppt.android.sdk.internal;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.dpppt.android.sdk.DP3T;
import org.dpppt.android.sdk.InfectionStatus;
import org.dpppt.android.sdk.backend.ResponseCallback;
import org.dpppt.android.sdk.internal.backend.ProxyConfig;
import org.dpppt.android.sdk.internal.backend.models.GaenKey;
import org.dpppt.android.sdk.internal.backend.models.GaenRequest;
import org.dpppt.android.sdk.internal.backend.models.GaenSecondDay;
import org.dpppt.android.sdk.internal.logger.LogLevel;
import org.dpppt.android.sdk.internal.logger.Logger;
import org.dpppt.android.sdk.internal.nearby.GaenStateHelper;
import org.dpppt.android.sdk.internal.nearby.GoogleExposureClient;
import org.dpppt.android.sdk.internal.nearby.TestGoogleExposureClient;
import org.dpppt.android.sdk.internal.storage.PendingKeyUploadStorage;
import org.dpppt.android.sdk.internal.util.Json;
import org.dpppt.android.sdk.models.ApplicationInfo;
import org.dpppt.android.sdk.models.ExposeeAuthMethodJson;
import org.dpppt.android.sdk.util.DateUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

@RunWith(AndroidJUnit4.class)
public class TEKReleaseTest {

Context context;
MockWebServer server;
TestGoogleExposureClient testGoogleExposureClient;
Instrumentation.ActivityMonitor monitor;
Instrumentation mInstrumentation;

@Before
public void setup() throws IOException {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
context = mInstrumentation.getTargetContext();

Logger.init(context, LogLevel.DEBUG);

ProxyConfig.DISABLE_SYSTEM_PROXY = true;
GaenStateHelper.SET_GAEN_AVAILABILITY_AVAILABLE_FOR_TESTS = true;

server = new MockWebServer();
server.start();
AppConfigManager appConfigManager = AppConfigManager.getInstance(context);
DP3T.init(context, new ApplicationInfo("test", server.url("/bucket/").toString(), server.url("/report/").toString()),
null);
appConfigManager.setTracingEnabled(false);
DP3T.clearData(context);
DP3T.init(context, new ApplicationInfo("test", server.url("/bucket/").toString(), server.url("/report/").toString()),
null);
appConfigManager.setTracingEnabled(true);
PendingKeyUploadStorage.getInstance(context).clear();
}


@Test
public void testIAmInfectedTodayKeyAlreadyReleased() throws Exception {
testIAmInfected(true, false, 5, true);
}

@Test
public void testIAmInfectedTodayKeyNotReleased() throws Exception {
testIAmInfected(false, true, 4, false);
}

private void testIAmInfected(boolean currentDayKeyReleased, boolean expectedTracingEnabledDirectlyAfterSendingInfection,
int expectedNumberOfTEKToday, boolean expectedNextDayRequestIsFake) throws Exception {

testGoogleExposureClient = new TestGoogleExposureClient(context, currentDayKeyReleased);
GoogleExposureClient.wrapTestClient(testGoogleExposureClient);
testGoogleExposureClient.setTime(System.currentTimeMillis());

Activity activity = startEmptyActivity();

AtomicInteger exposedFakeRequestCounter = new AtomicInteger(0);
AtomicInteger exposedRequestCounter = new AtomicInteger(0);
AtomicInteger exposednextdayFakeRequestCounter = new AtomicInteger(0);
AtomicInteger exposednextdayRequestCounter = new AtomicInteger(0);
ArrayList<Integer> sentRollingStartNumbers = new ArrayList<>();
ArrayList<Integer> nextdaySentRollingStartNumbers = new ArrayList<>();

//Onset Date is 4 days ago
long onsetDate = System.currentTimeMillis() - 1000 * 60 * 60 * 96;

server.setDispatcher(new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
String body = new String(request.getBody().readByteArray());
if (request.getPath().equals("/bucket/v1/gaen/exposed")) {
GaenRequest gaenRequest = Json.fromJson(body, GaenRequest.class);
int fake = gaenRequest.isFake();
if (fake == 1) exposedFakeRequestCounter.getAndIncrement();
else {
for (GaenKey k : gaenRequest.getGaenKeys()) {
if (!k.isFake()) {
sentRollingStartNumbers.add(k.getRollingStartNumber());
}
}
exposedRequestCounter.getAndIncrement();
}
}
if (request.getPath().equals("/bucket/v1/gaen/exposednextday")) {
GaenSecondDay gaenSecondDayRequest = Json.fromJson(body, GaenSecondDay.class);
int fake = gaenSecondDayRequest.isFake();
if (fake == 1) exposednextdayFakeRequestCounter.getAndIncrement();
else {
exposednextdayRequestCounter.getAndIncrement();
nextdaySentRollingStartNumbers.add(gaenSecondDayRequest.getDelayedKey().getRollingStartNumber());
}
}
return new MockResponse().setResponseCode(200);
}
});

CountDownLatch countDownLatch = new CountDownLatch(1);
DP3T.sendIAmInfected(activity, new Date(onsetDate), new ExposeeAuthMethodJson(""), new ResponseCallback<Void>() {
@Override
public void onSuccess(Void response) {
countDownLatch.countDown();
}

@Override
public void onError(Throwable throwable) {
countDownLatch.countDown();
}
});
countDownLatch.await();

AtomicLong today = new AtomicLong(System.currentTimeMillis());
try {
new SyncWorker.SyncImpl(context, today.get()).doSync();
} catch (Exception e) {
e.printStackTrace();
}

assertEquals(1, exposedRequestCounter.get());
assertEquals(0, exposedFakeRequestCounter.get());
assertEquals(0, exposednextdayFakeRequestCounter.get());
assertEquals(0, exposednextdayRequestCounter.get());
assertEquals(expectedTracingEnabledDirectlyAfterSendingInfection, DP3T.isTracingEnabled(context));
assertEquals(InfectionStatus.INFECTED, DP3T.getStatus(context).getInfectionStatus());
assertEquals(expectedNumberOfTEKToday, sentRollingStartNumbers.size());
for (int k : sentRollingStartNumbers) {
assertTrue(k >= DateUtil.getRollingStartNumberForDate(onsetDate));
if (currentDayKeyReleased) {
assertTrue(k <= DateUtil.getRollingStartNumberForDate(today.get()));
} else {
assertTrue(k < DateUtil.getRollingStartNumberForDate(today.get()));
}
}

AtomicLong tomorrow = new AtomicLong(today.get() + 1000 * 60 * 60 * 24);
testGoogleExposureClient.setTime(tomorrow.get());

try {
new SyncWorker.SyncImpl(context, tomorrow.get()).doSync();
} catch (Exception e) {
e.printStackTrace();
}

mInstrumentation.removeMonitor(monitor);

assertEquals(1, exposedRequestCounter.get());
assertEquals(0, exposedFakeRequestCounter.get());
if (expectedNextDayRequestIsFake) {
assertEquals(1, exposednextdayFakeRequestCounter.get());
assertEquals(0, exposednextdayRequestCounter.get());
} else {
assertEquals(0, exposednextdayFakeRequestCounter.get());
assertEquals(1, exposednextdayRequestCounter.get());
}
assertFalse(DP3T.isTracingEnabled(context));
assertEquals(InfectionStatus.INFECTED, DP3T.getStatus(context).getInfectionStatus());
for (int k : nextdaySentRollingStartNumbers) {
//the rolling start number that is sent tomorrow must be equals to today's rolling start number
assertEquals(k, DateUtil.getRollingStartNumberForDate(today.get()));
}
}


private Activity startEmptyActivity() {
Instrumentation.ActivityMonitor monitor = mInstrumentation.addMonitor(TestActivity.class.getName(), null, false);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(mInstrumentation.getTargetContext(), TestActivity.class.getName());
mInstrumentation.startActivitySync(intent);

Activity activity = mInstrumentation.waitForMonitor(monitor);
assertNotNull(activity);
return activity;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.dpppt.android.sdk.internal;

import android.app.Activity;

public class TestActivity extends Activity {


}
Loading

0 comments on commit a47fdad

Please sign in to comment.