UiAutomator源码学习(2)-- UiAutomationBridge
阅读原文时间:2023年07月13日阅读:2

从上一章对UiDevice的学习,可以看出几乎所有的操作都离不开 UiAutomationBridge。重新看一下UIDevice的构造方法:

private UiDevice(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
UiAutomation uiAutomation = instrumentation.getUiAutomation();
mUiAutomationBridge = new InstrumentationUiAutomatorBridge(
instrumentation.getContext(), uiAutomation);
// Enable multi-window support for API level 21 and up
if (UiDevice.API_LEVEL_ACTUAL >= Build.VERSION_CODES.LOLLIPOP) {
// Subscribe to window information
AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
uiAutomation.setServiceInfo(info);
}
}

UiAutomationBridge 是一个抽象类。我们先看UiDevice的构造函数中,UiAutomatorBridge的实现类InstrumentationUiAutomatorBridge。这个类比较简单复写了getRotation和isScreenOn方法。接下来我们看一下这个抽象类的构造方法:

UiAutomatorBridge(UiAutomation uiAutomation) {  
    mUiAutomation = uiAutomation;  
    mInteractionController = new InteractionController(this);  
    mQueryController = new QueryController(this);  
}

在这里初始化了 InteractionController和 QueryController这两个类的对象。在学习UiDevice的时候应该还记得,几乎所有的操作都是通过这两个类来完成的。这里是UiDevice里的pressHome方法:

/\*\*  
 \* Simulates a short press on the HOME button.  
 \* @return true if successful, else return false  
 \* @since API Level 16  
 \*/  
public boolean pressHome() {  
    Tracer.trace();  
    waitForIdle();  
    return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(  
            KeyEvent.KEYCODE\_HOME, 0, AccessibilityEvent.TYPE\_WINDOW\_CONTENT\_CHANGED,  
            KEY\_PRESS\_EVENT\_TIMEOUT);  
}

通过这个方法可以看到,这个InteractionController可以向系统注入事件。那接下来就来看看这个InteractionController到底是怎么向系统注入事件的。还是从构造方法看起:

public InteractionController(UiAutomatorBridge bridge) {
mUiAutomatorBridge = bridge;
}

这个InteractionController持有UiAutomatorBridge的引用。并且在这个类中定义了很多模拟用户的操作方法如,sendKeyAndWaitForEvent, touchDown,touchUp,swipe等,例如uiDevcie里用到的sendKeyAndWaitForEvent。

 /\*\*  
  \* Send keys and blocks until the first specified accessibility event.  
  \*  
  \* Most key presses will cause some UI change to occur. If the device is busy, this will  
  \* block until the device begins to process the key press at which point the call returns  
  \* and normal wait for idle processing may begin. If no events are detected for the  
  \* timeout period specified, the call will return anyway with false.  
  \*  
  \* @param keyCode  
  \* @param metaState  
  \* @param eventType  
  \* @param timeout  
  \* @return true if events is received, otherwise false.  
  \*/  
 public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,  
         final int eventType, long timeout) {  
     Runnable command = new Runnable() {  
         @Override  
         public void run() {  
             final long eventTime = SystemClock.uptimeMillis();  
             KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION\_DOWN,  
                     keyCode, 0, metaState, KeyCharacterMap.VIRTUAL\_KEYBOARD, 0, 0,  
                     InputDevice.SOURCE\_KEYBOARD);  
             if (injectEventSync(downEvent)) {  
                 KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION\_UP,  
                         keyCode, 0, metaState, KeyCharacterMap.VIRTUAL\_KEYBOARD, 0, 0,  
                         InputDevice.SOURCE\_KEYBOARD);  
                 injectEventSync(upEvent);  
             }  
         }  
     };  
     return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)  
             != null;  
 }

Line17,定义一个Runnable对象,Runnable只是一个接口,它里面只有一个run()方法,没有start()方法,所以该对象无法启动线程,必须依托其他类来启动这个线程。

在这个run方法中,定义了一个KeyEvent事件,KeyEnvet对象是android.view.*包下的类,用于报告键和按钮事件。每次按键是通过一系列按键事件来描述的。按键操作以ACTION_DOWN按键事件开始。如果密钥被保持足够长的时间以至于可以重复,则在初始按下后会出现其他具有ACTION_DOWN和getRepeatCount()非零值的密钥事件。最后一个按键事件是用于按键启动的ACTION_UP。如果取消按键,则按键事件将设置FLAG_CANCELED标志。

这个run方法里还有一个if判断条件injectEventSync,通过这个方法名就可以看出这是用来判断同步注入事件是否成功,在injectEventSync方法中,它调用了mUiAutomatorBridge.injectInputEvent(event, true);而mUiAutomatorBridge这个类的injectInputEvent方法里,是调用的mUiAutomation.injectInputEvent(event, sync);而mUiAutomation是Android SDK中 android.app.UiAutomation这个类的对象,我们回过头来看各个函数的构造函数发现,这个UiAutomation来自于UiDevice:

UiAutomation uiAutomation = instrumentation.getUiAutomation();

来看一下这个类中定义的injectInputEvent事件:

/**
* A method for injecting an arbitrary input event.
*

* Note: It is caller's responsibility to recycle the event. *


* @param event The event to inject.
* @param sync Whether to inject the event synchronously.
* @return Whether event injection succeeded.
*/
public boolean injectInputEvent(InputEvent event, boolean sync) {
synchronized (mLock) {
throwIfNotConnectedLocked();
}
try {
if (DEBUG) {
Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
}
// Calling out without a lock held.
return mUiAutomationConnection.injectInputEvent(event, sync);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while injecting input event!", re);
}
return false;
}

看来这里也不是真正做事件注入的地方,mUiAutomationConnection是一个接口对象,这个对象是在UiAutomaton构造函数里初始化的。看他的实现类UiAutomationConnection中的injectInputEvent方法。

@Override
public 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 mWindowManager.injectInputAfterTransactionsApplied(event, mode);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
}
return false;
}

private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Service.WINDOW_SERVICE));

package android.os;
public final class ServiceManager {
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
}

从这里可以看出mWindowManager是一个IBinder对象,通过这个对象调用openSession打开一个Session,实现IPC通信。看一下WindowManagerService里的

injectInputAfterTransactionsApplied方法:

@Override
public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
boolean isDown;
boolean isUp;

     if (ev instanceof KeyEvent) {  
         KeyEvent keyEvent = (KeyEvent) ev;  
         isDown = keyEvent.getAction() == KeyEvent.ACTION\_DOWN;  
         isUp = keyEvent.getAction() == KeyEvent.ACTION\_UP;  
     } else {  
         MotionEvent motionEvent = (MotionEvent) ev;  
         isDown = motionEvent.getAction() == MotionEvent.ACTION\_DOWN;  
         isUp = motionEvent.getAction() == MotionEvent.ACTION\_UP;  
     }  
     final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE\_MOUSE;

     // For ACTION\_DOWN, syncInputTransactions before injecting input.  
     // For all mouse events, also sync before injecting.  
     // For ACTION\_UP, sync after injecting.  
     if (isDown || isMouseEvent) {  
         syncInputTransactions();  
     }  
     final boolean result =  
             LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode);  
     if (isUp) {  
         syncInputTransactions();  
     }  
     return result;  
 }

syncInputTransactions()这个方法是同步系统注入事件的事物,对于action up事件是在注入之后同步,其他的事件是在事件注入之前同步。 我们主要看一下事件注入.

LocalServices 的getService方法,返回一个实现了InputManagerInternal类型的Service, InputManagerInternal是一个抽象类,而injectInputEvent也是一个抽象方法。

那接下来我们就看一下这个InputManger类型的service。这是一个系统的服务 SystemService。

/**
* Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed.
*

* Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into * windows that are owned by other applications. *

* Make sure you correctly set the event time and input source of the event * before calling this method. *


*
* @param event The event to inject.
* @param mode The synchronization mode. One of:
* {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
* {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
* {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
* @return True if input event injection succeeded.
*
* @hide
*/
@UnsupportedAppUsage
public boolean injectInputEvent(InputEvent event, int mode) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
&& mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
throw new IllegalArgumentException("mode is invalid");
}
try {
return mIm.injectInputEvent(event, mode);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

Line33,调用的是IInputManager.aidl里的injectInputEvent,通过进程之间的通信,实现了系统的事件注入。到此事件注入的流程分析完毕,先到此为止。再想深入研究就是Native层的逻辑了。