Skip to content

Commit

Permalink
Reduce minSDKVersion to 21
Browse files Browse the repository at this point in the history
The Google Exposure Notification service works down to
API 21, so the SDK should as well.

Handle the missing EncryptedSharedPreferences by simply
writing the preferences in cleartext when it is not available.

Fixes #16.
  • Loading branch information
Jeff R. Allen committed May 25, 2020
1 parent 3eec9b9 commit a78bb72
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 29 deletions.
2 changes: 1 addition & 1 deletion calibration-app/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ android {

defaultConfig {
applicationId "org.dpppt.android.calibration"
minSdkVersion 23
minSdkVersion 21
targetSdkVersion 29
versionCode 2
versionName "0.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private SpannableString formatStatusString(TracingStatus status) {
for (TracingStatus.ErrorState error : errors) {
builder.append("\n").append(error.getErrorString(getContext()));
}
builder.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.red, null)),
builder.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.red)),
start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.os.Build;
import android.os.PowerManager;

public class RequirementsUtil {
Expand All @@ -25,8 +26,13 @@ public static boolean isBluetoothEnabled() {
}

public static boolean isBatteryOptimizationDeactivated(Context context) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
} else {
// this phone is too old to turn them off, so we lie and say we did.
return true;
}
}

}
2 changes: 1 addition & 1 deletion dp3t-sdk/sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ android {
compileSdkVersion 29

defaultConfig {
minSdkVersion 23
minSdkVersion 21
targetSdkVersion 29
versionCode 50
versionName "0.5.0"
Expand Down
3 changes: 3 additions & 0 deletions dp3t-sdk/sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.dpppt.android.sdk">

<uses-sdk tools:overrideLibrary="androidx.security"/>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ public static Collection<ErrorState> checkTracingErrorStatus(Context context, Ap
}

PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
boolean batteryOptimizationsDeactivated =
powerManager == null || powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
boolean batteryOptimizationsDeactivated;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
batteryOptimizationsDeactivated = powerManager == null ||
powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
} else {
// This phone is too old to do it, so we lie and say they did.
batteryOptimizationsDeactivated = true;
}
if (!batteryOptimizationsDeactivated) {
errors.add(ErrorState.BATTERY_OPTIMIZER_ENABLED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;

Expand All @@ -33,7 +35,7 @@ public class ExposureDayStorage {

private static ExposureDayStorage instance;

private SharedPreferences esp;
private SharedPreferences sp;

public static synchronized ExposureDayStorage getInstance(Context context) {
if (instance == null) {
Expand All @@ -44,20 +46,24 @@ public static synchronized ExposureDayStorage getInstance(Context context) {

private ExposureDayStorage(Context context) {
try {
String KEY_ALIAS = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
esp = EncryptedSharedPreferences.create("dp3t_exposuredays_store",
KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String KEY_ALIAS = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
sp = EncryptedSharedPreferences.create("dp3t_exposuredays_store",
KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
} else {
sp = context.getSharedPreferences("dp3t_exposuredays_store_not_encrypted", Context.MODE_PRIVATE);
}
} catch (GeneralSecurityException | IOException ex) {
ex.printStackTrace();
}
}

private ExposureDayList getExposureDaysInternal() {
ExposureDayList list =
Json.safeFromJson(esp.getString(PREF_KEY_EEXPOSURE_DAYS, "[]"), ExposureDayList.class, ExposureDayList::new);
Json.safeFromJson(sp.getString(PREF_KEY_EEXPOSURE_DAYS, "[]"), ExposureDayList.class, ExposureDayList::new);

DayDate maxAgeForExposureDay = new DayDate().subtractDays(NUMBER_OF_DAYS_TO_KEEP_EXPOSED_DAYS);
Iterator<ExposureDay> iterator = list.iterator();
Expand Down Expand Up @@ -91,10 +97,10 @@ public void addExposureDay(Context context, ExposureDay exposureDay) {
}
}

int id = esp.getInt(PREF_KEY_LAST_ID, 0) + 1;
int id = sp.getInt(PREF_KEY_LAST_ID, 0) + 1;
exposureDay.setId(id);
previousExposureDays.add(exposureDay);
esp.edit()
sp.edit()
.putInt(PREF_KEY_LAST_ID, id)
.putString(PREF_KEY_EEXPOSURE_DAYS, Json.toJson(previousExposureDays))
.apply();
Expand All @@ -107,13 +113,13 @@ public void resetExposureDays() {
for (ExposureDay previousExposureDay : previousExposureDays) {
previousExposureDay.setDeleted(true);
}
esp.edit()
sp.edit()
.putString(PREF_KEY_EEXPOSURE_DAYS, Json.toJson(previousExposureDays))
.apply();
}

public void clear() {
esp.edit()
sp.edit()
.putString(PREF_KEY_EEXPOSURE_DAYS, Json.toJson(new ExposureDayList()))
.apply();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;

Expand All @@ -27,7 +29,7 @@ public class PendingKeyUploadStorage {

private static PendingKeyUploadStorage instance;

private SharedPreferences esp;
private SharedPreferences sp;

public static synchronized PendingKeyUploadStorage getInstance(Context context) {
if (instance == null) {
Expand All @@ -38,24 +40,28 @@ public static synchronized PendingKeyUploadStorage getInstance(Context context)

private PendingKeyUploadStorage(Context context) {
try {
String KEY_ALIAS = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
esp = EncryptedSharedPreferences.create("dp3t_pendingkeyupload_store",
KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String KEY_ALIAS = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
sp = EncryptedSharedPreferences.create("dp3t_pendingkeyupload_store",
KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
} else {
sp = context.getSharedPreferences("dp3t_pendingkeyupload_store_not_encrypted", Context.MODE_PRIVATE);
}
} catch (GeneralSecurityException | IOException ex) {
ex.printStackTrace();
}
}

private PendingKeyList getPendingKeys() {
return Json.fromJson(esp.getString(PREF_KEY_PENDING_KEYS, "[]"), PendingKeyList.class);
return Json.fromJson(sp.getString(PREF_KEY_PENDING_KEYS, "[]"), PendingKeyList.class);
}

private void setPendingKeys(PendingKeyList list) {
Collections.sort(list, (a, b) -> Integer.compare(a.getRollingStartNumber(), b.getRollingStartNumber()));
esp.edit().putString(PREF_KEY_PENDING_KEYS, Json.toJson(list)).apply();
sp.edit().putString(PREF_KEY_PENDING_KEYS, Json.toJson(list)).apply();
}

public int peekRollingStartNumber() {
Expand Down Expand Up @@ -89,7 +95,7 @@ public void addPendingKey(PendingKey key) {
}

public void clear() {
esp.edit().clear().apply();
sp.edit().clear().apply();
}

public static class PendingKey {
Expand Down

0 comments on commit a78bb72

Please sign in to comment.