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

		...
}

所以我们就从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;
            }
            ...
      	}
      }

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

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;

				....
}

这里的count是interceptPowerKeyUp传递过来的,等于1,mShortPressOnPowerBehavior值是定义在config.xml中的等于1,代表短按power键需要做的事,1 - Go to sleep (doze)

<!-- Control the behavior when the user short presses the power button.
            0 - Nothing
            1 - Go to sleep (doze)
            2 - Really go to sleep (don't doze)
            3 - Really go to sleep and go home (don't doze)
            4 - Go to home
            5 - Dismiss IME if shown. Otherwise go to home
    -->
    <integer name="config_shortPressOnPowerBehavior">1</integer>

所以调用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;
    }

灭屏的原因挺多的,定义在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;

}

goToSleep

 private void goToSleep(long eventTime, int reason, int flags) {
        mRequestedOrGoingToSleep = true;
        mPowerManager.goToSleep(eventTime, reason, flags);
    }

接着调用到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);
            }
        }

goToSleepInternal

private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }

如果power键灭屏没有传入PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,goToSleepNoUpdateLocked此方法会执行Doze相关策略,我们不去分析此方法返回false的情况,接着调用updatePowerStateLocked

updatePowerStateLocked

private void updatePowerStateLocked() {
           ...
            // Phase 5: Send notifications, if needed.
            finishWakefulnessChangeIfNeededLocked();
            ....
    }

finishWakefulnessChangeIfNeededLocked

private void finishWakefulnessChangeIfNeededLocked() {
          ....  
          mNotifier.onWakefulnessChangeFinished();
        }
    }

这里调到Notifier的onWakefulnessChangeFinished去

Notifier.onWakefulnessChangeFinished

public void onWakefulnessChangeFinished() {
        
        if (mInteractiveChanging) {
            mInteractiveChanging = false;
            handleLateInteractiveChange();
        }
    }

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

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

点击power亮屏时mInteractive为true,灭屏时mInteractive为false,所以我们只看else分支
why是灭屏原因,我们这里获取到的是如下值为2

/** Screen turned off because of power button */
    int OFF_BECAUSE_OF_USER = 2;

最终调到了PhoneWindowManager的finishedGoingToSleep方法

finishedGoingToSleep

public void finishedGoingToSleep(int why) {
        ......
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onFinishedGoingToSleep(why,
                    mCameraGestureTriggeredDuringGoingToSleep);
        }
        if (mDisplayFoldController != null) {
            mDisplayFoldController.finishedGoingToSleep();
        }
        mCameraGestureTriggeredDuringGoingToSleep = false;
    }

接着调用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;
    }

这里的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);
            ...
        }

调到了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;
            }

    }

如果不走启动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);
    }

这个方法里面首先会对诸多条件进行验证,只有满足锁屏显示条件才调用showLocked显示锁屏

showLocked

private void showLocked(Bundle options) {
        ...
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
        
    }

发送SHOW消息,调用handleShow

handleShow

private void handleShow(Bundle options) {
           .....
            mStatusBarKeyguardViewManager.show(options);
            ......
    }

StatusBarKeyguardViewManager.show

 public void show(Bundle options) {
        mShowing = true;
        ...
        //显示锁屏,隐藏bouncer
        reset(true /* hideBouncerWhenShowing */);
        
    }

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键完成锁屏加载的大致流程,仅仅分析了方法的调用链,很多细节流程没有时间去看,也不用现在花时间去看,我们了解了整个流程后遇到这条链上的问题再顺着某一点分析是很容易的

总结一下:

  1. 首先PhoneWindowManager收到power Key的点击事件,并处理其按下和抬起事件
  2. 抬起事件会一步一步通过PowerManagerService->Notifier->PhoneWindowManager,再通过Binder调用到SystemUI进程的KeyguardViewMediator中
  3. KeyguardViewMediator的锁屏核心方法doKeyguardLocked为入口一步一步调用最后将滑动锁屏显示出来
发布了39 篇原创文章 · 获赞 57 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_34211365/article/details/104648563