| /* |
| * 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 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 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 get 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; |
| } |
| } |
| } |
| } |
| } |