/* * 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; import android.view.IInputFilter; import android.view.InputEvent; import android.view.InputEventConsistencyVerifier; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManagerPolicy; /** * 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 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 are 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 WindowManagerPolicy#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 WindowManagerPolicy#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 set sent to the dispatcher should usually include the * {@link WindowManagerPolicy#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 WindowManagerPolicy#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. * * @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. * * @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. */ 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. * * @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. * * @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. * <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. * <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. * <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; } } } } }