本篇从按下power按键后,按键事件从InputManagerService 传到PhoneWindowManager.java开始分析power 按键做屏幕亮灭过程的分析,关于power 按键的其他行为参考另一篇博文(Android 7.0 Power 按键处理流程)
(注:博客园显示的图片很模糊,上传的为大图,可以图片另存为查看)
言归正传,本篇涉及的几个模块(文件)如下,先做个简单的介绍有个直观大概的了解,方便后面流程细节的理解。
Ø PowerManagerService.Java:(/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java)
PMS,是Android系统中的电源处理服务,主要负责电源相关的计算和决策,如是否应该灭屏 或者让屏幕变暗,是否应该让系统休眠等等。
Ø DisplayPowerController.java:(/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java)
DPC,管理显示设备(这里指的显示设备是屏幕)状态,主要处理距离传感器(如打电话时候靠近则灭屏,离开时候屏幕亮起)以及亮灭屏动画(包括根据光感传感器计算屏幕目标亮度值)。在DisplayManagerService.java(DMS)中实例化一个对象,以DMS为桥梁与PMS进行交互通过异步回调机制来通知PMS那些发生了改变。同时也与WMS进行交互。
Ø DisplayPowerState.java:(/frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java)
DPS,管理显示设备的状态仅在DPC中实例化一个对象,是DPC的一部分。
Ø Notifier.java:( /frameworks/base/services/core/java/com/android/server/power/Notifier.java)
将电源状态的重要变化,通过广播通知出去。
Ø ColorFade.java:(/frameworks/base/services/core/java/com/android/server/display/ColorFade.java)
是负责屏幕由关到开,由开到关的一些GL动画,由DPC进行控制。
Ø AutomaticBrightnessController.java:(/frameworks/base/services/core/java/com/android/server/display/AutomaticBrightnessController.java)
主要处理光传感器,将底层上传的参数进行处理计算,将计算的新的亮度值传给DPC来设定屏幕的亮度值(即根据环境的光线强度来计算屏幕的亮暗程度)。
Ø RampAnimator.java:(/frameworks/base/services/core/java/com/android/server/display/RampAnimator.java)
仅仅是屏幕亮度渐变动画。
一、Power按键的上报与处理
详细见【Android 7.0 Power 按键处理流程】此处仅略微的复习一下,方便了后面的理解。
1)Power按键的上报
在InputManagerService收到power按键事件经过一系列的处理和转换最终将会传递到PhoneWindowManager(PWM)的interceptKeyBeforeQueueing()函数来做具体的业务逻辑.下图为上报的流程图。
2)power 按键关于量灭屏处理
关于量灭屏的处理主要在interceptKeyBeforeQueueing()函数中。判断是否是isWakeKey,如果是则在此函数的最后通过调用wakeUp()函数来具体处理,这就拉开了本文的序幕。
注:此函数很长本文删除无关的部分,仅仅保留部分和量灭屏相关的说明具体处理流程即可。详细参考【Android 7.0 Power 按键处理流程】
A: 按键处理判断是否要量灭屏
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
|| event.isWakeKey();
if (interactive || (isInjected && !isWakeKey)) {
// When the device is interactive or the key is injected pass the
// key to the application.
result = ACTION_PASS_TO_USER;
isWakeKey = false;
if (interactive) {
// If the screen is awake, but the button pressed was the one that woke the device
// then don't pass it to the application
if (keyCode == mPendingWakeKey && !down) {
result = 0;
}
// Reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
}
} else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
// interactive.
result = ACTION_PASS_TO_USER;
// Since we're dispatching the input, reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
} else {
// When the screen is off and the key is not injected, determine whether
// to wake the device but don't pass the key to the application.
result = 0;
if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
isWakeKey = false;
}
// Cache the wake key on down event so we can also avoid sending the up event to the app
if (isWakeKey && down) {
mPendingWakeKey = keyCode;
}
}
// If the key would be handled globally, just return the result, don't worry about special
// key processing.
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
if (isWakeKey) {
wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
}
return result;
}
....................
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
hungUp = telecomManager.endCall();
}
if (interactive && !hungUp) {
mEndCallKeyHandled = false;
mHandler.postDelayed(mEndCallLongPress,
ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
} else {
mEndCallKeyHandled = true;
}
} else {
if (!mEndCallKeyHandled) {
mHandler.removeCallbacks(mEndCallLongPress);
if (!canceled) {
if ((mEndcallBehavior
& Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
if (goHome()) {
break;
}
}
if ((mEndcallBehavior
& Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
mPowerManager.goToSleep(event.getEventTime(),
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
isWakeKey = false;
}
}
}
}
break;
}
....................
if (useHapticFeedback) {
performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
}
if (isWakeKey) {
wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
}
return result;
}
B:wakeUp处理量灭屏
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
final boolean theaterModeEnabled = isTheaterModeEnabled();
if (!wakeInTheaterMode && theaterModeEnabled) {
return false;
}
if (theaterModeEnabled) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0);
}
mPowerManager.wakeUp(wakeTime, reason); //调用PowerManagerService亮屏操作
return true;
}
}
二、PowerManagerService处理量灭屏过程
从上面的分析可知,在PWM中处理按键事件如果需要唤醒屏幕则会调用PWM的wakeUp()函数,此函数会调用PMS 的wakeUp()函数来具体处理。
从上面的分析可知,在PWM中处理按键事件如果需要唤醒屏幕则会调用PWM的wakeUp()函数,此函数会调用PMS 的wakeUp()函数来具体处理。
注:限于篇幅本文仅列出重要的函数和调用过程
首先来个概览,了解一下主要完成了如下三件事
1) 由Notifier根据系统的具体状态来发出广播
2) PMS 通过updatePowerStateLocked()计算和更新电源的全局状态
3) PMS将最新的电源状态等传入DPC中,根据距离传感器和光感传感器,计算具体的屏幕亮度和量灭屏动画等
4) DPC通知PWM绘制keyguard与windows(此时会block等待绘制完成,灭屏时候无需绘制),绘制完成后通知DPC继续
5) DPC具体调用显示设备开启或关闭屏幕(执行量灭屏的动画效果)
1)PowerManagerService--wakeUpNoUpdateLocked()
PMS首先讲wakefullness状态. 之后发送亮灭屏广播通知其他应用手机处于亮屏还是灭屏状态。
private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
String opPackageName, int opUid) {
if (DEBUG_SPEW) {
Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
}
if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false; //判断是否要去亮屏
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
try {
switch (mWakefulness) {
case WAKEFULNESS_ASLEEP:
Slog.i(TAG, "Waking up from sleep due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");
break;
case WAKEFULNESS_DREAMING:
Slog.i(TAG, "Waking up from dream due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");
break;
case WAKEFULNESS_DOZING:
Slog.i(TAG, "Waking up from dozing due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");
break;
}
mLastWakeTime = eventTime; //设置最后一次唤醒的时间
setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); //Notifier调用onWakefulnessChangeStarted发送亮屏广播
mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid); //调用Notifier通知battery处理
userActivityNoUpdateLocked( //更新最后一次用户事件时间
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
return true;
}
1)Notifier 发送量灭屏广播
Notifier发送广播前会与与AMS,WMS,IMS进行交互,通知各模块电源状态的改变,各模块会自行处理电源状态改变通知。
A:onWakefulnessChangeStarted()
public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness); //亮屏true, 灭屏false
if (DEBUG) {
Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
+ ", reason=" + reason + ", interactive=" + interactive);
}
// Tell the activity manager about changes in wakefulness, not just interactivity.
// It needs more granularity than other components.
mHandler.post(new Runnable() {
@Override
public void run() {
mActivityManagerInternal.onWakefulnessChanged(wakefulness); //与AMS交互处理
}
});
// Handle any early interactive state changes.
// Finish pending incomplete ones from a previous cycle.
if (mInteractive != interactive) {
// Finish up late behaviors if needed.
if (mInteractiveChanging) {
handleLateInteractiveChange();
}
// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive); //在IMS中记录现在的屏幕状态
mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
mBatteryStats.noteInteractive(interactive); //唤醒battery状态
} catch (RemoteException ex) { }
// Handle early behaviors.
mInteractive = interactive;
mInteractiveChangeReason = reason;
mInteractiveChanging = true;
handleEarlyInteractiveChange(); //初期处理交互模式改变
}
}
B: handleEarlyInteractiveChange()
当屏幕在wakingup时需要通知window进行更新手势监听,更新方向监听,更新锁屏超时时间
private void handleEarlyInteractiveChange() {
synchronized (mLock) {
if (mInteractive) {
// Waking up... //亮屏
mHandler.post(new Runnable() {
@Override
public void run() {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
mPolicy.startedWakingUp();
}
});
// Send interactive broadcast.
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
updatePendingBroadcastLocked(); //更新亮屏广播
} else {
// Going to sleep... //灭屏
// Tell the policy that we started going to sleep.
final int why = translateOffReason(mInteractiveChangeReason);
mHandler.post(new Runnable() {
@Override
public void run() {
mPolicy.startedGoingToSleep(why);
}
});
}
}
(1):updatePendingBroadcastLocked()
private void updatePendingBroadcastLocked() {
if (!mBroadcastInProgress
&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
&& (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|| mPendingInteractiveState != mBroadcastedInteractiveState)) {
mBroadcastInProgress = true;
mSuspendBlocker.acquire();
Message msg = mHandler.obtainMessage(MSG_BROADCAST);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
(2)sendNextBroadcast()
private void sendNextBroadcast() {
final int powerState;
synchronized (mLock) {
if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
// Broadcasted power state is unknown. Send wake up.
mPendingWakeUpBroadcast = false;
mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
} else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
// Broadcasted power state is awake. Send asleep if needed.
if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|| mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
mPendingGoToSleepBroadcast = false;
mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
} else {
finishPendingBroadcastLocked();
return;
}
} else {
// Broadcasted power state is asleep. Send awake if needed.
if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|| mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
mPendingWakeUpBroadcast = false;
mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
} else {
finishPendingBroadcastLocked();
return;
}
}
mBroadcastStartTime = SystemClock.uptimeMillis();
powerState = mBroadcastedInteractiveState;
}
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
if (powerState == INTERACTIVE_STATE_AWAKE) {
sendWakeUpBroadcast();//这里发送亮屏广播
} else {
sendGoToSleepBroadcast();
}
}
(3)sendWakeUpBroadcast()
注意:Notifier 自身也会接收亮屏广播,其受到后会调用finishPendingBroadcastLocked()函数来释放wakeLock
private void sendWakeUpBroadcast() {
if (DEBUG) {
Slog.d(TAG, "Sending wake up broadcast.");
}
if (ActivityManagerNative.isSystemReady()) {
mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
mWakeUpBroadcastDone, mHandler, 0, null, null);
} else {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
sendNextBroadcast();
}
}