平台
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; }