Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 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 | |
| 17 | package com.android.server.wm; |
| 18 | |
| 19 | import android.os.Handler; |
| 20 | import android.os.Looper; |
| 21 | import android.os.Message; |
| 22 | import android.view.InputEvent; |
Jeff Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 23 | import android.view.InputEventConsistencyVerifier; |
Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 24 | import android.view.KeyEvent; |
| 25 | import android.view.MotionEvent; |
| 26 | import 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 | */ |
| 96 | public 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 Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 104 | // Consistency verifiers for debugging purposes. |
| 105 | private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier = |
| 106 | InputEventConsistencyVerifier.isInstrumentationEnabled() ? |
| 107 | new InputEventConsistencyVerifier(this, |
Svetoslav Ganov | 736c275 | 2011-04-22 18:30:36 -0700 | [diff] [blame] | 108 | InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, |
| 109 | "InputFilter#InboundInputEventConsistencyVerifier") : null; |
Jeff Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 110 | private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier = |
| 111 | InputEventConsistencyVerifier.isInstrumentationEnabled() ? |
| 112 | new InputEventConsistencyVerifier(this, |
Svetoslav Ganov | 736c275 | 2011-04-22 18:30:36 -0700 | [diff] [blame] | 113 | InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, |
| 114 | "InputFilter#OutboundInputEventConsistencyVerifier") : null; |
Jeff Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 115 | |
Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 116 | /** |
| 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 Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 168 | if (mOutboundInputEventConsistencyVerifier != null) { |
| 169 | mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0); |
| 170 | } |
Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 171 | 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 Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 220 | if (mInboundInputEventConsistencyVerifier != null) { |
| 221 | mInboundInputEventConsistencyVerifier.reset(); |
| 222 | } |
| 223 | if (mOutboundInputEventConsistencyVerifier != null) { |
| 224 | mOutboundInputEventConsistencyVerifier.reset(); |
| 225 | } |
Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 226 | 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 Brown | 21bc5c9 | 2011-02-28 18:27:14 -0800 | [diff] [blame] | 240 | if (mInboundInputEventConsistencyVerifier != null) { |
| 241 | mInboundInputEventConsistencyVerifier.onInputEvent(event, 0); |
| 242 | } |
Jeff Brown | 0029c66 | 2011-03-30 02:25:18 -0700 | [diff] [blame] | 243 | 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 | } |