關於Handler的攔截

爲什麼要Hook Handler?Android系統中存在大量Handler,我們要改變系統的某些行爲就需要Hook掉一些關鍵節點的Handler,爲此,我們要清楚Handler的工作原理。

先做一個實驗來看看Hook Handler的效果,如下:

public class MainActivity extends Activity {

    private Button mBtnShow;
    private Button mBtnHook;

    private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Toast.makeText(MainActivity.this, (CharSequence) msg.obj,
                    Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnShow = (Button) findViewById(R.id.btn);
        mBtnHook = (Button) findViewById(R.id.hook);

        mBtnShow.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                mHandler.obtainMessage(0, "hello world").sendToTarget();
            }
        });

        mBtnHook.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                try {
                    hookHandler();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
    }

    private void hookHandler() throws IllegalAccessException,
            IllegalArgumentException {
        Field field = FieldUtils.getDeclaredField(Handler.class, "mCallback",
                true);

        field.set(mHandler, new Handler.Callback() {

            @Override
            public boolean handleMessage(Message msg) {
                // TODO Auto-generated method stub
                msg.obj = "hello china";
                return false;
            }
        });
    }
}

放兩個按鈕,一個按鈕是發送消息來顯示Toast,另一個按鈕是Hook Handler,改變發送的消息內容。

這裏Hook所做的事情很簡單,其實就是通過反射改變Hander的mCallback,值得注意的是這個Callback的handleMessage中改變了Message後返回了false。這樣做是爲了繼續調用Handler的handleMessage,只是Handler不知道發給他的Message已經被暗中動了手腳。

我們看看Handler的內在實現,Handler其實只是一個工具,用於封裝消息再丟給MessageQueue而已,真正的核心在Looper和MessageQueue。我們重點看一下Looper,裏面有一個loop函數,就是循環處理消息的,如下:

public static void loop() {
    final Looper me = myLooper();

    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next();

        msg.target.dispatchMessage(msg);

        msg.recycle();
    }
}

邏輯很簡單,通過queue.next()依次從MessageQueue中取出Message,然後調用msg.target的dispatchMessage,這個target其實就是Handler,我們來看看Handler是怎麼dispatchMessage的:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

首先優先調用Message中的Callback,如果沒有再來調用Handler的全局Callback,如果沒有mCallback則調用Handler的handleMessage,如果有mCallback但是mCallback.handleMessage返回false,同樣會調用Handler的handleMessage,如果返回true則直接返回了。我們Hook的核心就在這裏了。

好了,順便再看看是怎麼從MessageQueue中取出消息的,如下:

Message next() {
    for (;;) {
        ..........
        nativePollOnce(mPtr, nextPollTimeoutMillis);
        ..........
        if (mQuitting) {
            return null;
        }
    }
}

這是個無限循環的過程,除非退出標誌被置位。而從消息隊列中取出消息可能被阻塞,原因是隊列爲空了。這裏nativePollOnce就是等待隊列中有消息,當有消息時立即返回。我們看看裏面的實現,這個是native的函數:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,
        jint ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, timeoutMillis);
}

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    ..........
    pollInner(timeoutMillis);
}

int Looper::pollInner(int timeoutMillis) {
    ..........

    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    for (int i = 0; i < eventCount; i++) {
        ..........
    }
}

真相大白,原來是用epoll_wait來監測fd是否可讀寫,這個fd通常是管道,那麼是否意味着當往隊列裏添加消息時,需要向這個fd裏寫一些數據呢,這樣epoll_wait就能被喚醒。我們來看看消息是如何添加到隊列裏的:

boolean enqueueMessage(Message msg, long when) {
    ..........
    if (needWake) {
        nativeWake(mPtr);
    }
    return true;
}

這裏給消息添加到隊列後,調用了nativeWake來喚醒隊列:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    return nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
    mLooper->wake();
}

void Looper::wake() {
    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

果然如我們所料,這裏喚醒就是往fd中寫入了一個W字符而已。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章