天天看點

Android系統中的螢幕狀态切換以及亮度設定

Android系統的狀态包括wake、earlysuspend以及suspend狀态,其使用鎖和定時器來進行狀态的切換。

而在wake狀态,螢幕首先是調至設定的亮度,如果沒有其他動作,當經過一段時間後螢幕會變暗,再經過一段時間螢幕會關閉,于是螢幕的狀态也包括3種:bright、dim、off。

在Android應用架構層中的PowerManagerService.java(framework/base/services/java/com/android/server/)中實作了上述螢幕狀态的切換。下面對PowerManagerService.java如何切換螢幕狀态進行分析。

在PowerManagerService的初始化函數init中,會進行必要參數的初始化,包括LightsService,BatteryService,Thread等等,然後會使用forceUserActivityLocked點亮螢幕。

void init(Context context, LightsService lights, IActivityManager activity,
            BatteryService battery) {
    mLightsService = lights;  // LightsService mLightsService
    mContext = context;
    mActivityService = activity;
    mBatteryStats = BatteryStatsService.getService();
    mBatteryService = battery;
    mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);  // LightsService.Light mLcdLight
    mButtonLight = lights.getLight(LightsService.LIGHT_ID_BUTTONS);
    mKeyboardLight = lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
    mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
    ......
    synchronized (mLocks) {
        updateNativePowerStateLocked();
        forceUserActivityLocked(); // 強制點亮螢幕
        mInitialized = true;
    }
}
           

在forceUserActivityLocked中主要是使用userActivity點亮螢幕

private void forceUserActivityLocked() {
    if (isScreenTurningOffLocked()) {
        // cancel animation so userActivity will succeed
        mScreenBrightness.animating = false;
    }
    boolean savedActivityAllowed = mUserActivityAllowed;
    mUserActivityAllowed = true;
    userActivity(SystemClock.uptimeMillis(), false);  // 使用userActivity點亮螢幕
    mUserActivityAllowed = savedActivityAllowed;
}

public void userActivity(long time, boolean noChangeLights) {
    ......
    userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
}
           

在userActivity方法中會收集所有鎖的狀态(mLocks存儲了所有申請的鎖),然後通過setPowerState方法來設定系統的狀态,最後通過setTimeoutLocked來開啟定時器

private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
        int eventType, boolean force) {
    ......
    if (!mAutoBrightnessButtonKeyboard) {
        // Turn on button (and keyboard) backlights on any event, so that they
        // don't suddenly disappear when the lock screen is unlocked (OTHER_EVENT),
        // and so capacitive buttons can be found on devices where they lack
        // identifying surface features.
        mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
    } else {
        // don't clear button/keyboard backlights when the screen is touched.
        mUserState |= SCREEN_BRIGHT;
    }
    mWakeLockState = mLocks.reactivateScreenLocksLocked();
    setPowerState(mUserState | mWakeLockState, noChangeLights, WindowManagerPolicy.OFF_BECAUSE_OF_USER);
    setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
    ......
}
           

setPowerState方法會根據輸入的狀态調用setScreenStateLocked方法來設定系統狀态

private void setPowerState(int newState, boolean noChangeLights, int reason)
{
    ......
    boolean oldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0; // 記錄系統目前螢幕狀态
    boolean newScreenOn = (newState & SCREEN_ON_BIT) != 0; // 記錄新的螢幕狀态
    final boolean stateChanged = mPowerState != newState; // 記錄狀态是否改變
    if (oldScreenOn != newScreenOn) { // 新的螢幕狀态和目前螢幕狀态不一緻時
if (newScreenOn) { // 新狀态是開啟螢幕
   boolean reallyTurnScreenOn = true;
   if (mPreventScreenOn) {
                reallyTurnScreenOn = false; // 如果螢幕開啟被阻止,則設定reallyTurnScreenOn為false
            }
   if (reallyTurnScreenOn) {
                err = setScreenStateLocked(true); // 使用setScreenStateLocked喚醒系統
......
            } else {
                setScreenStateLocked(false); // 使系統睡眠
                // But continue as if we really did turn the screen on...
                err = 0;
            }
   if (err == 0) {
            sendNotificationLocked(true, -1);
            if (stateChanged) {
                updateLightsLocked(newState, 0);
            }
            mPowerState |= SCREEN_ON_BIT;
} else { // 如果新狀态是關閉螢幕,則使用screenOffFinishedAnimatingLocked方法使系統睡眠
   if (stateChanged) {
                updateLightsLocked(newState, 0);
            }
   ......
   if (!mScreenBrightness.animating) {
                err = screenOffFinishedAnimatingLocked(reason); // 該方法也是調用setScreenStateLocked方法睡眠系統
            } else {
                err = 0;
                mLastTouchDown = 0;
            }
}
    } else if (stateChanged) {
        // Screen on/off didn't change, but lights may have.
        updateLightsLocked(newState, 0);
    }
    ......
}
           

在setScreenStateLocked方法中會使用Power.setScreenState方法調用jni層中的函數,最終會傳遞至核心層,在核心層中執行相應的睡眠系統或喚醒系統

private int setScreenStateLocked(boolean on) {
    ......
    int err = Power.setScreenState(on);
    ......
}
           

而setScreenStateLocked方法中,函數updateLightsLocked用來更新lights,後面将進行分析。

以上一小段介紹了userActivity喚醒系統的簡單流程,而在PowerManagerService的初始化函數中,不僅會通過userActivity中的setPowerState來喚醒系統,同時也會使用userActivity中的setTimeoutLocked來開啟一個定時器,用于切換螢幕的狀态

private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
    long timeoutOverride = originalTimeoutOverride;
    ......
    long when = 0;
    if (timeoutOverride <= 0) { // 時間設定<=0時,此時系統會使用預設的定時時間開啟計時器
        switch (nextState)
        {
            case SCREEN_BRIGHT: // 新狀态是BRIGHT,則when加上mKeylightDelay
                when = now + mKeylightDelay;
                break;
            case SCREEN_DIM: // 新狀态是DIM,則when加上mDimDelay
            if (mDimDelay >= 0) {
                when = now + mDimDelay;
                break;
            }
            case SCREEN_OFF: // 新狀态是OFF,則when加上mScreenOffDelay
                synchronized (mLocks) {
                    when = now + mScreenOffDelay;
                }
                break;
            default:
                when = now;
                break;
        }
    } else { // 如果定時時間設定,即為timeoutOverride
        override: {
            if (timeoutOverride <= mScreenOffDelay) {
                when = now + timeoutOverride;
                nextState = SCREEN_OFF;
                break override;
            }
            timeoutOverride -= mScreenOffDelay;
   if (mDimDelay >= 0) {
                if (timeoutOverride <= mDimDelay) {
                    when = now + timeoutOverride;
                    nextState = SCREEN_DIM;
                    break override;
                }
                timeoutOverride -= mDimDelay;
            }
   when = now + timeoutOverride;
            nextState = SCREEN_BRIGHT;
        }
    }
    mHandler.removeCallbacks(mTimeoutTask); // 移除舊的mTimeoutTask時間
    mTimeoutTask.nextState = nextState; // 指派狀态
    mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
                    ? (originalTimeoutOverride - timeoutOverride)
                    : -1;
    mHandler.postAtTime(mTimeoutTask, when); // 重新啟動定時器,在when時間後執行mTimeoutTask任務
    mNextTimeout = when;
}
           

在PowerManagerService的初始化函數中最終會通過setTimeoutLocked來啟動定時器,當定時器到時間後就會執行mTimeoutTask任務,下面可以看看mTimeoutTask任務的定義:

TimeoutTask mTimeoutTask = new TimeoutTask();

mTimeoutTask定義為類型是TimeoutTask的對象,TimeoutTask如下所示:

private class TimeoutTask implements Runnable
{
    int nextState; // access should be synchronized on mLocks
    long remainingTimeoutOverride;
    public void run()
    {
        synchronized (mLocks) {
            if (nextState == -1) {
                return;
            }
            mUserState = this.nextState;
            setPowerState(this.nextState | mWakeLockState); // 調用setPowerState來睡眠或喚醒系統
            long now = SystemClock.uptimeMillis();
            switch (this.nextState) // 更新定時器
            {
                case SCREEN_BRIGHT: // 如果狀态是BRIGHT,則更新定時器,是定時器在到期後執行SCREEN_DIM操作
                    if (mDimDelay >= 0) {
                        setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
                        break;
                    }
                case SCREEN_DIM: // 如果狀态是DIM,則更新定時器,是定時器在到期後執行SCREEN_OFF操作
                    setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
                    break;
            }
        }
    }
}
           

于是在PowerManagerService中通過定時器來切換螢幕的狀态,同時也會調用setPowerState方法來睡眠或喚醒系統,而具體的螢幕亮度是如何實作的呢?

在setPowerState中使用了updateLightsLocked來更新螢幕的狀态。

private void updateLightsLocked(int newState, int forceState) {
    final int oldState = mPowerState; // 将目前系統狀态指派于oldState
    ......
    final int realDifference = (newState ^ oldState); // 判斷新狀态和舊狀态的不同之處
    final int difference = realDifference | forceState;
    if (difference == 0) {
        return;
    }
    int offMask = 0;
    int dimMask = 0;
    int onMask = 0;
    int preferredBrightness = getPreferredBrightness(); // 擷取預設的亮度值
    if ((difference & KEYBOARD_BRIGHT_BIT) != 0) { // 如果是鍵盤亮度不同,則更新offMask和onMask
        if ((newState & KEYBOARD_BRIGHT_BIT) == 0) {
            offMask |= KEYBOARD_BRIGHT_BIT;
        } else {
            onMask |= KEYBOARD_BRIGHT_BIT;
        }
    }
    if ((difference & BUTTON_BRIGHT_BIT) != 0) { // 如果是按鍵亮度不同,則更新offMask和onMask
        if ((newState & BUTTON_BRIGHT_BIT) == 0) {
            offMask |= BUTTON_BRIGHT_BIT;
        } else {
            onMask |= BUTTON_BRIGHT_BIT;
        }
    }
    if ((difference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) { // 如果是螢幕開啟或點亮狀态位不同
int nominalCurrentValue = -1; // 目前亮度
if ((realDifference & (SCREEN_ON_BIT | SCREEN_BRIGHT_BIT)) != 0) {
            switch (oldState & (SCREEN_BRIGHT_BIT|SCREEN_ON_BIT)) { // 判斷舊的狀态
                case SCREEN_BRIGHT_BIT | SCREEN_ON_BIT: // 如果舊狀态是開啟并點亮螢幕
                    nominalCurrentValue = preferredBrightness; // 則将preferredBrightness賦給nominalCurrentValue
                    break;
                case SCREEN_ON_BIT: // 如果舊狀态僅僅是開啟螢幕
                    nominalCurrentValue = mScreenDim;
                    break;
                case 0: // 如果舊狀态是關閉螢幕
                    nominalCurrentValue = Power.BRIGHTNESS_OFF;
                    break;
                case SCREEN_BRIGHT_BIT:
                default:
                    // not possible
                    nominalCurrentValue = (int)mScreenBrightness.curValue;
                    break;
            }
        }
int brightness = preferredBrightness;
        int steps = ANIM_STEPS;
        if ((newState & SCREEN_BRIGHT_BIT) == 0) { // 如果新狀态不是點亮螢幕,即變暗螢幕或關閉螢幕
   // 此時會使用動畫操作漸漸的使螢幕變暗或關閉,這裡計算step
            // dim or turn off backlight, depending on if the screen is on
            // the scale is because the brightness ramp isn't linear and this biases
            // it so the later parts take longer.
            final float scale = 1.5f;
            float ratio = (((float)mScreenDim)/preferredBrightness);
            if (ratio > 1.0f) ratio = 1.0f;
            if ((newState & SCREEN_ON_BIT) == 0) {
                if ((oldState & SCREEN_BRIGHT_BIT) != 0) {
                    // was bright
                    steps = ANIM_STEPS;
                } else {
                    // was dim
                    steps = (int)(ANIM_STEPS*ratio*scale);
                }
                brightness = Power.BRIGHTNESS_OFF; // 如果新狀态是關閉螢幕,則指派brightness為Power.BRIGHTNESS_OFF
            } else {                               // brightness為新狀态的亮度
                if ((oldState & SCREEN_ON_BIT) != 0) {
                    // was bright
                    steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale);
                } else {
                    // was dim
                    steps = (int)(ANIM_STEPS*ratio);
                }
                if (mStayOnConditions != 0 && mBatteryService.isPowered(mStayOnConditions)) {
                    // If the "stay on while plugged in" option is
                    // turned on, then the screen will often not
                    // automatically turn off while plugged in.  To
                    // still have a sense of when it is inactive, we
                    // will then count going dim as turning off.
                    mScreenOffTime = SystemClock.elapsedRealtime();
                    mAlwaysOnAndDimmed = true;
                }
                brightness = mScreenDim; // 如果新狀态是變暗螢幕,則指派brightness為mScreenDim
            }
        }
if (!mSkippedScreenOn) {
            mScreenBrightness.setTargetLocked(brightness, steps,
                    INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); // 設定螢幕的亮度
        }
    }
    // 以下根據bit位的不同調用setLightBrightness來設定亮度,包括螢幕、鍵盤和按鍵
    if (offMask != 0) {
        setLightBrightness(offMask, Power.BRIGHTNESS_OFF);
    }
    if (dimMask != 0) {
        int brightness = mScreenBrightnessDim;
        if ((newState & BATTERY_LOW_BIT) != 0 &&
                brightness > Power.BRIGHTNESS_LOW_BATTERY) {
            brightness = Power.BRIGHTNESS_LOW_BATTERY;
        }
        setLightBrightness(dimMask, brightness);
    }
    if (onMask != 0) {
        int brightness = getPreferredBrightness();
        if ((newState & BATTERY_LOW_BIT) != 0 &&
                brightness > Power.BRIGHTNESS_LOW_BATTERY) {
            brightness = Power.BRIGHTNESS_LOW_BATTERY;
        }
        setLightBrightness(onMask, brightness);
    }
}
           

在updateLightsLocked方法中使用了mScreenBrightness.setTargetLocked方法來設定螢幕的亮度,而mScreenBrightness是類型為BrightnessState的對象,以下是其定義:

private final BrightnessState mScreenBrightness = new BrightnessState(SCREEN_BRIGHT_BIT);

此類是通過逐漸減少step值的亮度來是的螢幕到達最終的亮度,下面分析setTargetLocked方法

void setTargetLocked(int target, int stepsToTarget, int initialValue,
        int nominalCurrentValue) {
    if (!initialized) { // 如果正在進行且目标亮度相同則傳回
        initialized = true;
        curValue = (float)initialValue;
    } else if (targetValue == target) {
        return;
    }
    targetValue = target;
    delta = (targetValue -
            (nominalCurrentValue >= 0 ? nominalCurrentValue : curValue))
            / stepsToTarget; // 計算delta值
    animating = true;
    mScreenOffHandler.removeCallbacks(this); // 移除舊的事件
    mScreenOffHandler.post(this); // 開始新的事件
}
           

其中mScreenOffHandler移除或開始的事件即為mScreenBrightness本身,此時會執行BrightnessState類中的run方法。

public void run() {
    synchronized (mLocks) {
        final boolean turningOn = animating && (int)curValue == Power.BRIGHTNESS_OFF; // 是否是打開螢幕
        final boolean turningOff = animating && targetValue == Power.BRIGHTNESS_OFF; // 是否是關閉螢幕
        // Check for the electron beam for fully on/off transitions.
        // Otherwise, allow it to fade the brightness as normal.
        final boolean electrifying =
                ((mElectronBeamAnimationOff && turningOff) ||
                 (mElectronBeamAnimationOn && turningOn));
        if (!electrifying && (mAnimateScreenLights || !turningOff)) { 
            long now = SystemClock.uptimeMillis();
            boolean more = mScreenBrightness.stepLocked(); // 使用stepLocked逐漸調至目标亮度
            if (more) {
                mScreenOffHandler.postAtTime(this, now+(1000/60));
            }
         } else {
            // It's pretty scary to hold mLocks for this long, and we should
            // redesign this, but it works for now.
            if (turningOff) {
                if (electrifying) {
                    nativeStartSurfaceFlingerOffAnimation(
                            mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
                            ? 0 : mAnimationSetting);
                }
                mScreenBrightness.jumpToTargetLocked(); // 直接變成目标亮度
            } else if (turningOn) {
                if (electrifying) {
                    int delay=mContext.getResources().getInteger(com.android.internal.R.integer.config_screenOnAnimation);
                    if(delay>0) {
                        startElectronBeamDelayed(new Runnable() {
                            @Override
                            public void run() {
                                startElectronBeamOnAnimation();
                                synchronized(mElectronBeamOnHandler) {
                                    mElectronBeamOnHandler.notifyAll();
                                }
                            }
                        },delay);
                    } else {
                        startElectronBeamOnAnimation();
                    }
                } else {
                    mScreenBrightness.jumpToTargetLocked(); // 直接變成目标亮度
                }
            }
        }
    }
}
           

在run方法中,不論是逐漸調制目标亮度stepLocked,或者直接變成目标亮度jumpToTargetLocked,都會調用到setLightBrightness方法用于改變亮度,下面來分析setLightBrightness方法。

private void setLightBrightness(int mask, int value) {
    int brightnessMode = (mAutoBrightessEnabled
                        ? LightsService.BRIGHTNESS_MODE_SENSOR
                        : LightsService.BRIGHTNESS_MODE_USER);
    if ((mask & SCREEN_BRIGHT_BIT) != 0) { // 如果是螢幕亮度,則使用mLcdLight.setBrightness
        mLcdLight.setBrightness(value, brightnessMode);
        mLastLcdValue = value;
    }
    if ((mask & BUTTON_BRIGHT_BIT) != 0) { // 如果是按鍵亮度,則使用mButtonLight.setBrightness
        // Use sensor-determined brightness values when the button (or keyboard)
        // light is on, since users may want to specify a custom brightness setting
        // that disables the button (or keyboard) backlight entirely in low-ambient
        // light situations.
        mButtonLight.setBrightness(mLightSensorButtonBrightness >= 0 && value > 0 ?
                                   mLightSensorButtonBrightness : value);


    }
    if ((mask & KEYBOARD_BRIGHT_BIT) != 0) { // 如果是鍵盤亮度,則使用mKeyboardLight.setBrightness
        mKeyboardLight.setBrightness(mLightSensorKeyboardBrightness >= 0 && value > 0 ?
                                         mLightSensorKeyboardBrightness : value);
    }
}
           

在setLightBrightness方法中會根據mask的值來相應的改變螢幕、鍵盤和按鍵的亮度,螢幕的亮度主要使用mLcdLight.setBrightness方法,其中mLcdLight是類型為LightsService.Light的對象,在PowerManagerService的初始化函數中進行了指派:

mLcdLight = lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);

LightsService.Light類型定義在framework/base/services/java/com/android/server/LightsService.java中,下面來分析其setBrightness方法:

public void setBrightness(int brightness, int brightnessMode) {
    synchronized (this) {
        int color = brightness & 0x000000ff;
        color = 0xff000000 | (color << 16) | (color << 8) | color;
        setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
    }
}
           

在setBrightness方法中,主要是将亮度值擴充成32bit的color值來提供給setLightLocked處理。

private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
    if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
        mColor = color;
        mMode = mode;
        mOnMS = onMS;
        mOffMS = offMS;
        setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
    }
}
           

setLightLocked會調用setLight_native來将亮度值傳遞至jni層,setLight_native在檔案framework/base/services/jni/com_android_server_LightsService.cpp中實作。

static void setLight_native(JNIEnv *env, jobject clazz, int ptr,
        int light, int colorARGB, int flashMode, int onMS, int offMS, int brightnessMode)
{
    Devices* devices = (Devices*)ptr;
    light_state_t state;


    if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
        return ;
    }
    memset(&state, 0, sizeof(light_state_t));
    state.color = colorARGB;
    state.flashMode = flashMode;
    state.flashOnMS = onMS;
    state.flashOffMS = offMS;
    state.brightnessMode = brightnessMode;
    devices->lights[light]->set_light(devices->lights[light], &state);
}
           

而setLight_native方法使用devices->lights[light]->set_light來設定亮度值。在com_android_server_LightsService.cpp的初始化函數中,會對devices進行初始化,如下所示:

static jint init_native(JNIEnv *env, jobject clazz)
{
    int err;
    hw_module_t* module;
    Devices* devices;
    
    devices = (Devices*)malloc(sizeof(Devices));

    err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        devices->lights[LIGHT_INDEX_BACKLIGHT]
                = get_device(module, LIGHT_ID_BACKLIGHT);
        devices->lights[LIGHT_INDEX_KEYBOARD]
                = get_device(module, LIGHT_ID_KEYBOARD);
        devices->lights[LIGHT_INDEX_BUTTONS]
                = get_device(module, LIGHT_ID_BUTTONS);
        devices->lights[LIGHT_INDEX_BATTERY]
                = get_device(module, LIGHT_ID_BATTERY);
        devices->lights[LIGHT_INDEX_NOTIFICATIONS]
                = get_device(module, LIGHT_ID_NOTIFICATIONS);
        devices->lights[LIGHT_INDEX_ATTENTION]
                = get_device(module, LIGHT_ID_ATTENTION);
        devices->lights[LIGHT_INDEX_BLUETOOTH]
                = get_device(module, LIGHT_ID_BLUETOOTH);
        devices->lights[LIGHT_INDEX_WIFI]
                = get_device(module, LIGHT_ID_WIFI);
    } else {
        memset(devices, 0, sizeof(Devices));
    }

    return (jint)devices;
}

static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}
           

該方法使用hw_get_module來擷取動态庫子產品,其中LIGHTS_HARDWARE_MODULE_ID在/hardware/libhardware/include/hardware/lights.h中聲明:

#define LIGHTS_HARDWARE_MODULE_ID "lights"

是以在硬體抽象層,如果要編寫lights的子產品供上層使用,需要将自身命名為lights的子產品,下面以hardware/msm7k/liblights/lights.c為例,在其中就聲明了“lights”子產品

const struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = LIGHTS_HARDWARE_MODULE_ID,
    .name = "QCT MSM7K lights Module",
    .author = "Google, Inc.",
    .methods = &lights_module_methods,
};
           

然後在com_android_server_LightsService.cpp的初始化函數中使用get_device來打開相應的裝置,其調用了module->methods->open的方法,而在hardware/msm7k/liblights/lights.c也定義了回調函數:

static struct hw_module_methods_t lights_module_methods = {
    .open =  open_lights,
};

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_KEYBOARD, name)) {
        set_light = set_light_keyboard;
    }
    else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
        set_light = set_light_buttons;
    }
    else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
        set_light = set_light_battery;
    }
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
        set_light = set_light_notifications;
    }
    else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
        set_light = set_light_attention;
    }
    else {
        return -EINVAL;
    }

    pthread_once(&g_init, init_globals);

    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    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;
}
           

在open_lights方法中也映射了set_light方法,于是在com_android_server_LightsService.cpp的devices->lights[light]->set_light最終會調用hardware/msm7k/liblights/lights.c中set_light所映射的方法,set_light_backlight就是設定螢幕亮度的方法。

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;
    err = write_int(LCD_FILE, brightness);
    if (g_haveTrackballLight) {
        handle_trackball_light_locked(dev);
    }
    pthread_mutex_unlock(&g_lock);
    return err;
}
           

其中char const*const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness";

該方法會使用write_int函數将亮度寫入至路徑為LCD_FILE的檔案中,即傳遞至核心層。

使用者空間

//

核心空間

在核心空間中的kernel/drivers/leds/led-class.c子產品初始化函數中,使用了class_create建立了“leds”類目錄,同時在這個子產品中給出了led_classdev_register用于注冊led裝置,除此之外,還給出了這個class下的相關屬性:

// 子產品初始化函數
static int __init leds_init(void)
{
    leds_class = class_create(THIS_MODULE, "leds");
    if (IS_ERR(leds_class))
	return PTR_ERR(leds_class);
    leds_class->suspend = led_suspend;
    leds_class->resume = led_resume;
    leds_class->dev_attrs = led_class_attrs;
    return 0;
}

// led裝置注冊函數
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
    led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
    			      "%s", led_cdev->name);
    if (IS_ERR(led_cdev->dev))
	return PTR_ERR(led_cdev->dev);

#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
    /* add to the list of leds */
    down_write(&leds_list_lock);
    list_add_tail(&led_cdev->node, &leds_list);
    up_write(&leds_list_lock);
    if (!led_cdev->max_brightness)
	led_cdev->max_brightness = LED_FULL;
    led_update_brightness(led_cdev);
    init_timer(&led_cdev->blink_timer);
    led_cdev->blink_timer.function = led_timer_function;
    led_cdev->blink_timer.data = (unsigned long)led_cdev;
#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif
    printk(KERN_DEBUG "Registered led device: %s\n",
		led_cdev->name);
    return 0;
}
EXPORT_SYMBOL_GPL(led_classdev_register);

// led屬性
static struct device_attribute led_class_attrs[] = {
	__ATTR(brightness, 0644, led_brightness_show, led_brightness_store), 
	__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
	__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif
	__ATTR_NULL,
};
           

//

leds-lm3530.c子產品為例子(有regulator)

在kernel/drivers/leds/leds-lm3530.c子產品中,聲明了名稱為“lcd-backlight”的led裝置,

并使用了led_classdev_register将其注冊進入led class中。

drvdata->mode = pdata->mode;
drvdata->client = client;
drvdata->pdata = pdata;
drvdata->brightness = LED_OFF;
drvdata->enable = false;
drvdata->led_dev.name = LM3530_LED_DEV; // #define LM3530_LED_DEV "lcd-backlight"
drvdata->led_dev.brightness_set = lm3530_brightness_set;
i2c_set_clientdata(client, drvdata);
drvdata->regulator = regulator_get(&client->dev, "vin");
err = led_classdev_register(&client->dev, &drvdata->led_dev);
           

于是就會産生/sys/class/leds/lcd-backlight/brightness的目錄,是以上層使用write_int(LCD_FILE, brightness)向核心寫入亮度值會調用kernel/drivers/leds/led-class.c子產品中的led_brightness_store方法。

static ssize_t led_brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev); // 此處的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注冊的backlight_led
    ssize_t ret = -EINVAL;
    char *after;
    unsigned long state = simple_strtoul(buf, &after, 10);
    size_t count = after - buf;
    if (isspace(*after))
count++;
    if (count == size) {
ret = count;
if (state == LED_OFF)
   led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state); // 設定螢幕亮度
    }
    return ret;
}
           

該方法調用led_set_brightness設定螢幕亮度,其在kernel/drivers/leds/leds.h中

static inline void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
    if (value > led_cdev->max_brightness)
value = led_cdev->max_brightness;
    led_cdev->brightness = value;
    if (!(led_cdev->flags & LED_SUSPENDED))
        led_cdev->brightness_set(led_cdev, value); // 此處的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注冊的backlight_led
}
           

于是就調用了kernel/drivers/leds/leds-lm3530.c子產品中的回調函數brightness_set,即lm3530_brightness_set函數

static void lm3530_brightness_set(struct led_classdev *led_cdev,
    enum led_brightness brt_val)
{
    int err;
    struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
    switch (drvdata->mode) {
    case LM3530_BL_MODE_MANUAL:
        if (!drvdata->enable) {
   err = lm3530_init_registers(drvdata); // 如果沒有使能則會先使能,在這個方法中會調用regulator_enable來開啟regulator輸出
   if (err) {
       dev_err(&drvdata->client->dev, "Register Init failed: %d\n", err);
       break;
   }
        }
        /* set the brightness in brightness control register*/
        err = i2c_smbus_write_byte_data(drvdata->client,
       LM3530_BRT_CTRL_REG, brt_val / 2); // 設定亮度
        if (err)
   dev_err(&drvdata->client->dev, "Unable to set brightness: %d\n", err);
        else
   drvdata->brightness = brt_val / 2;
        if (brt_val == 0) {
   err = regulator_disable(drvdata->regulator); // 如果設定亮光為0,則會調用regulator_disable來關閉regulator輸出
   if (err)
       dev_err(&drvdata->client->dev, "Disable regulator failed\n");
   drvdata->enable = false;
}
break;
    case LM3530_BL_MODE_ALS:
break;
    case LM3530_BL_MODE_PWM:
break;
    default:
break;
    }
}
           

lm3530_brightness_set方法在打開螢幕時會使用regulator_enable開啟電源管理晶片的regulator輸出,在關閉螢幕時會調用regulator_disable關閉電源管理晶片的regulator輸出,在調整螢幕亮度時使用i2c_smbus_write_byte_data向寄存器中寫入數值來調整亮度。

///

msm_fb.c子產品為例子(無regulator)

在kernel/drivers/staging/msm/msm_fb.c子產品中,聲明了名稱為“lcd-backlight”的led裝置,并使用了led_classdev_register将其注冊進入led class中。

static struct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MAX_BACKLIGHT_BRIGHTNESS,
.brightness_set= msm_fb_set_bl_brightness,
};
           

于是就會産生/sys/class/leds/lcd-backlight/brightness的目錄,是以上層使用write_int(LCD_FILE, brightness)向核心寫入亮度值會調用kernel/drivers/leds/led-class.c子產品中的led_brightness_store方法。

static ssize_t led_brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev); // 此處的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注冊的backlight_led
    ssize_t ret = -EINVAL;
    char *after;
    unsigned long state = simple_strtoul(buf, &after, 10);
    size_t count = after - buf;
    if (isspace(*after))
count++;
    if (count == size) {
ret = count;
if (state == LED_OFF)
   led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state); // 設定螢幕亮度
    }
    return ret;
}
           

該方法調用led_set_brightness設定螢幕亮度,其在kernel/drivers/leds/leds.h中

static inline void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
    if (value > led_cdev->max_brightness)
value = led_cdev->max_brightness;
    led_cdev->brightness = value;
    if (!(led_cdev->flags & LED_SUSPENDED))
        led_cdev->brightness_set(led_cdev, value); // 此處的led_cdev就是kernel/drivers/staging/msm/msm_fb.c中注冊的backlight_led
}
           

于是就調用了kernel/drivers/staging/msm/msm_fb.c子產品中的回調函數brightness_set,即msm_fb_set_bl_brightness函數

static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
    struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
    int bl_lvl;
    if (value > MAX_BACKLIGHT_BRIGHTNESS)
value = MAX_BACKLIGHT_BRIGHTNESS;
    /* This maps android backlight level 0 to 255 into
       driver backlight level 0 to bl_max with rounding */
    bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS)
   /(2 * MAX_BACKLIGHT_BRIGHTNESS);
    if (!bl_lvl && value)
        bl_lvl = 1;
    msm_fb_set_backlight(mfd, bl_lvl, 1);
}
           

在msm_fb_set_bl_brightness函數中将亮度從0-255映射成0-bl_max,然後使用msm_fb_set_backlight設定螢幕亮度

void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl, u32 save)
{
    struct msm_fb_panel_data *pdata;
    pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
    if ((pdata) && (pdata->set_backlight)) {
down(&mfd->sem);
if ((bkl_lvl != mfd->bl_level) || (!save)) {
   u32 old_lvl;
   old_lvl = mfd->bl_level;
   mfd->bl_level = bkl_lvl;
   pdata->set_backlight(mfd);
   if (!save)
mfd->bl_level = old_lvl;
   }
up(&mfd->sem);
    }
}
           

在msm_fb_set_backlight方法中主要調用了各個具體裝置的set_backlight回調函數,實作了lcd螢幕的亮度調節。

crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr

Hufikyu的學習空間,歡迎大家提出問題,共同進步。

crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr