1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.credentials.playservices.controllers 18 19 import android.content.Context 20 import android.content.Intent 21 import android.os.Bundle 22 import android.os.Parcel 23 import android.os.ResultReceiver 24 import androidx.credentials.exceptions.CreateCredentialCancellationException 25 import androidx.credentials.exceptions.CreateCredentialException 26 import androidx.credentials.exceptions.CreateCredentialInterruptedException 27 import androidx.credentials.exceptions.CreateCredentialUnknownException 28 import androidx.credentials.exceptions.GetCredentialCancellationException 29 import androidx.credentials.exceptions.GetCredentialException 30 import androidx.credentials.exceptions.GetCredentialInterruptedException 31 import androidx.credentials.exceptions.GetCredentialUnknownException 32 import androidx.credentials.exceptions.NoCredentialException 33 import com.google.android.gms.common.api.CommonStatusCodes 34 35 /** Holds all non type specific details shared by the controllers. */ 36 internal open class CredentialProviderBaseController(private val context: Context) { 37 companion object { 38 39 // Common retryable status codes from the play modules found 40 // https://developers.google.com/android/reference/com/google/android/gms/common/api/CommonStatusCodes 41 val retryables: Set<Int> = 42 setOf( 43 CommonStatusCodes.NETWORK_ERROR, 44 CommonStatusCodes.CONNECTION_SUSPENDED_DURING_CALL 45 ) 46 47 // Generic controller request code used by all controllers 48 @JvmStatic internal val CONTROLLER_REQUEST_CODE: Int = 1 49 50 /** -- Used to avoid reflection, these constants map errors from HiddenActivity -- */ 51 const val GET_CANCELED = "GET_CANCELED_TAG" 52 const val GET_INTERRUPTED = "GET_INTERRUPTED" 53 const val GET_NO_CREDENTIALS = "GET_NO_CREDENTIALS" 54 const val GET_UNKNOWN = "GET_UNKNOWN" 55 56 const val CREATE_CANCELED = "CREATE_CANCELED" 57 const val CREATE_INTERRUPTED = "CREATE_INTERRUPTED" 58 const val CREATE_UNKNOWN = "CREATE_UNKNOWN" 59 60 /** ---- Data Constants to pass between the controllers and the hidden activity---- */ 61 62 // Key to indicate type sent from controller to hidden activity 63 const val TYPE_TAG = "TYPE" 64 65 // Value for the specific begin sign in type 66 const val BEGIN_SIGN_IN_TAG = "BEGIN_SIGN_IN" 67 68 // Key for the Sign-in Intent flow 69 const val SIGN_IN_INTENT_TAG = "SIGN_IN_INTENT" 70 71 // Value for the specific create password type 72 const val CREATE_PASSWORD_TAG = "CREATE_PASSWORD" 73 74 // Value for the specific create public key credential type 75 const val CREATE_PUBLIC_KEY_CREDENTIAL_TAG = "CREATE_PUBLIC_KEY_CREDENTIAL" 76 77 // Key for the actual parcelable type sent to the hidden activity 78 const val REQUEST_TAG = "REQUEST_TYPE" 79 80 // Key for the result intent to send back to the controller 81 const val RESULT_DATA_TAG = "RESULT_DATA" 82 83 // Key for the actual parcelable type sent to the hidden activity 84 const val EXTRA_GET_CREDENTIAL_INTENT = "EXTRA_GET_CREDENTIAL_INTENT" 85 86 // Key for the failure boolean sent back from hidden activity to controller 87 const val FAILURE_RESPONSE_TAG = "FAILURE_RESPONSE" 88 89 // Key for the exception type sent back from hidden activity to controllers if error 90 const val EXCEPTION_TYPE_TAG = "EXCEPTION_TYPE" 91 92 // Key for an error message propagated from hidden activity to controllers 93 const val EXCEPTION_MESSAGE_TAG = "EXCEPTION_MESSAGE" 94 95 // Key for the activity request code from controllers to activity 96 const val ACTIVITY_REQUEST_CODE_TAG = "ACTIVITY_REQUEST_CODE" 97 98 // Key for the result receiver sent from controller to activity 99 const val RESULT_RECEIVER_TAG = "RESULT_RECEIVER" 100 101 /** Shuttles back exceptions only related to the hidden activity that can't be parceled */ getCredentialExceptionTypeToExceptionnull102 internal fun getCredentialExceptionTypeToException( 103 typeName: String?, 104 msg: String? 105 ): GetCredentialException { 106 return when (typeName) { 107 GET_CANCELED -> { 108 GetCredentialCancellationException(msg) 109 } 110 GET_INTERRUPTED -> { 111 GetCredentialInterruptedException(msg) 112 } 113 GET_NO_CREDENTIALS -> { 114 NoCredentialException(msg) 115 } 116 else -> { 117 GetCredentialUnknownException(msg) 118 } 119 } 120 } 121 reportErrornull122 internal fun ResultReceiver.reportError(errName: String, errMsg: String) { 123 val bundle = Bundle() 124 bundle.putBoolean(FAILURE_RESPONSE_TAG, true) 125 bundle.putString(EXCEPTION_TYPE_TAG, errName) 126 bundle.putString(EXCEPTION_MESSAGE_TAG, errMsg) 127 this.send(Integer.MAX_VALUE, bundle) 128 } 129 reportResultnull130 internal fun ResultReceiver.reportResult(requestCode: Int, resultCode: Int, data: Intent?) { 131 val bundle = Bundle() 132 bundle.putBoolean(FAILURE_RESPONSE_TAG, false) 133 bundle.putInt(ACTIVITY_REQUEST_CODE_TAG, requestCode) 134 bundle.putParcelable(RESULT_DATA_TAG, data) 135 this.send(resultCode, bundle) 136 } 137 createCredentialExceptionTypeToExceptionnull138 internal fun createCredentialExceptionTypeToException( 139 typeName: String?, 140 msg: String? 141 ): CreateCredentialException { 142 return when (typeName) { 143 CREATE_CANCELED -> { 144 CreateCredentialCancellationException(msg) 145 } 146 CREATE_INTERRUPTED -> { 147 CreateCredentialInterruptedException(msg) 148 } 149 else -> { 150 CreateCredentialUnknownException(msg) 151 } 152 } 153 } 154 } 155 toIpcFriendlyResultReceivernull156 fun <T : ResultReceiver?> toIpcFriendlyResultReceiver(resultReceiver: T): ResultReceiver? { 157 val parcel: Parcel = Parcel.obtain() 158 resultReceiver!!.writeToParcel(parcel, 0) 159 parcel.setDataPosition(0) 160 val ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel) 161 parcel.recycle() 162 return ipcFriendly 163 } 164 generateHiddenActivityIntentnull165 fun generateHiddenActivityIntent( 166 resultReceiver: ResultReceiver, 167 hiddenIntent: Intent, 168 typeTag: String 169 ) { 170 hiddenIntent.putExtra(TYPE_TAG, typeTag) 171 hiddenIntent.putExtra(ACTIVITY_REQUEST_CODE_TAG, CONTROLLER_REQUEST_CODE) 172 hiddenIntent.putExtra(RESULT_RECEIVER_TAG, toIpcFriendlyResultReceiver(resultReceiver)) 173 hiddenIntent.flags = Intent.FLAG_ACTIVITY_NO_ANIMATION 174 } 175 } 176