EN: Add database export feature. Only exports the advertisements table.

This commit is contained in:
Thomas Lambertz 2020-11-30 18:12:21 +01:00 committed by Marvin W
parent 879b4c1396
commit 4158e71999
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
7 changed files with 81 additions and 0 deletions

View File

@ -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() {

View File

@ -33,6 +33,8 @@
<string name="pref_exposure_rpi_delete_all_title">Alle gesammelten IDs löschen</string>
<string name="pref_exposure_rpi_delete_all_warning">Nach dem Löschen der gesammelten IDs kannst du nicht mehr informiert werden, falls einer deiner Kontakte der letzten 14 Tage positiv getested wurde.</string>
<string name="pref_exposure_rpi_delete_all_warning_confirm_button">Trotzdem löschen</string>
<string name="pref_exposure_rpi_export_title">Datenbank Export</string>
<string name="pref_exposure_rpi_export_summary">Exportiere die aktuelle Datenbank um sie mit einer anderen App zu analysieren.</string>
<string name="pref_exposure_info_summary">"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."</string>

View File

@ -43,6 +43,8 @@
<string name="pref_exposure_rpi_delete_all_title">Delete all collected IDs</string>
<string name="pref_exposure_rpi_delete_all_warning">Deleting collected IDs will make it impossible to notify you in case any of your contacts of the last 14 days is diagnosed.</string>
<string name="pref_exposure_rpi_delete_all_warning_confirm_button">Delete anyways</string>
<string name="pref_exposure_rpi_export_title">Export Database</string>
<string name="pref_exposure_rpi_export_summary">Export the current database for analysis with another app.</string>
<string name="pref_exposure_info_summary">"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."</string>

View File

@ -18,6 +18,12 @@
android:key="pref_exposure_rpi_delete_all"
android:title="@string/pref_exposure_rpi_delete_all_title" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_category_no_label">
<Preference
android:key="pref_exposure_export_database"
android:title="@string/pref_exposure_rpi_export_title"
android:summary="@string/pref_exposure_rpi_export_summary" />
</PreferenceCategory>
<PreferenceCategory android:layout="@layout/preference_category_no_label">
<org.microg.gms.ui.TextPreference
android:icon="@drawable/ic_info_outline"

View File

@ -42,5 +42,15 @@
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.microg.gms.nearby.exposurenotification.export"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/preferences_exposure_notifications_exportedfiles" />
</provider>
</application>
</manifest>

View File

@ -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 <T> withSync(context: Context, call: (ExposureDatabase) -> T): T {
val it = runBlocking { ref(context) }

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="exposureDatabase" path="exposureDatabase" />
</paths>