以下分析基于Android Q.
一. preferredDisplayModeId改变 上篇文章讲到,只要将window的Param设置就可以更改屏幕分辨率:
1 wmParams!!.preferredDisplayModeId = highestMode.modeId
代码是和实现的呢, 在App接受vsync信号后,会回调Choreographer.CALLBACK_TRAVERSAL,也就会调用到ViewRootImpl.doTraversal.
调用栈如下:
Choreographer.onVsync()
Choreographer.doFrame() // doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
ViewRootImpl.doTraversal()
ViewRootImpl.performTraversals()
ViewRootImpl.relayoutWindow() // 这里将当前Window的Attr传入WindowManagerService
IWindowSession.relayout() // 通过binder调用 Session.relayout
WindowManagerService.relayoutWindow() // 进入SystenServer进程
WindowSurfacePlacer.performSurfacePlacement()
WindowSurfacePlacer.performSurfacePlacementLoop()
RootWindowContainer.performSurfacePlacement()
RootWindowContainer.performSurfacePlacementNoTrace()
RootWindowContainer.applySurfaceChangesTransaction()
DisplayContent.applySurfaceChangesTransaction()
DisplayContent.mApplySurfaceChangesTransaction // 对所有window遍历执行,如果有属性变化响应变化
DisplayManagerService.setDisplayProperties // 计算并保存合适的modeId
DisplayManagerInternal.performTraversal(mDisplayTransaction) // 应用modeId变化
1.1 RootWindowContainer.applySurfaceChangesTransaction 1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void applySurfaceChangesTransaction (boolean recoveringMemory) { ...... final int count = mChildren.size(); for (int j = 0 ; j < count; ++j) { final DisplayContent dc = mChildren.get(j); dc.applySurfaceChangesTransaction(recoveringMemory); } mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction); SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction); }
1.2 DisplayContent.applySurfaceChangesTransaction() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void applySurfaceChangesTransaction (boolean recoveringMemory) { ...... Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges" ); try { forAllWindows(mApplySurfaceChangesTransaction, true ); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } prepareSurfaces(); mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent; mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId, mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, true ); ...... }
1.3 DisplayContent.mApplySurfaceChangesTransaction 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> { ...... if (!mTmpApplySurfaceChangesTransactionState.obscured) { ...... final int preferredModeId = getDisplayPolicy().getRefreshRatePolicy() .getPreferredModeId(w); if (mTmpApplySurfaceChangesTransactionState.preferredModeId == 0 && preferredModeId != 0 ) { mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId; } } ...... }
1.4 DisplayManagerService.setDisplayProperties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Override public void setDisplayProperties (int displayId, boolean hasContent, float requestedRefreshRate, int requestedMode, boolean inTraversal) { setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, requestedMode, inTraversal); } private void setDisplayPropertiesInternal (int displayId, boolean hasContent, float requestedRefreshRate, int requestedModeId, boolean inTraversal) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); if (display == null ) { return ; } if (display.hasContentLocked() != hasContent) { if (DEBUG) { Slog.d(TAG, "Display " + displayId + " hasContent flag changed: " + "hasContent=" + hasContent + ", inTraversal=" + inTraversal); } display.setHasContentLocked(hasContent); scheduleTraversalLocked(inTraversal); } if (requestedModeId == 0 && requestedRefreshRate != 0 ) { requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate( requestedRefreshRate); } mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode( displayId, requestedModeId); } }
二. DisplayModeDirector 这个类是用于决策当前设备刷新率的
2.1 DisplayModeDirector.AppRequestObserver.setAppRequestedMode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public void setAppRequestedMode (int displayId, int modeId) { synchronized (mLock) { setAppRequestedModeLocked(displayId, modeId); } } private void setAppRequestedModeLocked (int displayId, int modeId) { final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId); if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) { return ; } final Vote refreshRateVote; final Vote sizeVote; if (requestedMode != null ) { mAppRequestedModeByDisplay.put(displayId, requestedMode); float refreshRate = requestedMode.getRefreshRate(); refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate); sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(), requestedMode.getPhysicalHeight()); } else { mAppRequestedModeByDisplay.remove(displayId); refreshRateVote = null ; sizeVote = null ; } updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote); updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); return ; }
2.2 DisplayModeDirector.Vote.updateVoteLocked 更新Vote策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private void updateVoteLocked (int displayId, int priority, Vote vote) { if (DEBUG) { Slog.i(TAG, "updateVoteLocked(displayId=" + displayId + ", priority=" + Vote.priorityToString(priority) + ", vote=" + vote + ")" ); } if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) { Slog.w(TAG, "Received a vote with an invalid priority, ignoring:" + " priority=" + Vote.priorityToString(priority) + ", vote=" + vote, new Throwable ()); return ; } final SparseArray<Vote> votes = getOrCreateVotesByDisplay(displayId); Vote currentVote = votes.get(priority); if (vote != null ) { votes.put(priority, vote); } else { votes.remove(priority); } if (votes.size() == 0 ) { if (DEBUG) { Slog.i(TAG, "No votes left for display " + displayId + ", removing." ); } mVotesByDisplay.remove(displayId); } notifyAllowedModesChangedLocked(); }
2.3 DisplayModeDirector.Vote.notifyAllowedModesChangedLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private void notifyAllowedModesChangedLocked () { if (mListener != null && !mHandler.hasMessages(MSG_ALLOWED_MODES_CHANGED)) { Message msg = mHandler.obtainMessage(MSG_ALLOWED_MODES_CHANGED, mListener); msg.sendToTarget(); } } private final class DisplayModeDirectorHandler extends Handler { DisplayModeDirectorHandler(Looper looper) { super (looper, null , true ); } @Override public void handleMessage (Message msg) { switch (msg.what) { case MSG_ALLOWED_MODES_CHANGED: Listener listener = (Listener) msg.obj; listener.onAllowedDisplayModesChanged(); break ; ...... } } }
转到DisplayThread线程处理,也就是回调onAllowedDisplayModesChanged. 这里的mListener是调用DisplayModeDirector.setListener设置的,这个是在DisplayManagerService中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void systemReady (boolean safeMode, boolean onlyCore) { synchronized (mSyncRoot) { mSafeMode = safeMode; mOnlyCore = onlyCore; mSystemReady = true ; recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY)); } mDisplayModeDirector.setListener(new AllowedDisplayModeObserver ()); mDisplayModeDirector.start(mSensorManager); mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); }
2.4 DisplayManagerService.onAllowedDisplayModesChangedInternal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private void onAllowedDisplayModesChangedInternal () { boolean changed = false ; synchronized (mSyncRoot) { final int count = mLogicalDisplays.size(); for (int i = 0 ; i < count; i++) { LogicalDisplay display = mLogicalDisplays.valueAt(i); int displayId = mLogicalDisplays.keyAt(i); int [] allowedModes = mDisplayModeDirector.getAllowedModes(displayId); if (!Arrays.equals(allowedModes, display.getAllowedDisplayModesLocked())) { display.setAllowedDisplayModesLocked(allowedModes); changed = true ; } } if (changed) { scheduleTraversalLocked(false ); } } }
2.5 DisplayModeDirector.getAllowedModes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @NonNull public int [] getAllowedModes(int displayId) { synchronized (mLock) { SparseArray<Vote> votes = getVotesLocked(displayId); Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId); if (modes == null || defaultMode == null ) { Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id=" + displayId + ")" ); return new int [0 ]; } return getAllowedModesLocked(votes, modes, defaultMode); } }
计算displayId对应允许的可自由切换的modeId列表
2.5.1 DisplayModeDirector.getAllowedModesLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 @NonNull private int [] getAllowedModesLocked(@NonNull SparseArray<Vote> votes, @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) { int lowestConsideredPriority = Vote.MIN_PRIORITY; while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { float minRefreshRate = 0f ; float maxRefreshRate = Float.POSITIVE_INFINITY; int height = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) { Vote vote = votes.get(priority); if (vote == null ) { continue ; } minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate); maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate); if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE && vote.height > 0 && vote.width > 0 ) { width = vote.width; height = vote.height; } } if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { width = defaultMode.getPhysicalWidth(); height = defaultMode.getPhysicalHeight(); } int [] availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate); if (availableModes.length > 0 ) { return availableModes; } lowestConsideredPriority++; } return new int [] { defaultMode.getModeId() }; }
这里刷新率的算法是,遍历所有优先级的Vote,最终的minRefreshRate取所有Vote的最小刷新率中的最大值。 maxRefreshRate取所有Vote中最大刷新率的最小值。
这里有个地方一开始比较难理解,就是为什么是用双层循环,而且第一层循环是从优先级最低的开始,最内层是优先级最大的开始。不着急我们先看#2.5.2
2.5.2 DisplayModeDirector.filterModes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private int [] filterModes(Display.Mode[] supportedModes, int width, int height, float minRefreshRate, float maxRefreshRate) { ArrayList<Display.Mode> availableModes = new ArrayList <>(); for (Display.Mode mode : supportedModes) { if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) { continue ; } final float refreshRate = mode.getRefreshRate(); if (refreshRate < (minRefreshRate - EPSILON) || refreshRate > (maxRefreshRate + EPSILON)) { continue ; } availableModes.add(mode); } final int size = availableModes.size(); int [] availableModeIds = new int [size]; for (int i = 0 ; i < size; i++) { availableModeIds[i] = availableModes.get(i).getModeId(); } return availableModeIds; }
其实实际推演一下就不难理解了, 假设我们有如下Votes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mSupportedModesByDisplay: 0 -> [ {id=1, width=1080, height=2376, fps=60.000004}, {id=2, width=1440, height=3168, fps=120.00001}, {id=3, width=1440, height=3168, fps=60.000004}, {id=4, width=1080, height=2376, fps=120.00001}] mVotesByDisplay: -1: PRIORITY_LOW_POWER_MODE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=60.0} PRIORITY_USER_SETTING_PEAK_REFRESH_RATE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=120.0} PRIORITY_USER_SETTING_MIN_REFRESH_RATE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=Infinity} 0: PRIORITY_APP_REQUEST_SIZE -> Vote{width=1080, height=2376, minRefreshRate=0.0, maxRefreshRate=Infinity} PRIORITY_APP_REQUEST_REFRESH_RATE -> Vote{width=-1, height=-1, minRefreshRate=120.00001, maxRefreshRate=120.00001}
注意这里的优先级:
name
Value
PRIORITY_LOW_BRIGHTNESS
0
PRIORITY_USER_SETTING_MIN_REFRESH_RATE
1
PRIORITY_APP_REQUEST_REFRESH_RATE
2
PRIORITY_APP_REQUEST_SIZE
3
PRIORITY_USER_SETTING_PEAK_REFRESH_RATE
4
PRIORITY_LOW_POWER_MODE
5
当外层循环第一次执行时 :lowestConsideredPriority = PRIORITY_LOW_BRIGHTNESS = 0
内层循环会遍历所有Vote(包含-1,和当前displayId,这里是0):
算出的minRefreshRate = Infinity, maxRefreshRate =0
当然,在filterModes中是找不到合适的mode的,所以优先级+1,继续搜索
外层循环第二次执行时 :lowestConsideredPriority = PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1
此时排除优先级为0的所有Vote,其实结果还是一样,所以lowestConsideredPriority继续+1
外层循环第三次执行时 :lowestConsideredPriority = PRIORITY_APP_REQUEST_REFRESH_RATE = 2
此时排除优先级小于PRIORITY_APP_REQUEST_REFRESH_RATE的所有Vote,结果还是一样,所以lowestConsideredPriority继续+1
外层循环第四次执行时 :lowestConsideredPriority = PRIORITY_APP_REQUEST_SIZE = 3
此时排除优先级小于PRIORITY_APP_REQUEST_SIZE的所有Vote,结果还是一样,所以lowestConsideredPriority继续+1
外层循环第五次执行时 :lowestConsideredPriority = PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4
此时排除优先级小于PRIORITY_USER_SETTING_PEAK_REFRESH_RATE的所有Vote
内层循环其实只有两个选项:
PRIORITY_LOW_POWER_MODE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=60.0}
PRIORITY_USER_SETTING_PEAK_REFRESH_RATE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=120.0}
此时结果为minRefreshRate = 60, maxRefreshRate =0,当然还是没有有效的modeId
外层循环第六次执行时 :lowestConsideredPriority = PRIORITY_LOW_POWER_MODE = 5
只有选项:PRIORITY_LOW_POWER_MODE -> Vote{width=-1, height=-1, minRefreshRate=0.0, maxRefreshRate=60.0}
所以最终的minRefreshRate = 0.0, maxRefreshRate = 60.0,width=1080, height=2376
最后满足条件的modeId就只有mSupportedModesByDisplay中的0了.
最终算出来了modeId, 这里面计算复杂,弯弯绕绕,为什么Google如此设计呢,个人猜测是为了尽可能满足低优先级下的刷新率要求,并不是优先级最高就能决定modeId的取值, 而是找到尽快满足更多优先级下合适刷新率的modeId集提供给SurfaceFlinger选择.
继续往下看,framework将这个modeId集传给SurfaceFlinger.
三. 通知SurfaceFlinger变化 1 2 3 4 @Override public void performTraversal (SurfaceControl.Transaction t) { performTraversalInternal(t); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @VisibleForTesting void performTraversalInternal (SurfaceControl.Transaction t) { synchronized (mSyncRoot) { if (!mPendingTraversal) { return ; } mPendingTraversal = false ; performTraversalLocked(t); } for (DisplayTransactionListener listener : mDisplayTransactionListeners) { listener.onDisplayTransaction(t); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void performTraversalLocked (SurfaceControl.Transaction t) { clearViewportsLocked(); final int count = mDisplayDevices.size(); for (int i = 0 ; i < count; i++) { DisplayDevice device = mDisplayDevices.get(i); configureDisplayLocked(t, device); device.performTraversalLocked(t); } ...... }
1 2 3 4 5 6 7 8 9 private void configureDisplayLocked (SurfaceControl.Transaction t, DisplayDevice device) { ...... LogicalDisplay display = findLogicalDisplayForDeviceLocked(device); ...... display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF); ...... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void configureDisplayLocked (SurfaceControl.Transaction t, DisplayDevice device, boolean isBlanked) { device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack); if (device == mPrimaryDisplayDevice) { device.setAllowedDisplayModesLocked(mAllowedDisplayModes); device.setRequestedColorModeLocked(mRequestedColorMode); } else { device.setAllowedDisplayModesLocked(new int [] {0 }); device.setRequestedColorModeLocked(0 ); } ...... }
3.3 LocalDisplayAdapter.LocalDisplayDevice.setAllowedDisplayModesLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Override public void setAllowedDisplayModesLocked (int [] modes) { updateAllowedModesLocked(modes); } public void updateAllowedModesLocked (int [] allowedModes) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { return ; } if (updateAllowedModesInternalLocked(allowedModes)) { updateDeviceInfoLocked(); } } public boolean updateAllowedModesInternalLocked (int [] allowedModes) { if (DEBUG) { Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" + Arrays.toString(allowedModes) + ")" ); } int [] allowedPhysIndexes = new int [allowedModes.length]; int size = 0 ; for (int modeId : allowedModes) { int physIndex = findDisplayInfoIndexLocked(modeId); if (physIndex < 0 ) { Slog.w(TAG, "Requested mode ID " + modeId + " not available," + " dropping from allowed set." ); } else { allowedPhysIndexes[size++] = physIndex; } } ...... SurfaceControl.setAllowedDisplayConfigs(getDisplayTokenLocked(), allowedPhysIndexes); int activePhysIndex = SurfaceControl.getActiveConfig(getDisplayTokenLocked()); return updateActiveModeLocked(activePhysIndex); }
3.4 SurfaceControl.setAllowedDisplayConfigs 1 2 3 4 5 6 7 8 9 10 11 12 13 private static native boolean nativeSetAllowedDisplayConfigs (IBinder displayToken, int [] allowedConfigs) ;public static boolean setAllowedDisplayConfigs (IBinder displayToken, int [] allowedConfigs) { if (displayToken == null ) { throw new IllegalArgumentException ("displayToken must not be null" ); } if (allowedConfigs == null ) { throw new IllegalArgumentException ("allowedConfigs must not be null" ); } return nativeSetAllowedDisplayConfigs(displayToken, allowedConfigs); }
3.5 android_view_SurfaceControl::nativeSetAllowedDisplayConfigs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static jboolean nativeSetAllowedDisplayConfigs (JNIEnv* env, jclass clazz, jobject tokenObj, jintArray configArray) { sp<IBinder> token (ibinderForJavaObject(env, tokenObj)) ; if (token == nullptr ) return JNI_FALSE; std::vector<int32_t > allowedConfigs; jsize configArraySize = env->GetArrayLength (configArray); allowedConfigs.reserve (configArraySize); jint* configArrayElements = env->GetIntArrayElements (configArray, 0 ); for (int i = 0 ; i < configArraySize; i++) { allowedConfigs.push_back (configArrayElements[i]); } env->ReleaseIntArrayElements (configArray, configArrayElements, 0 ); size_t result = SurfaceComposerClient::setAllowedDisplayConfigs (token, allowedConfigs); return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; }
3.5.1 SurfaceComposerClient::setAllowedDisplayConfigs 1 2 3 4 5 6 status_t SurfaceComposerClient::setAllowedDisplayConfigs ( const sp<IBinder>& displayToken, const std::vector<int32_t >& allowedConfigs) { return ComposerService::getComposerService ()->setAllowedDisplayConfigs (displayToken, allowedConfigs); }
3.6 SurfaceFlinger::setAllowedDisplayConfigs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 status_t SurfaceFlinger::setAllowedDisplayConfigs (const sp<IBinder>& displayToken, const std::vector<int32_t >& allowedConfigs) { ATRACE_CALL (); if (!displayToken || allowedConfigs.empty ()) { return BAD_VALUE; } if (mDebugDisplayConfigSetByBackdoor) { return NO_ERROR; } postMessageSync (new LambdaMessage ([&]() { const auto display = getDisplayDeviceLocked (displayToken); if (!display) { ALOGE ("Attempt to set allowed display configs for invalid display token %p" , displayToken.get ()); } else if (display->isVirtual ()) { ALOGW ("Attempt to set allowed display configs for virtual display" ); } else { Mutex::Autolock lock (mStateLock); setAllowedDisplayConfigsInternal (display, allowedConfigs); } })); return NO_ERROR; }
好了,app设置屏幕显示刷新的流程就走完了,接下来就是SurfaceFlinger去和硬件交互,通知切换刷新率了。