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 import static android.service.autofill.Flags.remoteFillServiceUseWeakReference;
21 
22 import static com.android.server.autofill.Helper.sVerbose;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentSender;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.ICancellationSignal;
33 import android.os.RemoteException;
34 import android.service.autofill.AutofillService;
35 import android.service.autofill.ConvertCredentialRequest;
36 import android.service.autofill.ConvertCredentialResponse;
37 import android.service.autofill.FillRequest;
38 import android.service.autofill.FillResponse;
39 import android.service.autofill.IAutoFillService;
40 import android.service.autofill.IConvertCredentialCallback;
41 import android.service.autofill.IFillCallback;
42 import android.service.autofill.ISaveCallback;
43 import android.service.autofill.SaveRequest;
44 import android.text.format.DateUtils;
45 import android.util.Slog;
46 
47 import com.android.internal.infra.AbstractRemoteService;
48 import com.android.internal.infra.ServiceConnector;
49 import com.android.internal.os.IResultReceiver;
50 
51 import java.lang.ref.WeakReference;
52 import java.util.concurrent.CancellationException;
53 import java.util.concurrent.CompletableFuture;
54 import java.util.concurrent.TimeUnit;
55 import java.util.concurrent.TimeoutException;
56 import java.util.concurrent.atomic.AtomicReference;
57 
58 final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> {
59 
60     private static final String TAG = "RemoteFillService";
61 
62     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
63     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
64 
65     private final FillServiceCallbacks mCallbacks;
66     private final Object mLock = new Object();
67     private CompletableFuture<FillResponse> mPendingFillRequest;
68     private int mPendingFillRequestId = INVALID_REQUEST_ID;
69     private AtomicReference<IFillCallback> mFillCallback;
70     private AtomicReference<ISaveCallback> mSaveCallback;
71     private AtomicReference<IConvertCredentialCallback> mConvertCredentialCallback;
72     private final ComponentName mComponentName;
73 
74     private final boolean mIsCredentialAutofillService;
75 
isCredentialAutofillService()76     public boolean isCredentialAutofillService() {
77         return mIsCredentialAutofillService;
78     }
79 
80     public interface FillServiceCallbacks
81             extends AbstractRemoteService.VultureCallback<RemoteFillService> {
onFillRequestSuccess(int requestId, @Nullable FillResponse response, @NonNull String servicePackageName, int requestFlags)82         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
83                 @NonNull String servicePackageName, int requestFlags);
84 
onFillRequestFailure(int requestId, Throwable t)85         void onFillRequestFailure(int requestId, Throwable t);
86 
onSaveRequestSuccess(@onNull String servicePackageName, @Nullable IntentSender intentSender)87         void onSaveRequestSuccess(@NonNull String servicePackageName,
88                 @Nullable IntentSender intentSender);
89 
90         // TODO(b/80093094): add timeout here too?
onSaveRequestFailure(@ullable CharSequence message, @NonNull String servicePackageName)91         void onSaveRequestFailure(@Nullable CharSequence message,
92                 @NonNull String servicePackageName);
93 
onConvertCredentialRequestSuccess(@onNull ConvertCredentialResponse convertCredentialResponse)94         void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
95                 convertCredentialResponse);
96     }
97 
RemoteFillService(Context context, ComponentName componentName, int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed, @Nullable ComponentName credentialAutofillService)98     RemoteFillService(Context context, ComponentName componentName, int userId,
99             FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
100             @Nullable ComponentName credentialAutofillService) {
101         super(context, new Intent(AutofillService.SERVICE_INTERFACE).setComponent(componentName),
102                 Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
103                         | (bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0),
104                 userId, IAutoFillService.Stub::asInterface);
105         mCallbacks = callbacks;
106         mComponentName = componentName;
107         mIsCredentialAutofillService = mComponentName.equals(credentialAutofillService);
108     }
109 
110     @Override // from ServiceConnector.Impl
onServiceConnectionStatusChanged(IAutoFillService service, boolean connected)111     protected void onServiceConnectionStatusChanged(IAutoFillService service, boolean connected) {
112         try {
113             service.onConnectedStateChanged(connected);
114         } catch (Exception e) {
115             Slog.w(TAG, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
116         }
117     }
118 
dispatchCancellationSignal(@ullable ICancellationSignal signal)119     private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
120         if (signal == null) {
121             return;
122         }
123         try {
124             signal.cancel();
125         } catch (RemoteException e) {
126             Slog.e(TAG, "Error requesting a cancellation", e);
127         }
128     }
129 
130     @Override // from ServiceConnector.Impl
getAutoDisconnectTimeoutMs()131     protected long getAutoDisconnectTimeoutMs() {
132         return TIMEOUT_IDLE_BIND_MILLIS;
133     }
134 
135     @Override // from ServiceConnector.Impl
addLast(Job<IAutoFillService, ?> iAutoFillServiceJob)136     public void addLast(Job<IAutoFillService, ?> iAutoFillServiceJob) {
137         // Only maintain single request at a time
138         cancelPendingJobs();
139         super.addLast(iAutoFillServiceJob);
140     }
141 
getComponentName()142     public ComponentName getComponentName() {
143         return mComponentName;
144     }
145 
146     /**
147      * Cancel the currently pending request.
148      *
149      * <p>This can be used when the request is unnecessary or will be superceeded by a request that
150      * will soon be queued.
151      *
152      * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no
153      *         {@link FillRequest} was canceled.
154      */
cancelCurrentRequest()155     public int cancelCurrentRequest() {
156         synchronized (mLock) {
157             return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
158                     ? mPendingFillRequestId
159                     : INVALID_REQUEST_ID;
160         }
161     }
162 
163     static class IFillCallbackDelegate extends IFillCallback.Stub {
164         private WeakReference<IFillCallback> mCallbackWeakRef;
165 
IFillCallbackDelegate(IFillCallback callback)166         IFillCallbackDelegate(IFillCallback callback) {
167             mCallbackWeakRef = new WeakReference(callback);
168         }
169 
170         @Override
onCancellable(ICancellationSignal cancellation)171         public void onCancellable(ICancellationSignal cancellation) throws RemoteException {
172             IFillCallback callback = mCallbackWeakRef.get();
173             if (callback != null) {
174                 callback.onCancellable(cancellation);
175             }
176         }
177 
178         @Override
onSuccess(FillResponse response)179         public void onSuccess(FillResponse response) throws RemoteException {
180             IFillCallback callback = mCallbackWeakRef.get();
181             if (callback != null) {
182                 callback.onSuccess(response);
183             }
184         }
185 
186         @Override
onFailure(int requestId, CharSequence message)187         public void onFailure(int requestId, CharSequence message) throws RemoteException {
188             IFillCallback callback = mCallbackWeakRef.get();
189             if (callback != null) {
190                 callback.onFailure(requestId, message);
191             }
192         }
193     }
194 
195     static class ISaveCallbackDelegate extends ISaveCallback.Stub {
196 
197         private WeakReference<ISaveCallback> mCallbackWeakRef;
198 
ISaveCallbackDelegate(ISaveCallback callback)199         ISaveCallbackDelegate(ISaveCallback callback) {
200             mCallbackWeakRef = new WeakReference(callback);
201         }
202 
203         @Override
onSuccess(IntentSender intentSender)204         public void onSuccess(IntentSender intentSender) throws RemoteException {
205             ISaveCallback callback = mCallbackWeakRef.get();
206             if (callback != null) {
207                 callback.onSuccess(intentSender);
208             }
209         }
210 
211         @Override
onFailure(CharSequence message)212         public void onFailure(CharSequence message) throws RemoteException {
213             ISaveCallback callback = mCallbackWeakRef.get();
214             if (callback != null) {
215                 callback.onFailure(message);
216             }
217         }
218     }
219 
220     static class IConvertCredentialCallbackDelegate extends IConvertCredentialCallback.Stub {
221 
222         private WeakReference<IConvertCredentialCallback> mCallbackWeakRef;
223 
IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback)224         IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback) {
225             mCallbackWeakRef = new WeakReference(callback);
226         }
227 
228         @Override
onSuccess(ConvertCredentialResponse convertCredentialResponse)229         public void onSuccess(ConvertCredentialResponse convertCredentialResponse)
230                 throws RemoteException {
231             IConvertCredentialCallback callback = mCallbackWeakRef.get();
232             if (callback != null) {
233                 callback.onSuccess(convertCredentialResponse);
234             }
235         }
236 
237         @Override
onFailure(CharSequence message)238         public void onFailure(CharSequence message) throws RemoteException {
239             IConvertCredentialCallback callback = mCallbackWeakRef.get();
240             if (callback != null) {
241                 callback.onFailure(message);
242             }
243         }
244     }
245 
246     /**
247      * Wraps an {@link IFillCallback} object using weak reference.
248      *
249      * This prevents lingering allocation issue by breaking the chain of strong references from
250      * Binder to {@link callback}. Since {@link callback} is not held by Binder anymore, it needs
251      * to be held by {@link mFillCallback} so it's not deallocated prematurely.
252      */
maybeWrapWithWeakReference(IFillCallback callback)253     private IFillCallback maybeWrapWithWeakReference(IFillCallback callback) {
254         if (remoteFillServiceUseWeakReference()) {
255             mFillCallback = new AtomicReference<>(callback);
256             return new IFillCallbackDelegate(callback);
257         }
258         return callback;
259     }
260 
261     /**
262      * Wraps an {@link ISaveCallback} object using weak reference.
263      */
maybeWrapWithWeakReference(ISaveCallback callback)264     private ISaveCallback maybeWrapWithWeakReference(ISaveCallback callback) {
265         if (remoteFillServiceUseWeakReference()) {
266             mSaveCallback = new AtomicReference<>(callback);
267             return new ISaveCallbackDelegate(callback);
268         }
269         return callback;
270     }
271 
272     /**
273      * Wraps an {@link IConvertCredentialCallback} object using weak reference
274      */
maybeWrapWithWeakReference( IConvertCredentialCallback callback)275     private IConvertCredentialCallback maybeWrapWithWeakReference(
276             IConvertCredentialCallback callback) {
277         if (remoteFillServiceUseWeakReference()) {
278             mConvertCredentialCallback = new AtomicReference<>(callback);
279             return new IConvertCredentialCallbackDelegate(callback);
280         }
281         return callback;
282     }
283 
onFillCredentialRequest(@onNull FillRequest request, IBinder autofillCallback)284     public void onFillCredentialRequest(@NonNull FillRequest request, IBinder autofillCallback) {
285         if (sVerbose) {
286             Slog.v(TAG, "onFillRequest:" + request);
287         }
288         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
289         AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
290 
291         CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
292             if (sVerbose) {
293                 Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
294             }
295 
296             CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
297             remoteService.onFillCredentialRequest(
298                     request, maybeWrapWithWeakReference(new IFillCallback.Stub() {
299                         @Override
300                         public void onCancellable(ICancellationSignal cancellation) {
301                             CompletableFuture<FillResponse> future = futureRef.get();
302                             if (future != null && future.isCancelled()) {
303                                 dispatchCancellationSignal(cancellation);
304                             } else {
305                                 cancellationSink.set(cancellation);
306                             }
307                         }
308 
309                         @Override
310                         public void onSuccess(FillResponse response) {
311                             fillRequest.complete(response);
312                         }
313 
314                         @Override
315                         public void onFailure(int requestId, CharSequence message) {
316                             String errorMessage = message == null ? "" : String.valueOf(message);
317                             fillRequest.completeExceptionally(
318                                     new RuntimeException(errorMessage));
319                         }
320                     }), autofillCallback);
321             return fillRequest;
322         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
323         futureRef.set(connectThenFillRequest);
324 
325         synchronized (mLock) {
326             mPendingFillRequest = connectThenFillRequest;
327             mPendingFillRequestId = request.getId();
328         }
329 
330         connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
331             synchronized (mLock) {
332                 mPendingFillRequest = null;
333                 mPendingFillRequestId = INVALID_REQUEST_ID;
334             }
335             if (mCallbacks == null) {
336                 Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
337                 return;
338             }
339             if (err == null) {
340                 mCallbacks.onFillRequestSuccess(request.getId(), res,
341                         mComponentName.getPackageName(), request.getFlags());
342             } else {
343                 Slog.e(TAG, "Error calling on fill request", err);
344                 if (err instanceof TimeoutException) {
345                     dispatchCancellationSignal(cancellationSink.get());
346                     mCallbacks.onFillRequestFailure(request.getId(), err);
347                 } else if (err instanceof CancellationException) {
348                     // Cancellation is a part of the user flow - don't mark as failure
349                     dispatchCancellationSignal(cancellationSink.get());
350                 } else {
351                     mCallbacks.onFillRequestFailure(request.getId(), err);
352                 }
353             }
354         }));
355     }
356 
onFillRequest(@onNull FillRequest request)357     public void onFillRequest(@NonNull FillRequest request) {
358         if (sVerbose) {
359             Slog.v(TAG, "onFillRequest:" + request);
360         }
361         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
362         AtomicReference<CompletableFuture<FillResponse>> futureRef = new AtomicReference<>();
363 
364         CompletableFuture<FillResponse> connectThenFillRequest = postAsync(remoteService -> {
365             if (sVerbose) {
366                 Slog.v(TAG, "calling onFillRequest() for id=" + request.getId());
367             }
368 
369             CompletableFuture<FillResponse> fillRequest = new CompletableFuture<>();
370             remoteService.onFillRequest(
371                     request, maybeWrapWithWeakReference(new IFillCallback.Stub() {
372                         @Override
373                         public void onCancellable(ICancellationSignal cancellation) {
374                             CompletableFuture<FillResponse> future = futureRef.get();
375                             if (future != null && future.isCancelled()) {
376                                 dispatchCancellationSignal(cancellation);
377                             } else {
378                                 cancellationSink.set(cancellation);
379                             }
380                         }
381 
382                         @Override
383                         public void onSuccess(FillResponse response) {
384                             fillRequest.complete(response);
385                         }
386 
387                         @Override
388                         public void onFailure(int requestId, CharSequence message) {
389                             String errorMessage = message == null ? "" : String.valueOf(message);
390                             fillRequest.completeExceptionally(
391                                     new RuntimeException(errorMessage));
392                         }
393                     }));
394             return fillRequest;
395         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
396         futureRef.set(connectThenFillRequest);
397 
398         synchronized (mLock) {
399             mPendingFillRequest = connectThenFillRequest;
400             mPendingFillRequestId = request.getId();
401         }
402 
403         connectThenFillRequest.whenComplete((res, err) -> Handler.getMain().post(() -> {
404             synchronized (mLock) {
405                 mPendingFillRequest = null;
406                 mPendingFillRequestId = INVALID_REQUEST_ID;
407             }
408             if (err == null) {
409                 mCallbacks.onFillRequestSuccess(request.getId(), res,
410                         mComponentName.getPackageName(), request.getFlags());
411             } else {
412                 Slog.e(TAG, "Error calling on fill request", err);
413                 if (err instanceof TimeoutException) {
414                     dispatchCancellationSignal(cancellationSink.get());
415                     mCallbacks.onFillRequestFailure(request.getId(), err);
416                 } else if (err instanceof CancellationException) {
417                     // Cancellation is a part of the user flow - don't mark as failure
418                     dispatchCancellationSignal(cancellationSink.get());
419                 } else {
420                     mCallbacks.onFillRequestFailure(request.getId(), err);
421                 }
422             }
423         }));
424     }
425 
onConvertCredentialRequest( @onNull ConvertCredentialRequest convertCredentialRequest)426     public void onConvertCredentialRequest(
427             @NonNull ConvertCredentialRequest convertCredentialRequest) {
428         if (sVerbose) Slog.v(TAG, "calling onConvertCredentialRequest()");
429         CompletableFuture<ConvertCredentialResponse>
430                 connectThenConvertCredentialRequest = postAsync(
431                     remoteService -> {
432                         if (sVerbose) {
433                             Slog.v(TAG, "calling onConvertCredentialRequest()");
434                         }
435                         CompletableFuture<ConvertCredentialResponse>
436                                 convertCredentialCompletableFuture = new CompletableFuture<>();
437                         remoteService.onConvertCredentialRequest(convertCredentialRequest,
438                                 maybeWrapWithWeakReference(
439                                         new IConvertCredentialCallback.Stub() {
440                                             @Override
441                                             public void onSuccess(ConvertCredentialResponse
442                                                     convertCredentialResponse) {
443                                                 convertCredentialCompletableFuture
444                                                         .complete(convertCredentialResponse);
445                                             }
446 
447                                             @Override
448                                             public void onFailure(CharSequence message) {
449                                                 String errorMessage =
450                                                         message == null ? "" :
451                                                                     String.valueOf(message);
452                                                 convertCredentialCompletableFuture
453                                                         .completeExceptionally(
454                                                             new RuntimeException(errorMessage));
455                                             }
456                                         })
457                         );
458                         return convertCredentialCompletableFuture;
459                     }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
460 
461         connectThenConvertCredentialRequest.whenComplete(
462                 (res, err) -> Handler.getMain().post(() -> {
463                     if (err == null) {
464                         mCallbacks.onConvertCredentialRequestSuccess(res);
465                     } else {
466                         // TODO: Add a callback function to log this failure
467                         Slog.e(TAG, "Error calling on convert credential request", err);
468                     }
469                 }));
470     }
471 
onSaveRequest(@onNull SaveRequest request)472     public void onSaveRequest(@NonNull SaveRequest request) {
473         postAsync(service -> {
474             if (sVerbose) Slog.v(TAG, "calling onSaveRequest()");
475 
476             CompletableFuture<IntentSender> save = new CompletableFuture<>();
477             service.onSaveRequest(request, maybeWrapWithWeakReference(new ISaveCallback.Stub() {
478                 @Override
479                 public void onSuccess(IntentSender intentSender) {
480                     save.complete(intentSender);
481                 }
482 
483                 @Override
484                 public void onFailure(CharSequence message) {
485                     save.completeExceptionally(new RuntimeException(String.valueOf(message)));
486                 }
487             }));
488             return save;
489         }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS)
490                 .whenComplete((res, err) -> Handler.getMain().post(() -> {
491                     if (err == null) {
492                         mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res);
493                     } else {
494                         mCallbacks.onSaveRequestFailure(
495                                 mComponentName.getPackageName(), err.getMessage());
496                     }
497                 }));
498     }
499 
onSavedPasswordCountRequest(IResultReceiver receiver)500     void onSavedPasswordCountRequest(IResultReceiver receiver) {
501         run(service -> service.onSavedPasswordCountRequest(receiver));
502     }
503 
destroy()504     public void destroy() {
505         unbind();
506     }
507 }
508