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();
        }
    }

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

看一下这几个条件判断
条件一,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 */));

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

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;
       }
    ......
}

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

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

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

public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;

准备添加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}

非首次启动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}

差别是首次启动的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;
    }

打印的是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);
		......
}

找到在哪里设置就好修改了,当我们需要某个应用显示在锁屏之上时我们不必设置此flag

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值