Loading services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.javadeleted 100644 → 0 +0 −293 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.server.autofill; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static com.android.server.autofill.Helper.sVerbose; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.ICancellationSignal; import android.os.RemoteException; import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.IFillCallback; import android.service.autofill.SaveInfo; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.IAutoFillManagerClient; import android.view.inputmethod.InlineSuggestionsRequest; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; /** * Maintains a client suggestions session with the * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}. * */ final class ClientSuggestionsSession { private static final String TAG = "ClientSuggestionsSession"; private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS; private final int mSessionId; private final IAutoFillManagerClient mClient; private final Handler mHandler; private final ComponentName mComponentName; private final RemoteFillService.FillServiceCallbacks mCallbacks; private final Object mLock = new Object(); @GuardedBy("mLock") private AndroidFuture<FillResponse> mPendingFillRequest; @GuardedBy("mLock") private int mPendingFillRequestId = INVALID_REQUEST_ID; ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler, ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) { mSessionId = sessionId; mClient = client; mHandler = handler; mComponentName = componentName; mCallbacks = callbacks; } void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) { final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>(); final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>(); mHandler.post(() -> { if (sVerbose) { Slog.v(TAG, "calling onFillRequest() for id=" + requestId); } try { mClient.requestFillFromClient(requestId, inlineRequest, new FillCallbackImpl(fillRequest, futureRef, cancellationSink)); } catch (RemoteException e) { fillRequest.completeExceptionally(e); } }); fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); futureRef.set(fillRequest); synchronized (mLock) { mPendingFillRequest = fillRequest; mPendingFillRequestId = requestId; } fillRequest.whenComplete((res, err) -> mHandler.post(() -> { synchronized (mLock) { mPendingFillRequest = null; mPendingFillRequestId = INVALID_REQUEST_ID; } if (err == null) { processAutofillId(res); mCallbacks.onFillRequestSuccess(requestId, res, mComponentName.getPackageName(), flags); } else { Slog.e(TAG, "Error calling on client fill request", err); if (err instanceof TimeoutException) { dispatchCancellationSignal(cancellationSink.get()); mCallbacks.onFillRequestTimeout(requestId); } else if (err instanceof CancellationException) { dispatchCancellationSignal(cancellationSink.get()); } else { mCallbacks.onFillRequestFailure(requestId, err.getMessage()); } } })); } /** * Gets the application info for the component. */ @Nullable static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) { try { ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo( comp.getPackageName(), PackageManager.GET_META_DATA, userId); if (si != null) { return si; } } catch (RemoteException e) { } return null; } /** * Gets the user-visible name of the application. */ @Nullable @GuardedBy("mLock") static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) { return appInfo == null ? null : appInfo.loadSafeLabel( context.getPackageManager(), 0 /* do not ellipsize */, TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM); } /** * Gets the user-visible icon of the application. */ @Nullable @GuardedBy("mLock") static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) { return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager()); } int cancelCurrentRequest() { synchronized (mLock) { return mPendingFillRequest != null && mPendingFillRequest.cancel(false) ? mPendingFillRequestId : INVALID_REQUEST_ID; } } /** * The {@link AutofillId} which the client gets from its view is not contain the session id, * but Autofill framework is using the {@link AutofillId} with a session id. So before using * those ids in the Autofill framework, applies the current session id. * * @param res which response need to apply for a session id */ private void processAutofillId(FillResponse res) { if (res == null) { return; } final List<Dataset> datasets = res.getDatasets(); if (datasets != null && !datasets.isEmpty()) { for (int i = 0; i < datasets.size(); i++) { final Dataset dataset = datasets.get(i); if (dataset != null) { applySessionId(dataset.getFieldIds()); } } } final SaveInfo saveInfo = res.getSaveInfo(); if (saveInfo != null) { applySessionId(saveInfo.getOptionalIds()); applySessionId(saveInfo.getRequiredIds()); applySessionId(saveInfo.getSanitizerValues()); applySessionId(saveInfo.getTriggerId()); } } private void applySessionId(List<AutofillId> ids) { if (ids == null || ids.isEmpty()) { return; } for (int i = 0; i < ids.size(); i++) { applySessionId(ids.get(i)); } } private void applySessionId(AutofillId[][] ids) { if (ids == null) { return; } for (int i = 0; i < ids.length; i++) { applySessionId(ids[i]); } } private void applySessionId(AutofillId[] ids) { if (ids == null) { return; } for (int i = 0; i < ids.length; i++) { applySessionId(ids[i]); } } private void applySessionId(AutofillId id) { if (id == null) { return; } id.setSessionId(mSessionId); } private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { if (signal == null) { return; } try { signal.cancel(); } catch (RemoteException e) { Slog.e(TAG, "Error requesting a cancellation", e); } } private class FillCallbackImpl extends IFillCallback.Stub { final AndroidFuture<FillResponse> mFillRequest; final AtomicReference<AndroidFuture<FillResponse>> mFutureRef; final AtomicReference<ICancellationSignal> mCancellationSink; FillCallbackImpl(AndroidFuture<FillResponse> fillRequest, AtomicReference<AndroidFuture<FillResponse>> futureRef, AtomicReference<ICancellationSignal> cancellationSink) { mFillRequest = fillRequest; mFutureRef = futureRef; mCancellationSink = cancellationSink; } @Override public void onCancellable(ICancellationSignal cancellation) { AndroidFuture<FillResponse> future = mFutureRef.get(); if (future != null && future.isCancelled()) { dispatchCancellationSignal(cancellation); } else { mCancellationSink.set(cancellation); } } @Override public void onSuccess(FillResponse response) { mFillRequest.complete(response); } @Override public void onFailure(int requestId, CharSequence message) { String errorMessage = message == null ? "" : String.valueOf(message); mFillRequest.completeExceptionally( new RuntimeException(errorMessage)); } } } services/autofill/java/com/android/server/autofill/Session.java +36 −152 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; Loading Loading @@ -54,7 +53,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; Loading Loading @@ -348,9 +346,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); @Nullable private ClientSuggestionsSession mClientSuggestionsSession; void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input Loading Loading @@ -421,10 +416,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Whether the current {@link FillResponse} is expired. */ @GuardedBy("mLock") private boolean mExpiredResponse; /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ @GuardedBy("mLock") private boolean mClientSuggestionsEnabled; } /** Loading @@ -450,19 +441,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; maybeRequestFillFromServiceLocked(); maybeRequestFillLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } : null; } void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { mPendingFillRequest = null; mWaitForInlineRequest = inlineRequest != null; mPendingInlineSuggestionsRequest = inlineRequest; } void maybeRequestFillFromServiceLocked() { void maybeRequestFillLocked() { if (mPendingFillRequest == null) { return; } Loading @@ -472,13 +457,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } if (mPendingInlineSuggestionsRequest.isServiceSupported()) { mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); } } mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; Loading Loading @@ -584,7 +566,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /*inlineSuggestionsRequest=*/null); mPendingFillRequest = request; maybeRequestFillFromServiceLocked(); maybeRequestFillLocked(); } if (mActivityToken != null) { Loading Loading @@ -746,19 +728,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** * Cancels the last request sent to the {@link #mRemoteFillService} or the * {@link #mClientSuggestionsSession}. * Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { if (mRemoteFillService == null && mClientSuggestionsSession == null) { wtf(null, "cancelCurrentRequestLocked() called without a remote service or a " + "client suggestions session. mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); if (mRemoteFillService == null) { wtf(null, "cancelCurrentRequestLocked() called without a remote service. " + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } if (mRemoteFillService != null) { final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); // Remove the FillContext as there will never be a response for the service Loading @@ -776,11 +754,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } if (mClientSuggestionsSession != null) { mClientSuggestionsSession.cancelCurrentRequest(); } } private boolean isViewFocusedLocked(int flags) { return (flags & FLAG_VIEW_NOT_FOCUSED) == 0; } Loading Loading @@ -843,30 +816,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // structure is taken. This causes only one fill request per burst of focus changes. cancelCurrentRequestLocked(); // Only ask IME to create inline suggestions request when // 1. Autofill provider supports it or client enabled client suggestions. // 2. The render service is available. // 3. The view is focused. (The view may not be focused if the autofill is triggered // manually.) // Only ask IME to create inline suggestions request if Autofill provider supports it and // the render service is available except the autofill is triggered manually and the view // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled) if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null && isViewFocusedLocked(flags)) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer; if (mSessionFlags.mClientSuggestionsEnabled) { final int finalRequestId = requestId; inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> { // Using client suggestions synchronized (mLock) { onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest); } viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); }; } else { inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked( viewState, /* isInlineRequest= */ true); } Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final AutofillId focusedId = mCurrentViewId; final int requestIdCopy = requestId; Loading @@ -882,24 +842,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ); viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } else if (mSessionFlags.mClientSuggestionsEnabled) { // Request client suggestions for the dropdown mode onClientFillRequestLocked(requestId, null); } else { mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false); } if (mSessionFlags.mClientSuggestionsEnabled) { // Using client suggestions, unnecessary request AssistStructure return; } // Now request the assist structure data. requestAssistStructureLocked(requestId, flags); } @GuardedBy("mLock") private void requestAssistStructureLocked(int requestId, int flags) { try { final Bundle receiverExtras = new Bundle(); receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); Loading Loading @@ -948,13 +895,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName = componentName; mCompatMode = compatMode; mSessionState = STATE_ACTIVE; synchronized (mLock) { mSessionFlags = new SessionFlags(); mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked(); mSessionFlags.mClientSuggestionsEnabled = (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0; setClientLocked(client); } Loading Loading @@ -1066,13 +1010,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } processNullResponseOrFallbackLocked(requestId, requestFlags); processNullResponseLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestId, requestFlags); return; Loading Loading @@ -1151,26 +1094,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } @GuardedBy("mLock") private void processNullResponseOrFallbackLocked(int requestId, int flags) { if (!mSessionFlags.mClientSuggestionsEnabled) { processNullResponseLocked(requestId, flags); return; } // fallback to the default platform password manager mSessionFlags.mClientSuggestionsEnabled = false; final InlineSuggestionsRequest inlineRequest = (mLastInlineSuggestionsRequest != null && mLastInlineSuggestionsRequest.first == requestId) ? mLastInlineSuggestionsRequest.second : null; mAssistReceiver.newAutofillRequestLocked(inlineRequest); requestAssistStructureLocked(requestId, flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); return; } // FillServiceCallbacks @Override public void onFillRequestFailure(int requestId, @Nullable CharSequence message) { Loading Loading @@ -3119,22 +3042,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState filterText = value.getTextValue().toString(); } final CharSequence targetLabel; final Drawable targetIcon; final CharSequence serviceLabel; final Drawable serviceIcon; synchronized (mLock) { if (mSessionFlags.mClientSuggestionsEnabled) { final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName, mService.getUserId()); targetLabel = ClientSuggestionsSession.getAppLabelLocked( mService.getMaster().getContext(), appInfo); targetIcon = ClientSuggestionsSession.getAppIconLocked( mService.getMaster().getContext(), appInfo); } else { targetLabel = mService.getServiceLabelLocked(); targetIcon = mService.getServiceIconLocked(); } serviceLabel = mService.getServiceLabelLocked(); serviceIcon = mService.getServiceIconLocked(); } if (targetLabel == null || targetIcon == null) { if (serviceLabel == null || serviceIcon == null) { wtf(null, "onFillReady(): no service label or icon"); return; } Loading @@ -3154,7 +3068,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mService.getServicePackageName(), mComponentName, targetLabel, targetIcon, this, id, mCompatMode); serviceLabel, serviceIcon, this, id, mCompatMode); mService.logDatasetShown(id, mClientState); Loading Loading @@ -3201,17 +3115,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } final InlineSuggestionsRequest request = inlineSuggestionsRequest.get(); if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported() || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) { if (sDebug) { Slog.d(TAG, "Inline suggestions not supported for " + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service") + ". Falling back to dropdown."); } return false; } final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService == null) { Loading @@ -3220,7 +3123,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = new InlineFillUi.InlineFillUiInfo(request, focusedId, new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, filterText, remoteRenderService, userId, id); InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, new InlineFillUi.InlineSuggestionUiCallback() { Loading Loading @@ -3822,25 +3725,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } @GuardedBy("mLock") private void onClientFillRequestLocked(int requestId, InlineSuggestionsRequest inlineSuggestionsRequest) { if (mClientSuggestionsSession == null) { mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler, mComponentName, this); } if (mContexts == null) { mContexts = new ArrayList<>(1); } if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) { inlineSuggestionsRequest = null; } mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags); } /** * The result of checking whether to show the save dialog, when session can be saved. * Loading Loading
services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.javadeleted 100644 → 0 +0 −293 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.server.autofill; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static com.android.server.autofill.Helper.sVerbose; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppGlobals; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.ICancellationSignal; import android.os.RemoteException; import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.IFillCallback; import android.service.autofill.SaveInfo; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.IAutoFillManagerClient; import android.view.inputmethod.InlineSuggestionsRequest; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; /** * Maintains a client suggestions session with the * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}. * */ final class ClientSuggestionsSession { private static final String TAG = "ClientSuggestionsSession"; private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS; private final int mSessionId; private final IAutoFillManagerClient mClient; private final Handler mHandler; private final ComponentName mComponentName; private final RemoteFillService.FillServiceCallbacks mCallbacks; private final Object mLock = new Object(); @GuardedBy("mLock") private AndroidFuture<FillResponse> mPendingFillRequest; @GuardedBy("mLock") private int mPendingFillRequestId = INVALID_REQUEST_ID; ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler, ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) { mSessionId = sessionId; mClient = client; mHandler = handler; mComponentName = componentName; mCallbacks = callbacks; } void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) { final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>(); final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>(); final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>(); mHandler.post(() -> { if (sVerbose) { Slog.v(TAG, "calling onFillRequest() for id=" + requestId); } try { mClient.requestFillFromClient(requestId, inlineRequest, new FillCallbackImpl(fillRequest, futureRef, cancellationSink)); } catch (RemoteException e) { fillRequest.completeExceptionally(e); } }); fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS); futureRef.set(fillRequest); synchronized (mLock) { mPendingFillRequest = fillRequest; mPendingFillRequestId = requestId; } fillRequest.whenComplete((res, err) -> mHandler.post(() -> { synchronized (mLock) { mPendingFillRequest = null; mPendingFillRequestId = INVALID_REQUEST_ID; } if (err == null) { processAutofillId(res); mCallbacks.onFillRequestSuccess(requestId, res, mComponentName.getPackageName(), flags); } else { Slog.e(TAG, "Error calling on client fill request", err); if (err instanceof TimeoutException) { dispatchCancellationSignal(cancellationSink.get()); mCallbacks.onFillRequestTimeout(requestId); } else if (err instanceof CancellationException) { dispatchCancellationSignal(cancellationSink.get()); } else { mCallbacks.onFillRequestFailure(requestId, err.getMessage()); } } })); } /** * Gets the application info for the component. */ @Nullable static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) { try { ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo( comp.getPackageName(), PackageManager.GET_META_DATA, userId); if (si != null) { return si; } } catch (RemoteException e) { } return null; } /** * Gets the user-visible name of the application. */ @Nullable @GuardedBy("mLock") static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) { return appInfo == null ? null : appInfo.loadSafeLabel( context.getPackageManager(), 0 /* do not ellipsize */, TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM); } /** * Gets the user-visible icon of the application. */ @Nullable @GuardedBy("mLock") static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) { return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager()); } int cancelCurrentRequest() { synchronized (mLock) { return mPendingFillRequest != null && mPendingFillRequest.cancel(false) ? mPendingFillRequestId : INVALID_REQUEST_ID; } } /** * The {@link AutofillId} which the client gets from its view is not contain the session id, * but Autofill framework is using the {@link AutofillId} with a session id. So before using * those ids in the Autofill framework, applies the current session id. * * @param res which response need to apply for a session id */ private void processAutofillId(FillResponse res) { if (res == null) { return; } final List<Dataset> datasets = res.getDatasets(); if (datasets != null && !datasets.isEmpty()) { for (int i = 0; i < datasets.size(); i++) { final Dataset dataset = datasets.get(i); if (dataset != null) { applySessionId(dataset.getFieldIds()); } } } final SaveInfo saveInfo = res.getSaveInfo(); if (saveInfo != null) { applySessionId(saveInfo.getOptionalIds()); applySessionId(saveInfo.getRequiredIds()); applySessionId(saveInfo.getSanitizerValues()); applySessionId(saveInfo.getTriggerId()); } } private void applySessionId(List<AutofillId> ids) { if (ids == null || ids.isEmpty()) { return; } for (int i = 0; i < ids.size(); i++) { applySessionId(ids.get(i)); } } private void applySessionId(AutofillId[][] ids) { if (ids == null) { return; } for (int i = 0; i < ids.length; i++) { applySessionId(ids[i]); } } private void applySessionId(AutofillId[] ids) { if (ids == null) { return; } for (int i = 0; i < ids.length; i++) { applySessionId(ids[i]); } } private void applySessionId(AutofillId id) { if (id == null) { return; } id.setSessionId(mSessionId); } private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) { if (signal == null) { return; } try { signal.cancel(); } catch (RemoteException e) { Slog.e(TAG, "Error requesting a cancellation", e); } } private class FillCallbackImpl extends IFillCallback.Stub { final AndroidFuture<FillResponse> mFillRequest; final AtomicReference<AndroidFuture<FillResponse>> mFutureRef; final AtomicReference<ICancellationSignal> mCancellationSink; FillCallbackImpl(AndroidFuture<FillResponse> fillRequest, AtomicReference<AndroidFuture<FillResponse>> futureRef, AtomicReference<ICancellationSignal> cancellationSink) { mFillRequest = fillRequest; mFutureRef = futureRef; mCancellationSink = cancellationSink; } @Override public void onCancellable(ICancellationSignal cancellation) { AndroidFuture<FillResponse> future = mFutureRef.get(); if (future != null && future.isCancelled()) { dispatchCancellationSignal(cancellation); } else { mCancellationSink.set(cancellation); } } @Override public void onSuccess(FillResponse response) { mFillRequest.complete(response); } @Override public void onFailure(int requestId, CharSequence message) { String errorMessage = message == null ? "" : String.valueOf(message); mFillRequest.completeExceptionally( new RuntimeException(errorMessage)); } } }
services/autofill/java/com/android/server/autofill/Session.java +36 −152 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; Loading Loading @@ -54,7 +53,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; Loading Loading @@ -348,9 +346,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); @Nullable private ClientSuggestionsSession mClientSuggestionsSession; void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input Loading Loading @@ -421,10 +416,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Whether the current {@link FillResponse} is expired. */ @GuardedBy("mLock") private boolean mExpiredResponse; /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ @GuardedBy("mLock") private boolean mClientSuggestionsEnabled; } /** Loading @@ -450,19 +441,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; maybeRequestFillFromServiceLocked(); maybeRequestFillLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } : null; } void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { mPendingFillRequest = null; mWaitForInlineRequest = inlineRequest != null; mPendingInlineSuggestionsRequest = inlineRequest; } void maybeRequestFillFromServiceLocked() { void maybeRequestFillLocked() { if (mPendingFillRequest == null) { return; } Loading @@ -472,13 +457,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } if (mPendingInlineSuggestionsRequest.isServiceSupported()) { mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); } } mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; Loading Loading @@ -584,7 +566,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /*inlineSuggestionsRequest=*/null); mPendingFillRequest = request; maybeRequestFillFromServiceLocked(); maybeRequestFillLocked(); } if (mActivityToken != null) { Loading Loading @@ -746,19 +728,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** * Cancels the last request sent to the {@link #mRemoteFillService} or the * {@link #mClientSuggestionsSession}. * Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { if (mRemoteFillService == null && mClientSuggestionsSession == null) { wtf(null, "cancelCurrentRequestLocked() called without a remote service or a " + "client suggestions session. mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); if (mRemoteFillService == null) { wtf(null, "cancelCurrentRequestLocked() called without a remote service. " + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); return; } if (mRemoteFillService != null) { final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); // Remove the FillContext as there will never be a response for the service Loading @@ -776,11 +754,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } if (mClientSuggestionsSession != null) { mClientSuggestionsSession.cancelCurrentRequest(); } } private boolean isViewFocusedLocked(int flags) { return (flags & FLAG_VIEW_NOT_FOCUSED) == 0; } Loading Loading @@ -843,30 +816,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // structure is taken. This causes only one fill request per burst of focus changes. cancelCurrentRequestLocked(); // Only ask IME to create inline suggestions request when // 1. Autofill provider supports it or client enabled client suggestions. // 2. The render service is available. // 3. The view is focused. (The view may not be focused if the autofill is triggered // manually.) // Only ask IME to create inline suggestions request if Autofill provider supports it and // the render service is available except the autofill is triggered manually and the view // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled) if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null && isViewFocusedLocked(flags)) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer; if (mSessionFlags.mClientSuggestionsEnabled) { final int finalRequestId = requestId; inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> { // Using client suggestions synchronized (mLock) { onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest); } viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); }; } else { inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked( viewState, /* isInlineRequest= */ true); } Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final AutofillId focusedId = mCurrentViewId; final int requestIdCopy = requestId; Loading @@ -882,24 +842,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ); viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } else if (mSessionFlags.mClientSuggestionsEnabled) { // Request client suggestions for the dropdown mode onClientFillRequestLocked(requestId, null); } else { mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false); } if (mSessionFlags.mClientSuggestionsEnabled) { // Using client suggestions, unnecessary request AssistStructure return; } // Now request the assist structure data. requestAssistStructureLocked(requestId, flags); } @GuardedBy("mLock") private void requestAssistStructureLocked(int requestId, int flags) { try { final Bundle receiverExtras = new Bundle(); receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); Loading Loading @@ -948,13 +895,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mComponentName = componentName; mCompatMode = compatMode; mSessionState = STATE_ACTIVE; synchronized (mLock) { mSessionFlags = new SessionFlags(); mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly; mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked(); mSessionFlags.mClientSuggestionsEnabled = (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0; setClientLocked(client); } Loading Loading @@ -1066,13 +1010,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } processNullResponseOrFallbackLocked(requestId, requestFlags); processNullResponseLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestId, requestFlags); return; Loading Loading @@ -1151,26 +1094,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } @GuardedBy("mLock") private void processNullResponseOrFallbackLocked(int requestId, int flags) { if (!mSessionFlags.mClientSuggestionsEnabled) { processNullResponseLocked(requestId, flags); return; } // fallback to the default platform password manager mSessionFlags.mClientSuggestionsEnabled = false; final InlineSuggestionsRequest inlineRequest = (mLastInlineSuggestionsRequest != null && mLastInlineSuggestionsRequest.first == requestId) ? mLastInlineSuggestionsRequest.second : null; mAssistReceiver.newAutofillRequestLocked(inlineRequest); requestAssistStructureLocked(requestId, flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); return; } // FillServiceCallbacks @Override public void onFillRequestFailure(int requestId, @Nullable CharSequence message) { Loading Loading @@ -3119,22 +3042,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState filterText = value.getTextValue().toString(); } final CharSequence targetLabel; final Drawable targetIcon; final CharSequence serviceLabel; final Drawable serviceIcon; synchronized (mLock) { if (mSessionFlags.mClientSuggestionsEnabled) { final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName, mService.getUserId()); targetLabel = ClientSuggestionsSession.getAppLabelLocked( mService.getMaster().getContext(), appInfo); targetIcon = ClientSuggestionsSession.getAppIconLocked( mService.getMaster().getContext(), appInfo); } else { targetLabel = mService.getServiceLabelLocked(); targetIcon = mService.getServiceIconLocked(); } serviceLabel = mService.getServiceLabelLocked(); serviceIcon = mService.getServiceIconLocked(); } if (targetLabel == null || targetIcon == null) { if (serviceLabel == null || serviceIcon == null) { wtf(null, "onFillReady(): no service label or icon"); return; } Loading @@ -3154,7 +3068,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillUi(filledId, response, filterText, mService.getServicePackageName(), mComponentName, targetLabel, targetIcon, this, id, mCompatMode); serviceLabel, serviceIcon, this, id, mCompatMode); mService.logDatasetShown(id, mClientState); Loading Loading @@ -3201,17 +3115,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } final InlineSuggestionsRequest request = inlineSuggestionsRequest.get(); if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported() || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) { if (sDebug) { Slog.d(TAG, "Inline suggestions not supported for " + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service") + ". Falling back to dropdown."); } return false; } final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); if (remoteRenderService == null) { Loading @@ -3220,7 +3123,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = new InlineFillUi.InlineFillUiInfo(request, focusedId, new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, filterText, remoteRenderService, userId, id); InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, new InlineFillUi.InlineSuggestionUiCallback() { Loading Loading @@ -3822,25 +3725,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } @GuardedBy("mLock") private void onClientFillRequestLocked(int requestId, InlineSuggestionsRequest inlineSuggestionsRequest) { if (mClientSuggestionsSession == null) { mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler, mComponentName, this); } if (mContexts == null) { mContexts = new ArrayList<>(1); } if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) { inlineSuggestionsRequest = null; } mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags); } /** * The result of checking whether to show the save dialog, when session can be saved. * Loading