RK3188 Android5.1 双屏异显副屏显示异常

简介: RK3188 Android5.1 双屏异显副屏显示异常

平台


RK3188 + Android 5.1 + 双屏异显补丁


概述


首先,要支持双屏异显,需先打上对应补丁。

在实现了双屏异显功能后,问题并非必现,需要在特定情况下,比如本文中的问题:


安装多几个应用, 就有一定概率出现 同显情况下, 看不出有问题, 当开始异显后, 副屏只亮背光而无图像信号, 切回同显也是好的


排查


先从dumpsys display开始


Logical Displays: size=2
  Display 0:
    mDisplayId=0
    mLayerStack=0
    mHasContent=true
    mPrimaryDisplayDevice=内置屏幕
    mBaseDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1366 x 768, real 1366 x 768, largest app 1366 x 768, smallest app 1366 x 768, 60.189003 fps, supportedRefreshRates [60.189003], rotation 0, density 160 (148.2752 x 140.33957) dpi, layerStack 0, appVsyncOff 0, presDeadline 17614331, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
    mOverrideDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1366 x 720, real 1366 x 768, largest app 1366 x 1293, smallest app 768 x 695, 60.189003 fps, supportedRefreshRates [60.189003], rotation 0, density 160 (148.2752 x 140.33957) dpi, layerStack 0, appVsyncOff 0, presDeadline 17614331, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
  Display 1:
    mDisplayId=1
    mLayerStack=1
    mHasContent=true
    mPrimaryDisplayDevice=HDMI 屏幕
    mBaseDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1280 x 800, real 1280 x 800, largest app 1280 x 800, smallest app 1280 x 800, 56.000004 fps, supportedRefreshRates [56.000004], rotation 0, density 237 (237.0 x 237.0) dpi, layerStack 1, appVsyncOff 0, presDeadline 18857142, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}
    mOverrideDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1280 x 800, real 1280 x 800, largest app 1280 x 800, smallest app 1280 x 800, 56.000004 fps, supportedRefreshRates [56.000004], rotation 0, density 237 (237.0 x 237.0) dpi, layerStack 1, appVsyncOff 0, presDeadline 18857142, type HDMI, state UNKNOWN, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION}


PS:HDMI 屏幕就是副屏

上面的LOG需关注的地方是:

mOverrideDisplayInfo …state UNKNOWN…

显示正常时, 状态应该为: state ON


mOverrideDisplayInfo的来源:


mOverrideDisplayInfo: frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java


/**
     * Sets overridden logical display information from the window manager.
     * This method can be used to adjust application insets, rotation, and other
     * properties that the window manager takes care of.
     *
     * @param info The logical display information, may be null.
     */
    public boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
        if (info != null) {
            if (mOverrideDisplayInfo == null) {
                mOverrideDisplayInfo = new DisplayInfo(info);
                mInfo = null;
                return true;
            }
            if (!mOverrideDisplayInfo.equals(info)) {
                mOverrideDisplayInfo.copyFrom(info);
                mInfo = null;
                return true;
            }
        } else if (mOverrideDisplayInfo != null) {
            mOverrideDisplayInfo = null;
            mInfo = null;
            return true;
        }
        return false;
    }


谁调用了LogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked ?


frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java


@Override
        public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
            setDisplayInfoOverrideFromWindowManagerInternal(displayId, info);
        }
    private void setDisplayInfoOverrideFromWindowManagerInternal(
            int displayId, DisplayInfo info) {
        synchronized (mSyncRoot) {
            LogicalDisplay display = mLogicalDisplays.get(displayId);
            if (display != null) {
                if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
                    sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
                    scheduleTraversalLocked(false);
                }
            }
        }
    }


正常时状态变化传递的堆栈信息如下(代码就不一一列出来了):


//初始化WindowManagerService时
setDisplayInfoOverrideFromWindowManager 内置屏幕,state=0
  com.android.server.display.DisplayManagerService$LocalService.setDisplayInfoOverrideFromWindowManager(DisplayManagerService.java:1507)
  com.android.server.wm.WindowManagerService.newDisplayContentLocked(WindowManagerService.java:11782)
  com.android.server.wm.WindowManagerService.getDisplayContentLocked(WindowManagerService.java:11814)
  com.android.server.wm.WindowManagerService.createDisplayContentLocked(WindowManagerService.java:11800)
  com.android.server.wm.WindowManagerService.<init>(WindowManagerService.java:876)
  com.android.server.wm.WindowManagerService.<init>(WindowManagerService.java:165)
  com.android.server.wm.WindowManagerService$2.run(WindowManagerService.java:830)
//<异常时>, 开机LOG,缺少以下流程调用
setDisplayInfoOverrideFromWindowManager HDMI 屏幕,state=2
  com.android.server.display.DisplayManagerService$LocalService.setDisplayInfoOverrideFromWindowManager(DisplayManagerService.java:1507)
  com.android.server.wm.WindowManagerService.newDisplayContentLocked(WindowManagerService.java:11782)
  com.android.server.wm.WindowManagerService.getDisplayContentLocked(WindowManagerService.java:11814)
  com.android.server.wm.WindowManagerService.createDisplayContentLocked(WindowManagerService.java:11800)
  com.android.server.wm.WindowManagerService.handleDisplayAdded(WindowManagerService.java:11861)
  com.android.server.wm.WindowManagerService.onDisplayAdded(WindowManagerService.java:11852)
  com.android.server.am.ActivityStackSupervisor.handleDisplayAddedLocked(ActivityStackSupervisor:3426)
  com.android.server.am.ActivityStackSupervisor.onDisplayAdded(ActivityStackSupervisor:3397)


从logcat中, 可以对比发现, 异常的时候, 缺少了以下LOG:


01-01 20:40:56.079 V/ActivityManager(  395): Display added displayId=1


frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java



@Override
    public void onDisplayAdded(int displayId) {
        Slog.v(TAG, "Display added displayId=" + displayId);
        mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
    }
    public void handleDisplayAddedLocked(int displayId) {
        boolean newDisplay;
        synchronized (mService) {
            newDisplay = mActivityDisplays.get(displayId) == null;
            if (newDisplay) {
                ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
                if (activityDisplay.mDisplay == null) {
                    Slog.w(TAG, "Display " + displayId + " gone before initialization complete");
                    return;
                }
                mActivityDisplays.put(displayId, activityDisplay);
            }
        }
        if (newDisplay) {
            mWindowManager.onDisplayAdded(displayId);
        }
    }


关于:onDisplayAdded

注册回调监听:mDisplayManager.registerDisplayListener(this, null); 当增加显示设备时调用onDisplayAdded


frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java


void setWindowManager(WindowManagerService wm) {
        synchronized (mService) {
            mWindowManager = wm;
            mDisplayManager =
                    (DisplayManager)mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
            mDisplayManager.registerDisplayListener(this, null);
            Display[] displays = mDisplayManager.getDisplays();
            for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
                final int displayId = displays[displayNdx].getDisplayId();
                ActivityDisplay activityDisplay = new ActivityDisplay(displayId);
                if (activityDisplay.mDisplay == null) {
                    throw new IllegalStateException("Default Display does not exist");
                }
                mActivityDisplays.put(displayId, activityDisplay);
            }
            createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY);
            mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
            mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
            // Initialize this here, now that we can get a valid reference to PackageManager.
            mLeanbackOnlyDevice = isLeanbackOnlyDevice();
        }
    }


frameworks/base/core/java/android/hardware/display/DisplayManagerGlobal.java


//由前面的ActivityStackSupervisor调用.
    public void registerDisplayListener(DisplayListener listener, Handler handler) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
        synchronized (mLock) {
            int index = findDisplayListenerLocked(listener);
            if (index < 0) {
                mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
                registerCallbackIfNeededLocked();
            }
        }
    }
    private static final class DisplayListenerDelegate extends Handler {
        public final DisplayListener mListener;
        public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
            mListener = listener;
        }
        public void sendDisplayEvent(int displayId, int event) {
            Log.d(TAG, "DisplayListenerDelegate.sendDisplayEvent displayId=" + displayId + ", event=" + event);
            Message msg = obtainMessage(event, displayId, 0);
            sendMessage(msg);
        }
        public void clearEvents() {
            removeCallbacksAndMessages(null);
        }
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "ALog DisplayListenerDelegate.handleMessage displayId=" + msg.arg1 + ", event=" + msg.what + "," + "Clz=" + mListener.getClass().getName());
            switch (msg.what) {
                case EVENT_DISPLAY_ADDED:
                    Log.d(TAG, "ALog DisplayListenerDelegate.handleMessage EVENT_DISPLAY_ADDED");
                    mListener.onDisplayAdded(msg.arg1);
                    break;
                case EVENT_DISPLAY_CHANGED:
                    mListener.onDisplayChanged(msg.arg1);
                    break;
                case EVENT_DISPLAY_REMOVED:
                    mListener.onDisplayRemoved(msg.arg1);
                    break;
            }
        }
    }
    private void registerCallbackIfNeededLocked() {
        if (mCallback == null) {
            mCallback = new DisplayManagerCallback();
            try {
                mDm.registerCallback(mCallback);
            } catch (RemoteException ex) {
                Log.e(TAG, "Failed to register callback with display manager service.", ex);
                mCallback = null;
            }
        }
    }
    private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
        @Override
        public void onDisplayEvent(int displayId, int event) {
            if (DEBUG) {
                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
            }
            handleDisplayEvent(displayId, event);
        }
    }
    private void handleDisplayEvent(int displayId, int event) {
        synchronized (mLock) {
            if (USE_CACHE) {
                mDisplayInfoCache.remove(displayId);
                if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
                    mDisplayIdCache = null;
                }
            }
            final int numListeners = mDisplayListeners.size();
            for (int i = 0; i < numListeners; i++) {
                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
            }
        }
    }


frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java


private final class BinderService extends IDisplayManager.Stub {
  //.......
        @Override // Binder call
        public void registerCallback(IDisplayManagerCallback callback) {
            if (callback == null) {
                throw new IllegalArgumentException("listener must not be null");
            }
            final int callingPid = Binder.getCallingPid();
            final long token = Binder.clearCallingIdentity();
            try {
                registerCallbackInternal(callback, callingPid);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }
    private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid) {
        synchronized (mSyncRoot) {
            if (mCallbacks.get(callingPid) != null) {
                throw new SecurityException("The calling process has already "
                        + "registered an IDisplayManagerCallback.");
            }
            CallbackRecord record = new CallbackRecord(callingPid, callback);
            try {
                IBinder binder = callback.asBinder();
                binder.linkToDeath(record, 0);
            } catch (RemoteException ex) {
                // give up
                throw new RuntimeException(ex);
            }
            mCallbacks.put(callingPid, record);
        }
    }


onDisplayAdded回调的堆栈:


com.android.server.am.ActivityStackSupervisor.onDisplayAdded(ActivityStackSupervisor.java)
    android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.handleMessage(DisplayManagerGlobal.java)
    android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.sendDisplayEvent(DisplayManagerGlobal.java)
    android.hardware.display.DisplayManagerGlobal.handleDisplayEvent(DisplayManagerGlobal.java)
    android.hardware.display.DisplayManagerGlobal$DisplayManagerCallback.onDisplayEvent(DisplayManagerGlobal.java)
    com.android.server.display.DisplayManagerService$CallbackRecord.notifyDisplayEventAsync(DisplayManagerService.java)
    com.android.server.display.DisplayManagerService.deliverDisplayEvent(DisplayManagerService.java)
    com.android.server.display.DisplayManagerService.sendDisplayEventLocked(DisplayManagerService.java)
  com.android.server.display.DisplayManagerService.addLogicalDisplayLocked(DisplayManagerService.java:753)
  com.android.server.display.DisplayManagerService.handleDisplayDeviceAddedLocked(DisplayManagerService.java:653)
  com.android.server.display.DisplayManagerService.handleDisplayDeviceAdded(DisplayManagerService.java:639)
  com.android.server.display.DisplayManagerService$DisplayAdapterListener.onDisplayDeviceEvent(DisplayManagerService.java:1055)
  com.android.server.display.DisplayAdapter.sendDisplayDeviceEventLocked(DisplayAdapter.java:108)
    com.android.server.display.LocalDisplayAdapter.tryConnectDisplayLocked(LocalDisplayAdapter.java:121)


frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java


private void sendDisplayEventLocked(int displayId, int event) {
        Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
        mHandler.sendMessage(msg);
    }
    private final class DisplayManagerHandler extends Handler {
        public DisplayManagerHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }
        @Override
        public void handleMessage(Message msg) {
    //......
                case MSG_DELIVER_DISPLAY_EVENT:
                    deliverDisplayEvent(msg.arg1, msg.arg2);
                    break;
   }
    private void deliverDisplayEvent(int displayId, int event) {
        if (DEBUG) {
            Slog.d(TAG, "Delivering display event: displayId="
                    + displayId + ", event=" + event);
        }
        // Grab the lock and copy the callbacks.
        final int count;
        synchronized (mSyncRoot) {
            count = mCallbacks.size();
            mTempCallbacks.clear();
            for (int i = 0; i < count; i++) {
                mTempCallbacks.add(mCallbacks.valueAt(i));
            }
        }
        // After releasing the lock, send the notifications out.
        for (int i = 0; i < count; i++) {
            mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
        }
        mTempCallbacks.clear();
    }
    private final class CallbackRecord implements DeathRecipient {
        public final int mPid;
        private final IDisplayManagerCallback mCallback;
        public boolean mWifiDisplayScanRequested;
        public CallbackRecord(int pid, IDisplayManagerCallback callback) {
            mPid = pid;
            mCallback = callback;
        }
        @Override
        public void binderDied() {
            if (DEBUG) {
                Slog.d(TAG, "Display listener for pid " + mPid + " died.");
            }
            onCallbackDied(this);
        }
        public void notifyDisplayEventAsync(int displayId, int event) {
            try {
                mCallback.onDisplayEvent(displayId, event);
            } catch (RemoteException ex) {
                Slog.w(TAG, "Failed to notify process "
                        + mPid + " that displays changed, assuming it died.", ex);
                binderDied();
            }
        }
    }


关于tryConnectDisplayLocked

从前面的onDisplayAdded的回调过程可以看出, 触发的条件是tryConnectDisplayLocked的调用成功

即是连接/打开显示设备成功后即会进入传递事件的流程

而调用tryConnectDisplayLocked的地方有两处:

1. 如下代码


|--DisplayManagerService
   onStart->registerDefaultDisplayAdapter
|-- LocalDisplayAdapter
  registerLocked->tryConnectDisplayLocked


2.LocalDisplayAdapter中的HotplugDisplayEventReceiver中的onHotplug被触发后


frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java


@Override
    public void onStart() {
        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
  //....
  }
    private final class DisplayManagerHandler extends Handler {
        public DisplayManagerHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
                    registerDefaultDisplayAdapter();
                    break;
  //......
  }
  DisplayAdapterListener mDisplayAdapterListener
    private void registerDefaultDisplayAdapter() {
        // Register default display adapter.
        synchronized (mSyncRoot) {
            registerDisplayAdapterLocked(new LocalDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
        }
    }
  private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
        mDisplayAdapters.add(adapter);
        adapter.registerLocked();
    }
    private final class DisplayAdapterListener implements DisplayAdapter.Listener {
        @Override
        public void onDisplayDeviceEvent(DisplayDevice device, int event) {
            switch (event) {
                case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
                    handleDisplayDeviceAdded(device);
                    break;
                case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
                    handleDisplayDeviceChanged(device);
                    break;
                case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
                    handleDisplayDeviceRemoved(device);
                    break;
            }
        }
        @Override
        public void onTraversalRequested() {
            synchronized (mSyncRoot) {
                scheduleTraversalLocked(false);
            }
        }
    }
    private void handleDisplayDeviceAdded(DisplayDevice device) {
        synchronized (mSyncRoot) {
            handleDisplayDeviceAddedLocked(device);
        }
    }


代码很简单, 服务启动后, 注册显示适配器的监听, 用于接收显示设备变化的事件, 主要关注DISPLAY_DEVICE_EVENT_ADDED的事件.

本文的问题主要围绕收不到副屏添加的事件展开.


frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java


private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
            SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
    };
    private HotplugDisplayEventReceiver mHotplugReceiver;
    @Override
    public void registerLocked() {
        super.registerLocked();
        mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
  //连接/打开两个显示设备
        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
            tryConnectDisplayLocked(builtInDisplayId);
        }
    }
    private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
        public HotplugDisplayEventReceiver(Looper looper) {
            super(looper);
        }
        @Override
        public void onHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
            synchronized (getSyncRoot()) {
                if (connected) {
                    tryConnectDisplayLocked(builtInDisplayId);
                } else {
                    tryDisconnectDisplayLocked(builtInDisplayId);
                }
            }
        }
    }


问题的根源


frameworks/base/services/java/com/android/server/SystemServer.java


/**
     * Starts the small tangle of critical services that are needed to get
     * the system off the ground.  These services have complex mutual dependencies
     * which is why we initialize them all in one place here.  Unless your service
     * is also entwined in these dependencies, it should be initialized in one of
     * the other functions.
     */
    private void startBootstrapServices() {
        // Wait for installd to finish starting up so that it has a chance to
        // create critical directories such as /data/user with the appropriate
        // permissions.  We need this to complete before we initialize other services.
        Installer installer = mSystemServiceManager.startService(Installer.class);
        // Activity manager runs the show.
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        // Power manager needs to be started early because other services need it.
        // Native daemons may be watching for it to be registered so it must be ready
        // to handle incoming binder calls immediately (including being able to verify
        // the permissions for those calls).
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
        // Now that the power manager has been started, let the activity manager
        // initialize power management features.
        mActivityManagerService.initPowerManagement();
        // Display manager is needed to provide display metrics before package manager
        // starts up.
        mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
  }
     /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
     * and organized.
     */
    private void startOtherServices() {
        ///....
            mActivityManagerService.setWindowManager(wm);
  //...
  }


    对android启动流程有了解过的人都知道, 在SystemServer启动了各种各样的服务, Activity, Package, Power…等等,

从上面的代码可以看到, DisplayManagerService启动的时间相当早, 在DisplayManagerService启动后, 会通过Handler消息,

让LocalDisplayAdapter的tryConnectDisplayLocked去打开两个显示设备 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN和SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI


    本文的问题点就在于: ActivityStackSupervisor 注册了显示设备的监听回调时, 两路显示早已经初始化完成了, 导致后面ActivityStackSupervisor.onDisplayAdded根本不会执行.


在出问题的LOG中可以看到, 两个设备的连接间隔只有30毫秒


//DisplayManagerService 启动-打开显示0 成功
01-01 21:55:37.376 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked 0
01-01 21:55:37.378 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked->sendDisplayDeviceEventLocked DISPLAY_DEVICE_EVENT_ADDED
//DisplayManagerService 启动-打开显示1 成功
01-01 21:55:37.407 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked 1
01-01 21:55:37.407 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked->sendDisplayDeviceEventLocked DISPLAY_DEVICE_EVENT_ADDED
//注册显示设备的监听则是在
01-01 21:55:51.711 D/ActivityManager(  395): setWindowManager


显示正常时的LOG


//DisplayManagerService 启动-打开显示0 成功
01-01 20:40:47.605 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked 0
01-01 20:40:47.607 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked->sendDisplayDeviceEventLocked DISPLAY_DEVICE_EVENT_ADDED
//DisplayManagerService 启动-打开显示1 失败
01-01 20:40:47.608 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked 1
01-01 20:40:47.608 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked FAILED: 1
//注册监听
01-01 20:40:52.314 D/ActivityManager(  395): setWindowManager->registerDisplayListener
//HotplugDisplayEventReceiver 监听到设备连接
01-01 20:40:53.922 I/hwcomposer(  103): connet to hotplug device[4338,1,0]
01-01 20:40:53.932 I/LocalDisplayAdapter(  395): onHotplug builtInDisplayId=1, connected=true
//HotplugDisplayEventReceiver 监听到设备连接 尝试打开/连接 成功
01-01 20:40:53.932 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked 1
01-01 20:40:53.936 D/LocalDisplayAdapter(  395): tryConnectDisplayLocked->sendDisplayDeviceEventLocked DISPLAY_DEVICE_EVENT_ADDED


解决


frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java


1.增加onSystemReady服务调用

2.延迟监听和打开副屏的时间


@Override
    public void registerLocked() {
        super.registerLocked();
        //move to system Ready
        //mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
  //只打开主屏, HDMI延后.
        tryConnectDisplayLocked(SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
        /* connect default display only
        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
            tryConnectDisplayLocked(builtInDisplayId);
        }
        */
    }
    //在系统Ready后执行, 尝试打开HDMI副屏, 有可能会失败, 若失败则重试
    //打开完后注册热插监听, 以监听其它扩展显示器的事件.
    public void onSystemReady(){
        new Thread(){
            public void run(){
                int retry = 0;
                while(mDevices.get(SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI) == null){
                    tryConnectDisplayLocked(SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI);
                    retry ++;
                    if(retry > 5){
                        Slog.e(TAG, "onSystemReady connect display failed after " + retry + " times");
                        break;
                    }
                    try{Thread.sleep(1000);}catch(Exception e){}
                }
                mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
            }
        }.start();
    }


frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java


/**
     * Called when the system is ready to go.
     */
    public void systemReady(boolean safeMode, boolean onlyCore) {
        synchronized (mSyncRoot) {
            mSafeMode = safeMode;
            mOnlyCore = onlyCore;
        }
  //调用onSystemReady去打开HDMI副屏, 
  //成功打开后, ActivityStackSupervisor.onDisplayAdded会被调用
        ((LocalDisplayAdapter)mDisplayAdapters.get(0)).onSystemReady();
        mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
    }


扩展


关于DisplayEventReceiver


frameworks/base/core/java/android/view/DisplayEventReceiver.java


public abstract class DisplayEventReceiver {
    private static final String TAG = "DisplayEventReceiver";
    private final CloseGuard mCloseGuard = CloseGuard.get();
    private long mReceiverPtr;
    // We keep a reference message queue object here so that it is not
    // GC'd while the native peer of the receiver is using them.
    private MessageQueue mMessageQueue;
    private static native long nativeInit(DisplayEventReceiver receiver,
            MessageQueue messageQueue);
    private static native void nativeDispose(long receiverPtr);
    private static native void nativeScheduleVsync(long receiverPtr);
    /**
     * Creates a display event receiver.
     *
     * @param looper The looper to use when invoking callbacks.
     */
    public DisplayEventReceiver(Looper looper) {
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(this, mMessageQueue);
        mCloseGuard.open("dispose");
    }
    @Override
    protected void finalize() throws Throwable {
        try {
            dispose(true);
        } finally {
            super.finalize();
        }
    }
  //....
    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {
        onHotplug(timestampNanos, builtInDisplayId, connected);
    }


frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp


bool NativeDisplayEventReceiver::processPendingEvents(
        nsecs_t* outTimestamp, int32_t* outId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        ALOGV("receiver %p ~ Read %d events.", this, int(n));
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                // Later vsync events will just overwrite the info from earlier
                // ones. That's fine, we only care about the most recent.
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outId = ev.header.id;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                dispatchHotplug(ev.header.timestamp, ev.header.id, ev.hotplug.connected);
                break;
            default:
                ALOGW("receiver %p ~ ignoring unknown event type %#x", this, ev.header.type);
                break;
            }
        }
    }
    if (n < 0) {
        ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
    }
    return gotVsync;
}
相关文章
|
9天前
|
开发工具 Android开发
android studio build异常
android studio build异常
18 3
|
1月前
|
Android开发
jack-server导致 Android 编译 出现异常
jack-server导致 Android 编译 出现异常
28 6
|
1月前
|
Android开发
android捕获全局异常,并对异常做出处理
android捕获全局异常,并对异常做出处理
26 4
|
1月前
|
存储 安全 文件存储
Android OTA升级后输入法异常和应用丢失的分析
Android OTA升级后输入法异常和应用丢失的分析
30 1
|
1月前
|
Android开发
android 12 U盘 /mnt/media_rw 下读取文件异常 没有权限
android 12 U盘 /mnt/media_rw 下读取文件异常 没有权限
75 0
|
10月前
|
数据库 Android开发 数据库管理
java.lang.NullPointerException: Attempt to invoke virtual method ‘int android.database.sqlite异常
java.lang.NullPointerException: Attempt to invoke virtual method ‘int android.database.sqlite异常
276 0
|
10月前
|
Android开发
Android > Project with path ‘:audiovisualize‘ could not be found in project ‘:app‘. 异常解决方案
Android > Project with path ‘:audiovisualize‘ could not be found in project ‘:app‘. 异常解决方案
53 0
|
Android开发
Android 的 Presentation 双屏异显,遇到的问题总结
Android 的 Presentation 双屏异显,遇到的问题总结
|
JSON Android开发 数据格式
Android:解析Json异常 Expected a string but was BEGIN_OBJECT at
今天解析后端数据时,发现了这个报错:Expected BEGIN_OBJECT but was STRING at 看来是自己哪儿解析错误了。 因为数据的特殊性,后端返回的Json串里面可能还会有Json数据,可能嵌套了三次层,
|
Android开发
Android oppo手机显示安装包异常(Bug6)
Android oppo手机显示安装包异常(Bug6)