Launch provider pending intent from the CredentialManager UI.

Test: locally
Bug: 246564035
Bug: 253156924
Bug: 253156958

Change-Id: Id56070db3fbbbef642fa126e23fdbfe619ef69d7
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 7e69987..a6e64ce 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -17,6 +17,7 @@
 package com.android.credentialmanager
 
 import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
+import android.app.PendingIntent
 import android.app.slice.Slice
 import android.app.slice.SliceSpec
 import android.content.Context
@@ -32,6 +33,7 @@
 import android.credentials.ui.ProviderData
 import android.credentials.ui.RequestInfo
 import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.ProviderPendingIntentResponse
 import android.credentials.ui.UserSelectionDialogResult
 import android.graphics.drawable.Icon
 import android.os.Binder
@@ -54,7 +56,7 @@
   private val context: Context,
   intent: Intent,
 ) {
-  private val requestInfo: RequestInfo
+  val requestInfo: RequestInfo
   private val providerEnabledList: List<ProviderData>
   private val providerDisabledList: List<DisabledProviderData>
   // TODO: require non-null.
@@ -75,7 +77,7 @@
       RequestInfo.TYPE_GET ->
         intent.extras?.getParcelableArrayList(
           ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
-          DisabledProviderData::class.java
+          GetCredentialProviderData::class.java
         ) ?: testGetCredentialProviderList()
       else -> {
         // TODO: fail gracefully
@@ -101,12 +103,19 @@
     resultReceiver?.send(BaseDialogResult.RESULT_CODE_DIALOG_CANCELED, resultData)
   }
 
-  fun onOptionSelected(providerPackageName: String, entryKey: String, entrySubkey: String) {
+  fun onOptionSelected(
+    providerPackageName: String,
+    entryKey: String,
+    entrySubkey: String,
+    resultCode: Int? = null,
+    resultData: Intent? = null,
+  ) {
     val userSelectionDialogResult = UserSelectionDialogResult(
       requestInfo.token,
       providerPackageName,
       entryKey,
-      entrySubkey
+      entrySubkey,
+      if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
     )
     val resultData = Bundle()
     UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultData)
@@ -328,6 +337,14 @@
     userDisplayName: String?,
     lastUsedTimeMillis: Long?,
   ): Entry {
+    val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+      .setPackage("com.androidauth.androidvault")
+    intent.putExtra("provider_extra_sample", "testprovider")
+
+    val pendingIntent = PendingIntent.getActivity(context, 1,
+      intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+              or PendingIntent.FLAG_ONE_SHOT))
+
     val slice = Slice.Builder(
       Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
     ).addText(
@@ -347,7 +364,9 @@
     return Entry(
       key,
       subkey,
-      slice.build()
+      slice.build(),
+      pendingIntent,
+      null
     )
   }
 
@@ -360,10 +379,22 @@
     totalCredentialCount: Int,
     lastUsedTimeMillis: Long,
   ): Entry {
+    val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+      .setPackage("com.androidauth.androidvault")
+    intent.putExtra("provider_extra_sample", "testprovider")
+    val pendingIntent = PendingIntent.getActivity(context, 1,
+      intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+              or PendingIntent.FLAG_ONE_SHOT))
+    val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
+      context.applicationInfo.packageName,
+      "PASSWORD",
+      toBundle("[email protected]", "password123")
+    )
+    val fillInIntent = Intent().putExtra("create_request_params", createPasswordRequest)
+
     val slice = Slice.Builder(
       Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
-    )
-      .addText(
+    ).addText(
         providerDisplayName, null, listOf(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME))
       .addIcon(
         Icon.createWithResource(context, R.drawable.ic_passkey),
@@ -384,7 +415,9 @@
     return Entry(
       key,
       subkey,
-      slice
+      slice,
+      pendingIntent,
+      fillInIntent,
     )
   }
 
@@ -415,7 +448,6 @@
   }
 
   private fun testGetRequestInfo(): RequestInfo {
-    val data = Bundle()
     return RequestInfo.newGetRequestInfo(
       Binder(),
       GetCredentialRequest.Builder()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 1041a33..d324f87 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -16,17 +16,21 @@
 
 package com.android.credentialmanager
 
-import android.credentials.ui.RequestInfo
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.ComponentActivity
+import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.compose.setContent
+import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import androidx.lifecycle.Observer
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.credentialmanager.common.DialogType
 import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ProviderActivityResult
 import com.android.credentialmanager.common.ResultState
 import com.android.credentialmanager.createflow.CreateCredentialScreen
 import com.android.credentialmanager.createflow.CreateCredentialViewModel
@@ -39,28 +43,23 @@
   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     CredentialManagerRepo.setup(this, intent)
-    val requestInfo = intent.extras?.getParcelable<RequestInfo>(RequestInfo.EXTRA_REQUEST_INFO)
-    if (requestInfo != null) {
-      val requestType = requestInfo.type
-      setContent {
-        CredentialSelectorTheme {
-          CredentialManagerBottomSheet(requestType)
-        }
-      }
-    } else {
-      // TODO: prototype only code to be removed. In production should exit.
-      setContent {
-        CredentialSelectorTheme {
-          CredentialManagerBottomSheet(RequestInfo.TYPE_CREATE)
-        }
+    val requestInfo = CredentialManagerRepo.getInstance().requestInfo
+    setContent {
+      CredentialSelectorTheme {
+        CredentialManagerBottomSheet(DialogType.toDialogType(requestInfo.type))
       }
     }
   }
 
   @ExperimentalMaterialApi
   @Composable
-  fun CredentialManagerBottomSheet(operationType: String) {
-    val dialogType = DialogType.toDialogType(operationType)
+  fun CredentialManagerBottomSheet(dialogType: DialogType) {
+    val providerActivityResult = remember { mutableStateOf<ProviderActivityResult?>(null) }
+    val launcher = rememberLauncherForActivityResult(
+      ActivityResultContracts.StartIntentSenderForResult()
+    ) {
+      providerActivityResult.value = ProviderActivityResult(it.resultCode, it.data)
+    }
     when (dialogType) {
       DialogType.CREATE_PASSKEY -> {
         val viewModel: CreateCredentialViewModel = viewModel()
@@ -68,7 +67,10 @@
           this@CredentialSelectorActivity,
           onCancel
         )
-        CreateCredentialScreen(viewModel = viewModel)
+        providerActivityResult.value?.let {
+          viewModel.onProviderActivityResult(it)
+        }
+        CreateCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
       }
       DialogType.GET_CREDENTIALS -> {
         val viewModel: GetCredentialViewModel = viewModel()
@@ -76,7 +78,10 @@
           this@CredentialSelectorActivity,
           onCancel
         )
-        GetCredentialScreen(viewModel = viewModel)
+        providerActivityResult.value?.let {
+          viewModel.onProviderActivityResult(it)
+        }
+        GetCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
       }
       else -> {
         Log.w("AccountSelector", "Unknown type, not rendering any UI")
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index fad9364..bea1e59 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -46,8 +46,15 @@
       val packageManager = context.packageManager
       return providerDataList.map {
         // TODO: get from the actual service info
+        val componentName = ComponentName.unflattenFromString(it.providerFlattenedComponentName)
+        var packageName = componentName?.packageName
+        if (componentName == null) {
+          // TODO: Remove once test data is fixed
+          packageName = it.providerFlattenedComponentName
+        }
+
         val pkgInfo = packageManager
-          .getPackageInfo(it.providerFlattenedComponentName,
+          .getPackageInfo(packageName!!,
             PackageManager.PackageInfoFlags.of(0))
         val providerDisplayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString()
         // TODO: decide what to do when failed to load a provider icon
@@ -86,6 +93,8 @@
           providerId = providerId,
           entryKey = it.key,
           entrySubkey = it.subkey,
+          pendingIntent = it.pendingIntent,
+          fillInIntent = it.frameworkExtrasIntent,
           credentialType = credentialEntryUi.credentialType.toString(),
           credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
           userName = credentialEntryUi.userName.toString(),
@@ -113,6 +122,8 @@
         providerId = providerId,
         entryKey = authEntry.key,
         entrySubkey = authEntry.subkey,
+        pendingIntent = authEntry.pendingIntent,
+        fillInIntent = authEntry.frameworkExtrasIntent,
         title = providerDisplayName,
         icon = providerIcon,
       )
@@ -127,6 +138,8 @@
         providerId = providerId,
         entryKey = remoteEntry.key,
         entrySubkey = remoteEntry.subkey,
+        pendingIntent = remoteEntry.pendingIntent,
+        fillInIntent = remoteEntry.frameworkExtrasIntent,
       )
     }
 
@@ -142,6 +155,8 @@
           providerId = providerId,
           entryKey = it.key,
           entrySubkey = it.subkey,
+          pendingIntent = it.pendingIntent,
+          fillInIntent = it.frameworkExtrasIntent,
           title = actionEntryUi.text.toString(),
           // TODO: gracefully fail
           icon = actionEntryUi.icon.loadDrawable(context)!!,
@@ -214,6 +229,8 @@
           // TODO: remove fallbacks
           entryKey = it.key,
           entrySubkey = it.subkey,
+          pendingIntent = it.pendingIntent,
+          fillInIntent = it.frameworkExtrasIntent,
           userProviderDisplayName = saveEntryUi.userProviderAccountName as String,
           credentialTypeIcon = saveEntryUi.credentialTypeIcon?.loadDrawable(context)
             ?: context.getDrawable(R.drawable.ic_passkey)!!,
@@ -235,6 +252,8 @@
         RemoteInfo(
           entryKey = remoteEntry.key,
           entrySubkey = remoteEntry.subkey,
+          pendingIntent = remoteEntry.pendingIntent,
+          fillInIntent = remoteEntry.frameworkExtrasIntent,
         )
       } else null
     }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt
new file mode 100644
index 0000000..9e33d51
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ProviderActivityResult.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.common
+
+import android.content.Intent
+
+data class ProviderActivityResult(
+    val resultCode: Int,
+    val data: Intent?,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 27d366d..3a277a6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -3,6 +3,9 @@
 package com.android.credentialmanager.createflow
 
 import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -50,7 +53,11 @@
 @Composable
 fun CreateCredentialScreen(
   viewModel: CreateCredentialViewModel,
+  providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
 ) {
+  val primaryEntryCallback: () -> Unit = {
+    viewModel.onPrimaryCreateOptionInfoSelected(providerActivityLauncher)
+  }
   val state = rememberModalBottomSheetState(
     initialValue = ModalBottomSheetValue.Expanded,
     skipHalfExpanded = true
@@ -73,8 +80,8 @@
           requestDisplayInfo = uiState.requestDisplayInfo,
           providerInfo = uiState.activeEntry?.activeProvider!!,
           createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
-          onOptionSelected = viewModel::onPrimaryCreateOptionInfoSelected,
-          onConfirm = viewModel::onPrimaryCreateOptionInfoSelected,
+          onOptionSelected = primaryEntryCallback,
+          onConfirm = primaryEntryCallback,
           onCancel = viewModel::onCancel,
           multiProvider = uiState.enabledProviders.size > 1,
           onMoreOptionsSelected = viewModel::onMoreOptionsSelected
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index 6be019f..093c88f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -17,6 +17,9 @@
 package com.android.credentialmanager.createflow
 
 import android.util.Log
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -25,6 +28,7 @@
 import androidx.lifecycle.ViewModel
 import com.android.credentialmanager.CredentialManagerRepo
 import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ProviderActivityResult
 import com.android.credentialmanager.common.ResultState
 
 data class CreateCredentialUiState(
@@ -59,7 +63,8 @@
       uiState = uiState.copy(
         currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
         activeEntry = ActiveEntry(uiState.enabledProviders.first(),
-          uiState.enabledProviders.first().createOptions.first())
+          uiState.enabledProviders.first().createOptions.first()
+        )
       )
     } else {
       throw java.lang.IllegalStateException("Empty provider list.")
@@ -70,7 +75,8 @@
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
       activeEntry = ActiveEntry(getProviderInfoByName(providerName),
-        getProviderInfoByName(providerName).createOptions.first())
+        getProviderInfoByName(providerName).createOptions.first()
+      )
     )
   }
 
@@ -119,22 +125,56 @@
     // TODO: implement the if choose as default or not logic later
   }
 
-  fun onPrimaryCreateOptionInfoSelected() {
-    val entryKey = uiState.activeEntry?.activeEntryInfo?.entryKey
-    val entrySubkey = uiState.activeEntry?.activeEntryInfo?.entrySubkey
-    Log.d(
-      "Account Selector",
-      "Option selected for creation: " +
-              "{key = $entryKey, subkey = $entrySubkey}"
-    )
-    if (entryKey != null && entrySubkey != null) {
+  fun onPrimaryCreateOptionInfoSelected(
+    launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
+  ) {
+    val selectedEntry = uiState.activeEntry?.activeEntryInfo
+    if (selectedEntry != null) {
+      val entryKey = selectedEntry.entryKey
+      val entrySubkey = selectedEntry.entrySubkey
+      Log.d(
+        "Account Selector",
+        "Option selected for creation: " +
+                "{key = $entryKey, subkey = $entrySubkey}"
+      )
+      if (selectedEntry.pendingIntent != null) {
+        val intentSenderRequest = IntentSenderRequest.Builder(selectedEntry.pendingIntent)
+          .setFillInIntent(selectedEntry.fillInIntent).build()
+        launcher.launch(intentSenderRequest)
+      } else {
+        CredentialManagerRepo.getInstance().onOptionSelected(
+          uiState.activeEntry?.activeProvider!!.name,
+          entryKey,
+          entrySubkey
+        )
+        dialogResult.value = DialogResult(
+          ResultState.COMPLETE,
+        )
+      }
+    } else {
+      dialogResult.value = DialogResult(
+        ResultState.COMPLETE,
+      )
+      TODO("Gracefully handle illegal state.")
+    }
+  }
+
+  fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
+    val entry = uiState.activeEntry?.activeEntryInfo
+    val resultCode = providerActivityResult.resultCode
+    val resultData = providerActivityResult.data
+    val providerId = uiState.activeEntry?.activeProvider!!.name
+    if (entry != null) {
+      Log.d("Account Selector", "Got provider activity result: {provider=" +
+              "$providerId, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
+              "resultCode=$resultCode, resultData=$resultData}"
+      )
       CredentialManagerRepo.getInstance().onOptionSelected(
-        uiState.activeEntry?.activeProvider!!.name,
-        entryKey,
-        entrySubkey
+        providerId, entry.entryKey, entry.entrySubkey, resultCode, resultData,
       )
     } else {
-      TODO("Gracefully handle illegal state.")
+      Log.w("Account Selector",
+        "Illegal state: received a provider result but found no matching entry.")
     }
     dialogResult.value = DialogResult(
       ResultState.COMPLETE,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 1ab234a..753dc3c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -16,6 +16,8 @@
 
 package com.android.credentialmanager.createflow
 
+import android.app.PendingIntent
+import android.content.Intent
 import android.graphics.drawable.Drawable
 
 open class ProviderInfo(
@@ -42,11 +44,15 @@
 open class EntryInfo (
   val entryKey: String,
   val entrySubkey: String,
+  val pendingIntent: PendingIntent?,
+  val fillInIntent: Intent?,
 )
 
 class CreateOptionInfo(
   entryKey: String,
   entrySubkey: String,
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
   val userProviderDisplayName: String?,
   val credentialTypeIcon: Drawable,
   val profileIcon: Drawable,
@@ -54,12 +60,14 @@
   val passkeyCount: Int?,
   val totalCredentialCount: Int?,
   val lastUsedTimeMillis: Long?,
-) : EntryInfo(entryKey, entrySubkey)
+) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 class RemoteInfo(
   entryKey: String,
   entrySubkey: String,
-) : EntryInfo(entryKey, entrySubkey)
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
+) : EntryInfo(entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 data class RequestDisplayInfo(
   val title: String,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 19a032f..db0c16c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -17,6 +17,9 @@
 package com.android.credentialmanager.getflow
 
 import android.text.TextUtils
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
 
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.layout.Arrangement
@@ -62,7 +65,11 @@
 @Composable
 fun GetCredentialScreen(
   viewModel: GetCredentialViewModel,
+  providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
 ) {
+  val entrySelectionCallback: (EntryInfo) -> Unit = {
+    viewModel.onEntrySelected(it, providerActivityLauncher)
+  }
   val state = rememberModalBottomSheetState(
     initialValue = ModalBottomSheetValue.Expanded,
     skipHalfExpanded = true
@@ -75,14 +82,14 @@
         GetScreenState.PRIMARY_SELECTION -> PrimarySelectionCard(
           requestDisplayInfo = uiState.requestDisplayInfo,
           providerDisplayInfo = uiState.providerDisplayInfo,
-          onEntrySelected = viewModel::onEntrySelected,
+          onEntrySelected = entrySelectionCallback,
           onCancel = viewModel::onCancel,
           onMoreOptionSelected = viewModel::onMoreOptionSelected,
         )
         GetScreenState.ALL_SIGN_IN_OPTIONS -> AllSignInOptionCard(
           providerInfoList = uiState.providerInfoList,
           providerDisplayInfo = uiState.providerDisplayInfo,
-          onEntrySelected = viewModel::onEntrySelected,
+          onEntrySelected = entrySelectionCallback,
           onBackButtonClicked = viewModel::onBackToPrimarySelectionScreen,
         )
       }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index 22370a9..6dea9c2 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -17,6 +17,9 @@
 package com.android.credentialmanager.getflow
 
 import android.util.Log
+import androidx.activity.compose.ManagedActivityResultLauncher
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -25,6 +28,7 @@
 import androidx.lifecycle.ViewModel
 import com.android.credentialmanager.CredentialManagerRepo
 import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ProviderActivityResult
 import com.android.credentialmanager.common.ResultState
 import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
 import com.android.internal.util.Preconditions
@@ -34,6 +38,7 @@
   val currentScreenState: GetScreenState,
   val requestDisplayInfo: RequestDisplayInfo,
   val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
+  val selectedEntry: EntryInfo? = null,
 )
 
 class GetCredentialViewModel(
@@ -51,14 +56,42 @@
     return dialogResult
   }
 
-  fun onEntrySelected(entry: EntryInfo) {
+  fun onEntrySelected(
+    entry: EntryInfo,
+    launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
+  ) {
     Log.d("Account Selector", "credential selected:" +
             " {provider=${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}}")
-    CredentialManagerRepo.getInstance().onOptionSelected(
-      entry.providerId,
-      entry.entryKey,
-      entry.entrySubkey
-    )
+    if (entry.pendingIntent != null) {
+      uiState = uiState.copy(selectedEntry = entry)
+      val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent)
+        .setFillInIntent(entry.fillInIntent).build()
+      launcher.launch(intentSenderRequest)
+    } else {
+      CredentialManagerRepo.getInstance().onOptionSelected(
+        entry.providerId, entry.entryKey, entry.entrySubkey,
+      )
+      dialogResult.value = DialogResult(ResultState.COMPLETE)
+    }
+  }
+
+  fun onProviderActivityResult(providerActivityResult: ProviderActivityResult) {
+    val entry = uiState.selectedEntry
+    val resultCode = providerActivityResult.resultCode
+    val resultData = providerActivityResult.data
+    if (entry != null) {
+      Log.d("Account Selector", "Got provider activity result: {provider=" +
+              "${entry.providerId}, key=${entry.entryKey}, subkey=${entry.entrySubkey}, " +
+                "resultCode=$resultCode, resultData=$resultData}"
+      )
+      CredentialManagerRepo.getInstance().onOptionSelected(
+        entry.providerId, entry.entryKey, entry.entrySubkey,
+        resultCode, resultData,
+      )
+    } else {
+      Log.w("Account Selector",
+        "Illegal state: received a provider result but found no matching entry.")
+    }
     dialogResult.value = DialogResult(ResultState.COMPLETE)
   }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 76d9847..0c3baff 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -16,6 +16,8 @@
 
 package com.android.credentialmanager.getflow
 
+import android.app.PendingIntent
+import android.content.Intent
 import android.graphics.drawable.Drawable
 
 data class ProviderInfo(
@@ -49,12 +51,16 @@
   val providerId: String,
   val entryKey: String,
   val entrySubkey: String,
+  val pendingIntent: PendingIntent?,
+  val fillInIntent: Intent?,
 )
 
 class CredentialEntryInfo(
   providerId: String,
   entryKey: String,
   entrySubkey: String,
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
   /** Type of this credential used for sorting. Not localized so must not be directly displayed. */
   val credentialType: String,
   /** Localized type value of this credential used for display purpose. */
@@ -63,30 +69,36 @@
   val displayName: String?,
   val icon: Drawable,
   val lastUsedTimeMillis: Long?,
-) : EntryInfo(providerId, entryKey, entrySubkey)
+) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 class AuthenticationEntryInfo(
   providerId: String,
   entryKey: String,
   entrySubkey: String,
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
   val title: String,
   val icon: Drawable,
-) : EntryInfo(providerId, entryKey, entrySubkey)
+) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 class RemoteEntryInfo(
   providerId: String,
   entryKey: String,
   entrySubkey: String,
-) : EntryInfo(providerId, entryKey, entrySubkey)
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
+) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 class ActionEntryInfo(
   providerId: String,
   entryKey: String,
   entrySubkey: String,
+  pendingIntent: PendingIntent?,
+  fillInIntent: Intent?,
   val title: String,
   val icon: Drawable,
   val subTitle: String?,
-) : EntryInfo(providerId, entryKey, entrySubkey)
+) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
 
 data class RequestDisplayInfo(
   val appDomainName: String,