学习笔记:
第一次接触 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 值改变。