码迷,mamicode.com
首页 > 移动开发 > 详细

Android 源码系列之<十一>从源码的角度深入理解AccessibilityService,打造自己的APP小外挂(下)

时间:2016-11-01 19:41:25      阅读:3394      评论:0      收藏:0      [点我收藏+]

标签:manage   setup   jsb   视图   自己的   java   detail   begin   enabled   

        转载请注明出处:http://blog.csdn.net/llew2011/article/details/52843637

        在上篇文章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.
 * 
 *
 * <h3>Lifecycle生命周期</h3>
 * <p>
 * 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()方法,如果想要执行绑定流程该方法可以被重写。
 * </p>
 * <p>
 * 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才会关闭。
 * </p>
 * 
 * ...省略部分说明...
 */
 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小外挂算是告一段落了,熟悉了原理之后我们就可以随心所欲的制作自己喜欢的小外挂了,需要注意的是制作小外挂要合情合理,不做对外有不利的事情,最后感谢收看(*^__^*) ……






Android 源码系列之<十一>从源码的角度深入理解AccessibilityService,打造自己的APP小外挂(下)

标签:manage   setup   jsb   视图   自己的   java   detail   begin   enabled   

原文地址:http://blog.csdn.net/llew2011/article/details/52843637

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!