1/*
2 * Copyright (C) 2011 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 android.view;
18
19import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22import android.os.RemoteException;
23
24/**
25 * Filters input events before they are dispatched to the system.
26 * <p>
27 * At most one input filter can be installed by calling
28 * {@link WindowManagerService#setInputFilter}.  When an input filter is installed, the
29 * system's behavior changes as follows:
30 * <ul>
31 * <li>Input events are first delivered to the {@link WindowManagerPolicy}
32 * interception methods before queuing as usual.  This critical step takes care of managing
33 * the power state of the device and handling wake keys.</li>
34 * <li>Input events are then asynchronously delivered to the input filter's
35 * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
36 * applications as usual.  The input filter only receives input events that were
37 * generated by an input device; the input filter will not receive input events that were
38 * injected into the system by other means, such as by instrumentation.</li>
39 * <li>The input filter processes and optionally transforms the stream of events.  For example,
40 * it may transform a sequence of motion events representing an accessibility gesture into
41 * a different sequence of motion events, key presses or other system-level interactions.
42 * The input filter can send events to be dispatched by calling
43 * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
44 * input event.</li>
45 * </ul>
46 * </p>
47 * <h3>The importance of input event consistency</h3>
48 * <p>
49 * The input filter mechanism is very low-level.  At a minimum, it needs to ensure that it
50 * sends an internally consistent stream of input events to the dispatcher.  There are
51 * very important invariants to be maintained.
52 * </p><p>
53 * For example, if a key down is sent, a corresponding key up should also be sent eventually.
54 * Likewise, for touch events, each pointer must individually go down with
55 * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
56 * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
57 * and the sequence of pointer ids used must be consistent throughout the gesture.
58 * </p><p>
59 * Sometimes a filter may wish to cancel a previously dispatched key or motion.  It should
60 * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
61 * </p><p>
62 * The input filter must take into account the fact that the input events coming from different
63 * devices or even different sources all consist of distinct streams of input.
64 * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
65 * the source of the event and its semantics.  There may be multiple sources of keys,
66 * touches and other input: they must be kept separate.
67 * </p>
68 * <h3>Policy flags</h3>
69 * <p>
70 * Input events received from the dispatcher and sent to the dispatcher have policy flags
71 * associated with them.  Policy flags control some functions of the dispatcher.
72 * </p><p>
73 * The early policy interception decides whether an input event should be delivered
74 * to applications or dropped.  The policy indicates its decision by setting the
75 * {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} policy flag.  The input filter may
76 * sometimes receive events that do not have this flag set.  It should take note of
77 * the fact that the policy intends to drop the event, clean up its state, and
78 * then send appropriate cancellation events to the dispatcher if needed.
79 * </p><p>
80 * For example, suppose the input filter is processing a gesture and one of the touch events
81 * it receives does not have the {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} flag set.
82 * The input filter should clear its internal state about the gesture and then send key or
83 * motion events to the dispatcher to cancel any keys or pointers that are down.
84 * </p><p>
85 * Corollary: Events that get sent to the dispatcher should usually include the
86 * {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} flag.  Otherwise, they will be dropped!
87 * </p><p>
88 * It may be prudent to disable automatic key repeating for synthetic key events
89 * by setting the {@link WindowManagerPolicyConstants#FLAG_DISABLE_KEY_REPEAT} policy flag.
90 * </p>
91 *
92 * @hide
93 */
94public abstract class InputFilter extends IInputFilter.Stub {
95    private static final int MSG_INSTALL = 1;
96    private static final int MSG_UNINSTALL = 2;
97    private static final int MSG_INPUT_EVENT = 3;
98
99    // Consistency verifiers for debugging purposes.
100    private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
101            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
102                    new InputEventConsistencyVerifier(this,
103                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
104                            "InputFilter#InboundInputEventConsistencyVerifier") : null;
105    private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
106            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
107                    new InputEventConsistencyVerifier(this,
108                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT,
109                            "InputFilter#OutboundInputEventConsistencyVerifier") : null;
110
111    private final H mH;
112
113    private IInputFilterHost mHost;
114
115    /**
116     * Creates the input filter.
117     *
118     * @param looper The looper to run callbacks on.
119     */
120    public InputFilter(Looper looper) {
121        mH = new H(looper);
122    }
123
124    /**
125     * Called when the input filter is installed.
126     * This method is guaranteed to be non-reentrant.
127     *
128     * @param host The input filter host environment.
129     */
130    public final void install(IInputFilterHost host) {
131        mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
132    }
133
134    /**
135     * Called when the input filter is uninstalled.
136     * This method is guaranteed to be non-reentrant.
137     */
138    public final void uninstall() {
139        mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
140    }
141
142    /**
143     * Called to enqueue the input event for filtering.
144     * The event will be recycled after the input filter processes it.
145     * This method is guaranteed to be non-reentrant.
146     *
147     * @param event The input event to enqueue.
148     */
149    final public void filterInputEvent(InputEvent event, int policyFlags) {
150        mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
151    }
152
153    /**
154     * Sends an input event to the dispatcher.
155     *
156     * @param event The input event to publish.
157     * @param policyFlags The input event policy flags.
158     */
159    public void sendInputEvent(InputEvent event, int policyFlags) {
160        if (event == null) {
161            throw new IllegalArgumentException("event must not be null");
162        }
163        if (mHost == null) {
164            throw new IllegalStateException("Cannot send input event because the input filter " +
165                    "is not installed.");
166        }
167        if (mOutboundInputEventConsistencyVerifier != null) {
168            mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
169        }
170        try {
171            mHost.sendInputEvent(event, policyFlags);
172        } catch (RemoteException re) {
173            /* ignore */
174        }
175    }
176
177    /**
178     * Called when an input event has been received from the dispatcher.
179     * <p>
180     * The default implementation sends the input event back to the dispatcher, unchanged.
181     * </p><p>
182     * The event will be recycled when this method returns.  If you want to keep it around,
183     * make a copy!
184     * </p>
185     *
186     * @param event The input event that was received.
187     * @param policyFlags The input event policy flags.
188     */
189    public void onInputEvent(InputEvent event, int policyFlags) {
190        sendInputEvent(event, policyFlags);
191    }
192
193    /**
194     * Called when the filter is installed into the dispatch pipeline.
195     * <p>
196     * This method is called before the input filter receives any input events.
197     * The input filter should take this opportunity to prepare itself.
198     * </p>
199     */
200    public void onInstalled() {
201    }
202
203    /**
204     * Called when the filter is uninstalled from the dispatch pipeline.
205     * <p>
206     * This method is called after the input filter receives its last input event.
207     * The input filter should take this opportunity to clean up.
208     * </p>
209     */
210    public void onUninstalled() {
211    }
212
213    private final class H extends Handler {
214        public H(Looper looper) {
215            super(looper);
216        }
217
218        @Override
219        public void handleMessage(Message msg) {
220            switch (msg.what) {
221                case MSG_INSTALL:
222                    mHost = (IInputFilterHost) msg.obj;
223                    if (mInboundInputEventConsistencyVerifier != null) {
224                        mInboundInputEventConsistencyVerifier.reset();
225                    }
226                    if (mOutboundInputEventConsistencyVerifier != null) {
227                        mOutboundInputEventConsistencyVerifier.reset();
228                    }
229                    onInstalled();
230                    break;
231
232                case MSG_UNINSTALL:
233                    try {
234                        onUninstalled();
235                    } finally {
236                        mHost = null;
237                    }
238                    break;
239
240                case MSG_INPUT_EVENT: {
241                    final InputEvent event = (InputEvent)msg.obj;
242                    try {
243                        if (mInboundInputEventConsistencyVerifier != null) {
244                            mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
245                        }
246                        onInputEvent(event, msg.arg1);
247                    } finally {
248                        event.recycle();
249                    }
250                    break;
251                }
252            }
253        }
254    }
255}
256