AndroidQ SystemUI之power键灭屏锁屏流程

本篇接着分析锁屏相关流程,通常我们点击power键会灭屏,灭屏时就会加载锁屏,以便用户能在下次亮屏时第一时间看到锁屏,我们就来看看点击power键灭屏锁屏的流程

Android的事件分发流程大概是:InputReader读取dev/input设备节点的原始事件->通过封装传递给InputDispatcher->InputDispatcher找到对应处理事件的窗口并将事件分发到上层ViewRootImpl->ViewRootImpl再进行上层的事件分发…

对于Key事件,InputDispatcher在分发前会先将事件发到上层PhoneWindowManager中,有个提前拦截的机会:

InputDispatcher.notifyKey
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {

mPolicy->interceptKeyBeforeQueueing(&event, /byref/ policyFlags);

 needWake = enqueueInboundEventLocked(newEntry);

	...

}

1
2
3
4
5
6
7
8
9
所以我们就从PhoneWindowManager的interceptKeyBeforeQueueing方法作为起始点分析power按键的灭屏流程

interceptKeyBeforeQueueing
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
if (!mSystemBooted) {
return 0;
}

switch (keyCode) {

case KeyEvent.KEYCODE_POWER: {

            ...
            
            if (down) {
                //按下事件
                interceptPowerKeyDown(event, interactive);
            } else {
                //抬起事件
                interceptPowerKeyUp(event, interactive, canceled);
            }
            break;
        }
        ...
  	}
  }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
根据keyCode处理各种类型的Key事件,我们这里只关心power事件,down为true代表按下,false代表抬起,灭屏是通过抬起事件完成的,这是因为power键组合需要完成多种任务,如长按出现GlobalActionDialog,双击进入camera,power+音量下完成截屏等,如果在按下处理灭屏肯定是不行的

interceptPowerKeyUp
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {

mPowerKeyPressCounter += 1;
powerPress(eventTime, interactive, mPowerKeyPressCounter);

// Done. Reset our state.
finishPowerKeyPress();
}

1
2
3
4
5
6
7
8
9
powerPress
private void powerPress(long eventTime, boolean interactive, int count) {

if (count == 2) {

} else if (count == 3) {

} else if (interactive && !mBeganFromNonInteractive) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
case SHORT_PRESS_POWER_GO_TO_SLEEP:
goToSleepFromPowerButton(eventTime, 0);
break;

			....

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里的count是interceptPowerKeyUp传递过来的,等于1,mShortPressOnPowerBehavior值是定义在config.xml中的等于1,代表短按power键需要做的事,1 - Go to sleep (doze)

<integer name="config_shortPressOnPowerBehavior">1</integer>

1
2
3
4
5
6
7
8
9
所以调用goToSleepFromPowerButton方法

goToSleepFromPowerButton
private boolean goToSleepFromPowerButton(long eventTime, int flags) {
/*
在我们真正进入睡眠之前,请检查最后一个唤醒原因。
如果设备最近刚从手势中醒来(例如用户抬起设备)
然后忽略睡眠指令。 这是因为用户倾向于在拿起设备时立即按下电源按钮,
而在这种情况下我们不希望设备重新进入睡眠状态。
*/
final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) {
final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
final long now = SystemClock.uptimeMillis();
if (mPowerButtonSuppressionDelayMillis > 0
&& (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {

            return false;
        }
    }
    //这里传递了一个灭屏的原因,为点击power键灭屏
    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
    return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
灭屏的原因挺多的,定义在PowerManager中

public final class PowerManager {
//某个应用调用灭屏相关API
public static final int GO_TO_SLEEP_REASON_APPLICATION = GO_TO_SLEEP_REASON_MIN;

//通过设备管理策略设置灭屏
public static final int GO_TO_SLEEP_REASON_DEVICE_ADMIN = 1;

//超时灭屏
public static final int GO_TO_SLEEP_REASON_TIMEOUT = 2;

//对于翻盖类型手机,关闭翻盖灭屏
public static final int GO_TO_SLEEP_REASON_LID_SWITCH = 3;

//点击power键灭屏
public static final int GO_TO_SLEEP_REASON_POWER_BUTTON = 4;

//因为HDMI灭屏
public static final int GO_TO_SLEEP_REASON_HDMI = 5;

//点击sleep键灭屏
public static final int GO_TO_SLEEP_REASON_SLEEP_BUTTON = 6;

//无障碍服务请求灭屏
public static final int GO_TO_SLEEP_REASON_ACCESSIBILITY = 7;

//强制暂停灭屏
public static final int GO_TO_SLEEP_REASON_FORCE_SUSPEND = 8;

}
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
goToSleep
private void goToSleep(long eventTime, int reason, int flags) {
mRequestedOrGoingToSleep = true;
mPowerManager.goToSleep(eventTime, reason, flags);
}
1
2
3
4
接着调用到PowerManagerService的goToSleep方法

PowerManagerService.goToSleep
@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {

try {
goToSleepInternal(eventTime, reason, flags, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
1
2
3
4
5
6
7
8
9
goToSleepInternal
private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
synchronized (mLock) {
if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
updatePowerStateLocked();
}
}
}
1
2
3
4
5
6
7
如果power键灭屏没有传入PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,goToSleepNoUpdateLocked此方法会执行Doze相关策略,我们不去分析此方法返回false的情况,接着调用updatePowerStateLocked

updatePowerStateLocked
private void updatePowerStateLocked() {

// Phase 5: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();

}
1
2
3
4
5
6
finishWakefulnessChangeIfNeededLocked
private void finishWakefulnessChangeIfNeededLocked() {

mNotifier.onWakefulnessChangeFinished();
}
}
1
2
3
4
5
这里调到Notifier的onWakefulnessChangeFinished去

Notifier.onWakefulnessChangeFinished
public void onWakefulnessChangeFinished() {

    if (mInteractiveChanging) {
        mInteractiveChanging = false;
        handleLateInteractiveChange();
    }
}

1
2
3
4
5
6
7
handleLateInteractiveChange
private void handleLateInteractiveChange() {
synchronized (mLock) {

if (mInteractive) {

} else {

final int why = translateOffReason(mInteractiveChangeReason);
mHandler.post(new Runnable() {
@Override
public void run() {

mPolicy.finishedGoingToSleep(why);
}
});

            // 发送广播
            ...
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
点击power亮屏时mInteractive为true,灭屏时mInteractive为false,所以我们只看else分支
why是灭屏原因,我们这里获取到的是如下值为2

/** Screen turned off because of power button */
int OFF_BECAUSE_OF_USER = 2;
1
2
最终调到了PhoneWindowManager的finishedGoingToSleep方法

finishedGoingToSleep
public void finishedGoingToSleep(int why) {

if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedGoingToSleep(why,
mCameraGestureTriggeredDuringGoingToSleep);
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedGoingToSleep();
}
mCameraGestureTriggeredDuringGoingToSleep = false;
}
1
2
3
4
5
6
7
8
9
10
11
接着调用KeyguardServiceDelegate的onFinishedGoingToSleep方法,mCameraGestureTriggeredDuringGoingToSleep这个值代表点击的power键行为是否满足启动camera的要求

onFinishedGoingToSleep
public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
if (mKeyguardService != null) {
mKeyguardService.onFinishedGoingToSleep(why, cameraGestureTriggered);
}
mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
}
1
2
3
4
5
6
这里的mKeyguardService = new KeyguardServiceWrapper(mContext,
IKeyguardService.Stub.asInterface(service), mCallback);
这是一个binder调用,最终到了SystemUI进程中

KeyguardService.onFinishedGoingToSleep
@Override // Binder interface
public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
checkPermission();
mKeyguardViewMediator.onFinishedGoingToSleep(reason, cameraGestureTriggered);

}
1
2
3
4
5
6
调到了Keyguard的核心类KeyguardViewMediator中

onFinishedGoingToSleep
public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
synchronized (this) {

//移除KEYGUARD_DONE_PENDING_TIMEOUT消息
resetKeyguardDonePendingLocked();
//如果灭屏之前Bouncer正在显示则需要通知其进去onPause状态
notifyFinishedGoingToSleep();
//cameraGestureTriggered为true代表不会显示锁屏,而是启动camera
if (cameraGestureTriggered) {

          mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
                    PowerManager.WAKE_REASON_CAMERA_LAUNCH,
                    "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
            mPendingLock = false;
            mPendingReset = false;
        }

        if (mPendingReset) {
            resetStateLocked();
            mPendingReset = false;
        }

        if (mPendingLock) {
            doKeyguardLocked(null);
            mPendingLock = false;
        }

}

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
如果不走启动camera的分支则会调用resetStateLocked和doKeyguardLocked,这两个方法其实最终都是通过调用StatusBarKeyguardViewManager的reset方法而锁屏,二者的区别是,resetStateLocked因为已经在锁屏界面,所以不需要AMS,WMS修改锁屏窗口相关参数以及Activity先关状态改变

doKeyguardLocked则会对是否显示锁屏进行一系列条件判断,都验证通过才会真正去show,如是否三方应用禁用了锁屏,是否在Setup Wizard界面等

doKeyguardLocked这个方法其实在AndroidQ SystemUI之锁屏加载(上)滑动锁屏已经详细分析过了,我们再来看看

doKeyguardLocked
private void doKeyguardLocked(Bundle options) {
if (KeyguardUpdateMonitor.CORE_APPS_ONLY) {
// 在半启动的cryptkeeper阶段不显示
if (DEBUG) Log.d(TAG, “doKeyguard: not showing because booting to cryptkeeper”);
return;
}

    // 被三方应用禁用则不显示
    if (!mExternallyEnabled) {
        if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");

        mNeedToReshowWhenReenabled = true;
        return;
    }

    // Keyguard已经显示则不需要重新显示,reset一下就行了
    if (mStatusBarKeyguardViewManager.isShowing()) {
        if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
        resetStateLocked();
        return;
    }

    ....

    if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
    showLocked(options);
}

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
这个方法里面首先会对诸多条件进行验证,只有满足锁屏显示条件才调用showLocked显示锁屏

showLocked
private void showLocked(Bundle options) {

Message msg = mHandler.obtainMessage(SHOW, options);
mHandler.sendMessage(msg);

}

1
2
3
4
5
6
发送SHOW消息,调用handleShow

handleShow
private void handleShow(Bundle options) {

mStatusBarKeyguardViewManager.show(options);

}
1
2
3
4
5
StatusBarKeyguardViewManager.show
public void show(Bundle options) {
mShowing = true;

//显示锁屏,隐藏bouncer
reset(true /* hideBouncerWhenShowing */);

}

1
2
3
4
5
6
7
reset
public void reset(boolean hideBouncerWhenShowing) {
if (mShowing) {
//锁屏被遮挡,如闹钟,来电界面
if (mOccluded && !mDozing) {
mStatusBar.hideKeyguard();
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
} else {
//否则走正常显示锁屏流程
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
}
}

showBouncerOrKeyguard
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
//执行显示bouncer的情况,插入带PIN或PUK码的SIM卡时走这里
if (mBouncer.needsFullscreenBouncer() && !mDozing) {

        mStatusBar.hideKeyguard();
        mBouncer.show(true /* resetSecuritySelection */);
    } else {
        mStatusBar.showKeyguard();
        if (hideBouncerWhenShowing) {
            hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
            mBouncer.prepare();
        }
    }
    updateStates();
}

showBouncerOrKeyguard有两种情况,显示滑动锁屏还是bouncer,只有插入带PIN或PUK码的SIM卡时显示Bouncer,其他情况则showKeyguard并且hideBouncer

后面的流程就是AndroidQ SystemUI之锁屏加载(上)滑动锁屏 分析一样的,显示滑动锁屏的流程了

好了到此为止我们已经分析完了通过点击power键完成锁屏加载的大致流程,仅仅分析了方法的调用链,很多细节流程没有时间去看,也不用现在花时间去看,我们了解了整个流程后遇到这条链上的问题再顺着某一点分析是很容易的

总结一下:

首先PhoneWindowManager收到power Key的点击事件,并处理其按下和抬起事件
抬起事件会一步一步通过PowerManagerService->Notifier->PhoneWindowManager,再通过Binder调用到SystemUI进程的KeyguardViewMediator中
KeyguardViewMediator的锁屏核心方法doKeyguardLocked为入口一步一步调用最后将滑动锁屏显示出来
————————————————

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值