Android Q安全锁屏下进入google photos不弹bouncer界面

复现步骤
Andoird 10平台,设置锁屏密码(pin/passowrd/pattern)->双击power键启动camera->拍照->点击左下角预览->进入google photo->(KO,此时无法弹出bouncer界面输密码,而是直接返回锁屏界面)
还有个比较奇怪的现象是当photos启动之后主activity未被销毁即没有点击back就不会复现此问题了,当主activity销毁后就会复现

初步分析
首先进行交叉验证确认是camera,google photos,还是平台问题,将公司camera装到google参考机也能复现此问题,确认可能是Android平台问题,需要着手分析源码

Keyguard这一块的主要还是由framework来控制的,PhoneWindowManager回调KeyguardViewMediator的各种方法,KeyguardViewMediator再来控制KeyguardBouncer,所以此问题原因还是在framework那边有什么条件没有满足bouncer弹出来,没有走到Keyguard这边的流程。

Keyguard和framework的交互主要由KeyguardController来控制

visibilitiesUpdated
此方法中的requestDismissKeyguard值是直接影响是否走到处理Keyguard流程的开关
/frameworks/base/services/core/java/com/android/server/wm/KeyguardController.java

private void visibilitiesUpdated() {
boolean requestDismissKeyguard = false;
for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx–) {
final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
final KeyguardDisplayState state = getDisplay(display.mDisplayId);
state.visibilitiesUpdated(this, display);
requestDismissKeyguard |= state.mRequestDismissKeyguard;
}
// Dismissing Keyguard happens globally using the information from all displays.
if (requestDismissKeyguard) {
//Keyguard dismiss的核心方法
handleDismissKeyguard();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
state.visibilitiesUpdated
此方法里满足了一定条件则会将mRequestDismissKeyguard置为true,所以bouncer不弹出来肯定是这里的某个条件没有满足,mDismissingKeyguardActivity等于当前处于Top的想要dismiss keyguard的Activity,lastDismissActivity用来保存mDismissingKeyguardActivity的值

void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
final boolean lastOccluded = mOccluded;
final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
mRequestDismissKeyguard = false;
mOccluded = false;
mDismissingKeyguardActivity = null;
final ActivityStack stack = getStackForControllingOccluding(display);
if (stack != null) {
final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
&& stack.topRunningActivityLocked() == topDismissing
&& controller.canShowWhileOccluded(
true /* dismissKeyguard /,
false /
showWhenLocked */));
if (stack.getTopDismissingKeyguardActivity() != null) {
mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
}
// FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
if (mDisplayId != DEFAULT_DISPLAY) {
mOccluded |= stack.canShowWithInsecureKeyguard()
&& controller.canDismissKeyguard();
}
}
// TODO(b/123372519): isShowingDream can only works on default display.
if (mDisplayId == DEFAULT_DISPLAY) {
mOccluded |= controller.mWindowManager.isShowingDream();
}

      if (lastOccluded != mOccluded) {
            controller.handleOccludedChanged(mDisplayId);
       }
     if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
                && mDismissingKeyguardActivity != null
                && controller.mWindowManager.isKeyguardSecure(
                        controller.mService.getCurrentUserId())) {
            mRequestDismissKeyguard = true;
        }
    }

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
看一下这几个条件判断
条件一,lastDismissActivity != mDismissingKeyguardActivity,这个条件很容易满足,lastDismissActivity既然是保存mDismissingKeyguardActivity的状态,那他们容易不等
条件二,!mOccluded 此条件比较可能不满足
条件三,mDismissingKeyguardActivity != null,此条件也很容易满足
条件四,controller.mWindowManager.isKeyguardSecure(
controller.mService.getCurrentUserId())代表当前设备是否处于保护模式即是否设置了密码

这几个条件唯一可能的就是条件二,通过log调试也是发现进google photos时不弹bouncer主要就是 !mOccluded 不满足

mOccluded这个值的控制条件也比较多,通过log调试发现主要是stack.topActivityOccludesKeyguard()控制了mOccluded

mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
&& stack.topRunningActivityLocked() == topDismissing
&& controller.canShowWhileOccluded(
true /* dismissKeyguard /,
false /
showWhenLocked */));
1
2
3
4
5
topActivityOccludesKeyguard
/frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
找mTopActivityOccludesKeyguard赋值的代码

/**
* @return true if the top visible activity wants to occlude the Keyguard, false otherwise
*/
boolean topActivityOccludesKeyguard() {
return mTopActivityOccludesKeyguard;
}
1
2
3
4
5
6
checkKeyguardVisibility
看注释的意思是只有当前top activity可以决定是否关闭Keyguard,锁屏启动应用一般有两种,第一种不需要解锁直接显示在锁屏之上,这种不需要弹出bouncer让用户解锁,因为它们可以直接显示了,类似闹钟,来电界面,另一种则需要先解锁再显示,到这里可以断定启动Photos用的第一种启动,但是由于Photos作为普通activity显然windows层级太低只能显示在keyguard底下,接着需要继续调查Photos为何会用第一种方式启动

boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {

boolean showWhenLocked = false;

showWhenLocked = r.canShowWhenLocked();
// Only the top activity may control occluded, as we can’t occlude the Keyguard if the
// top app doesn’t want to occlude it.
if (isTop) {
mTopActivityOccludesKeyguard |= showWhenLocked;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
r.canShowWhenLocked
/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

boolean canShowWhenLocked() {
if (!inPinnedWindowingMode() && (mShowWhenLocked
|| (mAppWindowToken != null && mAppWindowToken.containsShowWhenLockedWindow()))) {
return true;
} else if (mInheritShownWhenLocked) {
ActivityRecord r = getActivityBelow();
return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
|| (r.mAppWindowToken != null
&& r.mAppWindowToken.containsShowWhenLockedWindow()));
} else {
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
showWhenLocked也是由多个值控制的,继续跟mAppWindowToken.containsShowWhenLockedWindow()

containsShowWhenLockedWindow
此方法返回值根据activity是否设置FLAG_SHOW_WHEN_LOCKED来控制的,
/frameworks/base/services/core/java/com/android/server/wm/AppWindowToken.java

boolean containsShowWhenLockedWindow() {
    ......
    for (int i = mChildren.size() - 1; i >= 0; i--) {
        if ((mChildren.get(i).mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
            return true;
        }
    }
    return false;
}

1
2
3
4
5
6
7
8
9
FLAG_SHOW_WHEN_LOCKED定义在WindowManager中,含义是是否能够显示在锁屏之上,到这里大概明白了,google photos设置了FLAG_SHOW_WHEN_LOCKED,为什么要设置这个flag,应用设置的还是系统设置的,为什么首次启动的photos会有这个flag,不是首次就没有

public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
1
准备添加log调试,直接打印mChildren.get(i)的值,

首次启动photos的log:

com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.063 825 2735 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.063 825 2735 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.481 825 2593 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.481 825 2593 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.481 825 2593 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.481 825 2593 D djtang : mChildren.get(i) = :Window{dc012d7 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:31:35.616 825 1089 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.616 825 1089 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.617 825 1089 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.617 825 1089 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.617 825 1089 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.632 825 2735 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
12-17 08:31:35.632 825 2735 D djtang : mChildren.get(i) = :Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
非首次启动photos的log:

12-17 08:34:25.176 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:25.176 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:25.195 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:25.195 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:25.195 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:25.195 825 2206 D djtang : mChildren.get(i) = :Window{7965666 u0 com.tcl.camera/com.android.camera.SecureCameraActivity}
12-17 08:34:29.747 825 2206 D djtang : mChildren.get(i) = :Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}
12-17 08:34:29.748 825 2206 D djtang : mChildren.get(i) = :Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}
12-17 08:34:29.748 825 2206 D djtang : mChildren.get(i) = :Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}
12-17 08:34:29.783 825 2208 D djtang : mChildren.get(i) = :Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}
12-17 08:34:29.783 825 2208 D djtang : mChildren.get(i) = :Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}
1
2
3
4
5
6
7
8
9
10
11
差别是首次启动的photos打印的windowState为Window{5c6f6ae u0 Splash Screen com.google.android.apps.photos},非首次启动的photos打印的windowState为Window{ae03481 u0 com.google.android.apps.photos/com.google.android.apps.photos.pager.HostPhotoPagerActivity}

/frameworks/base/services/core/java/com/android/server/wm/WindowState.java

@Override
public String toString() {
final CharSequence title = getWindowTag();
if (mStringNameCache == null || mLastTitle != title || mWasExiting != mAnimatingExit) {
mLastTitle = title;
mWasExiting = mAnimatingExit;
mStringNameCache = “Window{” + Integer.toHexString(System.identityHashCode(this))
+ " u" + UserHandle.getUserId(mOwnerUid)
+ " " + mLastTitle + (mAnimatingExit ? " EXITING}" : “}”);
}
return mStringNameCache;
}
CharSequence getWindowTag() {
CharSequence tag = mAttrs.getTitle();
if (tag == null || tag.length() <= 0) {
tag = mAttrs.packageName;
}
return tag;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
打印的是getTitle,其实非首次启动的photos的打印比较正常,包名加activity名字,关键是首次启动的photos有个"Splash Screen",log中发现其他应用首次启动的WindowState也为"Splash Screen" + 包名,如:
Window{df6f4ad u0 Splash Screen com.android.settings}

在代码中全局搜索"Splash Screen",

addSplashScreen
找到了,addSplashScreen中给首次启动的应用主activity设置了title为"Splash Screen " + packageName,并且在mKeyguardOccluded为ture时给应用设置flag为FLAG_SHOW_WHEN_LOCKED
/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

@Override
public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
int logo, int windowFlags, Configuration overrideConfig, int displayId) {

if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}

params.setTitle("Splash Screen " + packageName);

}
1
2
3
4
5
6
7
8
9
10
11
12
找到在哪里设置就好修改了,当我们需要某个应用显示在锁屏之上时我们不必设置此flag
————————————————

原文链接:https://blog.csdn.net/qq_34211365/article/details/103583418

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值