Fix two SMS Autofill crashes (#985)

SMS OTP Autofill currently crashes for two reasons:

1.  Tasks.await has a precondition of not running on the UI thread.
2.  Exceptions thrown from Tasks are always wrapped into
    ExecutionExceptions and need to be unwrapped before they can be
    identified as ResolvableApiException.

This commit addresses both issues by making waitForSms a proper
coroutine using withContext and a custom wrapper around Task<T> that
relies on suspendCoroutine and automatically unwraps exceptions.
This commit is contained in:
Fabian Henneke 2020-07-30 10:29:01 +02:00 committed by GitHub
parent 8481491994
commit 3afeff45d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 5 deletions

View file

@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
- Allow creating nested directories directly - Allow creating nested directories directly
- I keep saying this but for real: error message for wrong SSH/HTTPS password is properly fixed now - I keep saying this but for real: error message for wrong SSH/HTTPS password is properly fixed now
- Correctly hide TOTP import button when TOTP secret/OTPAUTH URL is already present in extra content - Correctly hide TOTP import button when TOTP secret/OTPAUTH URL is already present in extra content
- SMS OTP Autofill no longer crashes when invoked and correctly asks for the required permission on first use
## [1.10.1] - 2020-07-23 ## [1.10.1] - 2020-07-23

View file

@ -24,13 +24,30 @@ import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.api.ResolvableApiException import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.tasks.Tasks import com.google.android.gms.tasks.Task
import com.zeapo.pwdstore.autofill.oreo.AutofillAction import com.zeapo.pwdstore.autofill.oreo.AutofillAction
import com.zeapo.pwdstore.autofill.oreo.Credentials import com.zeapo.pwdstore.autofill.oreo.Credentials
import com.zeapo.pwdstore.autofill.oreo.FillableForm import com.zeapo.pwdstore.autofill.oreo.FillableForm
import com.zeapo.pwdstore.databinding.ActivityOreoAutofillSmsBinding import com.zeapo.pwdstore.databinding.ActivityOreoAutofillSmsBinding
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import java.util.concurrent.ExecutionException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
suspend fun <T> Task<T>.suspendableAwait() = suspendCoroutine<T> { cont ->
addOnSuccessListener { result: T ->
cont.resume(result)
}
addOnFailureListener { e ->
// Unwrap specific exceptions (e.g. ResolvableApiException) from ExecutionException.
val cause = (e as? ExecutionException)?.cause ?: e
cont.resumeWithException(cause)
}
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
class AutofillSmsActivity : AppCompatActivity() { class AutofillSmsActivity : AppCompatActivity() {
@ -105,17 +122,23 @@ class AutofillSmsActivity : AppCompatActivity() {
} }
} }
private fun waitForSms() { private suspend fun waitForSms() {
val smsClient = SmsCodeRetriever.getAutofillClient(this@AutofillSmsActivity) val smsClient = SmsCodeRetriever.getAutofillClient(this@AutofillSmsActivity)
try { try {
Tasks.await(smsClient.startSmsCodeRetriever()) withContext(Dispatchers.IO) {
smsClient.startSmsCodeRetriever().suspendableAwait()
}
} catch (e: ResolvableApiException) { } catch (e: ResolvableApiException) {
e.startResolutionForResult(this, 1) withContext(Dispatchers.Main) {
e.startResolutionForResult(this@AutofillSmsActivity, 1)
}
} catch (e: Exception) { } catch (e: Exception) {
e(e) e(e)
withContext(Dispatchers.Main) {
finish() finish()
} }
} }
}
private val smsCodeRetrievedReceiver = object : BroadcastReceiver() { private val smsCodeRetrievedReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {