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