| /* |
| * Copyright (C) 2017 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.sDebug; |
| import static com.android.server.autofill.Helper.sVerbose; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.IntentSender; |
| import android.os.IBinder; |
| import android.os.ICancellationSignal; |
| import android.os.RemoteException; |
| import android.service.autofill.AutofillService; |
| import android.service.autofill.FillRequest; |
| import android.service.autofill.FillResponse; |
| import android.service.autofill.IAutoFillService; |
| import android.service.autofill.IFillCallback; |
| import android.service.autofill.ISaveCallback; |
| import android.service.autofill.SaveRequest; |
| import android.text.format.DateUtils; |
| import android.util.Slog; |
| |
| import com.android.internal.infra.AbstractSinglePendingRequestRemoteService; |
| |
| final class RemoteFillService |
| extends AbstractSinglePendingRequestRemoteService<RemoteFillService, 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 final FillServiceCallbacks mCallbacks; |
| |
| public interface FillServiceCallbacks extends VultureCallback<RemoteFillService> { |
| void onFillRequestSuccess(int requestId, @Nullable FillResponse response, |
| @NonNull String servicePackageName, int requestFlags); |
| void onFillRequestFailure(int requestId, @Nullable CharSequence message); |
| void onFillRequestTimeout(int requestId); |
| void onSaveRequestSuccess(@NonNull String servicePackageName, |
| @Nullable IntentSender intentSender); |
| // TODO(b/80093094): add timeout here too? |
| void onSaveRequestFailure(@Nullable CharSequence message, |
| @NonNull String servicePackageName); |
| } |
| |
| RemoteFillService(Context context, ComponentName componentName, int userId, |
| FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) { |
| super(context, AutofillService.SERVICE_INTERFACE, componentName, userId, callbacks, |
| context.getMainThreadHandler(), bindInstantServiceAllowed, sVerbose); |
| mCallbacks = callbacks; |
| } |
| |
| @Override // from AbstractRemoteService |
| protected void handleOnConnectedStateChanged(boolean state) { |
| if (mService == null) { |
| Slog.w(mTag, "onConnectedStateChanged(): null service"); |
| return; |
| } |
| try { |
| mService.onConnectedStateChanged(state); |
| } catch (Exception e) { |
| Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e); |
| } |
| } |
| |
| @Override // from AbstractRemoteService |
| protected IAutoFillService getServiceInterface(IBinder service) { |
| return IAutoFillService.Stub.asInterface(service); |
| } |
| |
| @Override // from AbstractRemoteService |
| protected long getTimeoutIdleBindMillis() { |
| return TIMEOUT_IDLE_BIND_MILLIS; |
| } |
| |
| @Override // from AbstractRemoteService |
| protected long getRemoteRequestMillis() { |
| return TIMEOUT_REMOTE_REQUEST_MILLIS; |
| } |
| |
| /** |
| * Cancel the currently pending request. |
| * |
| * <p>This can be used when the request is unnecessary or will be superceeded by a request that |
| * will soon be queued. |
| * |
| * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no |
| * {@link PendingFillRequest} was canceled. |
| */ |
| // TODO(b/117779333): move this logic to super class (and make mPendingRequest private) |
| public int cancelCurrentRequest() { |
| if (isDestroyed()) { |
| return INVALID_REQUEST_ID; |
| } |
| |
| int requestId = INVALID_REQUEST_ID; |
| if (mPendingRequest != null) { |
| if (mPendingRequest instanceof PendingFillRequest) { |
| requestId = ((PendingFillRequest) mPendingRequest).mRequest.getId(); |
| } |
| |
| mPendingRequest.cancel(); |
| mPendingRequest = null; |
| } |
| |
| return requestId; |
| } |
| |
| public void onFillRequest(@NonNull FillRequest request) { |
| scheduleRequest(new PendingFillRequest(request, this)); |
| } |
| |
| public void onSaveRequest(@NonNull SaveRequest request) { |
| scheduleRequest(new PendingSaveRequest(request, this)); |
| } |
| |
| private boolean handleResponseCallbackCommon( |
| @NonNull PendingRequest<RemoteFillService, IAutoFillService> pendingRequest) { |
| if (isDestroyed()) return false; |
| |
| if (mPendingRequest == pendingRequest) { |
| mPendingRequest = null; |
| } |
| return true; |
| } |
| |
| private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest, |
| @Nullable FillResponse response, int requestFlags) { |
| mHandler.post(() -> { |
| if (handleResponseCallbackCommon(pendingRequest)) { |
| mCallbacks.onFillRequestSuccess(pendingRequest.mRequest.getId(), response, |
| mComponentName.getPackageName(), requestFlags); |
| } |
| }); |
| } |
| |
| private void dispatchOnFillRequestFailure(@NonNull PendingFillRequest pendingRequest, |
| @Nullable CharSequence message) { |
| mHandler.post(() -> { |
| if (handleResponseCallbackCommon(pendingRequest)) { |
| mCallbacks.onFillRequestFailure(pendingRequest.mRequest.getId(), message); |
| } |
| }); |
| } |
| |
| private void dispatchOnFillRequestTimeout(@NonNull PendingFillRequest pendingRequest) { |
| mHandler.post(() -> { |
| if (handleResponseCallbackCommon(pendingRequest)) { |
| mCallbacks.onFillRequestTimeout(pendingRequest.mRequest.getId()); |
| } |
| }); |
| } |
| |
| private void dispatchOnFillTimeout(@NonNull ICancellationSignal cancellationSignal) { |
| mHandler.post(() -> { |
| try { |
| cancellationSignal.cancel(); |
| } catch (RemoteException e) { |
| Slog.w(mTag, "Error calling cancellation signal: " + e); |
| } |
| }); |
| } |
| |
| private void dispatchOnSaveRequestSuccess(PendingSaveRequest pendingRequest, |
| IntentSender intentSender) { |
| mHandler.post(() -> { |
| if (handleResponseCallbackCommon(pendingRequest)) { |
| mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), intentSender); |
| } |
| }); |
| } |
| |
| private void dispatchOnSaveRequestFailure(PendingSaveRequest pendingRequest, |
| @Nullable CharSequence message) { |
| mHandler.post(() -> { |
| if (handleResponseCallbackCommon(pendingRequest)) { |
| mCallbacks.onSaveRequestFailure(message, mComponentName.getPackageName()); |
| } |
| }); |
| } |
| |
| private static final class PendingFillRequest |
| extends PendingRequest<RemoteFillService, IAutoFillService> { |
| private final FillRequest mRequest; |
| private final IFillCallback mCallback; |
| private ICancellationSignal mCancellation; |
| |
| public PendingFillRequest(FillRequest request, RemoteFillService service) { |
| super(service); |
| mRequest = request; |
| |
| mCallback = new IFillCallback.Stub() { |
| @Override |
| public void onCancellable(ICancellationSignal cancellation) { |
| synchronized (mLock) { |
| final boolean cancelled; |
| synchronized (mLock) { |
| mCancellation = cancellation; |
| cancelled = isCancelledLocked(); |
| } |
| if (cancelled) { |
| try { |
| cancellation.cancel(); |
| } catch (RemoteException e) { |
| Slog.e(mTag, "Error requesting a cancellation", e); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onSuccess(FillResponse response) { |
| if (!finish()) return; |
| |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| remoteService.dispatchOnFillRequestSuccess(PendingFillRequest.this, |
| response, request.getFlags()); |
| } |
| } |
| |
| @Override |
| public void onFailure(int requestId, CharSequence message) { |
| if (!finish()) return; |
| |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| remoteService.dispatchOnFillRequestFailure(PendingFillRequest.this, |
| message); |
| } |
| } |
| }; |
| } |
| |
| @Override |
| protected void onTimeout(RemoteFillService remoteService) { |
| // NOTE: Must make these 2 calls asynchronously, because the cancellation signal is |
| // handled by the service, which could block. |
| final ICancellationSignal cancellation; |
| synchronized (mLock) { |
| cancellation = mCancellation; |
| } |
| if (cancellation != null) { |
| remoteService.dispatchOnFillTimeout(cancellation); |
| } |
| remoteService.dispatchOnFillRequestTimeout(PendingFillRequest.this); |
| } |
| |
| @Override |
| public void run() { |
| synchronized (mLock) { |
| if (isCancelledLocked()) { |
| if (sDebug) Slog.d(mTag, "run() called after canceled: " + mRequest); |
| return; |
| } |
| } |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId()); |
| try { |
| remoteService.mService.onFillRequest(mRequest, mCallback); |
| } catch (RemoteException e) { |
| Slog.e(mTag, "Error calling on fill request", e); |
| |
| remoteService.dispatchOnFillRequestFailure(PendingFillRequest.this, null); |
| } |
| } |
| } |
| |
| @Override |
| public boolean cancel() { |
| if (!super.cancel()) return false; |
| |
| final ICancellationSignal cancellation; |
| synchronized (mLock) { |
| cancellation = mCancellation; |
| } |
| if (cancellation != null) { |
| try { |
| cancellation.cancel(); |
| } catch (RemoteException e) { |
| Slog.e(mTag, "Error cancelling a fill request", e); |
| } |
| } |
| return true; |
| } |
| } |
| |
| private static final class PendingSaveRequest |
| extends PendingRequest<RemoteFillService, IAutoFillService> { |
| private final SaveRequest mRequest; |
| private final ISaveCallback mCallback; |
| |
| public PendingSaveRequest(@NonNull SaveRequest request, |
| @NonNull RemoteFillService service) { |
| super(service); |
| mRequest = request; |
| |
| mCallback = new ISaveCallback.Stub() { |
| @Override |
| public void onSuccess(IntentSender intentSender) { |
| if (!finish()) return; |
| |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| remoteService.dispatchOnSaveRequestSuccess(PendingSaveRequest.this, |
| intentSender); |
| } |
| } |
| |
| @Override |
| public void onFailure(CharSequence message) { |
| if (!finish()) return; |
| |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, |
| message); |
| } |
| } |
| }; |
| } |
| |
| @Override |
| protected void onTimeout(RemoteFillService remoteService) { |
| remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null); |
| } |
| |
| @Override |
| public void run() { |
| final RemoteFillService remoteService = getService(); |
| if (remoteService != null) { |
| if (sVerbose) Slog.v(mTag, "calling onSaveRequest()"); |
| try { |
| remoteService.mService.onSaveRequest(mRequest, mCallback); |
| } catch (RemoteException e) { |
| Slog.e(mTag, "Error calling on save request", e); |
| |
| remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isFinal() { |
| return true; |
| } |
| } |
| } |