在文章MTK 唤醒时间分析中分析了内核中的主要的亮屏重要阶段,此篇文章结合上层的log一起来分析下整个系统的亮屏流程。整个流程可以分为如下几个部分:
(1)power键(home键)产生并上报(在input子系统中已经介绍);
(2)上层接收到到键值,PowerManagerService执行相关处理;
(3)PMS更新全局电源状态,并开始唤醒屏幕和背光,并通知各个模块(如图形绘制Keyguard);
(4)调用surfaceflinger开始执行屏幕的和背光的操作;
(5)调用到驱动中的late_resume(在MTK 唤醒时间分析中已经介绍);
一、PMS接收键值过程及更新全局状态:
当PowerMangerService接收到power键值后,会打印出调用的堆栈信息如下:
01-01 20:08:44.042224 981 1162 D PowerManagerService: |----com.android.server.power.PowerManagerService.wakeUpNoUpdateLocked(PowerManagerService.java:1750)
01-01 20:08:44.042242 981 995 D NetworkPolicy: no need to update restrict data rules for uid 1000
01-01 20:08:44.042271 981 1162 D PowerManagerService: |----com.android.server.power.PowerManagerService.wakeUpInternal(PowerManagerService.java:1741)
01-01 20:08:44.042282 981 995 D NetworkPolicy: no need to update restrict power rules for uid 1000
01-01 20:08:44.042374 981 1162 D PowerManagerService: |----com.android.server.power.PowerManagerService.-wrap43(PowerManagerService.java)
01-01 20:08:44.042415 981 1162 D PowerManagerService: |----com.android.server.power.PowerManagerService$BinderService.wakeUp(PowerManagerService.java:4261)
01-01 20:08:44.042458 981 1162 D PowerManagerService: |----android.os.PowerManager.wakeUp(PowerManager.java:769)
01-01 20:08:44.042505 981 1162 D PowerManagerService: |----com.android.server.policy.PhoneWindowManager.wakeUp(PhoneWindowManager.java:7421)
01-01 20:08:44.042548 981 1162 D PowerManagerService: |----com.android.server.policy.PhoneWindowManager.interceptKeyBeforeQueueing(PhoneWindowManager.java:6874)
01-01 20:08:44.042579 981 1162 D PowerManagerService: |----com.android.server.wm.InputMonitor.interceptKeyBeforeQueueing(InputMonitor.java:465)
01-01 20:08:44.042609 981 1162 D PowerManagerService: |----com.android.server.input.InputManagerService.interceptKeyBeforeQueueing(InputManagerService.java:1897)
从上面的log中可以清晰的看到键值被InputManagerService.java的interceptKeyBeforeQueueing方法接收到:
private WindowManagerCallbacks mWindowManagerCallbacks;
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
interceptKeyBeforeQueueing会调用到PhoneManagerService(PhoneWindowManager.java)中相同的方法如下:
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
...//一些列的键值判断处理
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
if(!isKeysTest()){
interceptPowerKeyDown(event, interactive);
}else{
sendKey(KeyEvent.KEYCODE_POWER);
}
}
break;
}
......
case KeyEvent.KEYCODE_WAKEUP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
break;
}
if (isWakeKey) {
wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
}
return result;
}
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);
return true;
}
上面代码经过一些列的键值判断后,最终会调用到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);
return true;
}
PowerManager.java中的wakeUp比较简单:
public void wakeUp(long time, String reason) {
try {
mService.wakeUp(time, reason, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
最终会调用到PowerManagerService中的wakeUp做真正的唤醒工作:
public void wakeUp(long eventTime, String reason, String opPackageName) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
try {
wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
int opUid) {
synchronized (mLock) {
if (mIPOShutdown && reason != PowerManager.WAKE_UP_REASON_SHUTDOWN)
return;
if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
updatePowerStateLocked();//更新电源状态
}
}
}
onWakefulnessChangeStarted中会调用notify的onWakefulnessChangeStarted发送亮屏广播,通知亮屏。
private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
String opPackageName, int opUid) {
Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
try {
......
switch (mWakefulness) {
case WAKEFULNESS_ASLEEP:
Slog.i(TAG, "Waking up from sleep (uid " + reasonUid +")...");
break;
case WAKEFULNESS_DREAMING:
Slog.i(TAG, "Waking up from dream (uid " + reasonUid +")...");
break;
case WAKEFULNESS_DOZING:
Slog.i(TAG, "Waking up from dozing (uid " + reasonUid +")...");
break;
}
mLastWakeTime = eventTime;
setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
.....
}
private void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness != wakefulness) {
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
mNotifier.onWakefulnessChangeStarted(wakefulness, reason);//调用到notify中
}
}
在Notifier.java中与AMS,window,input进行交互,通知各模块手机状态发生了改变,根据屏幕状态各自进行处理, 最后发送亮灭屏广播, 通知关心的模块,Notify.java中的onWakefulnessChangeStarted方法列举一些重要的内容如下:
public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
......
mActivityManagerInternal.onWakefulnessChanged(wakefulness); //与AMS交互处理
mBatteryStats.noteInteractive(interactive);//唤醒battery状态
handleEarlyInteractiveChange(); //处理前期交互模式改变 ,如锁屏 Keyguard等
......
}
当上述各个模块初始化完成后会重新调用到wakeUpInternal中的updatePowerStateLocked更新全局电源状态,并开始执行DisplayPowerController.java中的方法。
private void updatePowerState() {
int state;
......
int brightness = PowerManager.BRIGHTNESS_DEFAULT;
// Apply the proximity sensor.
if (mProximitySensor != null) {
//获取psensor的状态
}
if (mScreenOffBecauseOfProximity) {
state = Display.STATE_OFF;
}
//获取自动背光的状态值后,调用animateScreenBrightness开始执行背光操作
animateScreenBrightness(brightness,
slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
//判断亮屏前是否有未完成的任务,如图形是否绘制成功等
// Grab a wake lock if we have unfinished business.
if (!finished && !mUnfinishedBusiness) {
if (DEBUG) {
Slog.d(TAG, "Unfinished business...");
}
mCallbacks.acquireSuspendBlocker();
mUnfinishedBusiness = true;
}
// Release the wake lock when we have no unfinished business.
if (finished && mUnfinishedBusiness) {
if (DEBUG) {
Slog.d(TAG, "Finished business...");
}
mUnfinishedBusiness = false;
mCallbacks.releaseSuspendBlocker();
}
// Record if dozing for future comparison.
mDozing = state != Display.STATE_ON;
}
updatePowerState()中有很多的操作,上面的代码只列举了部分重要阶段的代码,上面的任务完成后,下面会调用到LocalDisplayAdapter.java真正的开始亮屏和亮背光的操作。
LocalDisplayAdapter.java
public Runnable requestDisplayStateLocked(final int state, final int brightness)
private void setDisplayState(int state)
blockScreenOn会等待windws绘制完成,unblockScreenOn
private void setDisplayBrightness(int brightness)
SurfaceControl.setDisplayPowerMode调用到surfacefligner的setPowerMode
二、surfaceflinger亮屏和亮背光
在LocalDisplayAdapter中会通过相应的接口调用到surfacefligner的操作:
//frameworks/base/core/java/android/view/SurfaceControl.java
public static void setDisplayPowerMode(IBinder displayToken, int mode) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
nativeSetDisplayPowerMode(displayToken, mode);
}
这样又调用到native层的接口:
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw,
int mode, bool stateLockHeld) {
ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
this);
int32_t type = hw->getDisplayType();
int currentMode = hw->getPowerMode();
hw->setPowerMode(mode);
if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
ALOGW("Trying to set power mode for virtual display");
return;
}
//...这里做了很多工作,上面只列举了setPowerMode的部分
}
void HWCDisplay::setPowerMode(const int32_t& mode)
{
// screen blanking based on early_suspend in the kernel
HWC_LOGI("Display(%" PRId64 ") SetPowerMode(%d)", m_disp_id, mode);
m_power_mode = mode;
DisplayManager::getInstance().setDisplayPowerState(m_disp_id, mode);
HWCDispatcher::getInstance().setPowerMode(m_disp_id, mode);
DisplayManager::getInstance().setPowerMode(m_disp_id, mode);
......
}
这一块的代码都会被编译到hwcomposer.<platform>.so库中,之前部分平台此部分代码不开源,当前新的一些平台,这部分的代码已经开源,所以可以看到上面部分的代码,下面才是真正完成setPowerMode工作的函数体:
void DispDevice::setPowerMode(int dpy,int mode)
{
if (HWCMediator::getInstance().m_features.control_fb)
{
HWC_LOGD("DispDevice::setPowerMode() dpy:%d mode:%d", dpy, mode);
char filename[32] = {0};
//打开/dev/graphics/fb%d的节点进行ioctl的操作
snprintf(filename, sizeof(filename), "/dev/graphics/fb%d", dpy);
int fb_fd = open(filename, O_RDWR);
if (fb_fd <= 0)
{
HWC_LOGE("Failed to open fb%d device: %s", dpy, strerror(errno));
return;
}
switch (mode)
{
case HWC_POWER_MODE_OFF://灭屏
{
err = WDT_IOCTL(fb_fd, FBIOBLANK, FB_BLANK_POWERDOWN);
break;
}
case HWC_POWER_MODE_NORMAL://亮屏
{
err = WDT_IOCTL(fb_fd, FBIOBLANK, FB_BLANK_UNBLANK);
m_color_transform_info[dpy].resend_color_transform = true;
break;
}
case HWC_POWER_MODE_DOZE:
case HWC_POWER_MODE_DOZE_SUSPEND:
{
......
}
protectedClose(fb_fd);
}
}
三、kernel resume work:
上面setPowerMode会直接通过fb0-x的节点调用到kernel的代码中:
//kernel-3.18/drivers/misc/mediatek/video/common/mtkfb.c
static struct fb_ops mtkfb_ops = {
.owner = THIS_MODULE,
.fb_ioctl = mtkfb_ioctl,
#ifdef CONFIG_COMPAT
.fb_compat_ioctl = mtkfb_compat_ioctl,
#endif
#if defined(CONFIG_PM_AUTOSLEEP)
.fb_blank = mtkfb_blank,
#endif
}
static int mtkfb_blank(int blank_mode, struct fb_info *info)
{
enum mtkfb_power_mode prev_pm = primary_display_get_power_mode();
switch (blank_mode) {
case FB_BLANK_UNBLANK:
case FB_BLANK_NORMAL:
DISPDBG("mtkfb_blank mtkfb_late_resume\n");
if (bypass_blank) {
DISPERR("FB_BLANK_UNBLANK bypass_blank %d\n", bypass_blank);
break;
}
primary_display_set_power_mode(FB_RESUME);
mtkfb_late_resume();//正式进入到late_resume相关屏的操作
debug_print_power_mode_check(prev_pm, FB_RESUME);
if (!lcd_fps)
msleep(30);
else
msleep(2 * 100000 / lcd_fps); /* Delay 2 frames. */
break;
到这里,整个流程就已经彻底贯通,流程图如下:
kernel部分的唤醒流程如下:
作者:frank_zyp