/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;

Filters input events before they are dispatched to the system.

At most one input filter can be installed by calling setInputFilter.setInputFilter. When an input filter is installed, the system's behavior changes as follows:

  • Input events are first delivered to the WindowManagerPolicy interception methods before queuing as usual. This critical step takes care of managing the power state of the device and handling wake keys.
  • Input events are then asynchronously delivered to the input filter's onInputEvent(InputEvent) method instead of being enqueued for dispatch to applications as usual. The input filter only receives input events that were generated by an input device; the input filter will not receive input events that were injected into the system by other means, such as by instrumentation.
  • The input filter processes and optionally transforms the stream of events. For example, it may transform a sequence of motion events representing an accessibility gesture into a different sequence of motion events, key presses or other system-level interactions. The input filter can send events to be dispatched by calling sendInputEvent(InputEvent) and passing appropriate policy flags for the input event.

The importance of input event consistency

The input filter mechanism is very low-level. At a minimum, it needs to ensure that it sends an internally consistent stream of input events to the dispatcher. There are very important invariants to be maintained.

For example, if a key down is sent, a corresponding key up should also be sent eventually. Likewise, for touch events, each pointer must individually go down with MotionEvent.ACTION_DOWN or MotionEvent.ACTION_POINTER_DOWN and then individually go up with MotionEvent.ACTION_POINTER_UP or MotionEvent.ACTION_UP and the sequence of pointer ids used must be consistent throughout the gesture.

Sometimes a filter may wish to cancel a previously dispatched key or motion. It should use KeyEvent.FLAG_CANCELED or MotionEvent.ACTION_CANCEL accordingly.

The input filter must take into account the fact that the input events coming from different devices or even different sources all consist of distinct streams of input. Use InputEvent.getDeviceId() and InputEvent.getSource() to identify the source of the event and its semantics. There may be multiple sources of keys, touches and other input: they must be kept separate.

Policy flags

Input events received from the dispatcher and sent to the dispatcher have policy flags associated with them. Policy flags control some functions of the dispatcher.

The early policy interception decides whether an input event should be delivered to applications or dropped. The policy indicates its decision by setting the WindowManagerPolicyConstants.FLAG_PASS_TO_USER policy flag. The input filter may sometimes receive events that do not have this flag set. It should take note of the fact that the policy intends to drop the event, clean up its state, and then send appropriate cancellation events to the dispatcher if needed.

For example, suppose the input filter is processing a gesture and one of the touch events it receives does not have the WindowManagerPolicyConstants.FLAG_PASS_TO_USER flag set. The input filter should clear its internal state about the gesture and then send key or motion events to the dispatcher to cancel any keys or pointers that are down.

Corollary: Events that get sent to the dispatcher should usually include the WindowManagerPolicyConstants.FLAG_PASS_TO_USER flag. Otherwise, they will be dropped!

It may be prudent to disable automatic key repeating for synthetic key events by setting the WindowManagerPolicyConstants.FLAG_DISABLE_KEY_REPEAT policy flag.

@hide
/** * Filters input events before they are dispatched to the system. * <p> * At most one input filter can be installed by calling * {@link WindowManagerService#setInputFilter}. When an input filter is installed, the * system's behavior changes as follows: * <ul> * <li>Input events are first delivered to the {@link WindowManagerPolicy} * interception methods before queuing as usual. This critical step takes care of managing * the power state of the device and handling wake keys.</li> * <li>Input events are then asynchronously delivered to the input filter's * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to * applications as usual. The input filter only receives input events that were * generated by an input device; the input filter will not receive input events that were * injected into the system by other means, such as by instrumentation.</li> * <li>The input filter processes and optionally transforms the stream of events. For example, * it may transform a sequence of motion events representing an accessibility gesture into * a different sequence of motion events, key presses or other system-level interactions. * The input filter can send events to be dispatched by calling * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the * input event.</li> * </ul> * </p> * <h3>The importance of input event consistency</h3> * <p> * The input filter mechanism is very low-level. At a minimum, it needs to ensure that it * sends an internally consistent stream of input events to the dispatcher. There are * very important invariants to be maintained. * </p><p> * For example, if a key down is sent, a corresponding key up should also be sent eventually. * Likewise, for touch events, each pointer must individually go down with * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP} * and the sequence of pointer ids used must be consistent throughout the gesture. * </p><p> * Sometimes a filter may wish to cancel a previously dispatched key or motion. It should * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly. * </p><p> * The input filter must take into account the fact that the input events coming from different * devices or even different sources all consist of distinct streams of input. * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify * the source of the event and its semantics. There may be multiple sources of keys, * touches and other input: they must be kept separate. * </p> * <h3>Policy flags</h3> * <p> * Input events received from the dispatcher and sent to the dispatcher have policy flags * associated with them. Policy flags control some functions of the dispatcher. * </p><p> * The early policy interception decides whether an input event should be delivered * to applications or dropped. The policy indicates its decision by setting the * {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} policy flag. The input filter may * sometimes receive events that do not have this flag set. It should take note of * the fact that the policy intends to drop the event, clean up its state, and * then send appropriate cancellation events to the dispatcher if needed. * </p><p> * For example, suppose the input filter is processing a gesture and one of the touch events * it receives does not have the {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} flag set. * The input filter should clear its internal state about the gesture and then send key or * motion events to the dispatcher to cancel any keys or pointers that are down. * </p><p> * Corollary: Events that get sent to the dispatcher should usually include the * {@link WindowManagerPolicyConstants#FLAG_PASS_TO_USER} flag. Otherwise, they will be dropped! * </p><p> * It may be prudent to disable automatic key repeating for synthetic key events * by setting the {@link WindowManagerPolicyConstants#FLAG_DISABLE_KEY_REPEAT} policy flag. * </p> * * @hide */
public abstract class InputFilter extends IInputFilter.Stub { private static final int MSG_INSTALL = 1; private static final int MSG_UNINSTALL = 2; private static final int MSG_INPUT_EVENT = 3; // Consistency verifiers for debugging purposes. private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier = InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, "InputFilter#InboundInputEventConsistencyVerifier") : null; private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier = InputEventConsistencyVerifier.isInstrumentationEnabled() ? new InputEventConsistencyVerifier(this, InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT, "InputFilter#OutboundInputEventConsistencyVerifier") : null; private final H mH; private IInputFilterHost mHost;
Creates the input filter.
Params:
  • looper – The looper to run callbacks on.
/** * Creates the input filter. * * @param looper The looper to run callbacks on. */
public InputFilter(Looper looper) { mH = new H(looper); }
Called when the input filter is installed. This method is guaranteed to be non-reentrant.
Params:
  • host – The input filter host environment.
/** * Called when the input filter is installed. * This method is guaranteed to be non-reentrant. * * @param host The input filter host environment. */
public final void install(IInputFilterHost host) { mH.obtainMessage(MSG_INSTALL, host).sendToTarget(); }
Called when the input filter is uninstalled. This method is guaranteed to be non-reentrant.
/** * Called when the input filter is uninstalled. * This method is guaranteed to be non-reentrant. */
public final void uninstall() { mH.obtainMessage(MSG_UNINSTALL).sendToTarget(); }
Called to enqueue the input event for filtering. The event will be recycled after the input filter processes it. This method is guaranteed to be non-reentrant.
Params:
  • event – The input event to enqueue.
/** * Called to enqueue the input event for filtering. * The event will be recycled after the input filter processes it. * This method is guaranteed to be non-reentrant. * * @param event The input event to enqueue. */
final public void filterInputEvent(InputEvent event, int policyFlags) { mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget(); }
Sends an input event to the dispatcher.
Params:
  • event – The input event to publish.
  • policyFlags – The input event policy flags.
/** * Sends an input event to the dispatcher. * * @param event The input event to publish. * @param policyFlags The input event policy flags. */
public void sendInputEvent(InputEvent event, int policyFlags) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } if (mHost == null) { throw new IllegalStateException("Cannot send input event because the input filter " + "is not installed."); } if (mOutboundInputEventConsistencyVerifier != null) { mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0); } try { mHost.sendInputEvent(event, policyFlags); } catch (RemoteException re) { /* ignore */ } }
Called when an input event has been received from the dispatcher.

The default implementation sends the input event back to the dispatcher, unchanged.

The event will be recycled when this method returns. If you want to keep it around, make a copy!

Params:
  • event – The input event that was received.
  • policyFlags – The input event policy flags.
/** * Called when an input event has been received from the dispatcher. * <p> * The default implementation sends the input event back to the dispatcher, unchanged. * </p><p> * The event will be recycled when this method returns. If you want to keep it around, * make a copy! * </p> * * @param event The input event that was received. * @param policyFlags The input event policy flags. */
public void onInputEvent(InputEvent event, int policyFlags) { sendInputEvent(event, policyFlags); }
Called when the filter is installed into the dispatch pipeline.

This method is called before the input filter receives any input events. The input filter should take this opportunity to prepare itself.

/** * Called when the filter is installed into the dispatch pipeline. * <p> * This method is called before the input filter receives any input events. * The input filter should take this opportunity to prepare itself. * </p> */
public void onInstalled() { }
Called when the filter is uninstalled from the dispatch pipeline.

This method is called after the input filter receives its last input event. The input filter should take this opportunity to clean up.

/** * Called when the filter is uninstalled from the dispatch pipeline. * <p> * This method is called after the input filter receives its last input event. * The input filter should take this opportunity to clean up. * </p> */
public void onUninstalled() { } private final class H extends Handler { public H(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INSTALL: mHost = (IInputFilterHost) msg.obj; if (mInboundInputEventConsistencyVerifier != null) { mInboundInputEventConsistencyVerifier.reset(); } if (mOutboundInputEventConsistencyVerifier != null) { mOutboundInputEventConsistencyVerifier.reset(); } onInstalled(); break; case MSG_UNINSTALL: try { onUninstalled(); } finally { mHost = null; } break; case MSG_INPUT_EVENT: { final InputEvent event = (InputEvent)msg.obj; try { if (mInboundInputEventConsistencyVerifier != null) { mInboundInputEventConsistencyVerifier.onInputEvent(event, 0); } onInputEvent(event, msg.arg1); } finally { event.recycle(); } break; } } } } }