RemoteFillService.java revision 5b2ca0e0ad4be4d8070c51c8924fee05af9c813a
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
17package com.android.server.autofill;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.app.assist.AssistStructure;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.ServiceConnection;
26import android.os.Bundle;
27import android.os.IBinder;
28import android.os.IBinder.DeathRecipient;
29import android.os.ICancellationSignal;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.UserHandle;
33import android.service.autofill.AutoFillService;
34import android.service.autofill.FillResponse;
35import android.service.autofill.IAutoFillService;
36import android.service.autofill.IFillCallback;
37import android.service.autofill.ISaveCallback;
38import android.text.format.DateUtils;
39import android.util.Slog;
40import com.android.internal.os.HandlerCaller;
41import com.android.server.FgThread;
42
43import java.io.PrintWriter;
44import java.lang.ref.WeakReference;
45
46/**
47 * This class represents a remote fill service. It abstracts away the binding
48 * and unbinding from the remote implementation.
49 *
50 * <p>Clients can call methods of this class without worrying about when and
51 * how to bind/unbind/timeout. All state of this class is modified on a handler
52 * thread.
53 */
54final class RemoteFillService implements DeathRecipient {
55    private static final String LOG_TAG = "RemoteFillService";
56
57    private static final boolean DEBUG = Helper.DEBUG;
58
59    // How long after the last interaction with the service we would unbind
60    private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.MINUTE_IN_MILLIS;
61
62    private final Context mContext;
63
64    private final ComponentName mComponentName;
65
66    private final Intent mIntent;
67
68    private final FillServiceCallbacks mCallbacks;
69
70    private final int mUserId;
71
72    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
73
74    private final HandlerCaller mHandler;
75
76    private IAutoFillService mAutoFillService;
77
78    private boolean mBinding;
79
80    private boolean mDestroyed;
81
82    private boolean mServiceDied;
83
84    private boolean mCompleted;
85
86    private PendingRequest mPendingRequest;
87
88    public interface FillServiceCallbacks {
89        void onFillRequestSuccess(FillResponse response);
90        void onFillRequestFailure(CharSequence message);
91        void onSaveRequestSuccess();
92        void onSaveRequestFailure(CharSequence message);
93        void onServiceDied(RemoteFillService service);
94    }
95
96    public RemoteFillService(Context context, ComponentName componentName,
97            int userId, FillServiceCallbacks callbacks) {
98        mContext = context;
99        mCallbacks = callbacks;
100        mComponentName = componentName;
101        mIntent = new Intent(AutoFillService.SERVICE_INTERFACE)
102                .setComponent(mComponentName);
103        mUserId = userId;
104        mHandler = new MyHandler(context);
105    }
106
107    public void destroy() {
108        mHandler.obtainMessage(MyHandler.MSG_DESTROY).sendToTarget();
109    }
110
111    private void handleDestroy() {
112        if (mPendingRequest != null) {
113            mPendingRequest.cancel();
114            mPendingRequest = null;
115        }
116        ensureUnbound();
117        mDestroyed = true;
118    }
119
120    @Override
121    public void binderDied() {
122        mHandler.obtainMessage(MyHandler.MSG_BINDER_DIED).sendToTarget();
123    }
124
125    private void handleBinderDied() {
126        if (mAutoFillService != null) {
127            mAutoFillService.asBinder().unlinkToDeath(this, 0);
128        }
129        mAutoFillService = null;
130        mServiceDied = true;
131        mCallbacks.onServiceDied(this);
132    }
133
134    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
135        cancelScheduledUnbind();
136        PendingFillRequest request = new PendingFillRequest(structure, extras, this);
137        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
138    }
139
140    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
141        cancelScheduledUnbind();
142        PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
143        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
144    }
145
146    // Note: we are dumping without a lock held so this is a bit racy but
147    // adding a lock to a class that offloads to a handler thread would
148    // mean adding a lock adding overhead to normal runtime operation.
149    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
150        String tab = "  ";
151        pw.append(prefix).append("service:").println();
152        pw.append(prefix).append(tab).append("userId=")
153                .append(String.valueOf(mUserId)).println();
154        pw.append(prefix).append(tab).append("componentName=")
155                .append(mComponentName.flattenToString()).println();
156        pw.append(prefix).append(tab).append("destroyed=")
157                .append(String.valueOf(mDestroyed)).println();
158        pw.append(prefix).append(tab).append("bound=")
159                .append(String.valueOf(isBound())).println();
160        pw.append(prefix).append(tab).append("hasPendingRequest=")
161                .append(String.valueOf(mPendingRequest != null)).println();
162        pw.println();
163    }
164
165    private void cancelScheduledUnbind() {
166        mHandler.removeMessages(MyHandler.MSG_UNBIND);
167    }
168
169    private void scheduleUnbind() {
170        cancelScheduledUnbind();
171        Message message = mHandler.obtainMessage(MyHandler.MSG_UNBIND);
172        mHandler.sendMessageDelayed(message, TIMEOUT_IDLE_BIND_MILLIS);
173    }
174
175    private void handleUnbind() {
176        ensureUnbound();
177    }
178
179    private void handlePendingRequest(PendingRequest pendingRequest) {
180        if (mDestroyed || mCompleted) {
181            return;
182        }
183        if (pendingRequest.isFinal()) {
184            mCompleted = true;
185        }
186        if (!isBound()) {
187            if (mPendingRequest != null) {
188                mPendingRequest.cancel();
189            }
190            mPendingRequest = pendingRequest;
191            ensureBound();
192        } else {
193            if (DEBUG) {
194                Slog.d(LOG_TAG, "[user: " + mUserId + "] handlePendingRequest()");
195            }
196            pendingRequest.run();
197        }
198    }
199
200    private boolean isBound() {
201        return mAutoFillService != null;
202    }
203
204    private void ensureBound() {
205        if (isBound() || mBinding) {
206            return;
207        }
208        if (DEBUG) {
209            Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
210        }
211        mBinding = true;
212
213        boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection,
214                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
215                new UserHandle(mUserId));
216
217        if (!willBind) {
218            if (DEBUG) {
219                Slog.d(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent);
220            }
221            mBinding = false;
222
223            if (!mServiceDied) {
224                handleBinderDied();
225            }
226        }
227    }
228
229    private void ensureUnbound() {
230        if (!isBound() && !mBinding) {
231            return;
232        }
233        if (DEBUG) {
234            Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
235        }
236        mBinding = false;
237        if (isBound()) {
238            mAutoFillService.asBinder().unlinkToDeath(this, 0);
239            mAutoFillService = null;
240        }
241        mContext.unbindService(mServiceConnection);
242    }
243
244    private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
245            FillResponse response) {
246        mHandler.getHandler().post(() -> {
247            if (handleResponseCallbackCommon(pendingRequest)) {
248                mCallbacks.onFillRequestSuccess(response);
249            }
250        });
251    }
252
253    private void dispatchOnFillRequestFailure(PendingRequest pendingRequest,
254            CharSequence message) {
255        mHandler.getHandler().post(() -> {
256            if (handleResponseCallbackCommon(pendingRequest)) {
257                mCallbacks.onFillRequestFailure(message);
258            }
259        });
260    }
261
262    private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest) {
263        mHandler.getHandler().post(() -> {
264            if (handleResponseCallbackCommon(pendingRequest)) {
265                mCallbacks.onSaveRequestSuccess();
266            }
267        });
268    }
269
270    private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
271            CharSequence message) {
272        mHandler.getHandler().post(() -> {
273            if (handleResponseCallbackCommon(pendingRequest)) {
274                mCallbacks.onSaveRequestFailure(message);
275            }
276        });
277    }
278
279    private boolean handleResponseCallbackCommon(PendingRequest pendingRequest) {
280        if (mDestroyed) {
281            return false;
282        }
283        if (mPendingRequest == pendingRequest) {
284            mPendingRequest = null;
285        }
286        if (mPendingRequest == null) {
287            scheduleUnbind();
288        }
289        return true;
290    }
291
292    private class RemoteServiceConnection implements ServiceConnection {
293        @Override
294        public void onServiceConnected(ComponentName name, IBinder service) {
295            if (mDestroyed || !mBinding) {
296                mContext.unbindService(mServiceConnection);
297                return;
298            }
299            mBinding = false;
300            mAutoFillService = IAutoFillService.Stub.asInterface(service);
301            try {
302                service.linkToDeath(RemoteFillService.this, 0);
303            } catch (RemoteException re) {
304                handleBinderDied();
305                return;
306            }
307
308            if (mPendingRequest != null) {
309                handlePendingRequest(mPendingRequest);
310            }
311
312            mServiceDied = false;
313        }
314
315        @Override
316        public void onServiceDisconnected(ComponentName name) {
317            mBinding = true;
318            mAutoFillService = null;
319        }
320    }
321
322    private final class MyHandler extends HandlerCaller {
323        public static final int MSG_DESTROY = 1;
324        public static final int MSG_BINDER_DIED = 2;
325        public static final int MSG_UNBIND = 3;
326        public static final int MSG_ON_PENDING_REQUEST = 4;
327
328        public MyHandler(Context context) {
329            // Cannot use lambda - doesn't compile
330            super(context, FgThread.getHandler().getLooper(), new Callback() {
331                @Override
332                public void executeMessage(Message message) {
333                    if (mDestroyed) {
334                        Slog.w(LOG_TAG, "Not handling " + message + " as service for "
335                                + mComponentName + " is already destroyed");
336                        return;
337                    }
338                    switch (message.what) {
339                        case MSG_DESTROY: {
340                            handleDestroy();
341                        } break;
342
343                        case MSG_BINDER_DIED: {
344                            handleBinderDied();
345                        } break;
346
347                        case MSG_UNBIND: {
348                            handleUnbind();
349                        } break;
350
351                        case MSG_ON_PENDING_REQUEST: {
352                            handlePendingRequest((PendingRequest) message.obj);
353                        } break;
354                    }
355                }
356            }, false);
357        }
358    }
359
360    private static abstract class PendingRequest implements Runnable {
361        void cancel() {
362
363        }
364
365        /**
366         * @return whether this request leads to a final state where no
367         * other requests can be made.
368         */
369        boolean isFinal() {
370            return false;
371        }
372    }
373
374    private static final class PendingFillRequest extends PendingRequest {
375        private final Object mLock = new Object();
376        private final WeakReference<RemoteFillService> mWeakService;
377        private AssistStructure mStructure;
378        private Bundle mExtras;
379        private final IFillCallback mCallback;
380        private ICancellationSignal mCancellation;
381        private boolean mCancelled;
382
383        public PendingFillRequest(AssistStructure structure,
384                Bundle extras, RemoteFillService service) {
385            mStructure = structure;
386            mExtras = extras;
387            mWeakService = new WeakReference<>(service);
388            mCallback = new IFillCallback.Stub() {
389                @Override
390                public void onCancellable(ICancellationSignal cancellation) {
391                    synchronized (mLock) {
392                        final boolean cancelled;
393                        synchronized (mLock) {
394                            mCancellation = cancellation;
395                            cancelled = mCancelled;
396                        }
397                        if (cancelled) {
398                            try {
399                                cancellation.cancel();
400                            } catch (RemoteException e) {
401                                Slog.e(LOG_TAG, "Error requesting a cancellation", e);
402                            }
403                        }
404                    }
405                }
406
407                @Override
408                public void onSuccess(FillResponse response) {
409                    RemoteFillService remoteService = mWeakService.get();
410                    if (remoteService != null) {
411                        remoteService.dispatchOnFillRequestSuccess(
412                                PendingFillRequest.this, response);
413                    }
414                }
415
416                @Override
417                public void onFailure(CharSequence message) {
418                    RemoteFillService remoteService = mWeakService.get();
419                    if (remoteService != null) {
420                        remoteService.dispatchOnFillRequestFailure(
421                                PendingFillRequest.this, message);
422                    }
423                }
424            };
425        }
426
427        @Override
428        public void run() {
429            RemoteFillService remoteService = mWeakService.get();
430            if (remoteService != null) {
431                try {
432                    remoteService.mAutoFillService.onFillRequest(mStructure,
433                            mExtras, mCallback);
434                    synchronized (mLock) {
435                        mStructure = null;
436                        mExtras = null;
437                    }
438                } catch (RemoteException e) {
439                    Slog.e(LOG_TAG, "Error calling on fill request", e);
440                    cancel();
441                }
442            }
443        }
444
445        @Override
446        public void cancel() {
447            final ICancellationSignal cancellation;
448            synchronized (mLock) {
449                if (mCancelled) {
450                    return;
451                }
452                mCancelled = true;
453                cancellation = mCancellation;
454            }
455            if (cancellation == null) {
456                return;
457            }
458            try {
459                cancellation.cancel();
460            } catch (RemoteException e) {
461                Slog.e(LOG_TAG, "Error cancelling a fill request", e);
462            }
463        }
464    }
465
466    private static final class PendingSaveRequest extends PendingRequest {
467        private final Object mLock = new Object();
468        private final WeakReference<RemoteFillService> mWeakService;
469        private AssistStructure mStructure;
470        private Bundle mExtras;
471        private final ISaveCallback mCallback;
472
473        public PendingSaveRequest(@NonNull AssistStructure structure,
474                @Nullable Bundle extras, @NonNull RemoteFillService service) {
475            mStructure = structure;
476            mExtras = extras;
477            mWeakService = new WeakReference<>(service);
478            mCallback = new ISaveCallback.Stub() {
479                @Override
480                public void onSuccess() {
481                    RemoteFillService service = mWeakService.get();
482                    if (service != null) {
483                        service.dispatchOnSaveRequestSuccess(
484                                PendingSaveRequest.this);
485                    }
486                }
487
488                @Override
489                public void onFailure(CharSequence message) {
490                    RemoteFillService service = mWeakService.get();
491                    if (service != null) {
492                        service.dispatchOnSaveRequestFailure(
493                                PendingSaveRequest.this, message);
494                    }
495                }
496            };
497        }
498
499        @Override
500        public void run() {
501            RemoteFillService service = mWeakService.get();
502            if (service != null) {
503                try {
504                    service.mAutoFillService.onSaveRequest(mStructure,
505                            mExtras, mCallback);
506                    synchronized (mLock) {
507                        mStructure = null;
508                        mExtras = null;
509                    }
510                } catch (RemoteException e) {
511                    Slog.e(LOG_TAG, "Error calling on save request", e);
512                }
513            }
514        }
515
516        @Override
517        public boolean isFinal() {
518            return true;
519        }
520    }
521}
522