Additional error handling for registration v2.

This commit is contained in:
Nicholas Tinsley
2024-05-20 15:08:19 -04:00
committed by Cody Henthorne
parent 4f3ee9ca1d
commit afe3cd1098
6 changed files with 212 additions and 42 deletions

View File

@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.registration.PushChallengeRequest
import org.thoughtcrime.securesms.registration.RegistrationData
import org.thoughtcrime.securesms.registration.VerifyAccountRepository
import org.thoughtcrime.securesms.registration.v2.data.network.BackupAuthCheckResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCheckResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCreationResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionResult
@@ -337,10 +338,11 @@ object RegistrationRepository {
/**
* Submits the user-entered verification code to the service.
*/
suspend fun submitVerificationCode(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData): NetworkResult<RegistrationSessionMetadataResponse> =
suspend fun submitVerificationCode(context: Context, e164: String, password: String, sessionId: String, registrationData: RegistrationData): VerificationCodeRequestResult =
withContext(Dispatchers.IO) {
val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password).registrationApi
api.verifyAccount(sessionId = sessionId, verificationCode = registrationData.code)
val result = api.verifyAccount(sessionId = sessionId, verificationCode = registrationData.code)
return@withContext VerificationCodeRequestResult.from(result)
}
/**
@@ -356,7 +358,7 @@ object RegistrationRepository {
/**
* Submit the necessary assets as a verified account so that the user can actually use the service.
*/
suspend fun registerAccount(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): NetworkResult<AccountRegistrationResult> =
suspend fun registerAccount(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String? = null, masterKeyProducer: VerifyAccountRepository.MasterKeyProducer? = null): RegisterAccountResult =
withContext(Dispatchers.IO) {
val api: RegistrationApi = AccountManagerFactory.getInstance().createUnauthenticated(context, registrationData.e164, SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.password).registrationApi
@@ -389,7 +391,7 @@ object RegistrationRepository {
val aciPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(aciIdentity, SignalStore.account().aciPreKeys)
val pniPreKeyCollection = org.thoughtcrime.securesms.registration.RegistrationRepository.generateSignedAndLastResortPreKeys(pniIdentity, SignalStore.account().pniPreKeys)
api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
val result: NetworkResult<AccountRegistrationResult> = api.registerAccount(sessionId, registrationData.recoveryPassword, accountAttributes, aciPreKeyCollection, pniPreKeyCollection, registrationData.fcmToken, true)
.map { accountRegistrationResponse ->
AccountRegistrationResult(
uuid = accountRegistrationResponse.uuid,
@@ -402,6 +404,8 @@ object RegistrationRepository {
pniPreKeyCollection = pniPreKeyCollection
)
}
return@withContext RegisterAccountResult.from(result)
}
suspend fun createSessionAndBlockForPushChallenge(accountManager: RegistrationApi, fcmToken: String, mcc: String?, mnc: String?): NetworkResult<RegistrationSessionMetadataResponse> =

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.v2.data.network
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException
import org.whispersystems.signalservice.api.push.exceptions.IncorrectRegistrationRecoveryPasswordException
import org.whispersystems.signalservice.api.push.exceptions.MalformedRequestException
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
import org.whispersystems.signalservice.internal.push.AuthCredentials
import org.whispersystems.signalservice.internal.push.LockedException
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
/**
* This is a processor to map a [VerifyAccountResponse] to all the known outcomes.
*/
sealed class RegisterAccountResult(cause: Throwable?) : RegistrationResult(cause) {
companion object {
fun from(networkResult: NetworkResult<RegistrationRepository.AccountRegistrationResult>): RegisterAccountResult {
return when (networkResult) {
is NetworkResult.Success -> Success(networkResult.result)
is NetworkResult.ApplicationError -> UnknownError(networkResult.throwable)
is NetworkResult.NetworkError -> UnknownError(networkResult.exception)
is NetworkResult.StatusCodeError -> {
when (val cause = networkResult.exception) {
is IncorrectRegistrationRecoveryPasswordException -> IncorrectRecoveryPassword(cause)
is AuthorizationFailedException -> AuthorizationFailed(cause)
is MalformedRequestException -> MalformedRequest(cause)
is RateLimitException -> createRateLimitProcessor(cause)
is LockedException -> RegistrationLocked(cause = cause, timeRemaining = cause.timeRemaining, svr2Credentials = cause.svr2Credentials)
else -> {
if (networkResult.code == 422) {
ValidationError(cause)
} else {
UnknownError(cause)
}
}
}
}
}
}
private fun createRateLimitProcessor(exception: RateLimitException): RegisterAccountResult {
return if (exception.retryAfterMilliseconds.isPresent) {
RateLimited(exception, exception.retryAfterMilliseconds.get())
} else {
AttemptsExhausted(exception)
}
}
}
class Success(val accountRegistrationResult: RegistrationRepository.AccountRegistrationResult) : RegisterAccountResult(null)
class IncorrectRecoveryPassword(cause: Throwable) : RegisterAccountResult(cause)
class AuthorizationFailed(cause: Throwable) : RegisterAccountResult(cause)
class MalformedRequest(cause: Throwable) : RegisterAccountResult(cause)
class ValidationError(cause: Throwable) : RegisterAccountResult(cause)
class RateLimited(cause: Throwable, val timeRemaining: Long) : RegisterAccountResult(cause)
class AttemptsExhausted(cause: Throwable) : RegisterAccountResult(cause)
class RegistrationLocked(cause: Throwable, val timeRemaining: Long, val svr2Credentials: AuthCredentials?) : RegisterAccountResult(cause)
class UnknownError(cause: Throwable) : RegisterAccountResult(cause)
}

View File

@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.registration.RegistrationData
import org.thoughtcrime.securesms.registration.RegistrationUtil
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
import org.thoughtcrime.securesms.registration.v2.data.network.BackupAuthCheckResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCheckResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionCreationResult
import org.thoughtcrime.securesms.registration.v2.data.network.RegistrationSessionResult
@@ -50,10 +51,8 @@ import org.thoughtcrime.securesms.registration.v2.data.network.VerificationCodeR
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.dualsim.MccMncProducer
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.SvrNoDataException
import org.whispersystems.signalservice.api.kbs.MasterKey
import org.whispersystems.signalservice.internal.push.LockedException
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
import java.io.IOException
import kotlin.time.Duration.Companion.minutes
@@ -64,7 +63,7 @@ import kotlin.time.Duration.Companion.minutes
class RegistrationV2ViewModel : ViewModel() {
private val store = MutableStateFlow(RegistrationV2State())
private val password = Util.getSecret(18) // TODO [regv2]: persist this
private val password = Util.getSecret(18)
private val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
Log.w(TAG, "CoroutineExceptionHandler invoked.", exception)
@@ -168,7 +167,7 @@ class RegistrationV2ViewModel : ViewModel() {
}
viewModelScope.launch {
val svrCredentialsResult = RegistrationRepository.hasValidSvrAuthCredentials(context, e164, password)
val svrCredentialsResult: BackupAuthCheckResult = RegistrationRepository.hasValidSvrAuthCredentials(context, e164, password)
when (svrCredentialsResult) {
is BackupAuthCheckResult.UnknownError -> {
@@ -184,7 +183,9 @@ class RegistrationV2ViewModel : ViewModel() {
return@launch
}
is BackupAuthCheckResult.SuccessWithoutCredentials -> Log.d(TAG, "No local SVR auth credentials could be found and/or validated.")
is BackupAuthCheckResult.SuccessWithoutCredentials -> {
Log.d(TAG, "No local SVR auth credentials could be found and/or validated.")
}
}
val validSession = getOrCreateValidSession(context) ?: return@launch
@@ -412,6 +413,34 @@ class RegistrationV2ViewModel : ViewModel() {
return false
}
/**
* @return whether the request was successful and execution should continue
*/
private suspend fun handleRegistrationResult(context: Context, registrationData: RegistrationData, registrationResult: RegisterAccountResult, reglockEnabled: Boolean, errorHandler: (RegisterAccountResult) -> Unit): Boolean {
when (registrationResult) {
is RegisterAccountResult.Success -> {
onSuccessfulRegistration(context, registrationData, registrationResult.accountRegistrationResult, reglockEnabled)
return true
}
is RegisterAccountResult.IncorrectRecoveryPassword -> {
Log.i(TAG, "Registration recovery password was incorrect, falling back to SMS verification.", registrationResult.getCause())
setUserSkippedReRegisterFlow(true)
}
is RegisterAccountResult.RegistrationLocked -> {
Log.i(TAG, "Account is registration locked!", registrationResult.getCause())
}
is RegisterAccountResult.AttemptsExhausted,
is RegisterAccountResult.RateLimited,
is RegisterAccountResult.AuthorizationFailed,
is RegisterAccountResult.MalformedRequest,
is RegisterAccountResult.ValidationError,
is RegisterAccountResult.UnknownError -> Log.i(TAG, "Received error when trying to register!", registrationResult.getCause())
}
setInProgress(false)
errorHandler(registrationResult)
return false
}
private fun handleGenericError(cause: Throwable) {
Log.w(TAG, "Encountered unknown error!", cause)
store.update {
@@ -437,7 +466,7 @@ class RegistrationV2ViewModel : ViewModel() {
}
}
fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit) {
fun verifyReRegisterWithPin(context: Context, pin: String, wrongPinHandler: () -> Unit, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
setInProgress(true)
// Local recovery password
@@ -445,7 +474,7 @@ class RegistrationV2ViewModel : ViewModel() {
if (RegistrationRepository.doesPinMatchLocalHash(pin)) {
Log.d(TAG, "Found recovery password, attempting to re-register.")
viewModelScope.launch(context = coroutineExceptionHandler) {
verifyReRegisterInternal(context, pin, SignalStore.svr().getOrCreateMasterKey())
verifyReRegisterInternal(context, pin, SignalStore.svr().getOrCreateMasterKey(), registrationErrorHandler)
setInProgress(false)
}
} else {
@@ -465,7 +494,7 @@ class RegistrationV2ViewModel : ViewModel() {
val masterKey = RegistrationRepository.fetchMasterKeyFromSvrRemote(pin, authCredentials)
setRecoveryPassword(masterKey.deriveRegistrationRecoveryPassword())
updateSvrTriesRemaining(10)
verifyReRegisterInternal(context, pin, masterKey)
verifyReRegisterInternal(context, pin, masterKey, registrationErrorHandler)
} catch (rejectedPin: SvrWrongPinException) {
Log.w(TAG, "Submitted PIN was rejected by SVR.", rejectedPin)
updateSvrTriesRemaining(rejectedPin.triesRemaining)
@@ -486,7 +515,7 @@ class RegistrationV2ViewModel : ViewModel() {
}
}
private suspend fun verifyReRegisterInternal(context: Context, pin: String, masterKey: MasterKey) {
private suspend fun verifyReRegisterInternal(context: Context, pin: String, masterKey: MasterKey, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
updateFcmToken(context)
val registrationData = getRegistrationData("")
@@ -495,34 +524,31 @@ class RegistrationV2ViewModel : ViewModel() {
val result = resultAndRegLockStatus.first
val reglockEnabled = resultAndRegLockStatus.second
if (result !is NetworkResult.Success) {
Log.w(TAG, "Error during registration!", result.getCause())
return
}
onSuccessfulRegistration(context, registrationData, result.result, reglockEnabled)
handleRegistrationResult(context, registrationData, result, reglockEnabled, registrationErrorHandler)
}
private suspend fun registerAccountInternal(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String?, masterKey: MasterKey): Pair<NetworkResult<RegistrationRepository.AccountRegistrationResult>, Boolean> {
val registrationResult = RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }
/**
* @return a [Pair] containing the server response and a boolean signifying whether the current account is registration locked.
*/
private suspend fun registerAccountInternal(context: Context, sessionId: String?, registrationData: RegistrationData, pin: String?, masterKey: MasterKey): Pair<RegisterAccountResult, Boolean> {
val registrationResult: RegisterAccountResult = RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }
// TODO: check for wrong recovery password
// Check if reg lock is enabled
if (registrationResult !is NetworkResult.StatusCodeError || registrationResult.exception !is LockedException) {
if (registrationResult !is RegisterAccountResult.RegistrationLocked) {
return Pair(registrationResult, false)
}
Log.i(TAG, "Received a registration lock response when trying to register an account. Retrying with master key.")
val lockedException = registrationResult.exception as LockedException
store.update {
it.copy(svrAuthCredentials = lockedException.svr2Credentials)
it.copy(svrAuthCredentials = registrationResult.svr2Credentials)
}
return Pair(RegistrationRepository.registerAccount(context = context, sessionId = sessionId, registrationData = registrationData, pin = pin) { masterKey }, true)
}
fun verifyCodeWithoutRegistrationLock(context: Context, code: String) {
fun verifyCodeWithoutRegistrationLock(context: Context, code: String, submissionErrorHandler: (VerificationCodeRequestResult, RegistrationRepository.Mode) -> Unit, registrationErrorHandler: (RegisterAccountResult) -> Unit) {
store.update {
it.copy(inProgress = true, registrationCheckpoint = RegistrationCheckpoint.VERIFICATION_CODE_ENTERED)
}
@@ -532,23 +558,23 @@ class RegistrationV2ViewModel : ViewModel() {
Log.w(TAG, "Session ID was null. TODO: handle this better in the UI.")
return
}
val e164: String = getCurrentE164() ?: throw IllegalStateException()
viewModelScope.launch(context = coroutineExceptionHandler) {
val registrationData = getRegistrationData(code)
val verificationResponse = RegistrationRepository.submitVerificationCode(context, e164, password, sessionId, registrationData).successOrThrow()
val verificationResponse = RegistrationRepository.submitVerificationCode(context, e164, password, sessionId, registrationData)
if (!verificationResponse.body.verified) {
if (!verificationResponse.isSuccess()) {
Log.w(TAG, "Could not verify code!")
// TODO [regv2]: error handling
handleSessionStateResult(context, verificationResponse, RegistrationRepository.Mode.NONE, submissionErrorHandler)
return@launch
}
setRegistrationCheckpoint(RegistrationCheckpoint.VERIFICATION_CODE_VALIDATED)
val registrationResponse = RegistrationRepository.registerAccount(context, sessionId, registrationData).successOrThrow()
// TODO [regv2]: error handling
onSuccessfulRegistration(context, registrationData, registrationResponse, false)
val registrationResponse: RegisterAccountResult = RegistrationRepository.registerAccount(context, sessionId, registrationData)
handleRegistrationResult(context, registrationData, registrationResponse, false, registrationErrorHandler)
}
}

View File

@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.registration.fragments.ContactSupportBottomShe
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate.setDebugLogSubmitMultiTapView
import org.thoughtcrime.securesms.registration.fragments.SignalStrengthPhoneStateListener
import org.thoughtcrime.securesms.registration.v2.data.RegistrationRepository
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
import org.thoughtcrime.securesms.registration.v2.data.network.VerificationCodeRequestResult
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
@@ -69,7 +70,7 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
}
binding.code.setOnCompleteListener {
sharedViewModel.verifyCodeWithoutRegistrationLock(requireContext(), it)
sharedViewModel.verifyCodeWithoutRegistrationLock(requireContext(), it, ::handleSessionErrorResponse, ::handleRegistrationErrorResponse)
}
binding.havingTroubleButton.setOnClickListener {
@@ -79,14 +80,14 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
binding.callMeCountDown.apply {
setTextResources(R.string.RegistrationActivity_call, R.string.RegistrationActivity_call_me_instead_available_in)
setOnClickListener {
sharedViewModel.requestVerificationCall(requireContext(), ::handleErrorResponse)
sharedViewModel.requestVerificationCall(requireContext(), ::handleSessionErrorResponse)
}
}
binding.resendSmsCountDown.apply {
setTextResources(R.string.RegistrationActivity_resend_code, R.string.RegistrationActivity_resend_sms_available_in)
setOnClickListener {
sharedViewModel.requestSmsCode(requireContext(), ::handleErrorResponse)
sharedViewModel.requestSmsCode(requireContext(), ::handleSessionErrorResponse)
}
}
@@ -111,7 +112,7 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
}
}
private fun handleErrorResponse(requestResult: VerificationCodeRequestResult, mode: RegistrationRepository.Mode) {
private fun handleSessionErrorResponse(requestResult: VerificationCodeRequestResult, mode: RegistrationRepository.Mode) {
when (requestResult) {
is VerificationCodeRequestResult.Success -> binding.keyboard.displaySuccess()
is VerificationCodeRequestResult.RateLimited -> {
@@ -149,6 +150,49 @@ class EnterCodeV2Fragment : LoggingFragment(R.layout.fragment_registration_enter
}
}
private fun handleRegistrationErrorResponse(result: RegisterAccountResult) {
when (result) {
is RegisterAccountResult.Success -> Log.d(TAG, "Register account was successful.")
is RegisterAccountResult.AuthorizationFailed -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
is RegisterAccountResult.MalformedRequest -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
is RegisterAccountResult.RegistrationLocked -> {
Log.w(TAG, "Account is registration locked, cannot register.")
findNavController().safeNavigate(EnterCodeV2FragmentDirections.actionRequireKbsLockPin(result.timeRemaining))
}
is RegisterAccountResult.UnknownError -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
is RegisterAccountResult.ValidationError -> presentRemoteErrorDialog(getString(R.string.RegistrationActivity_error_connecting_to_service))
is RegisterAccountResult.IncorrectRecoveryPassword -> {
Log.w(TAG, "User somehow got recovery password error while entering code. This is very suspicious!")
sharedViewModel.setUserSkippedReRegisterFlow(true)
popBackStack()
}
is RegisterAccountResult.AttemptsExhausted,
is RegisterAccountResult.RateLimited -> presentRateLimitedDialog()
}
}
private fun presentRateLimitedDialog() {
binding.keyboard.displayFailure().addListener(
object : AssertedSuccessListener<Boolean?>() {
override fun onSuccess(result: Boolean?) {
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.RegistrationActivity_too_many_attempts)
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
binding.callMeCountDown.visibility = View.VISIBLE
binding.resendSmsCountDown.visibility = View.VISIBLE
binding.wrongNumber.visibility = View.VISIBLE
binding.code.clear()
binding.keyboard.displayKeyboard()
}
show()
}
}
}
)
}
private fun presentRemoteErrorDialog(message: String, title: String? = null, positiveButtonListener: DialogInterface.OnClickListener? = null) {
MaterialAlertDialogBuilder(requireContext()).apply {
title?.let {

View File

@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.lock.v2.PinKeyboardType
import org.thoughtcrime.securesms.lock.v2.SvrConstants
import org.thoughtcrime.securesms.registration.fragments.BaseRegistrationLockFragment
import org.thoughtcrime.securesms.registration.fragments.RegistrationViewDelegate
import org.thoughtcrime.securesms.registration.v2.data.network.RegisterAccountResult
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationCheckpoint
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2State
import org.thoughtcrime.securesms.registration.v2.ui.RegistrationV2ViewModel
@@ -125,9 +126,14 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
registrationViewModel.setRegistrationCheckpoint(RegistrationCheckpoint.PIN_CONFIRMED)
registrationViewModel.verifyReRegisterWithPin(requireContext(), pin) {
reRegisterViewModel.markIncorrectGuess()
}
registrationViewModel.verifyReRegisterWithPin(
context = requireContext(),
pin = pin,
wrongPinHandler = {
reRegisterViewModel.markIncorrectGuess()
},
registrationErrorHandler = ::registrationErrorHandler
)
// TODO [regv2]: check for registration lock + wrong pin and decrement SVR tries remaining
}
@@ -233,6 +239,15 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
registrationViewModel.setUserSkippedReRegisterFlow(true)
}
private fun presentRateLimitedDialog() {
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.RegistrationActivity_too_many_attempts)
setMessage(R.string.RegistrationActivity_you_have_made_too_many_attempts_please_try_again_later)
setPositiveButton(android.R.string.ok, null)
show()
}
}
private fun genericErrorDialog() {
MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.RegistrationActivity_error_connecting_to_service)
@@ -240,4 +255,26 @@ class ReRegisterWithPinV2Fragment : LoggingFragment(R.layout.fragment_registrati
.create()
.show()
}
private fun registrationErrorHandler(result: RegisterAccountResult) {
when (result) {
is RegisterAccountResult.Success -> Log.d(TAG, "Register account was successful.")
is RegisterAccountResult.AuthorizationFailed,
is RegisterAccountResult.MalformedRequest,
is RegisterAccountResult.UnknownError,
is RegisterAccountResult.ValidationError,
is RegisterAccountResult.RegistrationLocked -> {
Log.i(TAG, "Registration failed.", result.getCause())
genericErrorDialog()
}
is RegisterAccountResult.IncorrectRecoveryPassword -> {
registrationViewModel.setUserSkippedReRegisterFlow(true)
findNavController().safeNavigate(ReRegisterWithPinV2FragmentDirections.actionReRegisterWithPinFragmentToEnterPhoneNumberV2Fragment())
}
is RegisterAccountResult.AttemptsExhausted,
is RegisterAccountResult.RateLimited -> presentRateLimitedDialog()
}
}
}

View File

@@ -317,10 +317,6 @@ public class SignalServiceAccountManager {
}
}
public @Nonnull VerifyAccountResponse registerAccountV2(@Nullable String sessionId, @Nullable String recoveryPassword, AccountAttributes attributes, PreKeyCollection aciPreKeys, PreKeyCollection pniPreKeys, String fcmToken, boolean skipDeviceTransfer) throws IOException {
return pushServiceSocket.submitRegistrationRequest(sessionId, recoveryPassword, attributes, aciPreKeys, pniPreKeys, fcmToken, skipDeviceTransfer);
}
public @Nonnull ServiceResponse<VerifyAccountResponse> changeNumber(@Nonnull ChangePhoneNumberRequest changePhoneNumberRequest) {
try {
VerifyAccountResponse response = this.pushServiceSocket.changeNumber(changePhoneNumberRequest);