content: /*
   * 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.annotation.UnsupportedAppUsage;
  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.
   * <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.
       *
       * @param looper The looper to run callbacks on.
       */
      @UnsupportedAppUsage
      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.
       */
      @UnsupportedAppUsage
      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;
                  }
              }
          }
      }
  }
  