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