mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-28 14:13:00 +00:00
Do not use database.use (for pre-Q), ensure database stays open as needed
Related to #1115
This commit is contained in:
parent
b898878f26
commit
761b6dfd47
3 changed files with 103 additions and 92 deletions
|
@ -21,10 +21,25 @@ import java.util.concurrent.Future
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
import java.util.concurrent.ThreadPoolExecutor
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
class ExposureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
class ExposureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||||
|
private val refCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
fun ref(): ExposureDatabase {
|
||||||
|
refCount.incrementAndGet()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unref() {
|
||||||
|
val nu = refCount.decrementAndGet()
|
||||||
|
if (nu == 0) {
|
||||||
|
close()
|
||||||
|
} else if (nu < 0) {
|
||||||
|
throw RuntimeException("ref/unref mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
onUpgrade(db, 0, DB_VERSION)
|
onUpgrade(db, 0, DB_VERSION)
|
||||||
|
@ -149,7 +164,7 @@ class ExposureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyDiagnosisKeySearchResult(packageName: String, token: String, key: TemporaryExposureKey, matched: Boolean) = writableDatabase.run {
|
fun applyDiagnosisKeySearchResult(key: TemporaryExposureKey, matched: Boolean) = writableDatabase.run {
|
||||||
insert(TABLE_TEK_CHECK, "NULL", ContentValues().apply {
|
insert(TABLE_TEK_CHECK, "NULL", ContentValues().apply {
|
||||||
put("keyData", key.keyData)
|
put("keyData", key.keyData)
|
||||||
put("rollingStartNumber", key.rollingStartIntervalNumber)
|
put("rollingStartNumber", key.rollingStartIntervalNumber)
|
||||||
|
@ -195,10 +210,10 @@ class ExposureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
|
||||||
for (key in keys) {
|
for (key in keys) {
|
||||||
if (oldestRpi == null || key.rollingStartIntervalNumber * ROLLING_WINDOW_LENGTH_MS - ALLOWED_KEY_OFFSET_MS < oldestRpi) {
|
if (oldestRpi == null || key.rollingStartIntervalNumber * ROLLING_WINDOW_LENGTH_MS - ALLOWED_KEY_OFFSET_MS < oldestRpi) {
|
||||||
// Early ignore because key is older than since we started scanning.
|
// Early ignore because key is older than since we started scanning.
|
||||||
applyDiagnosisKeySearchResult(packageName, token, key, false)
|
applyDiagnosisKeySearchResult(key, false)
|
||||||
} else {
|
} else {
|
||||||
futures.add(executor.submit {
|
futures.add(executor.submit {
|
||||||
applyDiagnosisKeySearchResult(packageName, token, key, findMeasuredExposures(key).isNotEmpty())
|
applyDiagnosisKeySearchResult(key, findMeasuredExposures(key).isNotEmpty())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +222,7 @@ class ExposureDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
|
||||||
}
|
}
|
||||||
val time = (System.currentTimeMillis() - start).toDouble() / 1000.0
|
val time = (System.currentTimeMillis() - start).toDouble() / 1000.0
|
||||||
executor.shutdown()
|
executor.shutdown()
|
||||||
Log.d(TAG, "Processed ${keys.size} keys in ${System.currentTimeMillis() - start}s -> ${(keys.size.toDouble() / time * 1000).roundToInt().toDouble() / 1000.0} keys/s")
|
Log.d(TAG, "Processed ${keys.size} keys in ${time}s -> ${(keys.size.toDouble() / time * 1000).roundToInt().toDouble() / 1000.0} keys/s")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findAllMeasuredExposures(packageName: String, token: String): List<MeasuredExposure> {
|
fun findAllMeasuredExposures(packageName: String, token: String): List<MeasuredExposure> {
|
||||||
|
|
|
@ -22,12 +22,15 @@ class ExposureNotificationService : BaseService(TAG, GmsService.NEARBY_EXPOSURE)
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
database.close()
|
database.unref()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
database = ExposureDatabase(this)
|
if (!this::database.isInitialized) {
|
||||||
|
database = ExposureDatabase(this)
|
||||||
|
}
|
||||||
|
database.ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
override fun handleServiceRequest(callback: IGmsCallbacks, request: GetServiceRequest, service: GmsService) {
|
||||||
|
|
|
@ -59,11 +59,9 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
if (resultCode == SUCCESS) {
|
if (resultCode == SUCCESS) {
|
||||||
ExposurePreferences(context).scannerEnabled = true
|
ExposurePreferences(context).scannerEnabled = true
|
||||||
}
|
}
|
||||||
database.use {
|
database.noteAppAction(packageName, "start", JSONObject().apply {
|
||||||
it.noteAppAction(packageName, "start", JSONObject().apply {
|
put("result", resultCode)
|
||||||
put("result", resultCode)
|
}.toString())
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status(if (resultCode == SUCCESS) SUCCESS else FAILED_REJECTED_OPT_IN, resultData?.getString("message")))
|
params.callback.onResult(Status(if (resultCode == SUCCESS) SUCCESS else FAILED_REJECTED_OPT_IN, resultData?.getString("message")))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -77,11 +75,9 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
if (resultCode == SUCCESS) {
|
if (resultCode == SUCCESS) {
|
||||||
ExposurePreferences(context).scannerEnabled = false
|
ExposurePreferences(context).scannerEnabled = false
|
||||||
}
|
}
|
||||||
database.use {
|
database.noteAppAction(packageName, "stop", JSONObject().apply {
|
||||||
it.noteAppAction(packageName, "stop", JSONObject().apply {
|
put("result", resultCode)
|
||||||
put("result", resultCode)
|
}.toString())
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status.SUCCESS)
|
params.callback.onResult(Status.SUCCESS)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -106,12 +102,10 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
FAILED_REJECTED_OPT_IN to emptyList()
|
FAILED_REJECTED_OPT_IN to emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
database.use {
|
database.noteAppAction(packageName, "getTemporaryExposureKeyHistory", JSONObject().apply {
|
||||||
it.noteAppAction(packageName, "getTemporaryExposureKeyHistory", JSONObject().apply {
|
put("result", resultCode)
|
||||||
put("result", resultCode)
|
put("response_keys_size", response.size)
|
||||||
put("response_keys_size", response.size)
|
}.toString())
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status(status, resultData?.getString("message")), response)
|
params.callback.onResult(Status(status, resultData?.getString("message")), response)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -140,50 +134,51 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun provideDiagnosisKeys(params: ProvideDiagnosisKeysParams) {
|
override fun provideDiagnosisKeys(params: ProvideDiagnosisKeysParams) {
|
||||||
|
database.ref()
|
||||||
Thread(Runnable {
|
Thread(Runnable {
|
||||||
if (params.configuration != null) {
|
try {
|
||||||
database.storeConfiguration(packageName, params.token, params.configuration)
|
if (params.configuration != null) {
|
||||||
}
|
database.storeConfiguration(packageName, params.token, params.configuration)
|
||||||
|
}
|
||||||
|
|
||||||
// keys
|
// keys
|
||||||
for (key in params.keys.orEmpty()) {
|
for (key in params.keys.orEmpty()) {
|
||||||
database.storeDiagnosisKey(packageName, params.token, key)
|
database.storeDiagnosisKey(packageName, params.token, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key files
|
// Key files
|
||||||
var keys = params.keys?.size ?: 0
|
var keys = params.keys?.size ?: 0
|
||||||
for (file in params.keyFiles.orEmpty()) {
|
for (file in params.keyFiles.orEmpty()) {
|
||||||
try {
|
try {
|
||||||
ZipInputStream(ParcelFileDescriptor.AutoCloseInputStream(file)).use { stream ->
|
ZipInputStream(ParcelFileDescriptor.AutoCloseInputStream(file)).use { stream ->
|
||||||
do {
|
do {
|
||||||
val entry = stream.nextEntry ?: break
|
val entry = stream.nextEntry ?: break
|
||||||
if (entry.name == "export.bin") {
|
if (entry.name == "export.bin") {
|
||||||
val prefix = ByteArray(16)
|
val prefix = ByteArray(16)
|
||||||
var totalBytesRead = 0
|
var totalBytesRead = 0
|
||||||
var bytesRead = 0
|
var bytesRead = 0
|
||||||
while (bytesRead != -1 && totalBytesRead < prefix.size) {
|
while (bytesRead != -1 && totalBytesRead < prefix.size) {
|
||||||
bytesRead = stream.read(prefix, totalBytesRead, prefix.size - totalBytesRead)
|
bytesRead = stream.read(prefix, totalBytesRead, prefix.size - totalBytesRead)
|
||||||
if (bytesRead > 0) {
|
if (bytesRead > 0) {
|
||||||
totalBytesRead += bytesRead
|
totalBytesRead += bytesRead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalBytesRead == prefix.size && String(prefix).trim() == "EK Export v1") {
|
||||||
|
val fileKeys = storeDiagnosisKeyExport(params.token, TemporaryExposureKeyExport.ADAPTER.decode(stream))
|
||||||
|
keys + fileKeys
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "export.bin had invalid prefix")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (totalBytesRead == prefix.size && String(prefix).trim() == "EK Export v1") {
|
stream.closeEntry()
|
||||||
val fileKeys = storeDiagnosisKeyExport(params.token, TemporaryExposureKeyExport.ADAPTER.decode(stream))
|
} while (true);
|
||||||
keys + fileKeys
|
}
|
||||||
} else {
|
} catch (e: Exception) {
|
||||||
Log.d(TAG, "export.bin had invalid prefix")
|
Log.w(TAG, "Failed parsing file", e)
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.closeEntry()
|
|
||||||
} while (true);
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Failed parsing file", e)
|
|
||||||
}
|
}
|
||||||
}
|
Log.d(TAG, "$packageName/${params.token} provided $keys keys")
|
||||||
Log.d(TAG, "$packageName/${params.token} provided $keys keys")
|
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
database.noteAppAction(packageName, "provideDiagnosisKeys", JSONObject().apply {
|
database.noteAppAction(packageName, "provideDiagnosisKeys", JSONObject().apply {
|
||||||
put("request_token", params.token)
|
put("request_token", params.token)
|
||||||
put("request_keys_size", params.keys?.size)
|
put("request_keys_size", params.keys?.size)
|
||||||
|
@ -191,25 +186,27 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
put("request_keys_count", keys)
|
put("request_keys_count", keys)
|
||||||
}.toString())
|
}.toString())
|
||||||
|
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
try {
|
||||||
|
params.callback.onResult(Status.SUCCESS)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Callback failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database.finishMatching(packageName, params.token)
|
||||||
|
val match = database.findAllMeasuredExposures(packageName, params.token).isNotEmpty()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status.SUCCESS)
|
val intent = Intent(if (match) ACTION_EXPOSURE_STATE_UPDATED else ACTION_EXPOSURE_NOT_FOUND)
|
||||||
|
intent.`package` = packageName
|
||||||
|
Log.d(TAG, "Sending $intent")
|
||||||
|
context.sendOrderedBroadcast(intent, PERMISSION_EXPOSURE_CALLBACK)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "Callback failed", e)
|
Log.w(TAG, "Callback failed", e)
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
|
database.unref()
|
||||||
val match = database.use {
|
|
||||||
it.finishMatching(packageName, params.token)
|
|
||||||
it.findAllMeasuredExposures(packageName, params.token).isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val intent = Intent(if (match) ACTION_EXPOSURE_STATE_UPDATED else ACTION_EXPOSURE_NOT_FOUND)
|
|
||||||
intent.`package` = packageName
|
|
||||||
Log.d(TAG, "Sending $intent")
|
|
||||||
context.sendOrderedBroadcast(intent, PERMISSION_EXPOSURE_CALLBACK)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Callback failed", e)
|
|
||||||
}
|
}
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
@ -237,18 +234,16 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
.setSummationRiskScore(exposures.map { it.getRiskScore(configuration) }.sum())
|
.setSummationRiskScore(exposures.map { it.getRiskScore(configuration) }.sum())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
database.use {
|
database.noteAppAction(packageName, "getExposureSummary", JSONObject().apply {
|
||||||
it.noteAppAction(packageName, "getExposureSummary", JSONObject().apply {
|
put("request_token", params.token)
|
||||||
put("request_token", params.token)
|
put("response_days_since", response.daysSinceLastExposure)
|
||||||
put("response_days_since", response.daysSinceLastExposure)
|
put("response_matched_keys", response.matchedKeyCount)
|
||||||
put("response_matched_keys", response.matchedKeyCount)
|
put("response_max_risk", response.maximumRiskScore)
|
||||||
put("response_max_risk", response.maximumRiskScore)
|
put("response_attenuation_durations", JSONArray().apply {
|
||||||
put("response_attenuation_durations", JSONArray().apply {
|
response.attenuationDurationsInMinutes.forEach { put(it) }
|
||||||
response.attenuationDurationsInMinutes.forEach { put(it) }
|
})
|
||||||
})
|
put("response_summation_risk", response.summationRiskScore)
|
||||||
put("response_summation_risk", response.summationRiskScore)
|
}.toString())
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status.SUCCESS, response)
|
params.callback.onResult(Status.SUCCESS, response)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -271,12 +266,10 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
it.toExposureInformation(configuration)
|
it.toExposureInformation(configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.use {
|
database.noteAppAction(packageName, "getExposureInformation", JSONObject().apply {
|
||||||
database.noteAppAction(packageName, "getExposureInformation", JSONObject().apply {
|
put("request_token", params.token)
|
||||||
put("request_token", params.token)
|
put("response_size", response.size)
|
||||||
put("response_size", response.size)
|
}.toString())
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
params.callback.onResult(Status.SUCCESS, response)
|
params.callback.onResult(Status.SUCCESS, response)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
Loading…
Reference in a new issue