mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-30 23:23:01 +00:00
EN: Handle confirmation via resolution/pending intent instead of new task
This commit is contained in:
parent
fd6d915f0a
commit
d33391ebce
4 changed files with 82 additions and 50 deletions
|
@ -11,12 +11,14 @@ import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.gms.R
|
import com.google.android.gms.R
|
||||||
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.*
|
|
||||||
import org.microg.gms.nearby.exposurenotification.*
|
import org.microg.gms.nearby.exposurenotification.*
|
||||||
|
|
||||||
class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
||||||
private var resultCode: Int = FAILED
|
private var resultCode: Int = RESULT_CANCELED
|
||||||
private val resultData: Bundle = Bundle()
|
set(value) {
|
||||||
|
setResult(value)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
private val receiver: ResultReceiver?
|
private val receiver: ResultReceiver?
|
||||||
get() = intent.getParcelableExtra(KEY_CONFIRM_RECEIVER)
|
get() = intent.getParcelableExtra(KEY_CONFIRM_RECEIVER)
|
||||||
private val action: String?
|
private val action: String?
|
||||||
|
@ -47,22 +49,22 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
||||||
findViewById<Button>(android.R.id.button1).text = getString(R.string.exposure_confirm_keys_button)
|
findViewById<Button>(android.R.id.button1).text = getString(R.string.exposure_confirm_keys_button)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
resultCode = INTERNAL_ERROR
|
resultCode = RESULT_CANCELED
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
findViewById<Button>(android.R.id.button1).setOnClickListener {
|
findViewById<Button>(android.R.id.button1).setOnClickListener {
|
||||||
resultCode = SUCCESS
|
resultCode = RESULT_OK
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
findViewById<Button>(android.R.id.button2).setOnClickListener {
|
findViewById<Button>(android.R.id.button2).setOnClickListener {
|
||||||
resultCode = FAILED_REJECTED_OPT_IN
|
resultCode = RESULT_CANCELED
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun finish() {
|
||||||
super.onStop()
|
receiver?.send(resultCode, Bundle())
|
||||||
receiver?.send(resultCode, resultData)
|
super.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ const val KEY_CONFIRM_PACKAGE = "package"
|
||||||
const val CONFIRM_ACTION_START = "start"
|
const val CONFIRM_ACTION_START = "start"
|
||||||
const val CONFIRM_ACTION_STOP = "stop"
|
const val CONFIRM_ACTION_STOP = "stop"
|
||||||
const val CONFIRM_ACTION_KEYS = "keys"
|
const val CONFIRM_ACTION_KEYS = "keys"
|
||||||
|
const val CONFIRM_PERMISSION_VALIDITY = 60 * 60 * 1000L
|
||||||
|
|
||||||
const val PERMISSION_EXPOSURE_CALLBACK = "com.google.android.gms.nearby.exposurenotification.EXPOSURE_CALLBACK"
|
const val PERMISSION_EXPOSURE_CALLBACK = "com.google.android.gms.nearby.exposurenotification.EXPOSURE_CALLBACK"
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@ class ExposureDatabase private constructor(private val context: Context) : SQLit
|
||||||
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_DIAGNOSIS(package TEXT NOT NULL, token TEXT NOT NULL, tcid INTEGER REFERENCES $TABLE_TEK_CHECK(tcid) ON DELETE CASCADE, transmissionRiskLevel INTEGER NOT NULL);")
|
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_DIAGNOSIS(package TEXT NOT NULL, token TEXT NOT NULL, tcid INTEGER REFERENCES $TABLE_TEK_CHECK(tcid) ON DELETE CASCADE, transmissionRiskLevel INTEGER NOT NULL);")
|
||||||
db.execSQL("CREATE INDEX IF NOT EXISTS index_${TABLE_DIAGNOSIS}_package_token ON $TABLE_DIAGNOSIS(package, token);")
|
db.execSQL("CREATE INDEX IF NOT EXISTS index_${TABLE_DIAGNOSIS}_package_token ON $TABLE_DIAGNOSIS(package, token);")
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
db.execSQL("CREATE TABLE $TABLE_APP_PERMS(package TEXT NOT NULL, sig TEXT NOT NULL, perm TEXT NOT NULL, timestamp INTEGER NOT NULL);")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SQLiteDatabase.delete(table: String, whereClause: String, args: LongArray): Int =
|
fun SQLiteDatabase.delete(table: String, whereClause: String, args: LongArray): Int =
|
||||||
|
@ -68,7 +71,23 @@ class ExposureDatabase private constructor(private val context: Context) : SQLit
|
||||||
val appLogEntries = delete(TABLE_APP_LOG, "timestamp < ?", longArrayOf(rollingStartTime))
|
val appLogEntries = delete(TABLE_APP_LOG, "timestamp < ?", longArrayOf(rollingStartTime))
|
||||||
val temporaryExposureKeys = delete(TABLE_TEK, "(rollingStartNumber + rollingPeriod) < ?", longArrayOf(rollingStartTime / ROLLING_WINDOW_LENGTH_MS))
|
val temporaryExposureKeys = delete(TABLE_TEK, "(rollingStartNumber + rollingPeriod) < ?", longArrayOf(rollingStartTime / ROLLING_WINDOW_LENGTH_MS))
|
||||||
val checkedTemporaryExposureKeys = delete(TABLE_TEK_CHECK, "(rollingStartNumber + rollingPeriod) < ?", longArrayOf(rollingStartTime / ROLLING_WINDOW_LENGTH_MS))
|
val checkedTemporaryExposureKeys = delete(TABLE_TEK_CHECK, "(rollingStartNumber + rollingPeriod) < ?", longArrayOf(rollingStartTime / ROLLING_WINDOW_LENGTH_MS))
|
||||||
Log.d(TAG, "Deleted on daily cleanup: $advertisements adv, $appLogEntries applogs, $temporaryExposureKeys teks, $checkedTemporaryExposureKeys cteks")
|
val appPerms = delete(TABLE_APP_PERMS, "timestamp < ?", longArrayOf(System.currentTimeMillis() - CONFIRM_PERMISSION_VALIDITY))
|
||||||
|
Log.d(TAG, "Deleted on daily cleanup: $advertisements adv, $appLogEntries applogs, $temporaryExposureKeys teks, $checkedTemporaryExposureKeys cteks, $appPerms perms")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun grantPermission(packageName: String, signatureDigest: String, permission: String, timestamp: Long = System.currentTimeMillis()) = writableDatabase.run {
|
||||||
|
insert(TABLE_APP_PERMS, "NULL", ContentValues().apply {
|
||||||
|
put("package", packageName)
|
||||||
|
put("sig", signatureDigest)
|
||||||
|
put("perm", permission)
|
||||||
|
put("timestamp", timestamp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasPermission(packageName: String, signatureDigest: String, permission: String, maxAge: Long = CONFIRM_PERMISSION_VALIDITY) = readableDatabase.run {
|
||||||
|
query(TABLE_APP_PERMS, arrayOf("MAX(timestamp)"), "package = ? AND sig = ? and perm = ?", arrayOf(packageName, signatureDigest, permission), null, null, null).use { cursor ->
|
||||||
|
cursor.moveToNext() && cursor.getLong(0) + maxAge > System.currentTimeMillis()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun noteAdvertisement(rpi: ByteArray, aem: ByteArray, rssi: Int, timestamp: Long = Date().time) = writableDatabase.run {
|
fun noteAdvertisement(rpi: ByteArray, aem: ByteArray, rssi: Int, timestamp: Long = Date().time) = writableDatabase.run {
|
||||||
|
@ -496,13 +515,14 @@ class ExposureDatabase private constructor(private val context: Context) : SQLit
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DB_NAME = "exposure.db"
|
private const val DB_NAME = "exposure.db"
|
||||||
private const val DB_VERSION = 2
|
private const val DB_VERSION = 3
|
||||||
private const val TABLE_ADVERTISEMENTS = "advertisements"
|
private const val TABLE_ADVERTISEMENTS = "advertisements"
|
||||||
private const val TABLE_APP_LOG = "app_log"
|
private const val TABLE_APP_LOG = "app_log"
|
||||||
private const val TABLE_TEK = "tek"
|
private const val TABLE_TEK = "tek"
|
||||||
private const val TABLE_TEK_CHECK = "tek_check"
|
private const val TABLE_TEK_CHECK = "tek_check"
|
||||||
private const val TABLE_DIAGNOSIS = "diagnosis"
|
private const val TABLE_DIAGNOSIS = "diagnosis"
|
||||||
private const val TABLE_CONFIGURATIONS = "configurations"
|
private const val TABLE_CONFIGURATIONS = "configurations"
|
||||||
|
private const val TABLE_APP_PERMS = "app_perms"
|
||||||
|
|
||||||
private var instance: ExposureDatabase? = null
|
private var instance: ExposureDatabase? = null
|
||||||
fun ref(context: Context): ExposureDatabase = synchronized(this) {
|
fun ref(context: Context): ExposureDatabase = synchronized(this) {
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package org.microg.gms.nearby.exposurenotification
|
package org.microg.gms.nearby.exposurenotification
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -13,12 +15,14 @@ import android.os.*
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.android.gms.common.api.CommonStatusCodes
|
import com.google.android.gms.common.api.CommonStatusCodes
|
||||||
import com.google.android.gms.common.api.Status
|
import com.google.android.gms.common.api.Status
|
||||||
|
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes
|
||||||
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.*
|
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.*
|
||||||
import com.google.android.gms.nearby.exposurenotification.ExposureSummary
|
import com.google.android.gms.nearby.exposurenotification.ExposureSummary
|
||||||
import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey
|
import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey
|
||||||
import com.google.android.gms.nearby.exposurenotification.internal.*
|
import com.google.android.gms.nearby.exposurenotification.internal.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.microg.gms.common.PackageUtils
|
||||||
import org.microg.gms.nearby.exposurenotification.Constants.*
|
import org.microg.gms.nearby.exposurenotification.Constants.*
|
||||||
import org.microg.gms.nearby.exposurenotification.proto.TemporaryExposureKeyExport
|
import org.microg.gms.nearby.exposurenotification.proto.TemporaryExposureKeyExport
|
||||||
import org.microg.gms.nearby.exposurenotification.proto.TemporaryExposureKeyProto
|
import org.microg.gms.nearby.exposurenotification.proto.TemporaryExposureKeyProto
|
||||||
|
@ -26,48 +30,51 @@ import java.util.*
|
||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
class ExposureNotificationServiceImpl(private val context: Context, private val packageName: String) : INearbyExposureNotificationService.Stub() {
|
class ExposureNotificationServiceImpl(private val context: Context, private val packageName: String) : INearbyExposureNotificationService.Stub() {
|
||||||
private fun confirm(action: String, callback: (resultCode: Int, resultData: Bundle?) -> Unit) {
|
private fun pendingConfirm(permission: String): PendingIntent {
|
||||||
val intent = Intent(ACTION_CONFIRM)
|
val intent = Intent(ACTION_CONFIRM)
|
||||||
intent.`package` = context.packageName
|
intent.`package` = context.packageName
|
||||||
intent.putExtra(KEY_CONFIRM_PACKAGE, packageName)
|
intent.putExtra(KEY_CONFIRM_PACKAGE, packageName)
|
||||||
intent.putExtra(KEY_CONFIRM_ACTION, action)
|
intent.putExtra(KEY_CONFIRM_ACTION, permission)
|
||||||
intent.putExtra(KEY_CONFIRM_RECEIVER, object : ResultReceiver(Handler(Looper.getMainLooper())) {
|
intent.putExtra(KEY_CONFIRM_RECEIVER, object : ResultReceiver(null) {
|
||||||
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
||||||
Log.d(TAG, "Result from action $action: ${getStatusCodeString(resultCode)}")
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
callback(resultCode, resultData)
|
ExposureDatabase.with(context) { database -> database.grantPermission(packageName, PackageUtils.firstSignatureDigest(context, packageName)!!, permission) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
intent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
|
|
||||||
intent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
|
||||||
try {
|
try {
|
||||||
intent.component = ComponentName(context, context.packageManager.resolveActivity(intent, 0)?.activityInfo?.name!!)
|
intent.component = ComponentName(context, context.packageManager.resolveActivity(intent, 0)?.activityInfo?.name!!)
|
||||||
context.startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, e)
|
Log.w(TAG, e)
|
||||||
callback(CommonStatusCodes.INTERNAL_ERROR, null)
|
}
|
||||||
|
Log.d(TAG, "Pending: $intent")
|
||||||
|
val pi = PendingIntent.getActivity(context, permission.hashCode(), intent, PendingIntent.FLAG_ONE_SHOT)
|
||||||
|
Log.d(TAG, "Pending: $pi")
|
||||||
|
return pi
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun confirmPermission(permission: String): Status {
|
||||||
|
if (packageName == context.packageName) return Status.SUCCESS
|
||||||
|
return ExposureDatabase.with(context) { database ->
|
||||||
|
if (!database.hasPermission(packageName, PackageUtils.firstSignatureDigest(context, packageName)!!, permission)) {
|
||||||
|
Status(RESOLUTION_REQUIRED, "Permission EN#$permission required.", pendingConfirm(permission))
|
||||||
|
} else {
|
||||||
|
Status.SUCCESS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun start(params: StartParams) {
|
override fun start(params: StartParams) {
|
||||||
if (ExposurePreferences(context).enabled) {
|
if (ExposurePreferences(context).enabled) return
|
||||||
params.callback.onResult(Status(FAILED_ALREADY_STARTED))
|
val status = confirmPermission(CONFIRM_ACTION_START)
|
||||||
return
|
if (status.isSuccess) {
|
||||||
|
ExposurePreferences(context).enabled = true
|
||||||
|
ExposureDatabase.with(context) { database -> database.noteAppAction(packageName, "start") }
|
||||||
}
|
}
|
||||||
confirm(CONFIRM_ACTION_START) { resultCode, resultData ->
|
try {
|
||||||
if (resultCode == SUCCESS) {
|
params.callback.onResult(status)
|
||||||
ExposurePreferences(context).enabled = true
|
} catch (e: Exception) {
|
||||||
}
|
Log.w(TAG, "Callback failed", e)
|
||||||
ExposureDatabase.with(context) { database ->
|
|
||||||
database.noteAppAction(packageName, "start", JSONObject().apply {
|
|
||||||
put("result", resultCode)
|
|
||||||
}.toString())
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
params.callback.onResult(Status(if (resultCode == SUCCESS) SUCCESS else FAILED_REJECTED_OPT_IN, resultData?.getString("message")))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Callback failed", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,23 +98,25 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTemporaryExposureKeyHistory(params: GetTemporaryExposureKeyHistoryParams): Unit = ExposureDatabase.with(context) { database ->
|
override fun getTemporaryExposureKeyHistory(params: GetTemporaryExposureKeyHistoryParams) {
|
||||||
confirm(CONFIRM_ACTION_START) { resultCode, resultData ->
|
val status = confirmPermission(CONFIRM_ACTION_KEYS)
|
||||||
val (status, response) = if (resultCode == SUCCESS) {
|
val response = when {
|
||||||
SUCCESS to database.allKeys
|
status.isSuccess -> ExposureDatabase.with(context) { database ->
|
||||||
} else {
|
database.allKeys
|
||||||
FAILED_REJECTED_OPT_IN to emptyList()
|
|
||||||
}
|
}
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposureDatabase.with(context) { database ->
|
||||||
database.noteAppAction(packageName, "getTemporaryExposureKeyHistory", JSONObject().apply {
|
database.noteAppAction(packageName, "getTemporaryExposureKeyHistory", JSONObject().apply {
|
||||||
put("result", resultCode)
|
put("result", status.statusCode)
|
||||||
put("response_keys_size", response.size)
|
put("response_keys_size", response.size)
|
||||||
}.toString())
|
}.toString())
|
||||||
try {
|
}
|
||||||
params.callback.onResult(Status(status, resultData?.getString("message")), response)
|
try {
|
||||||
} catch (e: Exception) {
|
params.callback.onResult(status, response)
|
||||||
Log.w(TAG, "Callback failed", e)
|
} catch (e: Exception) {
|
||||||
}
|
Log.w(TAG, "Callback failed", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue