生活中有许多我们习以为常的事情,然而其中的细节往往为我们所忽略。今天,我们将深入探讨 Android 设备从按键到屏幕点亮的一系列过程,以期更好地理解它们的实现方式。
一、overview
首先,我们画一个大致的数据流图,对整个android输入到输出的反馈有一个大体的了解
如上图,power按键(短按)触发按下后,硬件会上报中断通知inux input子系统把按键上报给framework层的输入管理子系统(IMS),IMS通过PWM(电话窗口管理)的策略确定按键消息是丢弃还是转发给对应的处理组件,这里他识别到时电源短按键,会识别为唤醒亮屏流程,进而委托PMS(电源管理服务)进行唤醒流程处理。PMS会通过DMS(显示管理服务)亮屏显示,最终从framework层调用到底层驱动实现硬件上的亮屏反馈动作。
二、按键输入上报
power按键上报是和具体的硬件相关的,所以开始前我们需要对相关的硬件有一些认识。
这里简单画了个框图,物理的按键会通过io口连接到pmic(电源管理集成电路),然后通过pmic连接到soc内部。pmic有些是独立封装的一颗ic,有些是集成在soc内部,总之就是物理的按键按下后会触发cpu上的一个中断。
我们看下mtk power按键的软件的实现。
首先看dts配置:
power key的keycode为116,按键长按是2s以上,相关驱动实现可以通过compatible字段去找。其主要在drivers/input/keyboard/mtk-pmic-keys.c中。
static int mtk_pmic_keys_probe(struct platform_device *pdev)
{
...
keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
if (!keys)
return -ENOMEM;
keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
if (!input_dev) {
dev_err(keys->dev, "input allocate device fail.\n");
return -ENOMEM;
}
...
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev,
"register input device failed (%d)\n", error);
return error;
}
mtk_pmic_keys_lp_reset_setup(keys, mtk_pmic_regs);
platform_set_drvdata(pdev, keys);
return 0;
}
内核加载时会安装power key的驱动,并注册生成sys文件/dev/input/eventX(X为数字),用于输入消息的读取写入(input框架可以参考网上文章)。
当有按键按下的时候会触pmic的中断处理mtk_pmic_keys_release_irq_handler_thread:
// 这里当电源按键按下时,会触发中断,进入该中断线程进行处理
static irqreturn_t mtk_pmic_keys_release_irq_handler_thread(
int irq, void *data)
{
struct mtk_pmic_keys_info *info = data;
//上报116(实际上就是POWER)到/dev/input/event
input_report_key(info->keys->input_dev, info->keycode, 0);
input_sync(info->keys->input_dev);
if (info->suspend_lock)
__pm_relax(info->suspend_lock);
dev_info(info->keys->dev, "release key =%d using PMIC\n",
info->keycode);
return IRQ_HANDLED;
}
下面按键消息将会通过IMS(InputManagerService)注入到我们的framework:
三、输入消息传递
从驱动层的消息上报到linux系统的input子系统后,就轮到Android的IMS(Input Manager Service)子系统处理了。
IMS启动及主体功能
这里我们先看一下IMS子系统的启动流程。
IMS是随着SystemServer启动而启动的,主要InputReader和InputDispatcher两个主要的线程,前者读取/dev/input目录下eventx (x为数字)linux input子系统上报的输入事件,后者对InputReader进行分发处理。EventHub主要监听/dev/下与输入相关的设备变化通知InputReader进行读取。
InputReader核心功能实现
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
// 处理的主逻辑
mThread = std::make_unique<InputThread>(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
...
// 这里利用EventHub去获取/dev下输入设备的所有输入上报消息
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
std::scoped_lock _l(mLock);
mReaderIsAliveCondition.notify_all();
// 处理从linux input子系统读取到的输入上报rawEvent转换为NotifyKeyArgs消息
if (count) {
processEventsLocked(mEventBuffer, count);
}
...
} // release lock
// 如果有inpiut设备的变化则会通知到java层InputManagerService回调notifyInputDevicesChanged
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// 把NotifyKeyArgs消息转换为EventEntry消息,推送给InputDispatcher处理
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener.flush();
}
InputDispatcher核心功能实现
status_t InputDispatcher::start() {
if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
return OK;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptable()) {
nextWakeupTime = LONG_LONG_MIN;
}
// ANR的处理
// If we are still waiting for ack on some events,
// we might have to wake up earlier to check if an app is anr'ing.
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LONG_LONG_MAX) {
mDispatcherEnteredIdle.notify_all();
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
电源按键在IMS层的传递
我们看一下systrace中电源按键的处理过程
首先电源按键进入InputReader.loopOnce的读取,并做了简单的消息过滤和转换:
调用流程:processEventsLocked-->InputReader::processEventsForDeviceLocked-->InputDevice::process-->mapper.process-->KeyboardInputMapper::process-->KeyboardInputMapper::processKey-->getListener().notifyKey-->QueuedInputListener::notifyKey
processEventsLocked把事件先交由了对应的InputDevice,然后找对处理该事件类型的InputMapper 进行处理。以及一些基本的入队前的过滤(NativeInputManager.interceptKeyBeforeQueueing),然后InputMapper将事件等信息构造了NotifyArgs,然后加入到了mArgsQueue中。
mQueuedListener.flush()-->QueuedInputListener::flush-->NotifyKeyArgs::notify-->InputClassifier::notifyKey-->InputDispatcher::notifyKey-->InputDispatcher::enqueueInboundEventLocked
mQueuedListener.flush把mArgsQueue中的NotifyArgs消息依次读出,传递到InputDispatcher,封装成EventEntry并加入mInboundQueue队列尾部并wakeup分发线程。
至此,按键消息读取过程完毕。接下来,进入分发线程InputDispatcher::dispatchOnce处理。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
// 读取mInboundQueue中的消息进行处理
if (!mPendingEvent) {
if (mInboundQueue.empty()) {
...
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.front();
mInboundQueue.pop_front();
traceInboundQueueLengthLocked();
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(*mPendingEvent);
}
}
// 进行消息类型进行实际的分发处理
// All events are eventually dequeued and processed this way, even if we intend to drop them.
ALOG_ASSERT(mPendingEvent != nullptr);
bool done = false;
DropReason dropReason = DropReason::NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DropReason::POLICY;
} else if (!mDispatchEnabled) {
dropReason = DropReason::DISABLED;
}
switch (mPendingEvent->type) {
...
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*keyEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
break;
}
...
}
if (done) {
if (dropReason != DropReason::NOT_DROPPED) {
dropInboundEventLocked(*mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
对于power按键我们会进入dispatchKeyLocked的处理
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
//分发前再次对消息进行过滤
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
sp<IBinder> focusedWindowToken =
mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
// doInterceptKeyBeforeDispatchingCommand 会调用到NativeInputManager::interceptKeyBeforeDispatching
auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
};
postCommandLocked(std::move(command));
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
}
...
// 查找到当前焦点所在的窗口
std::vector<InputTarget> inputTargets;
InputEventInjectionResult injectionResult =
findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
if (injectionResult == InputEventInjectionResult::PENDING) {
return false;
}
setInjectionResult(*entry, injectionResult);
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
NativeInputManager::interceptKeyBeforeDispatching实际上是调用的
nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
const sp<IBinder>& token,
const KeyEvent* keyEvent, uint32_t policyFlags) {
ATRACE_CALL();
...
nsecs_t result = 0;
if (policyFlags & POLICY_FLAG_TRUSTED) {
JNIEnv* env = jniEnv();
ScopedLocalFrame localFrame(env);
// Token may be null
jobject tokenObj = javaObjectForIBinder(env, token);
// 调用上层 InputManagerService#interceptKeyBeforeQueueing() 来执行拦截策略
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (keyEventObj) {
jlong delayMillis = env->CallLongMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeDispatching,
tokenObj, keyEventObj, policyFlags);
bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
if (!error) {
if (delayMillis < 0) {
result = -1;
} else if (delayMillis > 0) {
result = milliseconds_to_nanoseconds(delayMillis);
}
}
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
}
}
return result;
}
调用java层 InputManagerService::interceptKeyBeforeQueueing() 来执行拦截策略
@SuppressWarnings("unused")
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
final int keyCode = event.getKeyCode();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
...
// Handle special keys.
switch (keyCode) {
...
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0,
mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
// Any activity on the power button stops the accessibility shortcut
// power 按键消息不会给到用户即activity收不到power按键消息
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
interceptPowerKeyDown(event, interactiveAndOn);
} else {
interceptPowerKeyUp(event, canceled);
}
break;
}
...
}
if (useHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
}
if (isWakeKey) {
wakeUpFromWakeKey(event);
}
...
return result;
}
power按键分两个消息上报,按键按下和弹起,所以这里处理也分两种情况interceptPowerKeyDown和interceptPowerKeyUp。按键亮屏是在interceptPowerKeyDown中处理的。
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
// 获取一个wakelock,防止cpu在此流程期间休眠
// 底层是 /sys/power/wake_lock 和 /sys/power/wake_unlock相关功能实现
if (!mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.acquire();
}
mWindowManagerFuncs.onPowerKeyDown(interactive);
...
final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event.getKeyCode());
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
mPowerKeyHandled = mPowerKeyHandled || hungUp
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
wakeUpFromPowerKey(event.getDownTime());
}
} else {
...
}
}
正常熄屏状态下,按下power按键会先触发wakeUpFromPowerKey的分支。
private void wakeUpFromPowerKey(long eventTime) {
if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent()) {
startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
}
}
}
wakeup会调用PowerManagerService(简称PMS,模块负责协调、管理设备CPU资源,应用层及框架层其他组件对CPU的资源请求就是通过PowerManager模块进行。如系统亮灭屏、WakeLock管理、休眠时间等)的wakeUp方法。
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
...
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
// mBootCompleted 是判断系统是否启动完成的标记,启动完成设置为true,所以正常情况下我们会直接进入wakePowerGroupLocked的处理,前面的分支是开机时的流程。
if (!mBootCompleted && sQuiescent) {
mDirty |= DIRTY_QUIESCENT;
updatePowerStateLocked();
return;
}
wakePowerGroupLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime,
reason, details, uid, opPackageName, uid);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
实际上在wakePowerGroupLocked中也会间接的调用updatePowerStateLocked()更新电源的状态,它是PowerManagerService的核心方法,所有电源状态变化最终都会调用到updatePowerStateLocked方法。
private void wakePowerGroupLocked(final PowerGroup powerGroup, long eventTime,
@WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
...
if (mForceSuspendActive || !mSystemReady) {
return;
}
powerGroup.wakeUpLocked(eventTime, reason, details, uid, opPackageName, opUid,
LatencyTracker.getInstance(mContext));
}
powerGroup.wakeUpLocked会通过调用链PowerGroup::setWakefulnessLocked-->PowerManagerService::onWakefulnessChangedLocked调用到onWakefulnessChangedLocked。
public void onWakefulnessChangedLocked(int groupId, int wakefulness, long eventTime,
int reason, int uid, int opUid, String opPackageName, String details) {
if (wakefulness == WAKEFULNESS_AWAKE) {
// Kick user activity to prevent newly awake group from timing out instantly.
// The dream may end without user activity if the dream app crashes / is updated,
// don't poke the user activity timer for these wakes.
int flags = reason == PowerManager.WAKE_REASON_DREAM_FINISHED
? PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS : 0;
userActivityNoUpdateLocked(mPowerGroups.get(groupId), eventTime,
PowerManager.USER_ACTIVITY_EVENT_OTHER, flags, uid);
}
// 获取wake锁并唤醒系统
mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
updateGlobalWakefulnessLocked(eventTime, reason, uid, opUid, opPackageName, details);
mNotifier.onPowerGroupWakefulnessChanged(groupId, wakefulness, reason,
getGlobalWakefulnessLocked());
updatePowerStateLocked();
}
}
updateGlobalWakefulnessLocked主要获取了wakeup锁,然后通过调用链:PowerManagerService::updateGlobalWakefulnessLocked-->Notifier::onWakefulnessChangeStarted-->Notifier::handleEarlyInteractiveChange-->Notifier::updatePendingBroadcastLocked()-->Notifier::handleMessage()-->Notifier::sendNextBroadcast()-->Notifier::sendWakeUpBroadcast() 触发亮屏广播(android.intent.action.SCREEN_ON)的发送。其他组件或app可以订阅这个系统intent消息做自己的处理。
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);
}
}
我们重点看一下updatePowerStateLocked这个方法:
private void updatePowerStateLocked() {
...
try {
// Phase 0: Basic state updates.
// 更新了电池当前的电量,当前是否正常充电,充电的类型,判断是否是低电量状态
updateIsPoweredLocked(mDirty);
// 根据当前的充电状态,来判断充电的时候手机是否保持唤醒以及是否屏幕长亮
updateStayOnLocked(mDirty);
// 更新屏幕最大亮度状态信息
updateScreenBrightnessBoostLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = mClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
// 得到当前系统wakelock的信息并更新到mWakeLockSummary
updateWakeLockSummaryLocked(dirtyPhase1);
// 根据用户最后的活动来决定当前屏幕的状态
updateUserActivitySummaryLocked(now, dirtyPhase1);
// 更新用户行为,比如我们按键亮屏幕行为为USER_ACTIVITY_SCREEN_BRIGHT
updateAttentiveStateLocked(now, dirtyPhase1);
// 根据各种状态决定系统是否进入休眠/dream/doze(打盹)状态
// WAKEFULNESS_ASLEEP : 表示设备处于休眠状态。屏幕处于灭屏状态,或者处于灭屏的过程中。设备只能被 wakeup() 唤醒,例如 Power 键唤醒设备。
// WAKEFULNESS_AWAKE :表示设备处于唤醒状态。屏幕处于亮屏或者暗屏的状态。当用户行为超时(屏幕超时),设备可能会开启梦境(指屏保)或者进入休眠状态。当然 goToSleep() 也能使设备进入休眠状态。
// WAKEFULNESS_DREAMING :设备处于梦境状态,这里指的是显示屏保。可以通过 wakeup() 结束屏保,唤醒设备,例如点击屏保。 也可以通过 goToSleep() 结束屏保,使设备进入休眠,例如,屏保时按 Power 键。
// WAKEFULNESS_DOZING : 设备处于打盹状态(dozing)。这种状态几乎是一种休眠状态,但是屏幕处于一种低功耗状态。系统会让 dream manager 启动一个 doze 组件,这个组件会绘制一些简单的信息在屏幕上,但是前提是屏幕要支持 AOD(always on display)功能
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Lock profiles that became inactive/not kept awake.
updateProfilesLocked(now);
// Phase 3: Update power state of all PowerGroups.
final boolean powerGroupsBecameReady = updatePowerGroupsLocked(dirtyPhase2);
// Phase 4: Update dream state (depends on power group ready signal).
// 更新dream状态下信息,主要是决定是否使用屏保
updateDreamLocked(dirtyPhase2, powerGroupsBecameReady);
// Phase 5: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Phase 6: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
// 如果是亮屏则阻塞系统自动进入休眠
updateSuspendBlockerLocked();
}
...
}
updatePowerGroupsLocked中会更新系统的电源状态,最后委托DisplayManagerService(用来管理显示的生命周期以及状态,它决定如何根据当前连接的物理显示设备控制其逻辑显示,并且在状态更改时,向系统和应用程序发送通知等)进行显示模块的电源状态的更新。调用为:updatePowerGroupsLocked-->PowerGroup::updateLocked->mDisplayManagerInternal.requestPowerState
finishWakefulnessChangeIfNeededLocked会更新做电源状态切换后的通知处理:
private void handleLateInteractiveChange() {
synchronized (mLock) {
final int interactiveChangeLatency =
(int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
if (mInteractive) {
// Finished waking up...
mHandler.post(() -> {
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
log.setType(MetricsEvent.TYPE_OPEN);
log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(
mInteractiveChangeReason));
log.setLatency(interactiveChangeLatency);
log.addTaggedData(
MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
MetricsLogger.action(log);
EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
mPolicy.finishedWakingUp(mInteractiveChangeReason);
});
} else {
...
}
}
mPolicy.finishedWakingUp 实际会调用PhoneWindowManager::finishedWakingUp通知keygurd(锁屏)和DisplayFoldController唤醒完成,便于他们做后续操作。
public void finishedWakingUp(@PowerManager.WakeReason int pmWakeReason) {
if (DEBUG_WAKEUP) {
Slog.i(TAG, "Finished waking up... (why="
+ WindowManagerPolicyConstants.onReasonToString(
WindowManagerPolicyConstants.translateWakeReasonToOnReason(
pmWakeReason)) + ")");
}
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onFinishedWakingUp();
}
if (mDisplayFoldController != null) {
mDisplayFoldController.finishedWakingUp();
}
}
updateSuspendBlockerLocked 会使用linux的休眠唤醒机制去实现系统的休眠和唤醒功能:
PMS会通过HAL接口请求android.system.suspend这个service的SystemSuspendHidl::acquireWakeLock阻塞系统休眠。
主要原理是根据系统往sys文件在中写入不同的值触发系统不同的休眠或唤醒动作。各组件需要组织cpu休眠的时候申请不同的wakelock锁,当任务完成后释放这些锁,内核会不停的检查系统是否还有wakelock处于上锁状态,如果有就不会进入休眠状态,而如果一旦发现系统没有wakelock上锁时就自动进入休眠状态(前提是启动了自动休眠功能)。
用一张简要的时序图表示按键消息在framework层的主要传递过程,power按键消息会进行两次拦截,然后发送给PhoneWindowManager,根据当前状态决定是wake还是sleep,我们这里分析的是wakeup的流程,所以wakeup消息会传递给PMS进行实际的处理(PMS会申请wakelock阻塞系统自动进入suspend状态,然后发送亮屏广播),最后,当收到PMS对wakeup成功响应后会通知keyguard和displayFoldController唤醒完成,可以进行后续的处理了。
具体PMS是如何响应的我们留在下节详细分析。用一张序列图总结一下消息的传递过程:
四、输入消息响应
我们继续上一节的wakeUp触发DisplayManagerService::requestPowerState看看显示模块是如何响应电源短按键的。
requestPowerState会通过handler调用到DisplayPowerController::updatePowerState去更新显示模块的电源状态,它DisplayPowerController中的核心方法,主要处理亮灭屏、调节亮度等。
private void updatePowerState() {
...
// 1. 根据电源前期策略决定显示状态
int state;
float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
boolean performScreenOffTransition = false;
switch (mPowerRequest.policy) {
...
case DisplayPowerRequest.POLICY_DIM:
case DisplayPowerRequest.POLICY_BRIGHT:
default:
state = Display.STATE_ON;
break;
}
// 2. 各种场景下(光传感器自动调节下、手动亮度下、DIM下、低电量下、VR场景等等)亮度的计算
// Initialize things the first time the power state is changed.
if (mustInitialize) {
initialize(state);
}
// Animate the screen state change unless already animating.
// The transition may be deferred, so after this point we will use the
// actual state instead of the desired one.
// 3. 检查屏幕状态是否有变化,如果有变化则生效屏幕的变化,这里屏幕上显示的内容会在这里更新
final int oldState = mPowerState.getScreenState();
animateScreenStateChange(state, performScreenOffTransition);
...
// Always use the VR brightness when in the VR state.
if (state == Display.STATE_VR) {
brightnessState = mScreenBrightnessForVr;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
}
...
boolean hadUserBrightnessPoint = false;
// Configure auto-brightness.
if (mAutomaticBrightnessController != null) {
hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
mAutomaticBrightnessController.configure(autoBrightnessState,
mBrightnessConfiguration,
mLastUserSetScreenBrightness,
userSetBrightnessChanged, autoBrightnessAdjustment,
autoBrightnessAdjustmentChanged, mPowerRequest.policy);
}
if (mBrightnessTracker != null) {
mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
}
...
if (!mPendingScreenOff) {
...
// HighBrightnessMode(高亮的环境)模式下屏幕亮度值计算
float animateValue = clampScreenBrightness(brightnessState);
float sdrAnimateValue = animateValue;
// TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
// done in HighBrightnessModeController.
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
&& ((mBrightnessReason.modifier & BrightnessReason.MODIFIER_DIMMED) == 0
|| (mBrightnessReason.modifier & BrightnessReason.MODIFIER_LOW_POWER) == 0)) {
// We want to scale HDR brightness level with the SDR level
animateValue = mHbmController.getHdrBrightnessValue();
}
// 5. 计算是否要做亮度动画(亮度渐变)并调用animateScreenBrightness进行实际的背光设置
final float currentBrightness = mPowerState.getScreenBrightness();
final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
if (isValidBrightnessValue(animateValue)
&& (animateValue != currentBrightness
|| sdrAnimateValue != currentSdrBrightness)) {
if (initialRampSkip || hasBrightnessBuckets
|| wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
animateScreenBrightness(animateValue, sdrAnimateValue,
SCREEN_ANIMATION_RATE_MINIMUM);
} else {
boolean isIncreasing = animateValue > currentBrightness;
final float rampSpeed;
if (isIncreasing && slowChange) {
rampSpeed = mBrightnessRampRateSlowIncrease;
} else if (isIncreasing && !slowChange) {
rampSpeed = mBrightnessRampRateFastIncrease;
} else if (!isIncreasing && slowChange) {
rampSpeed = mBrightnessRampRateSlowDecrease;
} else {
rampSpeed = mBrightnessRampRateFastDecrease;
}
animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed);
}
}
...
}
...
// 6. 计算白平衡 色温?
if (mDisplayWhiteBalanceController != null) {
if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
mDisplayWhiteBalanceController.setEnabled(true);
mDisplayWhiteBalanceController.updateDisplayColorTemperature();
} else {
mDisplayWhiteBalanceController.setEnabled(false);
}
}
final boolean ready = mPendingScreenOnUnblocker == null &&
(!mColorFadeEnabled ||
(!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
&& mPowerState.waitUntilClean(mCleanListener);
final boolean finished = ready
&& !mScreenBrightnessRampAnimator.isAnimating();
// Notify policy about screen turned on.
if (ready && state != Display.STATE_OFF
&& mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
mWindowManagerPolicy.screenTurnedOn(mDisplayId);
}
// Grab a wake lock if we have unfinished business.
if (!finished && !mUnfinishedBusiness) {
if (DEBUG) {
Slog.d(TAG, "Unfinished business...");
}
mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
mUnfinishedBusiness = true;
}
// Notify the power manager when ready.
if (ready && mustNotify) {
// Send state change.
synchronized (mLock) {
if (!mPendingRequestChangedLocked) {
mDisplayReadyLocked = true;
if (DEBUG) {
Slog.d(TAG, "Display ready!");
}
}
}
sendOnStateChangedWithWakelock();
}
// Release the wake lock when we have no unfinished business.
if (finished && mUnfinishedBusiness) {
if (DEBUG) {
Slog.d(TAG, "Finished business...");
}
mUnfinishedBusiness = false;
mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
}
...
}
updatePowerState中注释1~2进行了复杂的亮度计算,暂不展开分析。注释3中调用了animateScreenStateChange来做显示内容的关系,这个是比较重要的一部分工作。我们现在主要分析亮屏流程,其中省略掉了大部分与按键亮屏幕无关的分支代码。
private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
...
// 亮屏幕绘制和动画处理,setScreenState在灭屏到亮屏的流程中会创建ScreenOnUnblocker()阻塞亮屏幕的点亮工作
// 等待屏幕绘制完成后使用unblockScreenOn解锁后重新执行updatePowerStat
if (target == Display.STATE_ON) {
// Want screen on. The contents of the screen may not yet
// be visible if the color fade has not been dismissed because
// its last frame of animation is solid black.
if (!setScreenState(Display.STATE_ON)) {
return; // screen on blocked
}
if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
// Perform screen on animation.
if (mPowerState.getColorFadeLevel() == 1.0f) {
mPowerState.dismissColorFade();
} else if (mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
ColorFade.MODE_FADE :
ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
}
} else {
// Skip screen on animation.
mPowerState.setColorFadeLevel(1.0f);
mPowerState.dismissColorFade();
}
} else if (target == Display.STATE_VR) {
...
} else if (target == Display.STATE_DOZE) {
...
} else if (target == Display.STATE_DOZE_SUSPEND) {
...
} else if (target == Display.STATE_ON_SUSPEND) {
...
} else {
...
}
}
setScreenState 会检查是否有亮屏阻塞(屏幕绘制),如果有阻塞会返回false,终止animateScreenStateChange的执行,一般场景下这里会阻塞在锁屏绘制服务,待其完成后去掉阻塞。
通过trace我们可以很清楚的看到block这段时间长度。
private boolean setScreenState(int state, boolean reportOnly) {
final boolean isOff = (state == Display.STATE_OFF);
...
if (mPowerState.getScreenState() != state
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
// If we are trying to turn screen off, give policy a chance to do something before we
// actually turn the screen off.
if (isOff && !mScreenOffBecauseOfProximity) {
...
}
// 1.如果屏幕状态有变化则调用DisplayPowerState::setScreenState更新屏幕状态
if (!reportOnly && mPowerState.getScreenState() != state) {
Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
// TODO(b/153319140) remove when we can get this from the above trace invocation
SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
noteScreenState(state);
}
}
// 2.从灭屏到亮屏会触发blockScreenOn流程,阻塞屏幕进行绘制操作
if (!isOff
&& (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
|| mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
if (mPowerState.getColorFadeLevel() == 0.0f) {
blockScreenOn();
} else {
unblockScreenOn();
}
mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
}
// Return true if the screen isn't blocked.
return mPendingScreenOnUnblocker == null;
}
这里有两个重要的处理,我们分别来详细分析:
硬件模块上电
首先,我们会同步屏幕状态到底层硬件上去。调用路径为:mPowerState::setScreenState-->DisplayPowerState::scheduleScreenUpdate-->DisplayPowerState::postScreenUpdateThreadSafe-->mPhotonicModulator.setState-->mLock.notifyAll-->DisplayPowerState::run()-->mBlanker.requestDisplayState-->DisplayManagerService::requestDisplayState-->DisplayManagerService::requestDisplayStateInternal-->DisplayManagerService::updateDisplayStateLocked-->LocalDisplayAdapter::requestDisplayStateLocked-->LocalDisplayAdapter::setDisplayState-->SurfaceControl::nativeSetDisplayPowerMode-->SurfaceComposerClient::setDisplayPowerMode-->SurfaceComposerAIDL::setPowerMode-->SurfaceFlinger::setPowerModeInternal
PMS通过ComposerService以binder的通信方式获取了SurfaceFlinger的服务。
bool ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");
mComposerService = waitForService<ISurfaceComposer>(name);
if (mComposerService == nullptr) {
return false; // fatal error or permission problem
}
// Create the death listener.
class DeathObserver : public IBinder::DeathRecipient {
ComposerService& mComposerService;
virtual void binderDied(const wp<IBinder>& who) {
ALOGW("ComposerService remote (surfaceflinger) died [%p]",
who.unsafe_get());
mComposerService.composerServiceDied();
}
public:
explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
return true;
}
然后,PMS调用了SurfaceFlinger提供的setPowerMode方法设置显示模块的电源状态,在SurfaceFlinger内部其实现为setPowerModeInternal。
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
...
const auto displayId = display->getPhysicalId();
ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
const hal::PowerMode currentMode = display->getPowerMode();
if (mode == currentMode) {
return;
}
...
if (currentMode == hal::PowerMode::OFF) {
// Turn on the display
if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
onActiveDisplayChangedLocked(display);
}
// Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
// We can merge the syscall later.
if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
ALOGW("Couldn't set uclamp.min on display on: %s\n", strerror(errno));
}
if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
}
// 调用composer中的setPowerMode方法打开显示模块的电源
getHwComposer().setPowerMode(displayId, mode);
if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
mVisibleRegionsDirty = true;
mHasPoweredOff = true;
scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
// Turn off the display
...
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) {
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, refreshRate);
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
...
} else {
ALOGE("Attempting to set unknown power mode: %d\n", mode);
getHwComposer().setPowerMode(displayId, mode);
}
if (isDisplayActiveLocked(display)) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
}
surfaceflinger内部又把设置显示电源模块的状态委托给了hwc ,通过HWComposer::setPowerMode()实现最终的硬件设置。
这里简单介绍一下HWC。HWC是Hardware Composer的缩写,直译为硬件合成器。它是Android系统中负责处理显示器输出的组件。HWC的主要作用是将图形处理器(GPU)生成的图像合成为一帧完整的显示器输出,并输出到显示器上。当前,没有HWC其实也是可以工作的,图层的合成可以使用GPU或者CPU去做,但是那样性能就会降低很多,为了减轻CPU和GPU的负担,提高系统的响应速度和性能,具有图层合成功能的硬件单元就应运而生,这就是HWC。
status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
if (mode == hal::PowerMode::OFF) {
setVsyncEnabled(displayId, hal::Vsync::DISABLE);
}
const auto& displayData = mDisplayData[displayId];
auto& hwcDisplay = displayData.hwcDisplay;
switch (mode) {
case hal::PowerMode::OFF:
case hal::PowerMode::ON:
ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str());
{
auto error = hwcDisplay->setPowerMode(mode);
if (error != hal::Error::NONE) {
LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error,
displayId);
}
}
break;
case hal::PowerMode::DOZE:
case hal::PowerMode::DOZE_SUSPEND:
...
}
return NO_ERROR;
}
上述重点实现即为hwcDisplay->setPowerMode(mode),其中hwcDisplay是由allocatePhysicalDisplay创建的一个HWC2::impl::Display实例。
void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
PhysicalDisplayId displayId) {
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
if (!mPrimaryHwcDisplayId) {
mPrimaryHwcDisplayId = hwcDisplayId;
}
auto& displayData = mDisplayData[displayId];
auto newDisplay =
std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
hal::DisplayType::PHYSICAL);
newDisplay->setConnected(true);
displayData.hwcDisplay = std::move(newDisplay);
}
HWC2::impl::Display中setPowerMode如下:
Display::Display(android::Hwc2::Composer& composer,
const std::unordered_set<AidlCapability>& capabilities, HWDisplayId id,
DisplayType type)
: mComposer(composer), mCapabilities(capabilities), mId(id), mType(type) {
ALOGV("Created display %" PRIu64, id);
}
Error Display::setPowerMode(PowerMode mode)
{
auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
auto intError = mComposer.setPowerMode(mId, intMode);
...
return static_cast<Error>(intError);
}
HWC2::impl::Display中setPowerMode又调用了android::Hwc2::Composer的方法。
现在的Composer HAL接口分AIDL和HIDL之分,虽然google主推AIDL访问HAL,但是从trace中我们可以看到mtk上仍然使用的是HIDL接口:
Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
Return<Error> ret(Error::UNSUPPORTED);
if (mClient_2_2) {
ret = mClient_2_2->setPowerMode_2_2(display, mode);
} else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
}
return unwrapRet(ret);
}
HWC在mt6983 平台上是以一个后台HAL service进程(android.hardware.graphics.composer@2.3-service)运行的,他与SurfaceFlinger之间仍然通过binder进行通信。服务端加载了vendor厂商的实现库(android.hardware.graphics.composer@2.1.so)共同为framework提供了HAL(硬件抽象层)接口。mtk hwc代码我们今天暂不详细分析,只需要知道mClient_2_2->setPowerMode_2_2会调用到
HWCMediator::displaySetPowerMode方法(HWCMediator 是 MTK 平台上的一个重要的 HAL 层中介层,它主要负责处理系统服务和硬件设备之间的通信。源码中还有一个HWCMediator_simple 类的实现,他是 HWCMediator 的简化版,它是为了更加轻量化而设计的。相比于 HWCMediator,它提供的接口更少,但也能满足一些简单的应用场景。
int32_t /*hwc2_error_t*/ HWCMediator::displaySetPowerMode(
hwc2_device_t* /*device*/,
hwc2_display_t display,
int32_t /*hwc2_power_mode_t*/ mode)
{
CHECK_DISP_CONNECT(display);
HWC_LOGD("%s display:%" PRIu64 " mode:%d", __func__, display, mode);
if (!HwcFeatureList::getInstance().getFeature().aod &&
(mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND))
{
return HWC2_ERROR_UNSUPPORTED;
}
switch (mode)
{
case HWC2_POWER_MODE_OFF:
case HWC2_POWER_MODE_ON:
case HWC2_POWER_MODE_DOZE:
case HWC2_POWER_MODE_DOZE_SUSPEND:
getHWCDisplay(display)->setPowerMode(mode);
break;
default:
HWC_LOGE("%s: display(%" PRIu64 ") a unknown parameter(%d)!", __func__, display, mode);
return HWC2_ERROR_BAD_PARAMETER;
}
return HWC2_ERROR_NONE;
}
这里我们主要关注设备上的主屏幕(HWC_DISPLAY_PRIMARY),在HWCMediator构造函数中我们可以看到主屏幕使用的是drm框架设备:
IOverlayDevice* getHwDevice()
{
#ifndef MTK_HWC_USE_DRM_DEVICE
return &DispDevice::getInstance();
#else
return &DrmDevice::getInstance();
#endif
}
HWCMediator::HWCMediator()
...
{
sp<IOverlayDevice> primary_disp_dev = getHwDevice();
...
//init primary dispaly
m_displays.push_back(new HWCDisplay(HWC_DISPLAY_PRIMARY, HWC2_DISPLAY_TYPE_PHYSICAL,
primary_disp_dev));
...
}
所以最终我们调用的就是DrmDevice::setPowerMode:
void DrmDevice::setPowerMode(uint64_t dpy,int mode)
{
HWC_LOGD("DispDevice::setPowerMode() dpy:%" PRIu64 " mode:%d", dpy, mode);
...
std::string filename("/dev/graphics/fb");
filename += std::to_string(dpy);
int fb_fd = open(filename.c_str(), O_RDWR);
...
int err = NO_ERROR;
switch (mode)
{
...
case HWC_POWER_MODE_NORMAL:
{
err |= m_drm->blankDisplay(dpy, mode);
if (fb_fd > 0)
{
err |= WDT_IOCTL(fb_fd, FBIOBLANK, FB_BLANK_UNBLANK);
}
break;
}
...
default:
HWC_LOGE("(%" PRIu64 ") setPowerMode: receive unknown power mode: %d", dpy, mode);
}
protectedClose(fb_fd);
}
DrmDevice::setPowerMode中首先会打开/dev/graphics/fbx(x为屏幕编号)设备,然后调用m_drm->blankDisplay,再次对/dev/graphics/fbx设备进行了FBIOBLANK操作(这个是旧的devFB框架的power on操作,drm框架下一般是不存在的,所以不再分析)。
int DrmModeResource::blankDisplay(uint64_t dpy, int mode)
{
drmModeAtomicReqPtr atomic_req;
atomic_req = drmModeAtomicAlloc();
...
int res = 0;
DrmModeCrtc *crtc = getDisplay(dpy);
...
if (mode == HWC_POWER_MODE_DOZE || mode == HWC_POWER_MODE_NORMAL)
{
res |= crtc->addProperty(atomic_req, DRM_PROP_CRTC_ACTIVE, true) < 0;
}
else
{
res |= crtc->addProperty(atomic_req, DRM_PROP_CRTC_ACTIVE, false) < 0;
}
res = atomicCommit(atomic_req, DRM_MODE_ATOMIC_ALLOW_MODESET, this);
...
drmModeAtomicFree(atomic_req);
return res;
}
blankDisplay操作是我们常见的drm用户态提交流程,主要设置了DRM的ACTIVE这个标准properties(这个属性在DRM中表示CRTC的电源状态,ON表示上电,OFF表示下电)。内核DRM框架中如何处理我们今天就不继续跟踪了,后续我们单独对不同厂家在DRM框架的实现进行分析。总之,这一步我们完成了显示模块硬件的上电工作。需要注意的是,这里的上电仅仅是打开了显示模块内部硬件的电源,我们目前还不能从屏幕上看到任何实质性的内容,熟悉显示屏的朋友会比较了解,屏幕要能显现出图像我们还需要在屏幕上画出图像并打开屏幕的背光灯,所以,后续内容我们将继续分析屏幕图像是如何绘制以及背光灯是如何点亮的。
屏幕绘制
回到setScreenState调用的最后流程,亮屏流程会阻塞并调用mWindowManagerPolicy.screenTurningOn进行亮屏幕的绘制操作。mWindowManagerPolicy其实是WindowManagerPolicy的子类实例,实现了phone设备的窗口策略。
public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
...
if (displayId == DEFAULT_DISPLAY) {
// 对于默认的显示设备,我们会把接听者传递给mDefaultDisplayPolicy,当完成绘制后发送消息
// MSG_WINDOW_MANAGER_DRAWN_COMPLETE触发PhoneWindowManager执行finishWindowsDrawn方法,最后
// 通知screenOnListener执行onScreenOn()方法,screenOnListener就是ScreenOnUnblocker的实例化,
// 最终触发DisplayPowerController::unblockScreenOn()去除亮屏阻塞并尝试再次执行
// DisplayPowerController::updatePowerState()完成之前未完成的亮度设置
updateScreenOffSleepToken(false);
mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
mBootAnimationDismissable = false;
synchronized (mLock) {
// 如果设置了锁屏,则会通知锁屏服务进行锁屏的绘制,并在锁屏绘制完成后回调mKeyguardDrawnCallback
// 触发finishKeyguardDrawn去除亮屏阻塞
if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
getKeyguardDrawnTimeout());
mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
} else {
...
}
}
} else {
...
}
}
如果设置了锁屏,则会通知锁屏服务进行锁屏的绘制。锁屏服务(KeyguardService)是一个后台binder服务,它与systemUI(它为户提供的系统级别的信息显示与交互的一套UI组件,包含状态栏、底部导航栏显示、最近使用App列表、通知面板显示、截图服务、壁纸服务WallpaperService、管理窗口放大镜等等非常多的功能)一起打包在SystemUI.apk作为系统应用,运行在在同一个进程(com.android.systemui)内。
public void onScreenTurningOn(final DrawnListener drawnListener) {
if (mKeyguardService != null) {
if (DEBUG) Log.v(TAG, "onScreenTurnedOn(showListener = " + drawnListener + ")");
mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(drawnListener));
} else {
// try again when we establish a connection
Slog.w(TAG, "onScreenTurningOn(): no keyguard service!");
// This shouldn't happen, but if it does, show the scrim immediately and
// invoke the listener's callback after the service actually connects.
mDrawnListenerWhenConnect = drawnListener;
}
mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
}
mKeyguardDelegate是锁屏服务的本地代理,onScreenTurningOn会把依次传递给keyguard服务,在keyguard服务做完onScreenTurningOn处理后会回调锁屏的回调mKeyguardDrawnCallback发送MSG_KEYGUARD_DRAWN_COMPLETE消息给PhoneWindowManager进行其他窗口的绘制。
KeyguardServiceDelegate::onScreenTurningOn-->KeyguardServiceWrapper::onScreenTurningOn-->KeyguardService::onScreenTurningOn-->KeyguardLifecyclesDispatcher::handleMessage-->ScreenOnCoordinator::onScreenTurningOn-->mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE)-->PhoneWindowManager::finishKeyguardDrawn-->mWindowManagerInternal.waitForAllWindowsDrawn-->WindowManagerService::waitForAllWindowsDrawn。
我们看一下WMS里面窗口的绘制(/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java):
public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
final WindowContainer container = displayId == INVALID_DISPLAY
? mRoot : mRoot.getDisplayContent(displayId);
if (container == null) {
// The waiting container doesn't exist, no need to wait to run the callback. Run and
// return;
callback.run();
return;
}
boolean allWindowsDrawn = false;
synchronized (mGlobalLock) {
//等待其他窗口比如动画绘制
container.waitForAllWindowsDrawn();
mWindowPlacerLocked.requestTraversal();
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
if (container.mWaitingForDrawn.isEmpty()) {
allWindowsDrawn = true;
} else {
mWaitingForDrawnCallbacks.put(container, callback);
mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
}
}
// 所有窗口都绘制完成后回调以下Runnable,发送MSG_WINDOW_MANAGER_DRAWN_COMPLETE消息
// mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
// if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for every display");
// mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
// INVALID_DISPLAY, 0));
// }, WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
if (allWindowsDrawn) {
callback.run();
}
}
waitForAllWindowsDrawn中首先等待所有可以显示出来的窗口绘制完成,比如窗口动画。
在亮屏幕中我们常常遇到亮屏慢的问题,其实往往就是绘制超时(一般超时是1s)导致的,如果超过 在这个时长在log中我们就可以看到“Keyguard drawn timeout. Setting mKeyguardDrawComplete”的打印。
在所有窗口都绘制完成后的回调中会发送MSG_WINDOW_MANAGER_DRAWN_COMPLETE消息给PhoneWindowManager,触发finishWindowsDrawn调用。
private void finishWindowsDrawn(int displayId) {
if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY) {
final ScreenOnListener screenOnListener = mScreenOnListeners.removeReturnOld(displayId);
if (screenOnListener != null) {
screenOnListener.onScreenOn();
}
return;
}
if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
return;
}
//所有窗口的都绘制完成后通知DisplayPolicy监听者
finishScreenTurningOn();
}
private void finishScreenTurningOn() {
// We have just finished drawing screen content. Since the orientation listener
// gets only installed when all windows are drawn, we try to install it again.
mDefaultDisplayRotation.updateOrientationListener();
final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
return; // Spurious or not ready yet.
}
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
enableScreen(listener, true /* report */);
}
private void enableScreen(ScreenOnListener listener, boolean report) {
...
// 这里的listenter就是我们setScreenState 中设置的 mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
if (report) {
if (listener != null) {
listener.onScreenOn();
}
}
if (enableScreen) {
try {
mWindowManager.enableScreenIfNeeded();
} catch (RemoteException unhandled) {
}
}
}
具体的绘制原理我们这里只做简单的概念描述,详细的原理分析有专门的文章介绍。
锁屏是在systemui里面绘制的,它与其他的普通android app实现是一样的,所有的界面显示都是一个个Activity,每个Activity都对应一个PhoneWindow(Window),PhoneWindow中持有一个DecorView负责窗口的布局,是所有View的最顶层View。
如下图, app绘制就是对view进行编排,然后交给Choreography把view变为gpu纹理数据,通过gpu渲染变为特定的图形数据后统一由SurfaceFlinger合成,并在正确的时间发送给显示屏幕,让其显示出来。
finishWindowsDrawn中主要完成了在所有窗口绘制完成后的消息上报工作(listener.onScreenOn(),这里的上报对象就是前面setScreenState中设置 ScreenOnUnblocker,负责发送给DisplayPowerController发送MSG_SCREEN_ON_UNBLOCKED消息,去除亮屏幕阻塞,重新调用updatePowerState进行绘制后的其他工作。
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
...
case MSG_SCREEN_ON_UNBLOCKED:
if (mPendingScreenOnUnblocker == msg.obj) {
// 去除亮屏幕阻塞,重新调用updatePowerState进行绘制后的其他工作
unblockScreenOn();
updatePowerState();
}
break;
...
}
}
}
我们进行进行updatePowerState(DisplayPowerController.java)中分析绘制完成后的剩余工作。updatePowerState判断查看绘制完成后就会设置合适的亮度到屏幕中,完成亮屏的最后一个工作。
调用链为:DisplayPowerState::postScreenUpdateThreadSafe-->mPhotonicModulator.setState-->DisplayPowerState::setState-->DisplayPowerState::run-->DisplayManagerService::requestDisplayState-->DisplayManagerService::updateDisplayStateLocked-->LocalDisplayAdapter::requestDisplayStateLocked-->setDisplayBrightness-->BacklightAdapter::setBacklight-->LightsService::setBrightness-->LightsService::setLightLocked-->LightsService::setLightUnchecked-->setLight_native-->Light::setLight,最后亮度的配置其实是请求背光服务,通过HAL层接口完成的点亮屏幕操作。
借用网上的一个图可以理解背光服务的框架:
MTK平台上背光服务(LightsService)实现中关键代码如下(vendor/mediatek/proprietary/hardware/liblights/light.c):
static int open_lights(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);
if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
set_light = set_light_backlight;
}
else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
set_light = set_light_attention;
if (access(RED_LED_FILE, F_OK) < 0)
return -errno;
if (access(GREEN_LED_FILE, F_OK) < 0)
return -errno;
if (access(BLUE_LED_FILE, F_OK) < 0)
return -errno;
}
...
pthread_once(&g_init, init_globals);
struct light_device_t *dev = malloc(sizeof(struct light_device_t));
if (!dev)
return -ENOMEM;
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = (int (*)(struct hw_device_t*))close_lights;
dev->set_light = set_light;
*device = (struct hw_device_t*)dev;
return 0;
}
static int
set_light_backlight(struct light_device_t* dev,
struct light_state_t const* state)
{
int err = 0;
int brightness = rgb_to_brightness(state);
pthread_mutex_lock(&g_lock);
g_backlight = brightness;
// 这里LCD_FILE就是/sys/class/leds/lcd-backlight/brightness
err = write_int(LCD_FILE, brightness);
if (g_haveTrackballLight) {
handle_trackball_light_locked(dev);
}
pthread_mutex_unlock(&g_lock);
return err;
}
从实现上看亮度的设置就是往/sys/class/leds/lcd-backlight/brightness这个sys文件中写入了亮度值。这个sys文件是由内核背光驱动生成的,所有的写操作就会触发背光驱动亮度的设置动作。
关于 /sys/class/leds/lcd-backlight/brightness 这个文件的实现主要是利用 linux LED框架实现,具体可查看 led_classdev_register 相关代码,代码逻辑比较简单,此处不详细描述。
同样,我们用一张序列图总结一下底层是如何进行绘制的:DisplayPowerController首先给显示模块硬件上电,然后在触发各个窗口(比如状态栏 锁屏 通知栏 导航栏等)进行绘制,等待所有的需要显示的窗口绘制完成后,把屏幕的背光设置为预设亮度,最终将画面显示到屏幕上。