From 4158e719996da307a22f8d25f63727358eac91a6 Mon Sep 17 00:00:00 2001 From: Thomas Lambertz Date: Mon, 30 Nov 2020 18:12:21 +0100 Subject: [PATCH] EN: Add database export feature. Only exports the advertisements table. --- .../ui/ExposureNotificationsRpisFragment.kt | 6 +++ .../src/main/res/values-de/strings.xml | 2 + .../src/main/res/values/strings.xml | 2 + ...references_exposure_notifications_rpis.xml | 6 +++ .../src/main/AndroidManifest.xml | 10 ++++ .../exposurenotification/ExposureDatabase.kt | 51 +++++++++++++++++++ ...s_exposure_notifications_exportedfiles.xml | 4 ++ 7 files changed, 81 insertions(+) create mode 100644 play-services-nearby-core/src/main/res/xml/preferences_exposure_notifications_exportedfiles.xml diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt index 111baf23..d643a1c3 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt @@ -24,6 +24,7 @@ class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() { private lateinit var histogramCategory: PreferenceCategory private lateinit var histogram: BarChartPreference private lateinit var deleteAll: Preference + private lateinit var exportDb: Preference override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.preferences_exposure_notifications_rpis) @@ -33,6 +34,7 @@ class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() { histogramCategory = preferenceScreen.findPreference("prefcat_exposure_rpi_histogram") ?: histogramCategory histogram = preferenceScreen.findPreference("pref_exposure_rpi_histogram") ?: histogram deleteAll = preferenceScreen.findPreference("pref_exposure_rpi_delete_all") ?: deleteAll + exportDb = preferenceScreen.findPreference("pref_exposure_export_database") ?: exportDb deleteAll.onPreferenceClickListener = Preference.OnPreferenceClickListener { AlertDialog.Builder(requireContext()) .setTitle(R.string.pref_exposure_rpi_delete_all_title) @@ -48,6 +50,10 @@ class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() { .show() true } + exportDb.onPreferenceClickListener = Preference.OnPreferenceClickListener { + ExposureDatabase.export(requireContext()) + true + } } override fun onResume() { diff --git a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml index 175a19bf..9183c387 100644 --- a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml @@ -33,6 +33,8 @@ Alle gesammelten IDs löschen Nach dem Löschen der gesammelten IDs kannst du nicht mehr informiert werden, falls einer deiner Kontakte der letzten 14 Tage positiv getested wurde. Trotzdem löschen + Datenbank Export + Exportiere die aktuelle Datenbank um sie mit einer anderen App zu analysieren. "Die Exposure Notifications API ermöglicht es Apps, dich zu warnen, falls du Kontakt zu einer positiv getesteten Person hattest. Das Datum, die Zeitdauer und die Signalstärke, die dem Kontakt zugeordnet sind, werden mit der zugehörigen App geteilt." diff --git a/play-services-nearby-core-ui/src/main/res/values/strings.xml b/play-services-nearby-core-ui/src/main/res/values/strings.xml index 71a2787b..21a7b7a1 100644 --- a/play-services-nearby-core-ui/src/main/res/values/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values/strings.xml @@ -43,6 +43,8 @@ Delete all collected IDs Deleting collected IDs will make it impossible to notify you in case any of your contacts of the last 14 days is diagnosed. Delete anyways + Export Database + Export the current database for analysis with another app. "Exposure Notifications API allows apps to notify you if you were exposed to someone who reported to be diagnosed positive. The date, duration, and signal strength associated with an exposure will be shared with the corresponding app." diff --git a/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml b/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml index 7ef08d91..63737f95 100644 --- a/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml +++ b/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml @@ -18,6 +18,12 @@ android:key="pref_exposure_rpi_delete_all" android:title="@string/pref_exposure_rpi_delete_all_title" /> + + + + + + + diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureDatabase.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureDatabase.kt index 5f16ede7..f0bd9a4a 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureDatabase.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureDatabase.kt @@ -8,13 +8,17 @@ package org.microg.gms.nearby.exposurenotification import android.annotation.TargetApi import android.content.ContentValues import android.content.Context +import android.content.Intent import android.database.sqlite.SQLiteCursor import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE import android.database.sqlite.SQLiteOpenHelper +import android.net.Uri import android.os.Parcel import android.os.Parcelable import android.util.Log +import androidx.core.content.FileProvider +import com.google.android.gms.nearby.exposurenotification.CalibrationConfidence import com.google.android.gms.nearby.exposurenotification.DiagnosisKeysDataMapping import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey @@ -964,6 +968,53 @@ class ExposureDatabase private constructor(private val context: Context) : SQLit } } + fun export(context: Context) { + // FileProvider cannot directly access /databases, so we have to copy the database first + // In addition, we filter out only the Advertisements table to not export sensitive TEKs + + // Create a new database which will store only the Advertisements table + val exportDir = File(context.getCacheDir(), "exposureDatabase") + exportDir.mkdir() + val exportFile = File(exportDir, "database.db") + if (exportFile.delete()) { + Log.d("EN-DB-Exporter", "Deleted old export database.") + } + val db = SQLiteDatabase.openOrCreateDatabase(exportFile, null) + + // Attach the full database to the new empty db + val databaseFile = context.getDatabasePath(DB_NAME); + db.execSQL("ATTACH '$databaseFile' AS fulldb;") + + // copy TABLE_ADVERTISEMENTS over + db.execSQL("CREATE TABLE $TABLE_ADVERTISEMENTS AS SELECT * FROM fulldb.$TABLE_ADVERTISEMENTS;") + + // Detach original db, close new db + db.execSQL("DETACH DATABASE fulldb;") + db.close() + + // Use the FileProvider to get a content URI for the new DB + val fileUri: Uri? = try { + FileProvider.getUriForFile(context,"org.microg.gms.nearby.exposurenotification.export", exportFile) + } catch (e: IllegalArgumentException) { + Log.e("EN-DB-Exporter", "The database file can't be shared: $exportFile $e") + null + } + + // Open a sharesheet + if (fileUri != null) { + // Grant temporary read permission to the content URI + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, fileUri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + type = "application/vnd.sqlite3" + } + + val shareIntent = Intent.createChooser(sendIntent, null) + context.startActivity(shareIntent) + } + } + @Deprecated(message = "Sync database access is slow", replaceWith = ReplaceWith("with(context, call)")) fun withSync(context: Context, call: (ExposureDatabase) -> T): T { val it = runBlocking { ref(context) } diff --git a/play-services-nearby-core/src/main/res/xml/preferences_exposure_notifications_exportedfiles.xml b/play-services-nearby-core/src/main/res/xml/preferences_exposure_notifications_exportedfiles.xml new file mode 100644 index 00000000..2d62db66 --- /dev/null +++ b/play-services-nearby-core/src/main/res/xml/preferences_exposure_notifications_exportedfiles.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file