android uiautomator 点击分析

来源:互联网 发布:淘宝上的东西是正品吗 编辑:程序博客网 时间:2024/06/06 06:44

之前调研过自动化测试框架,最近有一点时间,查查里面的实现原理。

当时是选了uiautomator.这个只是对UIAutomator 的简单封装,以d.click(x,y)为例,分析下uiautomator做了什么。

python封装部分相对简单,就是封装了一个app,然后jsonrpc调用,实际上走的是

boolean click(int x, int y)
Perform a click at arbitrary coordinates specified by the user

https://developer.android.com/reference/android/support/test/uiautomator/UiDevice.html?hl=zh-cn#click(int, int)

一堆封装之后,源码里面是注入到InputServer

./frameworks/base/core/java/android/app/UiAutomationConnection.java

@Overridepublic boolean injectInputEvent(InputEvent event, boolean sync) {    synchronized (mLock) {        throwIfCalledByNotTrustedUidLocked();        throwIfShutdownLocked();        throwIfNotConnectedLocked();    }    final int mode = (sync) ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH            : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;    final long identity = Binder.clearCallingIdentity();    try {        return InputManager.getInstance().injectInputEvent(event, mode);    } finally {        Binder.restoreCallingIdentity(identity);    }}

这个和输入法输入文字部分的东西好像。举个栗子:

adb input text “hi yeshen”

./frameworks/base/cmds/input/src/com/android/commands/input/Input.java

/** * Convert the characters of string text into key event's and send to * device. * * @param text is a string of characters you want to input to the device. */private void sendText(int source, String text) {    StringBuffer buff = new StringBuffer(text);    boolean escapeFlag = false;    for (int i=0; i<buff.length(); i++) {        if (escapeFlag) {            escapeFlag = false;            if (buff.charAt(i) == 's') {                buff.setCharAt(i, ' ');                buff.deleteCharAt(--i);            }        }        if (buff.charAt(i) == '%') {            escapeFlag = true;        }    }    char[] chars = buff.toString().toCharArray();    KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);    KeyEvent[] events = kcm.getEvents(chars);    for(int i = 0; i < events.length; i++) {        KeyEvent e = events[i];        if (source != e.getSource()) {            e.setSource(source);        }        injectKeyEvent(e);    }}
private void injectKeyEvent(KeyEvent event) {    Log.i(TAG, "injectKeyEvent: " + event);    InputManager.getInstance().injectInputEvent(event,            InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);}

好了,现在大部分自动化测试框架都会依赖Android UIAutomator,所以都是走的 InputManager.getInstance().injectInputEvent

注释写着

Injects an input event into the event system on behalf of an application.

binder 调用到

./frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

然后进入native层

./frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId,int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,uint32_t policyFlags) {    ...    bool needWake = false;    for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {        EventEntry* nextEntry = entry->next;        needWake |= enqueueInboundEventLocked(entry);        entry = nextEntry;    }    ...}

入队之后找到最顶层的window进行事件分发

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

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {    ...    case EventEntry::TYPE_MOTION: {        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);        if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN                && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)                && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY                && mInputTargetWaitApplicationHandle != NULL) {            int32_t displayId = motionEntry->displayId;            int32_t x = int32_t(motionEntry->pointerCoords[0].                    getAxisValue(AMOTION_EVENT_AXIS_X));            int32_t y = int32_t(motionEntry->pointerCoords[0].                    getAxisValue(AMOTION_EVENT_AXIS_Y));            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);            if (touchedWindowHandle != NULL                    && touchedWindowHandle->inputApplicationHandle                            != mInputTargetWaitApplicationHandle) {                // User touched a different application than the one we are waiting on.                // Flag the event, and start pruning the input queue.                mNextUnblockedEvent = motionEntry;                needWake = true;            }        }        break;    }    ...}

整个流程下来,就是作为一个第三方应用,走完了全部的鉴权等操作之后,在InputManagerServer统一处理,入队后分发点击事件给最顶层的窗口。

1,入队后,caller的信息没传过去,所以被自动测试的应用是没有办法直接感知它是被系统的其他应用控制,还是由硬件直接输入控制的。
2,因为是注入到InputManagerServer中,所以如果有多个自动测试应用对内控制都没有影响。最多就是InputManagerServer中会携带多个自动测试应用的信息。

但是被控制还是有许多特征的,举个栗子

ps | grep uiautomator
pm list instrumentation
pm list packages | grep atx

原创粉丝点击