Android Input指的是输入事情,首要是接触滑动,当然还包括相似蓝牙外设的输入。Input涉及到的首要模块

  1. EventHub :对输入事情进行映射
  2. InputReader : 收集input事情
  3. InputDispatcher : 将事情分发到上层
  4. InputManager : framework中对input事情的接纳和分发
  5. WMS : 管理窗口,收集和分发input事情

本篇首要以framework的视角来debug input问题,介绍input的资料现已很多了,所以不讲input传递流程和机制,只看怎么去解决问题。

从framework的视角,首要咱们要排查input driver的问题,比如从屏幕接触输入的,那便是显示屏的input驱动;假如是蓝牙外设输入的,那就需求找BT的驱动层。

adb shell getEvent

然后再输入,看键值是否正常,假如getEvent都没有收到,就不属于framework的领域了。

确认驱动没有问题之后,就能够经过动态或静态开启debug log。不同厂商的开关log的指令有些差异,打印log的内容也不太一样。

这儿咱们直接以本地debug为例,参考Android T版别的common code自己增加要害log,然后开端复现问题,检查问题时刻点的log。趁便弥补一下,能够经过如下指令使时刻显示到秒,这样便利复现问题时对应log时刻

adb shell settings put secure clock_seconds 1

Step1.检查ViewRootImpl是否有收到input event

/frameworks/base/core/java/android/view/ViewRootImpl.java

8741      @UnsupportedAppUsage
8742      void enqueueInputEvent(InputEvent event,
8743              InputEventReceiver receiver, int flags, boolean processImmediately) {
8744          QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
8745  
8746          if (event instanceof MotionEvent) {
8747              MotionEvent me = (MotionEvent) event;
8748              if (me.getAction() == MotionEvent.ACTION_CANCEL) {
8749                  EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Motion - Cancel",
8750                          getTitle().toString());
8751              }
8752          } else if (event instanceof KeyEvent) {
8753              KeyEvent ke = (KeyEvent) event;
8754              if (ke.isCanceled()) {
8755                  EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Key - Cancel",
8756                          getTitle().toString());
8757              }
8758          }
8759          // Always enqueue the input event in order, regardless of its time stamp.
8760          // We do this because the application or the IME may inject key events
8761          // in response to touch events and we want to ensure that the injected keys
8762          // are processed in the order they were received and we cannot trust that
8763          // the time stamp of injected events are monotonic.
8764          QueuedInputEvent last = mPendingInputEventTail;
8765          if (last == null) {
8766              mPendingInputEventHead = q;
8767              mPendingInputEventTail = q;
8768          } else {
8769              last.mNext = q;
8770              mPendingInputEventTail = q;
8771          }
8772          mPendingInputEventCount += 1;
8773          Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
8774                  mPendingInputEventCount);
              //增加log打印要害信息
              Log.i(">_<!!","enqueueInputEvent: event = " + event + " ,this = " + this);
8776          if (processImmediately) {
8777              doProcessInputEvents();
8778          } else {
8779              scheduleProcessInputEvents();
8780          }
8781      }

这儿只需求根据增加的log检查两个参数即可,event会打印出来 KeyEvent的action和keyCode,咱们需求看下这儿的action和keyCode是否有紊乱的状况,假如输入和get到的不对应,那仍是需求driver来协调。后边打印出来的this便是此ViewRootImpl目标,具体内容能够看它的toString办法。咱们只需求在终究的log中调查这句是否打印出来,假如打印出来了,阐明input事情现已成功发送到使用端了,越过下面过程,直接检查Step5,假如没打印这段log,再看Step2

Step2. 检查inputDispatcher是否有收到input event

/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

1472  bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
1473                                          DropReason* dropReason, nsecs_t* nextWakeupTime) {
1474      // Preprocessing.
1475      if (!entry->dispatchInProgress) {
              // 这个是AOSP的log机制,不用再另外增加log
1518          logOutboundKeyDetails("dispatchKey - ", *entry);
1519      }
1583  void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
1584      //if (DEBUG_OUTBOUND_EVENT_DETAILS) {
            if (true) {
1585          ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
1586                "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
1587                "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
1588                prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
1589                entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode,
1590                entry.metaState, entry.repeatCount, entry.downTime);
1591      }
1592  }

这儿AOSP的log现已增加的很全面了,咱们只需求手动将打印条件置为true即可。这段log中同样能够对应上action和keyCode,不过c++代码打印出来的是十六进制,可是也和上面java code中打印出来的字符串是一一对应的。假如咱们终究能够查找到这段log,阐明inputDispatcher现已收到input event了,那么直接快进到Step4检查inputDispatcher状况是否正常。假如没有检查到这句log,再看Step3

Step3. 检查inputreader线程里边是否有keycode

/frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

272                                       int32_t usageCode) {
273      int32_t keyCode;
274      int32_t keyMetaState;
275      uint32_t policyFlags;
276
277      if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
278                                    &policyFlags)) {
279          keyCode = AKEYCODE_UNKNOWN;
280          keyMetaState = mMetaState;
281          policyFlags = 0;
282      }
348  
349      if (mParameters.handlesKeyRepeat) {
350          policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
351      }
352  
353      NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
354                         getDisplayId(), policyFlags,
355                         down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
356                         AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
357      getListener().notifyKey(&args);
         ALOGI("device: %s, keyCode=%d, scanCode=%d, eventTime = %lld, action=0x%x,duwnTime=%lld",getDeviceName().c_str(), keyCode, scanCode, args,eventTime, args.action. args.downTime);
358  }

KeyboardInputMapper.cpp 是在Android R之后增加的东西,假如是比较旧的版别,需求在InputReader.cpp中增加log。此处能够确认input event被发送到了inputReader了,这儿的值便是从getEvent读取的,假如getEvent的值是对的,但这儿没有打印log,就需求打印cpp文件的callstack,看看是流程中哪一步出错。

Step4. 检查inputDispatcher的状况是否正常

能够经过adb指令来检查inputDispatcher的状况

adb shell dumpsys input

/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

5217  void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
5218      dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
5219      dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
5220      dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled));
5221      dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId);

DispatcherEnabled 有必要为1,而且DispatcherFrozen 有必要为0,假如是inputDispatcher状况有问题,需求在代码中检查哪些地方有修正inputDispatcher的状况mDispatchEnabled,mDispatchFrozen,找到将修正状况的地方来剖析问题。假如打印出来的FocusedDisplayId或FocusedApplications不契合预期,那便是display or WMS相关问题,与input流程没有关系。

Step5. 检查终究input消费event的是哪个页面

/frameworks/base/core/java/android/view/View.java

14929      public boolean dispatchKeyEvent(KeyEvent event) {
14930          if (mInputEventConsistencyVerifier != null) {
14931              mInputEventConsistencyVerifier.onKeyEvent(event, 0);
14932          }
          Log.i(">_<!!","dispatchKeyEvent event:" + event + " to :" + v);
14934          // Give any attached key listener a first crack at the event.
14935          //noinspection SimplifiableIfStatement
14936          ListenerInfo li = mListenerInfo;
14937          if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
14938                  && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
                   //标明input被消费了
                   Log.i(">_<!!","Event:" + event+ " handle in: " + v 
                       + " ,ListenerInfo = " + li.toString());
14939              return true;
14940          }
14941  
14942          if (event.dispatch(this, mAttachInfo != null
14943                  ? mAttachInfo.mKeyDispatchState : null, this)) {
14944              return true;
14945          }
14946  
14947          if (mInputEventConsistencyVerifier != null) {
14948              mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
14949          }
14950          return false;
14951      }

这儿的log能够标明input event正在按照view的层级顺次dispatch并终究被哪个view消费,假如这个view并不是所希望的view,那么就需求检查为什么消费到这个view上面了,是layout区域有透明鸿沟?仍是希望的view并不存在,可能性就很多,细节能够再深思下。假如这儿的view是契合希望的,那么问题就回到使用层了,看使用层对此input事情的响应是否有反常。