Android5.1 雙輸入的處理--理解inputChannel

近期在處理雙屏異顯雙觸摸屏的問題, 發現副屏上的事件處理區域只能和主屏一樣大,當副屏小於主屏時不會有問題,但是當副屏大於主屏時問題就比較明顯;跟代碼發現副屏在設置觸摸區域大小時是按照主屏大小設置的,看了很多代碼,還是總結下窗口接收按鍵和自更新的過程,這裏其實就是inputDispatcher和窗口的交互。

inputDispatcher和窗口是通過inputChannel來傳遞信息的,來看看inputChannel的本質:

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        ....
    }
//配置緩衝區 ......
    String8 serverChannelName = name;
    serverChannelName.append(" (server)");//創建server
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
//創建client對象
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

通過openInputChannelPair方法構造兩個inputChannel,而這兩個inputChannel是傳入了兩個socket,而這兩個socket是可以互相通信的。可以說inputChannel就是socket的封裝,而openInputChannelPair會生成inputChannel對來封裝socketpair。有了這個認識,接下來我們就進入流程。

在WMS中,apk端添加窗口時會新建windowstate,更新窗口列表,並同步到ims中,看看具體代碼:

 public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
			//創建windowstate
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
				//創建兩個inputChannel來相互通信
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);//windowstate保存一個
                inputChannels[1].transferTo(outInputChannel);//調用端保存一個,即app進程保存一個
				//將inputChannel和inputwindowhandle保存到ims中
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }
            // 更新窗口列表到ims中
            mInputMonitor.updateInputWindowsLw(false /*force*/);
        }
    }

一:openInputChannelPair

//InputChannel.java 
	     public static InputChannel[] openInputChannelPair(String name) {
        return nativeOpenInputChannelPair(name);
    }

進入jni調用:

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8 name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);
    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
	//生成兩個channel並回傳給server和client
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
	//生成java數組,用來返回給上層java,要注意數組的元素是gInputChannelClassInfo,其實就是InputChannel
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
	//生成元素
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
	//設置數組元素
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

上面的代碼邏輯很清楚,就是在native層先生成一個inputChannelPair即serverchannel和clientchannel,然後用這兩個channel構造NativeInputChannel,在用這個NativeInputChannel構造java層的InputChannel,然後返回給上層。InputChannel::openInputChannelPair的方法在文章開頭已經介紹了,就是生成兩個inputChannel。關於構造java數組的方式還是可以看一下,從這個方式可以看到native層和java層如何交互的:

//返回的靜態變量數組
static struct {
    jclass clazz;
    jfieldID mPtr;   // native object attached to the DVM InputChannel
    jmethodID ctor;
} gInputChannelClassInfo;
//通過如下代碼將native層和java層的元素對應
//gInputChannelClassInfo.clazz,  --->  "android/view/InputChannel
//gInputChannelClassInfo.mPtr  ----->InputChannel.mPtr
//gInputChannelClassInfo.ctor   ----->InputChannel.init(猜測是構造函數)
#define FIND_CLASS(var, className) \
        var = env->FindClass(className); \
        LOG_FATAL_IF(! var, "Unable to find class " className); \
        var = jclass(env->NewGlobalRef(var));

#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
        LOG_FATAL_IF(! var, "Unable to find method " methodName);

#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
        LOG_FATAL_IF(! var, "Unable to find field " fieldName);

int register_android_view_InputChannel(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "android/view/InputChannel",
            gInputChannelMethods, NELEM(gInputChannelMethods));
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");

    FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel");

    GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz,
            "mPtr", "J");
    
    GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz,
            "<init>", "()V");

    return 0;
}
從上面的代碼可知,系統把java層inputChannel中的一些變量保存在了native層gInputChannelClassInfo結構體成員中,方便使用。

接下來看android_view_InputChannel_createInputChannel方法:

static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
        NativeInputChannel* nativeInputChannel) {
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);//構造方法,創建java層inputChannel對象
    if (inputChannelObj) {//設置NativeInputChannel到java層inputChannel中
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    }
    return inputChannelObj;
}
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
        NativeInputChannel* nativeInputChannel) {//設置到java層inputChannel對象的mPtr中
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}
第一個方法openInputChannelPair分析完成,這個函數其實做的事情就是進入native層,創建兩個inputChannel,然後傳入inputChannel創建NativeInputChannel,最後在創建java層inputChannel對象,並將創建的NativeInputChannel對象地址設置到java層inputChannel對象的mPtr變量中,然後返回這兩個java層對象。


二:setInputChannel

    void setInputChannel(InputChannel inputChannel) {
        mInputChannel = inputChannel;
        mInputWindowHandle.inputChannel = inputChannel;
    }

mInputChannel的創建在windowstate的構造方法:

 WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
           WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
           int viewVisibility, final DisplayContent displayContent) {
        //.......
        mInputWindowHandle = new InputWindowHandle(
                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this,
                displayContent.getDisplayId());
    }
    public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
            Object windowState, int displayId) {
        this.inputApplicationHandle = inputApplicationHandle;
        this.windowState = windowState;
        this.displayId = displayId;
    }

可以看到構造函數也很簡單,推測大部分工作都是在native層做的。

三:registerInputChannel


    public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
//先從java層的inputChannel中獲取mPtr轉化爲NativeInputChannel,在從中取出指向的InputChannel
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}
sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
    return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {//取出InputChannel中的mPtr,強制轉化爲NativeInputChannel
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}

接下來看android_server_InputWindowHandle_getHandle

sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(
        JNIEnv* env, jobject inputWindowHandleObj) {
    jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr);
    NativeInputWindowHandle* handle;
    if (ptr) {
        handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
    } else {
        jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,
                gInputWindowHandleClassInfo.inputApplicationHandle);
        sp<InputApplicationHandle> inputApplicationHandle =
                android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
        env->DeleteLocalRef(inputApplicationHandleObj);
        jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
		//native層新建一個NativeInputWindowHandle
        handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
        handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
		//將native層的NativeInputWindowHandle設置到java層inputwindowhandle的ptr變量中
        env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
                reinterpret_cast<jlong>(handle));
    }
    return handle;
}

接下來看registerInputChannel:

status_t NativeInputManager::registerInputChannel(JNIEnv* env,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);//調用InputDispatcher中的方法
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

    { //創建Connection類,封裝inputChannel和inputwindowhandle
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
		//獲取inputChannel中的fd,監控事件
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } 
    mLooper->wake();
    return OK;
}

至此,registerInputChannel分析結束:

1.取出在第一步創建的native層的inputChannel

2.新建native層nativeinputwindowhandle,並設置到java層inputwindowhandle對象中的ptr變量中

3.將native層的inputwindowhandle和inputChannel註冊到inputdispatcher中,並創建Connection類封裝上面兩個類,用來和app端交互

四:mInputMonitor.updateInputWindowsLw(false /*force*/);

 public void updateInputWindowsLw(boolean force) {
        if (false) Slog.d(WindowManagerService.TAG, ">>>>>> ENTERED updateInputWindowsLw");
//.....
        // Add all windows on the default display.
        final int numDisplays = mService.mDisplayContents.size();
		//遍歷主副屏
        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
            WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
			//遍歷每個窗口
            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                final WindowState child = windows.get(winNdx);
                final InputChannel inputChannel = child.mInputChannel;
                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
               //將窗口放到窗口列表中
                addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible,
                            hasFocus, hasWallpaper);
            }
        }
        // Send windows to native code.將列表設置到ims中,注意不是窗口列表,而是inputwindowhandles列表,即窗口在ims中的化身
        mService.mInputManager.setInputWindows(mInputWindowHandles);
        if (false) Slog.d(WindowManagerService.TAG, "<<<<<<< EXITED updateInputWindowsLw");
    }
	
	    private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,
            final WindowState child, int flags, final int type, final boolean isVisible,
            final boolean hasFocus, final boolean hasWallpaper) {
        // Add a window to our list of input windows.
        inputWindowHandle.name = child.toString();
        final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
		//根據條件從窗口獲得觸摸區域的大小,並將設置觸摸區域的大小設置到inputWindowHandle.touchableRegion中
        if (modal && child.mAppToken != null ) {
            // Limit the outer touch to the activity stack region.
            flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
            child.getStackBounds(mTmpRect);
            inputWindowHandle.touchableRegion.set(mTmpRect);
        } else {
            // Not modal or full screen modal
            child.getTouchableRegion(inputWindowHandle.touchableRegion);
        }
        inputWindowHandle.layoutParamsFlags = flags;
        //從窗口讀取其屬性,設置到inputWindowHandle中
        inputWindowHandle.frameBottom = frame.bottom;

        if (child.mGlobalScale != 1) {
            inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
        } else {
            inputWindowHandle.scaleFactor = 1;
        }//添加到mInputWindowHandles中
        addInputWindowHandleLw(inputWindowHandle);
    }
	
	    private void addInputWindowHandleLw(final InputWindowHandle windowHandle) {
        if (mInputWindowHandles == null) {
            mInputWindowHandles = new InputWindowHandle[16];
        }
        if (mInputWindowHandleCount >= mInputWindowHandles.length) {
            mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
                    mInputWindowHandleCount * 2);
        }
        mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
    }

上面的代碼邏輯比較清晰,就是將遍歷每個窗口,搞一個inputwindowhandles列表出來,然後同步到ims中去,這樣ims就保存了當前所有窗口的信息,然後就可以根據窗口觸摸區域大小等信息發送事件。所以inputwindowhandle就是窗口在ims中的化身,即其相應輸入相關事件的一面。

接下來看看ims如何處理要同步的inputwindowhandles

    public void setInputWindows(InputWindowHandle[] windowHandles) {
        nativeSetInputWindows(mPtr, windowHandles);
    }

不出所料,進入native層,其實可以看出,對於輸入事件,其實窗口的各種屬性如inputwindowhandle和inputChannel之類的都是一個空殼,只是一個保存信息的作用,真正要幹活的還是在native層,從java層的對象中獲取native等的對象。

static void nativeSetInputWindows(JNIEnv* env, jclass clazz,
        jlong ptr, jobjectArray windowHandleObjArray) {
		//強制轉化爲NativeInputManager,並調用其setInputWindows方法
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    im->setInputWindows(env, windowHandleObjArray);
}
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
    Vector<sp<InputWindowHandle> > windowHandles;

    if (windowHandleObjArray) {
        jsize length = env->GetArrayLength(windowHandleObjArray);
        for (jsize i = 0; i < length; i++) {
            jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
            sp<InputWindowHandle> windowHandle =//從java層獲取到ptr然後強制轉化爲NativeInputWindowHandle
                    android_server_InputWindowHandle_getHandle(env, windowHandleObj);
            if (windowHandle != NULL) {
                windowHandles.push(windowHandle);
            }
            env->DeleteLocalRef(windowHandleObj);
        }
    }
	//設置到inputdispatcher中
    mInputManager->getDispatcher()->setInputWindows(windowHandles);

    // Do this after the dispatcher has updated the window handle state.
    bool newPointerGesturesEnabled = true;
    size_t numWindows = windowHandles.size();
    for (size_t i = 0; i < numWindows; i++) {
        const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
        const InputWindowInfo* windowInfo = windowHandle->getInfo();
        if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures
                & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) {
            newPointerGesturesEnabled = false;
        }
    }

    uint32_t changes = 0;
    { // acquire lock
        AutoMutex _l(mLock);

        if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) {
            mLocked.pointerGesturesEnabled = newPointerGesturesEnabled;
            changes |= InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT;
        }
    } // release lock

    if (changes) {
        mInputManager->getReader()->requestRefreshConfiguration(changes);
    }
}

進入inputdispatcher中看看是什麼動作:

void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
    { // acquire lock
        AutoMutex _l(mLock);

        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
        mWindowHandles = inputWindowHandles;

        sp<InputWindowHandle> newFocusedWindowHandle;
        bool foundHoveredWindow = false;
        for (size_t i = 0; i < mWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
			//更新底層和ims相關的窗口信息
            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                mWindowHandles.removeAt(i--);
                continue;
            }
            if (windowHandle->getInfo()->hasFocus) {
                newFocusedWindowHandle = windowHandle;
            }
            if (windowHandle == mLastHoverWindowHandle) {
                foundHoveredWindow = true;
            }
        }

        if (!foundHoveredWindow) {
            mLastHoverWindowHandle = NULL;
        }

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            if (mFocusedWindowHandle != NULL) {
                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
                if (focusedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                            "focus left window");
                    synthesizeCancelationEventsForInputChannelLocked(
                            focusedInputChannel, options);
                }
            }
            if (newFocusedWindowHandle != NULL) {
            }
            mFocusedWindowHandle = newFocusedWindowHandle;
        }

        for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
            TouchState& state = mTouchStatesByDisplay.editValueAt(d);
            for (size_t i = 0; i < state.windows.size(); i++) {
                TouchedWindow& touchedWindow = state.windows.editItemAt(i);
                if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
                    sp<InputChannel> touchedInputChannel =
                            touchedWindow.windowHandle->getInputChannel();
                    if (touchedInputChannel != NULL) {
                        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                "touched window was removed");
                        synthesizeCancelationEventsForInputChannelLocked(
                                touchedInputChannel, options);
                    }
                    state.windows.removeAt(i--);
                }
            }
        }

        // Release information for windows that are no longer present.
        // This ensures that unused input channels are released promptly.
        // Otherwise, they might stick around until the window handle is destroyed
        // which might not happen until the next GC.
        for (size_t i = 0; i < oldWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i);
            if (!hasWindowHandleLocked(oldWindowHandle)) {
                oldWindowHandle->releaseInfo();
            }
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}
bool NativeInputWindowHandle::updateInfo() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject obj = env->NewLocalRef(mObjWeak);
    if (!obj) {
        releaseInfo();
        return false;
    }

    if (!mInfo) {
        mInfo = new InputWindowInfo();
    } else {
        mInfo->touchableRegion.clear();
    }

    jobject inputChannelObj = env->GetObjectField(obj,
            gInputWindowHandleClassInfo.inputChannel);
    if (inputChannelObj) {//設置inputChannel
        mInfo->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
        env->DeleteLocalRef(inputChannelObj);
    } else {
        mInfo->inputChannel.clear();
    }

    jstring nameObj = jstring(env->GetObjectField(obj,
            gInputWindowHandleClassInfo.name));
    if (nameObj) {
        const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
        mInfo->name.setTo(nameStr);
        env->ReleaseStringUTFChars(nameObj, nameStr);
        env->DeleteLocalRef(nameObj);
    } else {
        mInfo->name.setTo("<null>");
    }

    mInfo->layoutParamsFlags = env->GetIntField(obj,
            gInputWindowHandleClassInfo.layoutParamsFlags);
    mInfo->layoutParamsType = env->GetIntField(obj,
            gInputWindowHandleClassInfo.layoutParamsType);
    mInfo->dispatchingTimeout = env->GetLongField(obj,
            gInputWindowHandleClassInfo.dispatchingTimeoutNanos);
    mInfo->frameLeft = env->GetIntField(obj,
            gInputWindowHandleClassInfo.frameLeft);
    mInfo->frameTop = env->GetIntField(obj,
            gInputWindowHandleClassInfo.frameTop);
    mInfo->frameRight = env->GetIntField(obj,
            gInputWindowHandleClassInfo.frameRight);
    mInfo->frameBottom = env->GetIntField(obj,
            gInputWindowHandleClassInfo.frameBottom);
    mInfo->scaleFactor = env->GetFloatField(obj,
            gInputWindowHandleClassInfo.scaleFactor);
	//從java中拿到touchableregion,即java中的Region對象
    jobject regionObj = env->GetObjectField(obj,
            gInputWindowHandleClassInfo.touchableRegion);
    if (regionObj) {//從java的region類中獲取mNativeRegion(從名字就知道是指向native層的對象的),然後強制轉化爲SkRegion
        SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
        for (SkRegion::Iterator it(*region); !it.done(); it.next()) {
            const SkIRect& rect = it.rect();
            mInfo->addTouchableRegion(Rect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom));
        }
        env->DeleteLocalRef(regionObj);
    }

    mInfo->visible = env->GetBooleanField(obj,
            gInputWindowHandleClassInfo.visible);
    mInfo->canReceiveKeys = env->GetBooleanField(obj,
            gInputWindowHandleClassInfo.canReceiveKeys);
    mInfo->hasFocus = env->GetBooleanField(obj,
            gInputWindowHandleClassInfo.hasFocus);
    mInfo->hasWallpaper = env->GetBooleanField(obj,
            gInputWindowHandleClassInfo.hasWallpaper);
    mInfo->paused = env->GetBooleanField(obj,
            gInputWindowHandleClassInfo.paused);
    mInfo->layer = env->GetIntField(obj,
            gInputWindowHandleClassInfo.layer);
    mInfo->ownerPid = env->GetIntField(obj,
            gInputWindowHandleClassInfo.ownerPid);
    mInfo->ownerUid = env->GetIntField(obj,
            gInputWindowHandleClassInfo.ownerUid);
    mInfo->inputFeatures = env->GetIntField(obj,
            gInputWindowHandleClassInfo.inputFeatures);
    mInfo->displayId = env->GetIntField(obj,
            gInputWindowHandleClassInfo.displayId);

    env->DeleteLocalRef(obj);
    return true;
}

總結下這個步驟:

1.上層更新窗口列表,並更新touchableregion的大小

2.將更新的窗口列表設置到ims,在ims,ims又將窗口信息設置到inputdispatcher中。


回到開頭提出的問題,猜測肯定是上層設置大小的時候出問題了,做如下修改即可

@@ -174,7 +174,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
         inputWindowHandle.name = child.toString();
         final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0;
-        if (modal && child.mAppToken != null) {
+        if (modal && child.mAppToken != null && (child.getDisplayId() == Display.DEFAULT_DISPLAY)) {
             // Limit the outer touch to the activity stack region.
             flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
             child.getStackBounds(mTmpRect);



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