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