Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 源碼系列之(十一)從源碼的角度深入理解AccessibilityService,打造自己的APP小外掛(下)

Android 源碼系列之(十一)從源碼的角度深入理解AccessibilityService,打造自己的APP小外掛(下)

編輯:關於Android編程

在上篇文章Android 源碼系列之<十>從源碼的角度深入理解AccessibilityService,打造自己的APP小外掛(上)中我們講解了通過AccessibilityService實現自動安裝APK小外掛的操作流程,如果你還沒有看過上篇文章請點擊這裡。在這篇文章中我將帶領小伙伴從源碼的角度來深入學習一下AccessibilityServie的技術實現原理,希望這篇文章能給小伙伴們一點幫助,如果你對這塊很熟悉了,恭喜你可以過本文了(*^__^*) ……

在上篇文章中我們提到AccessibilityService是Service的子類,它的生命周期由系統來維護和管理的,該類的官方注解如下所示:

/**
 * Accessibility services are intended to assist users with disabilities in using
 * Android devices and apps. They run in the background and receive callbacks by the system
 * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
 * in the user interface, for example, the focus has changed, a button has been clicked,
 * etc. Such a service can optionally request the capability for querying the content
 * of the active window. Development of an accessibility service requires extending this
 * class and implementing its abstract methods.
 * 
 *
 * 

Lifecycle生命周期

*

* The lifecycle of an accessibility service is managed exclusively by the system and * follows the established service life cycle. Starting an accessibility service is triggered * exclusively by the user explicitly turning the service on in device settings. After the system * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can * be overriden by clients that want to perform post binding setup. * AccessibilityService的生命周期是由系統專門管理的,它遵循service的生命周期流程, * 開啟AccessibilityService需要通過用戶在Android設備上的設置頁面中明確的打開它, * 在系統綁定了該服務後會調用該服務onServiceConnected()方法,如果想要執行綁定流程該方法可以被重寫。 *

*

* An accessibility service stops either when the user turns it off in device settings or when * it calls {@link AccessibilityService#disableSelf()}. * 只有當用戶在Android設備上的設置頁面中關閉了該服務後或者是調用了AccessibilityService的disableSelf()方法AccessibilityService才會關閉。 *

* * ...省略部分說明... */  public abstract class AccessibilityService extends Service { ......  } 通過官方注解我們知道AccessibilityService的生命周期是由系統來管理的,開啟該服務是在Android設備上的設置頁面中來開啟,關閉該服務也是通過在設置頁面來關閉的或者是調用AccessibilityService的disableSelf()方法。因為AccessibilityService是Service的子類,所以AccessibilityService擁有Service的所有可訪問方法,在整體閱讀浏覽完AccessibilityService的源碼後發現AccessibilityService只重寫了onBind()方法,代碼如下:
@Override
public final IBinder onBind(Intent intent) {
    return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {/** 省略 */});
}
onBind()方法返回的是一個IAccessibilityServiceClientWrapper(以下簡稱IASCW)實例對象,該實例對象初始化的時候需要傳遞三個參數:Context,Looper和Callbacks,那為什麼要這三個參數呢,這三個參數的作用分別是什麼?IASCW又是何方聖神呢?目前我們只能猜測IASCW肯定是實現了或者是間接實現了IBinder接口,接著往下看該類的源碼,如下所示:
/**
 * Implements the internal {@link IAccessibilityServiceClient} interface to convert
 * incoming calls to it back to calls on an {@link AccessibilityService}.
 *
 * @hide
 */
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
        implements HandlerCaller.Callback {

    // ...... 省略 ......

    private final HandlerCaller mCaller;
    private final Callbacks mCallback;


    public IAccessibilityServiceClientWrapper(Context context, Looper looper, Callbacks callback) {
        mCallback = callback;
        mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
    }

    // ...... 省略 ......

    @Override
    public void executeMessage(Message message) {
        // ...... 省略 ......
    }
}
根據源碼我們發現IASCW繼承了IAccessibilityServiceClient.Stub並且實現了HandlerCaller.Callback接口,看到IAccessibilityServiceClient.Stub後小伙伴們如果對進程間的通信比較熟悉就應該明白AccessibilityService是采用了進程間通信的機制,而IAccessibilityServiceClient定義的AIDL源碼如下所示:
oneway interface IAccessibilityServiceClient {
    void init(in IAccessibilityServiceConnection connection, int connectionId, IBinder windowToken);
    void onAccessibilityEvent(in AccessibilityEvent event);
    void onInterrupt();
    void onGesture(int gesture);
    void clearAccessibilityCache();
    void onKeyEvent(in KeyEvent event, int sequence);
    void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
    void onSoftKeyboardShowModeChanged(int showMode);
    void onPerformGestureResult(int sequence, boolean completedSuccessfully);
}
IAccessibilityServiceClient.aidl文件保存後編譯器會自動的在對應包目錄下生成相應的Java類,並且在該類中自動生成一個內部靜態抽象類Stub,而Stub類繼承android.os.Binder(Binder實現了IBinder接口)並且實現了IAccessibilityServiceClient接口,在Stub內部又生成了一個內部靜態類Proxy,該類也實現了IAccessibilityServiceClient接口,所以最終aidl執行結果是Stub中的Proxy代理執行的結果。在IASCW的構造方法中初始化了一個HandlerCaller對象,那HandlerCaller是干嘛的呢?我們進入源碼看一下:
public class HandlerCaller {
    final Looper mMainLooper;
    final Handler mH;
    final Callback mCallback;

    class MyHandler extends Handler {
        // ...... 省略 ......

        @Override
        public void handleMessage(Message msg) {
            mCallback.executeMessage(msg);
        }
    }

    public interface Callback {
        public void executeMessage(Message msg);
    }

    public HandlerCaller(Context context, Looper looper, Callback callback, boolean asyncHandler) {
        mMainLooper = looper != null ? looper : context.getMainLooper();
        mH = new MyHandler(mMainLooper, asyncHandler);
        mCallback = callback;
    }
    
    // 以下省略了定義的Handler的同名方法,在這些方法中輾轉調用的是Handler的對應方法
    
}
通過閱讀HandlerCaller的源碼我們可以總結出HandlerCaller的主要作用就是通過持有Context所在線程的Handler實例對象mH,然後不斷的往Context所在線程的消息隊列發送消息,然後在mH的回調方法handleMessage()中調用mCallback的executeMessage()方法,而mCallback實例對象正是IASCW,所以最終調用的是IASCW的executeMessage()方法。總結起來就是讓IASCW的executeMessage()方法在Context所在的線程中執行(其實就是在主線程中執行)。executeMessage()方法代碼如下:
@Override
public void executeMessage(Message message) {
    switch (message.what) {
        
        case DO_ON_INTERRUPT: {
            mCallback.onInterrupt();
        } return;
        
        // ...... 省略 ......
    }
}
executeMessage()方法通過消息類型來調用相應方法,例如當消息類型為DO_ON_INTERRUPT時調用mCallback的onInterrupt()方法,在mCallback的onInterrupt()方法中調用的是AccessibilityService的onInterrupt()方法,源碼如下:
@Override
public final IBinder onBind(Intent intent) {
    return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {

        @Override
        public void onInterrupt() {
            AccessibilityService.this.onInterrupt();
        }

        // ...... 省略 ......
    });
}

到這裡我們大概已經清晰了系統把事件消息反饋給AccessibilityService的大致流程了,主要流程就是系統通過進程間通信機制把事件消息傳遞給AccessibilityService後調用AccessibilityService的onAccessibilityEvent()方法,在該方法中我們就可以根據事件類型做不同的業務操作了。

清楚了AccessibilityService的響應流程,這時候可能會有小伙伴問那Android系統是怎麼知道何時給AccessibilityService發送事件消息呢?難道Android系統是一直在監視各個應用嗎?帶著這個疑問我們繼續往下看源碼,答案都在源碼中。

小伙伴們應該對Android系統中的View體系結構以及事件傳遞都比較熟悉吧,不熟悉的話請自行查閱(我已經在准備這塊知識點了,不久就可以發布),View是Android UI中的最小顯示單元,看過View源碼的童靴應該記得View實現了三個接口,其中一個接口是AccessibilityEventSource,該結構定義如下;

public interface AccessibilityEventSource {

    /**
     * 發送輔助事件,對參數進行校驗
     */
    public void sendAccessibilityEvent(int eventType);

    /**
     * 發送輔助事件,不對參數進行校驗
     */
    public void sendAccessibilityEventUnchecked(AccessibilityEvent event);
}
AccessibilityEventSource接口表示的是給系統發送相關輔助事件,View既然實現了此接口那也就是說View及其子類都具有向系統發送輔助事件的功能,我們先記住這個結論,接著往下看代碼。
在View系統中當我們點擊了一個按鈕,如果給該按鈕設置了OnClickListener事件監聽器,那麼當點擊了按鈕後就會回調該監聽器的onClick()方法,那onClick()函數是什麼時候調用的呢?我們從源碼找答案,源碼如下:
public boolean onTouchEvent(MotionEvent event) {
    // ...... 省略 ......

    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    // ...... 省略 ......
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        if (!focusTaken) {
                            // 重點在這裡,最終回調了performClick()方法
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }
                }
            }
            // ...... 省略 ......
        }
        return true;
    }
    return false;
}
通過查找源碼我們發現在View的onTouchEvent()的ACTION_UP分支中,經過層層判斷最後調用了performClick()方法,根據該方法名可以猜測是執行click方法的,我們進入performClick()方法看一看,源碼如下:
public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }
    // 注意看這裡,在手指離開屏幕後發送了一個AccessibilityEvent事件
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}
果然不出我們所料,在performClick()方法中如果li.mOnClickListener不為null就會調用onClick()方法,這時候細心的童靴應該會發現在performClick()方法最後調用了sendAccessibilityEvent()方法,該方法就是View實現AccessibilityEventSource接口中的方法,那也就是說View被點擊後就會向系統發送一個事件類型為AccessibilityEvent.TYPE_VIEW_CLICKED的輔助事件,由此可猜測當點擊事件發生後會向系統發送一個輔助事件消息,在系統接收到此消息就會回調注冊進系統的各種AccessibilityService服務的onAccessibilityEvent()方法。伴隨著這種猜測我們繼續跟蹤sendAccessibilityEvent()方法,發現該方法最終調用的是sendAccessibilityEventUncheckedInternal()方法,該方法源碼如下:
public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
    if (!isShown()) {
        // 若當前View不可見就直接返回
        return;
    }
    // 初始化event事件,比如設置source,classname,packagename,enabled,contentDescription等等
    onInitializeAccessibilityEvent(event);
    // Only a subset of accessibility events populates text content.
    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
        dispatchPopulateAccessibilityEvent(event);
    }
    // In the beginning we called #isShown(), so we know that getParent() is not null.
    // 在這裡需要注意getParent()方法為遞歸調用
    getParent().requestSendAccessibilityEvent(this, event);
}
sendAccessibilityEventUncheckedInternal()方法中經過對event進行一系列的處理後調用了getParent()的requestSendAccessibilityEvent()方法,在View體系中getParent()方法為遞歸調用,通常返回的是當前View的ViewGroup,我剛開始也以為最終調用的是View根節點的requestSendAccessibilityEvent()方法,在這篇文章中講過當前窗口的根視圖為DecorView,然後進入DecorView中並沒有發現該方法,又跟進ViewGroup以及View中都沒有發現該方法,所以可以肯定getParent()的最終返回值並不是View,經過查閱資料得到結論getParent()最終返回的是ViewRootImpl實例對象,也就是最終調用了ViewRootImpl的requestSendAccessibilityEvent()方法,該方法如下所示:
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
    if (mView == null || mStopped || mPausedForTransition) {
        return false;
    }

    // ...... 省略部分代碼 ......
    
    mAccessibilityManager.sendAccessibilityEvent(event);
    return true;
}
在ViewRootImpl的requestSendAccessibilityEvent()方法中又調用了AccessibilityManager的sendAccessibilityEvent()方法,也就是說事件最終是通過AccessibilityManager來發送的,我們接著進入該方法中看看,源碼如下:
public void sendAccessibilityEvent(AccessibilityEvent event) {
    final IAccessibilityManager service;
    final int userId;
    synchronized (mLock) {
        service = getServiceLocked();
        if (service == null) {
            // service為null,直接返回
            return;
        }
        if (!mIsEnabled) {
            throw new IllegalStateException("Accessibility off. Did you forget to check that?");
        }
        userId = mUserId;
    }
    boolean doRecycle = false;
    try {
        event.setEventTime(SystemClock.uptimeMillis());
        long identityToken = Binder.clearCallingIdentity();

        // 調用IAccessibilityManager的sendAccessibilityEvent()方法發送輔助事件
        doRecycle = service.sendAccessibilityEvent(event, userId);
        Binder.restoreCallingIdentity(identityToken);
        if (DEBUG) {
            Log.i(LOG_TAG, event + " sent");
        }
    } catch (RemoteException re) {
        Log.e(LOG_TAG, "Error during sending " + event + " ", re);
    } finally {
        if (doRecycle) {
            event.recycle();
        }
    }
}
在AccessibilityManager的sendAccessibilityEvent()方法中又輾轉調用了IAccessibilityManager實例對象的的sendAccessibilityEvent()方法把事件發送出去,而IAccessibilityManger是誰了?由以上源碼可知IAccessibilityManager是通過getServiceLocked()方法獲取的,其源碼如下:
private  IAccessibilityManager getServiceLocked() {
    if (mService == null) {
        // 如果mService為null,則嘗試著重新初始化mService對象
        tryConnectToServiceLocked();
    }
    return mService;
}

private void tryConnectToServiceLocked() {
    // 通過ServiceManager獲取對應的IBinder對象
    IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
    if (iBinder == null) {
        // 如果為null就直接返回,此時不再發送
        return;
    }
    // 這一步可知根據iBinder轉化一個IAccessibilityManager服務實例出來
    IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
    try {
        final int stateFlags = service.addClient(mClient, mUserId);
        setStateLocked(stateFlags);
        mService = service;
    } catch (RemoteException re) {
        Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
    }
}

在getServiceLocked()方法中對mService做了校驗,如果為null就調用tryConnectToServiceLocked()方法,在tryConnectToServiceLocked()方法中先調用ServiceManager.getService()方法獲取一個IBinder對象,需要注意ServiceManger是Android系統中的重量級守護進程,它是在init進程啟動之後就啟動的,它用來管理Android系統比較常見的各種系統服務(比如:InputMethodService,ActivityManagerService等)並向Client提供查詢相關Service的功能,這篇博文有對ServiceManager做了詳細解說。獲取了service後就通過該service把輔助事件消息傳遞給了系統,然後系統接收到了輔助事件消息後會根據事件的包名來查找相關的AccessibilityService,如果查找到了就會調用AccessibilityService的onAccessibilityEvent()方法並把該輔助事件傳遞進來,總結起來可用下圖說明:

\

好了,根據以上流程圖我們基本上是理清了AccessibilityService的操作流程,主要就是目標APP進程發送消息給系統進程,在系統進程接收到消息後再把消息發送給相關的目標AccessibilityService,主要流程就是這些,如果有小伙伴想深入的了解系統進程是如何把消息傳遞給目標服務的,由於這裡邊需要分析的代碼太多這裡就不再往下寫了,請小伙伴們私下自行查找相關資料。

到這裡有關使用AccessibilityService打造自己的APP小外掛算是告一段落了,熟悉了原理之後我們就可以隨心所欲的制作自己喜歡的小外掛了,需要注意的是制作小外掛要合情合理,不做對外有不利的事情,最後感謝收看(*^__^*) 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved