第一次接触 Occluded 是在唤起安全相机时,见Android 13 双击power键唤起安全相机。当时在这里遇到个bug:双击power键唤起安全相机,却弹出了bouncer界面,把安全相机界面遮挡了,后面就深入去看了下;看了几天都没找出问题,最后慢放复现视频,发现测试在安全相机界面有误触,根本没问题.......。
不过我们是退出锁屏还是进入锁屏,都将走这么个流程到 KeyguardController:
RootWindowContainer#ensureActivitiesVisible() -> DisplayContent#ensureActivitiesVisible() -> Task#ensureActivitiesVisible() -> ActivityTaskSupervisor#beginActivityVisibilityUpdate() -> KeyguardController#updateVisibility()
Occluded 的改变是从 system_server 进程中的 KeyguardController 开始的;
KeyguardController#updateVisibility()
// KeyguardController.java // 确保在完成设置所有可见性之前,根据需要更新锁定屏幕遮挡/关闭/打开屏幕状态 void updateVisibility() { for (int displayNdx = mRootWindowContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx); if (display.isRemoving() || display.isRemoved()) continue; final KeyguardDisplayState state = getDisplayState(display.mDisplayId); // 重点关注 state.updateVisibility(this, display); if (state.mRequestDismissKeyguard) { handleDismissKeyguard(display.getDisplayId()); } } }
state 为 KeyguardController 内部类KeyguardDisplayState 的对象;
KeyguardController#KeyguardDisplayState.updateVisibility()
// KeyguardController.java # KeyguardDisplayState.class void updateVisibility(KeyguardController controller, DisplayContent display) { final boolean lastOccluded = mOccluded; // 省略部分代码...... mOccluded = false; final Task task = getRootTaskForControllingOccluding(display); final ActivityRecord top = task != null ? task.getTopNonFinishingActivity() : null; if (top != null) { // 省略部分代码...... // 只有顶级 activity 可以控制遮挡,因为如果顶级应用不想遮挡键盘,我们就无法遮挡键盘。 occludedByActivity = mTopOccludesActivity != null || (mDismissingKeyguardActivity != null && task.topRunningActivity() == mDismissingKeyguardActivity && controller.canShowWhileOccluded( true /* dismissKeyguard */, false /* showWhenLocked */)); // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display. if (mDisplayId != DEFAULT_DISPLAY) { occludedByActivity |= display.canShowWithInsecureKeyguard() && controller.canDismissKeyguard(); } } mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null && top.getActivityType() == ACTIVITY_TYPE_DREAM); mOccluded = mShowingDream || occludedByActivity; mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity && !mOccluded && !mKeyguardGoingAway && mDismissingKeyguardActivity != null; // 省略部分代码...... if (lastOccluded != mOccluded) { // 重点关注,当遮挡状态改变时调用 controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity); } else if (!lastKeyguardGoingAway && mKeyguardGoingAway) { controller.handleKeyguardGoingAwayChanged(display); } }
在该方法里对变量 mOccluded 进行了更新,完成了 KeyguardController 的 occluded 属性改变。
KeyguardController# handleOccludedChanged()
// KeyguardController.java private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) { if (displayId != DEFAULT_DISPLAY) { updateKeyguardSleepToken(displayId); return; } // 重点关注 mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY)); if (isKeyguardLocked(displayId)) { mService.deferWindowLayout(); try { mRootWindowContainer.getDefaultDisplay() .requestTransitionAndLegacyPrepare( isDisplayOccluded(DEFAULT_DISPLAY) ? TRANSIT_KEYGUARD_OCCLUDE : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */); updateKeyguardSleepToken(DEFAULT_DISPLAY); mWindowManager.executeAppTransition(); } finally { mService.continueWindowLayout(); } } dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null ? topActivity.getRootTask() : null); }
mWindowManager为 WindowManagerService 对象,mPolicy 是 WindowManagerPolicy 接口的对象,而 WindowManagerPolicy 被 PhoneWindowManager 继承,实现了其相关方法;则上述代码将调用到:PhoneWindowManager#onKeyguardOccludedChangedLw()
// PhoneWindowManager.java @Override public void onKeyguardOccludedChangedLw(boolean occluded) { if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing() && !WindowManagerService.sEnableShellTransitions) { mPendingKeyguardOccluded = occluded; mKeyguardOccludedChanged = true; } else { // 重点关注 setKeyguardOccludedLw(occluded, false /* force */, false /* transitionStarted */); } }
这里当 mKeyguardDelegate.isShowing() 为 true 时,将会绕过 setKeyguardOccludedLw(),但是在后面的过渡动画会再调用 到setKeyguardOccludedLw(),该处过渡动画流程不作介绍。
直接看 PhoneWindowManager#setKeyguardOccludedLw():
// PhoneWindowManager.java // 更新 Keyguard 的遮挡状态 private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force, boolean transitionStarted) { if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded); mKeyguardOccludedChanged = false; if (isKeyguardOccluded() == isOccluded && !force) { return false; } final boolean showing = mKeyguardDelegate.isShowing(); final boolean animate = showing && !isOccluded; // 当为keyguard((un)occlude 转换启用远程动画时, // KeyguardService 使用远程动画开始作为更新其遮挡状态的信号,因此我们不需要在这里通知。 final boolean notify = !WindowManagerService.sEnableRemoteKeyguardOccludeAnimation || !transitionStarted; // 重点关注 mKeyguardDelegate.setOccluded(isOccluded, animate, notify); return showing; }
这里改变了mKeyguardDelegate 的值,也就完成了PhoneWidnowManager 的 occluded 属性改变。
KeyguardServiceDelegate#setOccluded()
// KeyguardServiceDelegate.java public void setOccluded(boolean isOccluded, boolean animate, boolean notify) { if (mKeyguardService != null && notify) { if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate); // 这里最终是调到 KeyguardService 的; mKeyguardService.setOccluded(isOccluded, animate); } mKeyguardState.occluded = isOccluded; }
KeyguardService#setOccluded()
// KeyguardService.java @Override // Binder interface public void setOccluded(boolean isOccluded, boolean animate) { Log.d(TAG, "setOccluded(" + isOccluded + ")"); Trace.beginSection("KeyguardService.mBinder#setOccluded"); checkPermission(); // 重点关注 mKeyguardViewMediator.setOccluded(isOccluded, animate); Trace.endSection(); }
KeyguardViewMediator#setOccluded()
// KeyguardViewMediator.java /** * 当键盘被另一个窗口遮挡时通知我们 */ public void setOccluded(boolean isOccluded, boolean animate) { Log.d(TAG, "setOccluded(" + isOccluded + ")"); Trace.beginSection("KeyguardViewMediator#setOccluded"); if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded); mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_TRANSITION_FROM_AOD); mHandler.removeMessages(SET_OCCLUDED); Message msg = mHandler.obtainMessage(SET_OCCLUDED, isOccluded ? 1 : 0, animate ? 1 : 0); // 重点关注 mHandler.sendMessage(msg); Trace.endSection(); }
上述代码通过 Handler 发送了一条消息,调用到 KeyguardViewMediator#handleSetOccluded()
// KeyguardViewMediator.java private void handleSetOccluded(boolean isOccluded, boolean animate) { Trace.beginSection("KeyguardViewMediator#handleSetOccluded"); Log.d(TAG, "handleSetOccluded(" + isOccluded + ")"); synchronized (KeyguardViewMediator.this) { if (mHiding && isOccluded) { // We're in the process of going away but WindowManager wants to show a // SHOW_WHEN_LOCKED activity instead. // TODO(bc-unlock): Migrate to remote animation. startKeyguardExitAnimation(0, 0); } if (mOccluded != isOccluded) { mOccluded = isOccluded; mUpdateMonitor.setKeyguardOccluded(isOccluded); // 重点关注 mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate && mDeviceInteractive); adjustStatusBarLocked(); } } Trace.endSection(); }
这里完成了 mOccluded 值的改变,并把值传到 StatusBarKeyguardViewManager。
KeyguardViewController 被 StatusBarKeyguardViewManager 继承,实现了其相关方法。则继续看:
StatusBarKeyguardViewManager #setOccluded():
// StatusBarKeyguardViewManager.java @Override public void setOccluded(boolean occluded, boolean animate) { final boolean isOccluding = !mOccluded && occluded; final boolean isUnOccluding = mOccluded && !occluded; setOccludedAndUpdateStates(occluded); if (mShowing && isOccluding) { SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED, SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED); if (mCentralSurfaces.isInLaunchTransition()) { final Runnable endRunnable = new Runnable() { @Override public void run() { ///M: [ALPS01807921] /// mOccluded may be changed before the runnable is executed. if (mOccluded) { mNotificationShadeWindowController.setKeyguardOccluded(mOccluded); reset(true /* hideBouncerWhenShowing */); } else { Log.d(TAG, "setOccluded.run() - mOccluded was set to false") ; } } }; mCentralSurfaces.fadeKeyguardAfterLaunchTransition( null /* beforeFading */, endRunnable, endRunnable); return; } // 省略部分代码...... }
至此,完成了从系统到SystemUI Occluded 值改变。