1. 程式人生 > >【android睡眠喚醒 二】MTK平臺喚醒框架分解

【android睡眠喚醒 二】MTK平臺喚醒框架分解

    在文章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;
    

    上面的流程MTK 喚醒時間分析一文中已經介紹。

    到這裡,整個流程就已經徹底貫通,流程圖如下:

    kernel部分的喚醒流程如下:

 

作者:frank_zyp 
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。 
本文無所謂版權,歡迎轉載。