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