Merge "Bring up credMan bottom sheet from pinned entry" into main
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index ad3ccc4..3fcb3da 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -37,6 +37,7 @@
import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.util.Log;
+import android.view.autofill.IAutoFillManagerClient;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -135,7 +136,8 @@
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<GetCandidateCredentialsResponse,
- GetCandidateCredentialsException> callback
+ GetCandidateCredentialsException> callback,
+ @NonNull IAutoFillManagerClient clientCallback
) {
requireNonNull(request, "request must not be null");
requireNonNull(callingPackage, "callingPackage must not be null");
@@ -153,6 +155,7 @@
mService.getCandidateCredentials(
request,
new GetCandidateCredentialsTransport(executor, callback),
+ clientCallback,
callingPackage);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 1b130a9..530fead 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -18,6 +18,7 @@
import android.annotation.Hide;
import android.annotation.NonNull;
+import android.app.PendingIntent;
import android.credentials.ui.GetCredentialProviderData;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,22 +36,39 @@
*/
@Hide
public final class GetCandidateCredentialsResponse implements Parcelable {
- // TODO(b/299321990): Add members
-
@NonNull
private final List<GetCredentialProviderData> mCandidateProviderDataList;
+ private final PendingIntent mPendingIntent;
+
+ private final GetCredentialResponse mGetCredentialResponse;
+
/**
* @hide
*/
@Hide
public GetCandidateCredentialsResponse(
- List<GetCredentialProviderData> candidateProviderDataList
+ GetCredentialResponse getCredentialResponse
+ ) {
+ mCandidateProviderDataList = null;
+ mPendingIntent = null;
+ mGetCredentialResponse = getCredentialResponse;
+ }
+
+ /**
+ * @hide
+ */
+ @Hide
+ public GetCandidateCredentialsResponse(
+ List<GetCredentialProviderData> candidateProviderDataList,
+ PendingIntent pendingIntent
) {
Preconditions.checkCollectionNotEmpty(
candidateProviderDataList,
/*valueName=*/ "candidateProviderDataList");
mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
+ mPendingIntent = pendingIntent;
+ mGetCredentialResponse = null;
}
/**
@@ -62,17 +80,40 @@
return mCandidateProviderDataList;
}
+ /**
+ * Returns candidate provider data list.
+ *
+ * @hide
+ */
+ public GetCredentialResponse getGetCredentialResponse() {
+ return mGetCredentialResponse;
+ }
+
+ /**
+ * Returns candidate provider data list.
+ *
+ * @hide
+ */
+ public PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
protected GetCandidateCredentialsResponse(Parcel in) {
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
mCandidateProviderDataList = candidateProviderDataList;
AnnotationValidations.validate(NonNull.class, null, mCandidateProviderDataList);
+
+ mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ mGetCredentialResponse = in.readTypedObject(GetCredentialResponse.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(mCandidateProviderDataList);
+ dest.writeTypedObject(mPendingIntent, flags);
+ dest.writeTypedObject(mGetCredentialResponse, flags);
}
@Override
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index d081576..726bc97 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -22,6 +22,7 @@
import android.credentials.ClearCredentialStateRequest;
import android.credentials.CreateCredentialRequest;
import android.credentials.GetCandidateCredentialsRequest;
+import android.view.autofill.IAutoFillManagerClient;
import android.credentials.GetCredentialRequest;
import android.credentials.RegisterCredentialDescriptionRequest;
import android.credentials.UnregisterCredentialDescriptionRequest;
@@ -47,7 +48,7 @@
@nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
- @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, String callingPackage);
+ @nullable ICancellationSignal getCandidateCredentials(in GetCredentialRequest request, in IGetCandidateCredentialsCallback callback, in IAutoFillManagerClient clientCallback, String callingPackage);
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index cb1b5d3..5ad2502 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -37,6 +37,7 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
@@ -621,6 +622,23 @@
new FillCallback(callback, request.getId())));
}
+
+ @Override
+ public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
+ IAutoFillManagerClient autofillClientCallback) {
+ ICancellationSignal transport = CancellationSignal.createTransport();
+ try {
+ callback.onCancellable(transport);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ mHandler.sendMessage(obtainMessage(
+ AutofillService::onFillCredentialRequest,
+ AutofillService.this, request, CancellationSignal.fromTransport(transport),
+ new FillCallback(callback, request.getId()),
+ autofillClientCallback));
+ }
+
@Override
public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
mHandler.sendMessage(obtainMessage(
@@ -683,6 +701,15 @@
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
/**
+ * Variant of onFillRequest for internal credential manager proxy autofill service only.
+ *
+ * @hide
+ */
+ public void onFillCredentialRequest(@NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
+ IAutoFillManagerClient autofillClientCallback) {}
+
+ /**
* Called when the user requests the service to save the contents of a screen.
*
* <p>If the service could not handle the request right away—for example, because it must
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index d88e094..03ead32 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -20,6 +20,7 @@
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
import android.service.autofill.SaveRequest;
+import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.os.IResultReceiver;
/**
@@ -30,6 +31,8 @@
oneway interface IAutoFillService {
void onConnectedStateChanged(boolean connected);
void onFillRequest(in FillRequest request, in IFillCallback callback);
+ void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
+ in IAutoFillManagerClient client);
void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
void onSavedPasswordCountRequest(in IResultReceiver receiver);
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 0ff1c7f..dfa5735 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -19,16 +19,18 @@
import android.R
import android.app.assist.AssistStructure
import android.content.Context
-import android.credentials.CredentialManager
-import android.credentials.CredentialOption
-import android.credentials.GetCandidateCredentialsException
-import android.credentials.GetCandidateCredentialsResponse
+import android.app.PendingIntent
+import android.credentials.GetCredentialResponse
import android.credentials.GetCredentialRequest
-import android.credentials.ui.GetCredentialProviderData
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.CredentialOption
import android.graphics.drawable.Icon
+import android.credentials.ui.GetCredentialProviderData
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
+import android.credentials.Credential
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -41,8 +43,11 @@
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
+import android.view.autofill.AutofillValue
+import android.view.autofill.IAutoFillManagerClient
import android.view.autofill.AutofillId
import android.widget.inline.InlinePresentationSpec
+import android.credentials.CredentialManager
import androidx.autofill.inline.v1.InlineSuggestionUi
import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
@@ -58,11 +63,13 @@
import org.json.JSONObject
import java.util.concurrent.Executors
+
class CredentialAutofillService : AutofillService() {
companion object {
private const val TAG = "CredAutofill"
+ private const val SESSION_ID_KEY = "session_id"
private const val CRED_HINT_PREFIX = "credential="
private const val REQUEST_DATA_KEY = "requestData"
private const val CANDIDATE_DATA_KEY = "candidateQueryData"
@@ -77,10 +84,27 @@
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
+ }
+
+ override fun onFillCredentialRequest(
+ request: FillRequest,
+ cancellationSignal: CancellationSignal,
+ callback: FillCallback,
+ autofillCallback: IAutoFillManagerClient
+ ) {
val context = request.fillContexts
val structure = context[context.size - 1].structure
val callingPackage = structure.activityComponent.packageName
- Log.i(TAG, "onFillRequest called for $callingPackage")
+ Log.i(TAG, "onFillCredentialRequest called for $callingPackage")
+
+ var sessionId = request.clientState?.getInt(SESSION_ID_KEY)
+
+ Log.i(TAG, "Autofill sessionId: " + sessionId)
+ if (sessionId == null) {
+ Log.i(TAG, "Session Id not found")
+ callback.onFailure("Session Id not found")
+ return
+ }
val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
if (getCredRequest == null) {
@@ -95,7 +119,31 @@
GetCandidateCredentialsException> {
override fun onResult(result: GetCandidateCredentialsResponse) {
Log.i(TAG, "getCandidateCredentials onResponse")
- val fillResponse = convertToFillResponse(result, request)
+
+ if (result.getCredentialResponse != null) {
+ val autofillId: AutofillId? = result.getCredentialResponse
+ .credential.data.getParcelable(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ AutofillId::class.java)
+ Log.i(TAG, "getCandidateCredentials final response, autofillId: " +
+ autofillId)
+
+ if (autofillId != null) {
+ autofillCallback.autofill(
+ sessionId,
+ mutableListOf(autofillId),
+ mutableListOf(
+ AutofillValue.forText(
+ convertResponseToJson(result.getCredentialResponse)
+ )
+ ),
+ false)
+ }
+ return
+ }
+
+ val fillResponse = convertToFillResponse(result, request,
+ this@CredentialAutofillService)
if (fillResponse != null) {
callback.onSuccess(fillResponse)
} else {
@@ -115,10 +163,62 @@
callingPackage,
CancellationSignal(),
Executors.newSingleThreadExecutor(),
- outcome
+ outcome,
+ autofillCallback
)
}
+ // TODO(b/318118018): Use from Jetpack
+ private fun convertResponseToJson(response: GetCredentialResponse): String? {
+ try {
+ val jsonObject = JSONObject()
+ jsonObject.put("type", "get")
+ val jsonCred = JSONObject()
+ jsonCred.put("type", response.credential.type)
+ jsonCred.put("data", credentialToJSON(
+ response.credential))
+ jsonObject.put("credential", jsonCred)
+ return jsonObject.toString()
+ } catch (e: JSONException) {
+ Log.i(
+ TAG, "Exception while constructing response JSON: " +
+ e.message
+ )
+ }
+ return null
+ }
+
+ // TODO(b/318118018): Replace with calls to Jetpack
+ private fun credentialToJSON(credential: Credential): JSONObject? {
+ Log.i(TAG, "credentialToJSON")
+ try {
+ if (credential.type == "android.credentials.TYPE_PASSWORD_CREDENTIAL") {
+ Log.i(TAG, "toJSON PasswordCredential")
+
+ val json = JSONObject()
+ val id = credential.data.getString("androidx.credentials.BUNDLE_KEY_ID")
+ val pass = credential.data.getString("androidx.credentials.BUNDLE_KEY_PASSWORD")
+ json.put("androidx.credentials.BUNDLE_KEY_ID", id)
+ json.put("androidx.credentials.BUNDLE_KEY_PASSWORD", pass)
+ return json
+ } else if (credential.type == "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL") {
+ Log.i(TAG, "toJSON PublicKeyCredential")
+
+ val json = JSONObject()
+ val responseJson = credential
+ .data
+ .getString("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON")
+ json.put("androidx.credentials.BUNDLE_KEY_AUTHENTICATION_RESPONSE_JSON",
+ responseJson)
+ return json
+ }
+ } catch (e: JSONException) {
+ Log.i(TAG, "issue while converting credential response to JSON")
+ }
+ Log.i(TAG, "Unsupported credential type")
+ return null
+ }
+
private fun getEntryToIconMap(
candidateProviderDataList: MutableList<GetCredentialProviderData>
): Map<String, Icon> {
@@ -150,14 +250,16 @@
private fun convertToFillResponse(
getCredResponse: GetCandidateCredentialsResponse,
- filLRequest: FillRequest
+ filLRequest: FillRequest,
+ context: Context
): FillResponse? {
val providerList = GetFlowUtils.toProviderList(
getCredResponse.candidateProviderDataList,
- this@CredentialAutofillService)
+ context)
if (providerList.isEmpty()) {
return null
}
+
val entryIconMap: Map<String, Icon> =
getEntryToIconMap(getCredResponse.candidateProviderDataList)
val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
@@ -166,7 +268,8 @@
var validFillResponse = false
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
validFillResponse = processProvidersForAutofillId(
- filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder)
+ filLRequest, autofillId, providers, entryIconMap, fillResponseBuilder,
+ getCredResponse.pendingIntent)
.or(validFillResponse)
}
if (!validFillResponse) {
@@ -180,7 +283,8 @@
autofillId: AutofillId,
providerList: List<ProviderInfo>,
entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ fillResponseBuilder: FillResponse.Builder,
+ bottomSheetPendingIntent: PendingIntent?
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -227,9 +331,9 @@
?: getDefaultIcon()
}
// Create inline presentation
- var inlinePresentation: InlinePresentation? = null;
+ var inlinePresentation: InlinePresentation? = null
+ var spec: InlinePresentationSpec?
if (inlinePresentationSpecs != null) {
- val spec: InlinePresentationSpec
if (i < inlinePresentationSpecsCount) {
spec = inlinePresentationSpecs[i]
} else {
@@ -265,9 +369,52 @@
.build())
datasetAdded = true
}
+ val pinnedSpec = getLastInlinePresentationSpec(inlinePresentationSpecs,
+ inlinePresentationSpecsCount)
+ if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
+ addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
+ fillResponseBuilder)
+ }
return datasetAdded
}
+ private fun getLastInlinePresentationSpec(
+ inlinePresentationSpecs: List<InlinePresentationSpec>?,
+ inlinePresentationSpecsCount: Int
+ ): InlinePresentationSpec? {
+ if (inlinePresentationSpecs != null) {
+ return inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+ }
+ return null
+ }
+
+ private fun addPinnedInlineSuggestion(
+ bottomSheetPendingIntent: PendingIntent,
+ spec: InlinePresentationSpec,
+ autofillId: AutofillId,
+ fillResponseBuilder: FillResponse.Builder
+ ) {
+ val dataSetBuilder = Dataset.Builder()
+ val sliceBuilder = InlineSuggestionUi
+ .newContentBuilder(bottomSheetPendingIntent)
+ .setStartIcon(Icon.createWithResource(this,
+ com.android.credentialmanager.R.drawable.ic_other_sign_in_24))
+ val presentationBuilder = Presentations.Builder()
+ .setInlinePresentation(InlinePresentation(
+ sliceBuilder.build().slice, spec, /* pinned= */ true))
+
+ fillResponseBuilder.addDataset(
+ dataSetBuilder
+ .setField(
+ autofillId,
+ Field.Builder().setPresentations(
+ presentationBuilder.build())
+ .build())
+ .setAuthentication(bottomSheetPendingIntent.intentSender)
+ .build()
+ )
+ }
+
/**
* Maps Autofill Id to provider list. For example, passing in a provider info
*
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4688658..6d0915b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -38,6 +38,7 @@
import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;
@@ -56,12 +57,22 @@
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+ private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.credentialmanager",
+ "com.android.credentialmanager.autofill.CredentialAutofillService");
+
private final FillServiceCallbacks mCallbacks;
private final Object mLock = new Object();
private CompletableFuture<FillResponse> mPendingFillRequest;
private int mPendingFillRequestId = INVALID_REQUEST_ID;
private final ComponentName mComponentName;
+ private final boolean mIsCredentialAutofillService;
+
+ public boolean isCredentialAutofillService() {
+ return mIsCredentialAutofillService;
+ }
+
public interface FillServiceCallbacks
extends AbstractRemoteService.VultureCallback<RemoteFillService> {
void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@@ -83,6 +94,7 @@
userId, IAutoFillService.Stub::asInterface);
mCallbacks = callbacks;
mComponentName = componentName;
+ mIsCredentialAutofillService = mComponentName.equals(CREDMAN_SERVICE_COMPONENT_NAME);
}
@Override // from ServiceConnector.Impl
@@ -117,6 +129,10 @@
super.addLast(iAutoFillServiceJob);
}
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
/**
* Cancel the currently pending request.
*
@@ -134,6 +150,78 @@
}
}
+ public void onFillCredentialRequest(@NonNull FillRequest request,
+ IAutoFillManagerClient autofillCallback) {
+ if (sVerbose) {
+ Slog.v(TAG, "onFillRequest:" + request);
+ }
+ AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+ AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
+
+ CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
+ if (sVerbose) {
+ Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
+ }
+
+ CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
+ remoteService.onFillCredentialRequest(request, new IFillCallback.Stub() {
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ CompletableFuture<FillResponse> future = futureRef.get();
+ if (future != null && future.isCancelled()) {
+ dispatchCancellationSignal(cancellation);
+ } else {
+ cancellationSink.set(cancellation);
+ }
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ fillRequest.complete(response);
+ }
+
+ @Override
+ public void onFailure(int requestId, CharSequence message) {
+ String errorMessage = message == null ? "" : String.valueOf(message);
+ fillRequest.completeExceptionally(
+ new RuntimeException(errorMessage));
+ }
+ }, autofillCallback);
+ return fillRequest;
+ }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+ futureRef.set(connectThenFillRequest);
+
+ synchronized (mLock) {
+ mPendingFillRequest = connectThenFillRequest;
+ mPendingFillRequestId = request.getId();
+ }
+
+ connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
+ synchronized (mLock) {
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ }
+ if (mCallbacks == null) {
+ Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+ return;
+ }
+ if (err == null) {
+ mCallbacks.onFillRequestSuccess(request.getId(), res,
+ mComponentName.getPackageName(), request.getFlags());
+ } else {
+ Slog.e(TAG, "Error calling on fill request", err);
+ if (err instanceof TimeoutException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ mCallbacks.onFillRequestTimeout(request.getId());
+ } else if (err instanceof CancellationException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ } else {
+ mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
+ }
+ }
+ }));
+ }
+
public void onFillRequest(@NonNull FillRequest request) {
if (sVerbose) {
Slog.v(TAG, "onFillRequest:" + request);
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 4a6d5c9b..553ba12 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -16,14 +16,19 @@
package com.android.server.autofill;
+import static com.android.server.autofill.Session.SESSION_ID_KEY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
+import android.os.Bundle;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
/**
* Requests autofill response from a Remote Autofill Service. This autofill service can be
@@ -95,10 +100,33 @@
/**
* Requests a new fill response.
*/
- public void onFillRequest(FillRequest pendingFillRequest, int flag) {
+ public void onFillRequest(FillRequest pendingFillRequest,
+ InlineSuggestionsRequest pendingInlineSuggestionsRequest, int flag, int id,
+ IAutoFillManagerClient client) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
mLastFlag = flag;
- mRemoteFillService.onFillRequest(pendingFillRequest);
+ if (mRemoteFillService != null && mRemoteFillService.isCredentialAutofillService()) {
+ Slog.v(TAG, "About to call CredAutofill service as secondary provider");
+ addSessionIdToClientState(pendingFillRequest, pendingInlineSuggestionsRequest, id);
+ mRemoteFillService.onFillCredentialRequest(pendingFillRequest, client);
+ } else {
+ mRemoteFillService.onFillRequest(pendingFillRequest);
+ }
+ }
+
+ private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest,
+ InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) {
+ if (pendingFillRequest.getClientState() == null) {
+ pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+ pendingFillRequest.getFillContexts(),
+ pendingFillRequest.getHints(),
+ new Bundle(),
+ pendingFillRequest.getFlags(),
+ pendingInlineSuggestionsRequest,
+ pendingFillRequest.getDelayedFillIntentSender());
+ }
+ pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id);
+ return pendingFillRequest;
}
public void destroy() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d71258a..a49f9db 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -234,6 +234,8 @@
new ComponentName("com.android.credentialmanager",
"com.android.credentialmanager.autofill.CredentialAutofillService");
+ static final String SESSION_ID_KEY = "session_id";
+
final Object mLock;
private final AutofillManagerServiceImpl mService;
@@ -383,7 +385,7 @@
*/
private boolean mHasCallback;
- /** Whether the session has credential provider as the primary provider. */
+ /** Whether the session has credential manager provider as the primary provider. */
private boolean mIsPrimaryCredential;
@GuardedBy("mLock")
@@ -723,9 +725,16 @@
&& mSecondaryProviderHandler != null) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
- mPendingFillRequest.getFlags());
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getFlags(), id, mClient);
} else if (mRemoteFillService != null) {
- mRemoteFillService.onFillRequest(mPendingFillRequest);
+ if (mIsPrimaryCredential) {
+ mPendingFillRequest = addSessionIdToClientState(mPendingFillRequest,
+ mPendingInlineSuggestionsRequest, id);
+ mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, mClient);
+ } else {
+ mRemoteFillService.onFillRequest(mPendingFillRequest);
+ }
}
mPendingInlineSuggestionsRequest = null;
mWaitForInlineRequest = false;
@@ -868,6 +877,21 @@
}
}
+ private FillRequest addSessionIdToClientState(FillRequest pendingFillRequest,
+ InlineSuggestionsRequest pendingInlineSuggestionsRequest, int id) {
+ if (pendingFillRequest.getClientState() == null) {
+ pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
+ pendingFillRequest.getFillContexts(),
+ pendingFillRequest.getHints(),
+ new Bundle(),
+ pendingFillRequest.getFlags(),
+ pendingInlineSuggestionsRequest,
+ pendingFillRequest.getDelayedFillIntentSender());
+ }
+ pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, id);
+ return pendingFillRequest;
+ }
+
/**
* Get the list of valid autofill hint types from Device flags
* Returns empty list if PCC is off or no types available
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 686b2a8..dfb5a57 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -63,6 +63,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.annotations.GuardedBy;
import com.android.server.credentials.metrics.ApiName;
@@ -483,6 +484,7 @@
public ICancellationSignal getCandidateCredentials(
GetCredentialRequest request,
IGetCandidateCredentialsCallback callback,
+ IAutoFillManagerClient clientCallback,
final String callingPackage) {
Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+ callingPackage);
@@ -503,7 +505,8 @@
request,
constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
getEnabledProvidersForUser(userId),
- CancellationSignal.fromTransport(cancelTransport)
+ CancellationSignal.fromTransport(cancelTransport),
+ clientCallback
);
addSessionLocked(userId, session);
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 6d9b7e8..ca5600e 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -16,6 +16,7 @@
package com.android.server.credentials;
+import android.Manifest;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -23,6 +24,7 @@
import android.credentials.GetCandidateCredentialsException;
import android.credentials.GetCandidateCredentialsResponse;
import android.credentials.GetCredentialRequest;
+import android.credentials.GetCredentialResponse;
import android.credentials.IGetCandidateCredentialsCallback;
import android.credentials.ui.GetCredentialProviderData;
import android.credentials.ui.ProviderData;
@@ -30,7 +32,9 @@
import android.os.CancellationSignal;
import android.os.RemoteException;
import android.service.credentials.CallingAppInfo;
+import android.service.credentials.PermissionUtils;
import android.util.Slog;
+import android.view.autofill.IAutoFillManagerClient;
import java.util.ArrayList;
import java.util.List;
@@ -42,18 +46,22 @@
*/
public class GetCandidateRequestSession extends RequestSession<GetCredentialRequest,
IGetCandidateCredentialsCallback, GetCandidateCredentialsResponse>
- implements ProviderSession.ProviderInternalCallback<GetCandidateCredentialsResponse> {
+ implements ProviderSession.ProviderInternalCallback<GetCredentialResponse> {
private static final String TAG = "GetCandidateRequestSession";
+ private final IAutoFillManagerClient mAutoFillCallback;
+
public GetCandidateRequestSession(
Context context, SessionLifetime sessionCallback,
Object lock, int userId, int callingUid,
IGetCandidateCredentialsCallback callback, GetCredentialRequest request,
CallingAppInfo callingAppInfo, Set<ComponentName> enabledProviders,
- CancellationSignal cancellationSignal) {
+ CancellationSignal cancellationSignal,
+ IAutoFillManagerClient autoFillCallback) {
super(context, sessionCallback, lock, userId, callingUid, request, callback,
RequestInfo.TYPE_GET, callingAppInfo, enabledProviders,
cancellationSignal, 0L);
+ mAutoFillCallback = autoFillCallback;
}
/**
@@ -92,12 +100,25 @@
return;
}
+ cancelExistingPendingIntent();
+ mPendingIntent = mCredentialManagerUi.createPendingIntent(
+ RequestInfo.newGetRequestInfo(
+ mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
+ PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
+ Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+ providerDataList);
+
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
for (ProviderData providerData : providerDataList) {
candidateProviderDataList.add((GetCredentialProviderData) (providerData));
}
- respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(
- candidateProviderDataList));
+
+ try {
+ invokeClientCallbackSuccess(new GetCandidateCredentialsResponse(
+ candidateProviderDataList, mPendingIntent));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Issue while responding to client with error : " + e);
+ }
}
@Override
@@ -151,7 +172,8 @@
@Override
public void onFinalResponseReceived(ComponentName componentName,
- GetCandidateCredentialsResponse response) {
- // Not applicable for session without UI
+ GetCredentialResponse response) {
+ Slog.d(TAG, "onFinalResponseReceived");
+ respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response));
}
}