远程桌面连接、断开时,避免改变前台app窗口尺寸

  • 在用方法是修改SurfaceView的updateSurface(),当前台app是com.kos.launcher,并请求app高度是屏幕高度(1080)时,忽略这次updateSurface。
  • 上面方法总感觉不那么可靠。希望是在源头不让android认为切换了输入法。但溯源到Session::remove,不知道是谁调用它了。

 

远程桌面包括两个模块。一是录屏,即实现server发送桌面图像到client。二是模拟输入,即让server可以接收client发过来的键盘、鼠标指令。

在android,模拟输入技术用的是/dev/uinput,uinput创建的新设备放在/dev/input,这样在inputflinger看来,这设备已和其它输入设备没什么两样。更详细模拟输入看“libkosapi api”中“模拟输入”,在这里要说是,当用uinput创建新设备,会让android认为当前正发生了切换输入法。于是导致去销毁底部TaskBar,这TaskBar一销毁,给app可用的矩形高度就上升到整屏高度(1080)。于是乎前台app的surfaceChanged被调用了,myHeight参数值是1080。以下是用uinput创建的新设备,输出的部分logcat。

// 下面这条警告,表示输入通道对象“8b2b139 Taskbar (client)”被直接销毁而未通过输入管理器正常移除。
10:53:54.424   729-755   InputManager-JNI         W  Input channel object '8b2b139 Taskbar (client)' was disposed without first being removed with the input manager!

// Config changes=20000500,mAppBounds高度(前台app可用高度)由997变成1080
10:53:54.432   729-755   ActivityTaskManager      I  Config changes=20000500 {1.0 ?mcc0mnc [zh_CN] ldltr sw785dp w1396dp h761dp 220dpi xlrg long land finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 1080) mMaxBounds=Rect(0, 0 - 1920, 1080) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} as.1 s.146 fontWeightAdjustment=0}
10:53:54.444   729-755   ActivityTaskManager      W  Current config: {1.0 ?mcc0mnc [zh_CN] ldltr sw785dp w1396dp h701dp 220dpi lrg long land finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 997) mMaxBounds=Rect(0, 0 - 1920, 1080) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} as.1 s.146 fontWeightAdjustment=0} unchanged for IME proc com.google.android.inputmethod.pinyin

// 很快,又一个Config changes=20000500,只是mAppBounds高度由1080变成997
10:53:54.573   729-755   ActivityTaskManager      I  Config changes=20000500 {1.0 ?mcc0mnc [zh_CN] ldltr sw785dp w1396dp h701dp 220dpi lrg long land finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 997) mMaxBounds=Rect(0, 0 - 1920, 1080) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} as.1 s.180 fontWeightAdjustment=0}
10:53:54.587   729-755   ActivityTaskManager      W  Current config: {1.0 ?mcc0mnc [zh_CN] ldltr sw785dp w1396dp h761dp 220dpi xlrg long land finger qwerty/v/v -nav/h winConfig={ mBounds=Rect(0, 0 - 1920, 1080) mAppBounds=Rect(0, 0 - 1920, 1080) mMaxBounds=Rect(0, 0 - 1920, 1080) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} as.1 s.180 fontWeightAdjustment=0} unchanged for IME proc com.google.android.inputmethod.pinyin

不清楚内中逻辑,很快的,发到前台app的又一个surfaceChanged过来了,此时myHeight参数值变成去掉TaskBar高度的997。

就在这很短时间,前台app的surface从997变成1080,又由1080变成997。这个短暂变化,要是app处理不好,容易造成问题。这里会问,用uinput创建新设备,能不能不让andorid认为是在切换输入法,或至少不让认为需要销毁TaskBar,从而触发后续改变前台app尺寸。

顺着不销毁TaskBar、不改变前台app尺寸这个目标,去尝试修改android代码。

 

一、uinput创建设备时的参数:absmax[ABS_Y]

用uinput创建设备,须要填一个高度参数。之前填的是屏幕高度1080。改为997后,结果没变化。

<aosp>/frameworks/native/libs/kosapi/sendinput.cpp
------
NativeConnection* NativeConnection::open(const char* name, const char* uniqueId,
        bool keyboard, int32_t screenWidth, int32_t screenHeight) {
    int32_t maxPointers = 1;

    int fd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
    ...
    uinp.absmin[ABS_X] = 0;
    uinp.absmax[ABS_X] = screenWidth - 1;
    uinp.absmin[ABS_Y] = 0;
    // scrrenHeight由1080,改为997。结果没变化。
    uinp.absmax[ABS_Y] = screenHeight - 1;
    ...
}

 

二、frameworks

“Config changes=20000500”这条logcat输出函数在ActivityTaskManagerService::updateGlobalConfigurationLocked。向上溯源,定位到WindowState::removeIfPossible()。

<aosp>/frameworks/base/services/core/java/com/android/server/wm/WindowState.java
------
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
        InsetsControlTarget, InputTarget {
    @Override
    void removeIfPossible() {
            ...
            // disposeInputChannel会调用mInputChannel.dispose(),mInputChannel类型是InputChannel。
            // InputChanne实现靠JNI,InputChannel::dispose对应cpp中NativeInputChannel::dispose。
            // 内中mDisposeCallback是个函数指针,
            // 对应com_android_server_input_InputManagerService.cpp中的handleInputChannelDisposed,
            // 在这函数输出logcat:... was disposed without first being removed with ...
            disposeInputChannel();
            ...
            if (needToSendNewConfiguration) {
                // 下面这个sendNewConfiguration会调用updateGlobalConfigurationLocked
                displayContent.sendNewConfiguration();
            }
            // 注释掉下面这条updateFocusedWindowLocked,不能阻止回调前台app的surfaceChanged。
            mWmService.updateFocusedWindowLocked(isFocused()
                            ? UPDATE_FOCUS_REMOVING_FOCUS
                            : UPDATE_FOCUS_NORMAL,
                    true /*updateInputWindows*/);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

不改变前台app尺寸,尝试几种修改。

  1. 不调用displayContent.sendNewConfiguration(),即不会执行updateGlobalConfigurationLocked。依旧要求改变前台app尺寸。
  2. 不调用mWmService.updateFocusedWindowLocked。依旧要求改变前台app尺寸。

可能吧,修改removeIfPossible,不让调某些函数,达不到目标。就想着怎么不调用removeIfPossible。这就要向上溯源。

<aosp>/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WindowManagerService::removeWindow(Session session, IWindow client),调用了removeIfPossible

<aosp/frameworks/base/services/core/java/com/android/server/wm/Session.java
Session::remove(IWindow window),调用了WindowManagerService::removeWindow

但没找到是谁调用了Session::remove。在溯源Session::remove上遇到困难,只好退而求其次,阻止framworks调用surfaceChanged。能定位到,是SurfadeView的updateSurface在调用surfaceChanged。

<aosp>/frameworks/base/core/java/android/view/SurfaceView.java
------
public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback {
    protected void updateSurface() {
        ...
        int myWidth = mRequestedWidth;
        if (myWidth <= 0) myWidth = getWidth();
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
        // 增加下面这个if判断
        if (viewRoot.mBasePackageName.equals("com.kos.launcher") && myHeight == 1080) {
            return;
        }
 
        ...
    }

当前台app是com.kos.launcher,并要求app高度是屏幕高度时,忽略这次updateSurface。这个修改有几个问题。

  1. 直接忽略updateSurface,这种修改是否安全。
  2. 连接、断开远程桌面,界面还是会抖一下。
  3. 远程桌面时,前台app从com.kos.launcher切到其它app,再切回com.kos.launcher。这时com.kos.launcher和Task bar之间有条黑色横线,而且不会再消失。

全部评论: 0

    写评论: