1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.autofill;
18 
19 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
20 
21 import static com.android.server.autofill.Helper.sVerbose;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentSender;
29 import android.os.Handler;
30 import android.os.ICancellationSignal;
31 import android.os.RemoteException;
32 import android.service.autofill.AutofillService;
33 import android.service.autofill.FillRequest;
34 import android.service.autofill.FillResponse;
35 import android.service.autofill.IAutoFillService;
36 import android.service.autofill.IFillCallback;
37 import android.service.autofill.ISaveCallback;
38 import android.service.autofill.SaveRequest;
39 import android.text.format.DateUtils;
40 import android.util.Slog;
41 
42 import com.android.internal.infra.AbstractRemoteService;
43 import com.android.internal.infra.ServiceConnector;
44 import com.android.internal.os.IResultReceiver;
45 
46 import java.util.concurrent.CancellationException;
47 import java.util.concurrent.CompletableFuture;
48 import java.util.concurrent.TimeUnit;
49 import java.util.concurrent.TimeoutException;
50 import java.util.concurrent.atomic.AtomicReference;
51 
52 final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
53 
54     private static final String TAG = "RemoteFillService";
55 
56     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
57     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
58 
59     private final FillServiceCallbacks mCallbacks;
60     private final Object mLock = new Object();
61     private CompletableFuture<FillResponse> mPendingFillRequest;
62     private int mPendingFillRequestId = INVALID_REQUEST_ID;
63     private final ComponentName mComponentName;
64 
65     public interface FillServiceCallbacks
66             extends AbstractRemoteService.VultureCallback<RemoteFillService> {
onFillRequestSuccess(int requestId, @Nullable FillResponse response, @NonNull String servicePackageName, int requestFlags)67         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
68                 @NonNull String servicePackageName, int requestFlags);
onFillRequestFailure(int requestId, @Nullable CharSequence message)69         void onFillRequestFailure(int requestId, @Nullable CharSequence message);
onFillRequestTimeout(int requestId)70         void onFillRequestTimeout(int requestId);
onSaveRequestSuccess(@onNull String servicePackageName, @Nullable IntentSender intentSender)71         void onSaveRequestSuccess(@NonNull String servicePackageName,
72                 @Nullable IntentSender intentSender);
73         // TODO(b/80093094): add timeout here too?
onSaveRequestFailure(@ullable CharSequence message, @NonNull String servicePackageName)74         void onSaveRequestFailure(@Nullable CharSequence message,
75                 @NonNull String servicePackageName);
76     }
77 
RemoteFillService(Context context, ComponentName componentName, int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed)78     RemoteFillService(Context context, ComponentName componentName, int userId,
79             FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) {
80         super(context, new Intent(AutofillService.SERVICE_INTERFACE).setComponent(componentName),
81                 Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
82                         | (bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0),
83                 userId, IAutoFillService.Stub::asInterface);
84         mCallbacks = callbacks;
85         mComponentName = componentName;
86     }
87 
88     @Override // from ServiceConnector.Impl
onServiceConnectionStatusChanged(IAutoFillService service, boolean connected)89     protected void onServiceConnectionStatusChanged(IAutoFillService service, boolean connected) {
90         try {
91             service.onConnectedStateChanged(connected);
92         } catch (Exception e) {
93             Slog.w(TAG, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
94         }
95     }
96 
dispatchCancellationSignal(@ullable ICancellationSignal signal)97     private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
98         if (signal == null) {
99             return;
100         }
101         try {
102             signal.cancel();
103         } catch (RemoteException e) {
104             Slog.e(TAG, "Error requesting a cancellation", e);
105         }
106     }
107 
108     @Override // from ServiceConnector.Impl
getAutoDisconnectTimeoutMs()109     protected long getAutoDisconnectTimeoutMs() {
110         return TIMEOUT_IDLE_BIND_MILLIS;
111     }
112 
113     @Override // from ServiceConnector.Impl
addLast(Job<IAutoFillService, ?> iAutoFillServiceJob)114     public void addLast(Job<IAutoFillService, ?> iAutoFillServiceJob) {
115         // Only maintain single request at a time
116         cancelPendingJobs();
117         super.addLast(iAutoFillServiceJob);
118     }
119 
120     /**
121      * Cancel the currently pending request.
122      *
123      * <p>This can be used when the request is unnecessary or will be superceeded by a request that
124      * will soon be queued.
125      *
126      * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no
127      *         {@link FillRequest} was canceled.
128      */
cancelCurrentRequest()129     public int cancelCurrentRequest() {
130         synchronized (mLock) {
131             return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
132                     ? mPendingFillRequestId
133                     : INVALID_REQUEST_ID;
134         }
135     }
136 
onFillRequest(@onNull FillRequest request)137     public void onFillRequest(@NonNull FillRequest request) {
138         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
139         AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
140 
141         CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
142             if (sVerbose) {
143                 Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
144             }
145 
146             CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
147             remoteService.onFillRequest(request, new IFillCallback.Stub() {
148                 @Override
149                 public void onCancellable(ICancellationSignal cancellation) {
150                     CompletableFuture<FillResponse> future = futureRef.get();
151                     if (future != null && future.isCancelled()) {
152                         dispatchCancellationSignal(cancellation);
153                     } else {
154                         cancellationSink.set(cancellation);
155                     }
156                 }
157 
158                 @Override
159                 public void onSuccess(FillResponse response) {
160                     fillRequest.complete(response);
161                 }
162 
163                 @Override
164                 public void onFailure(int requestId, CharSequence message) {
165                     String errorMessage = message == null ? "" : String.valueOf(message);
166                     fillRequest.completeExceptionally(
167                             new RuntimeException(errorMessage));
168                 }
169             });
170             return fillRequest;
171         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
172         futureRef.set(connectThenFillRequest);
173 
174         synchronized (mLock) {
175             mPendingFillRequest = connectThenFillRequest;
176             mPendingFillRequestId = request.getId();
177         }
178 
179         connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
180             synchronized (mLock) {
181                 mPendingFillRequest = null;
182                 mPendingFillRequestId = INVALID_REQUEST_ID;
183             }
184             if (err == null) {
185                 mCallbacks.onFillRequestSuccess(request.getId(), res,
186                         mComponentName.getPackageName(), request.getFlags());
187             } else {
188                 Slog.e(TAG, "Error calling on fill request", err);
189                 if (err instanceof TimeoutException) {
190                     dispatchCancellationSignal(cancellationSink.get());
191                     mCallbacks.onFillRequestTimeout(request.getId());
192                 } else if (err instanceof CancellationException) {
193                     dispatchCancellationSignal(cancellationSink.get());
194                 } else {
195                     mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
196                 }
197             }
198         }));
199     }
200 
onSaveRequest(@onNull SaveRequest request)201     public void onSaveRequest(@NonNull SaveRequest request) {
202         postAsync(service -> {
203             if (sVerbose) Slog.v(TAG, "calling onSaveRequest()");
204 
205             CompletableFuture<IntentSender> save = new CompletableFuture<>();
206             service.onSaveRequest(request, new ISaveCallback.Stub() {
207                 @Override
208                 public void onSuccess(IntentSender intentSender) {
209                     save.complete(intentSender);
210                 }
211 
212                 @Override
213                 public void onFailure(CharSequence message) {
214                     save.completeExceptionally(new RuntimeException(String.valueOf(message)));
215                 }
216             });
217             return save;
218         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS)
219                 .whenComplete((res, err) -> Handler.getMain().post(() -> {
220                     if (err == null) {
221                         mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res);
222                     } else {
223                         mCallbacks.onSaveRequestFailure(
224                                 mComponentName.getPackageName(), err.getMessage());
225                     }
226                 }));
227     }
228 
onSavedPasswordCountRequest(IResultReceiver receiver)229     void onSavedPasswordCountRequest(IResultReceiver receiver) {
230         run(service -> service.onSavedPasswordCountRequest(receiver));
231     }
232 
destroy()233     public void destroy() {
234         unbind();
235     }
236 }
237