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