diff options
| author | 2024-01-02 18:24:55 +0000 | |
|---|---|---|
| committer | 2024-01-02 18:24:55 +0000 | |
| commit | 031dd588455d8a758637ceba1336fa89aac12d72 (patch) | |
| tree | 013ce0ed867e2cd9645547a620df581793e8c989 | |
| parent | b255e38845ca2ee1d9486e1f290afeee3c2327bf (diff) | |
| parent | 0e952656f3e2176963ac283f450cbc8783c656c0 (diff) | |
Merge "Bring up credMan bottom sheet from pinned entry" into main
11 files changed, 418 insertions, 31 deletions
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index ad3ccc41cf6d..3fcb3daaa1f2 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -37,6 +37,7 @@ import android.os.OutcomeReceiver; 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 @@ public final class CredentialManager { @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 @@ public final class CredentialManager { 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 1b130a9fb64d..530feadae836 100644 --- a/core/java/android/credentials/GetCandidateCredentialsResponse.java +++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java @@ -18,6 +18,7 @@ package android.credentials; 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 @@ import java.util.List; */ @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 @@ public final class GetCandidateCredentialsResponse implements Parcelable { 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 d0815766024a..726bc979ea8f 100644 --- a/core/java/android/credentials/ICredentialManager.aidl +++ b/core/java/android/credentials/ICredentialManager.aidl @@ -22,6 +22,7 @@ import android.credentials.CredentialProviderInfo; 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 @@ interface ICredentialManager { @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 cb1b5d3d20b8..5ad2502d1546 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -37,6 +37,7 @@ import android.view.ViewStructure; 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 @@ public abstract class AutofillService extends Service { 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 @@ public abstract class AutofillService extends Service { @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 d88e0945bdca..03ead3266521 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.FillRequest; 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 @@ import com.android.internal.os.IResultReceiver; 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 0ff1c7fd8953..dfa5735fcaec 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 @@ package com.android.credentialmanager.autofill 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.SaveCallback 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.JSONException 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { 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 @@ class CredentialAutofillService : AutofillService() { ?: 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 @@ class CredentialAutofillService : AutofillService() { .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 4688658bf1c3..6d0915bf7d92 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.ISaveCallback; 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 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { 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 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { userId, IAutoFillService.Stub::asInterface); mCallbacks = callbacks; mComponentName = componentName; + mIsCredentialAutofillService = mComponentName.equals(CREDMAN_SERVICE_COMPONENT_NAME); } @Override // from ServiceConnector.Impl @@ -117,6 +129,10 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { super.addLast(iAutoFillServiceJob); } + public ComponentName getComponentName() { + return mComponentName; + } + /** * Cancel the currently pending request. * @@ -134,6 +150,78 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { } } + 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 4a6d5c9bc65e..553ba124402c 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 @@ final class SecondaryProviderHandler implements RemoteFillService.FillServiceCal /** * 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 d71258a564f2..a49f9dbe7d91 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && 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 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + 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 686b2a813bd3..dfb5a5758448 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.text.TextUtils; 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 final class CredentialManagerService public ICancellationSignal getCandidateCredentials( GetCredentialRequest request, IGetCandidateCredentialsCallback callback, + IAutoFillManagerClient clientCallback, final String callingPackage) { Slog.i(TAG, "starting getCandidateCredentials with callingPackage: " + callingPackage); @@ -503,7 +505,8 @@ public final class CredentialManagerService 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 6d9b7e824e28..ca5600e2049a 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.CredentialProviderInfo; 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.credentials.ui.RequestInfo; 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 @@ import java.util.Set; */ 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 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ 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 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ @Override public void onFinalResponseReceived(ComponentName componentName, - GetCandidateCredentialsResponse response) { - // Not applicable for session without UI + GetCredentialResponse response) { + Slog.d(TAG, "onFinalResponseReceived"); + respondToClientWithResponseAndFinish(new GetCandidateCredentialsResponse(response)); } } |