本文共 16855 字,大约阅读时间需要 56 分钟。
对于平板来说,设置背光对于省点来说比较重要。本文从应用层到内核层讲解系统是如何成功的设置背光的。设置背光,本质上是调节LCD的电压,达到对亮度的控制,Framework层的一系列的接口都是kernel层gpio操作的层层封装和相应的管理。
对于app来说亮度的控制如下:
/*
* 关闭自动调节亮度,改为手动调节亮度
*/
public static void stopAutoBrightness(Activity activity)
{
Settings.System.putInt(activity.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
}
/*
* 开启自动调节亮度
*/
public static void startAutoBrightness(Activity activity)
{
Settings.System.putInt(activity.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
}
/*
* 设置屏幕亮度
*/
public boolean setScreenBrightness(Activity activity, int light)
{
try
{
Settings.System.putInt(activity.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, light);
}
catch (Exception localException)
{
localException.printStackTrace();
return false;
}
Window localWindow = activity.getWindow();
WindowManager.LayoutParams localLayoutParams = localWindow.getAttributes();
float f = light / 255.0F;
localLayoutParams.screenBrightness = f;
localWindow.setAttributes(localLayoutParams);
return true;
}
调用以上几个函数,需要在AndroidManifest.xml中添加下面的权限
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
在调用setScreenBrightness之前,需要用调用stopAutoBrightness,将亮度设置为手动模式。
说到这里,有个小小的问题,就算设置亮度为0,为什么屏幕没变黑,原因是在framework层,在设置亮度时,最低的亮度值是30。
在Framework层,Window.java的setAttributes函数如下:
public void setAttributes(WindowManager.LayoutParams a) {
mWindowAttributes.copyFrom(a);
if (mCallback != null) {
mCallback.onWindowAttributesChanged(mWindowAttributes);
}
}
这里面调用了一个mCallback.onWindowAttributesChanged(mWindowAttributes); 来实现。这里面的mCallback是Window.java的一个接口类CallBack,这个CallBack包含了如下接口:
public interface Callback{
/**
* Called to process key events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchKeyEvent} to do the
* standard key processing.
*
* @param event The key event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchKeyEvent(KeyEvent event);
/**
* Called to process a key shortcut event.
* At the very least your implementation must call
* {@link android.view.Window#superDispatchKeyShortcutEvent} to do the
* standard key shortcut processing.
*
* @param event The key shortcut event.
* @return True if this event was consumed.
*/
public boolean dispatchKeyShortcutEvent(KeyEvent event);
/**
* Called to process touch screen events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchTouchEvent} to do the
* standard touch screen processing.
*
* @param event The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent event);
/**
* Called to process trackball events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchTrackballEvent} to do the
* standard trackball processing.
*
* @param event The trackball event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTrackballEvent(MotionEvent event);
/**
* Called to process generic motion events. At the very least your
* implementation must call
* {@link android.view.Window#superDispatchGenericMotionEvent} to do the
* standard processing.
*
* @param event The generic motion event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchGenericMotionEvent(MotionEvent event);
/**
* Called to process population of {@link AccessibilityEvent}s.
*
* @param event The event.
*
* @return boolean Return true if event population was completed.
*/
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
/**
* Instantiate the view to display in the panel for 'featureId'.
* You can return null, in which case the default content (typically
* a menu) will be created for you.
*
* @param featureId Which panel is being created.
*
* @return view The top-level view to place in the panel.
*
* @see #onPreparePanel
*/
public View onCreatePanelView(int featureId);
/**
* Initialize the contents of the menu for panel 'featureId'. This is
* called if onCreatePanelView() returns null, giving you a standard
* menu in which you can place your items. It is only called once for
* the panel, the first time it is shown.
*
* <p>You can safely hold on to <var>menu</var> (and any items created
* from it), making modifications to it as desired, until the next
* time onCreatePanelMenu() is called for this feature.
*
* @param featureId The panel being created.
* @param menu The menu inside the panel.
*
* @return boolean You must return true for the panel to be displayed;
* if you return false it will not be shown.
*/
public boolean onCreatePanelMenu(int featureId, Menu menu);
/**
* Prepare a panel to be displayed. This is called right before the
* panel window is shown, every time it is shown.
*
* @param featureId The panel that is being displayed.
* @param view The View that was returned by onCreatePanelView().
* @param menu If onCreatePanelView() returned null, this is the Menu
* being displayed in the panel.
*
* @return boolean You must return true for the panel to be displayed;
* if you return false it will not be shown.
*
* @see #onCreatePanelView
*/
public boolean onPreparePanel(int featureId, View view, Menu menu);
/**
* Called when a panel's menu is opened by the user. This may also be
* called when the menu is changing from one type to another (for
* example, from the icon menu to the expanded menu).
*
* @param featureId The panel that the menu is in.
* @param menu The menu that is opened.
* @return Return true to allow the menu to open, or false to prevent
* the menu from opening.
*/
public boolean onMenuOpened(int featureId, Menu menu);
/**
* Called when a panel's menu item has been selected by the user.
*
* @param featureId The panel that the menu is in.
* @param item The menu item that was selected.
*
* @return boolean Return true to finish processing of selection, or
* false to perform the normal menu handling (calling its
* Runnable or sending a Message to its target Handler).
*/
public boolean onMenuItemSelected(int featureId, MenuItem item);
/**
* This is called whenever the current window attributes change.
*
*/
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
/**
* This hook is called whenever the content view of the screen changes
* (due to a call to
* {@link Window#setContentView(View, android.view.ViewGroup.LayoutParams)
* Window.setContentView} or
* {@link Window#addContentView(View, android.view.ViewGroup.LayoutParams)
* Window.addContentView}).
*/
public void onContentChanged();
/**
* This hook is called whenever the window focus changes. See
* {@link View#onWindowFocusChanged(boolean)
* View.onWindowFocusChanged(boolean)} for more information.
*
* @param hasFocus Whether the window now has focus.
*/
public void onWindowFocusChanged(boolean hasFocus);
/**
* Called when the window has been attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
* for more information.
*/
public void onAttachedToWindow();
/**
* Called when the window has been attached to the window manager.
* See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
* for more information.
*/
public void onDetachedFromWindow();
/**
* Called when a panel is being closed. If another logical subsequent
* panel is being opened (and this panel is being closed to make room for the subsequent
* panel), this method will NOT be called.
*
* @param featureId The panel that is being displayed.
* @param menu If onCreatePanelView() returned null, this is the Menu
* being displayed in the panel.
*/
public void onPanelClosed(int featureId, Menu menu);
/**
* Called when the user signals the desire to start a search.
*
* @return true if search launched, false if activity refuses (blocks)
*
* @see android.app.Activity#onSearchRequested()
*/
public boolean onSearchRequested();
/**
* Called when an action mode is being started for this window. Gives the
* callback an opportunity to handle the action mode in its own unique and
* beautiful way. If this method returns null the system can choose a way
* to present the mode or choose not to start the mode at all.
*
* @param callback Callback to control the lifecycle of this action mode
* @return The ActionMode that was started, or null if the system should present it
*/
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback);
/**
* Called when an action mode has been started. The appropriate mode callback
* method will have already been invoked.
*
* @param mode The new mode that has just been started.
*/
public void onActionModeStarted(ActionMode mode);
/**
* Called when an action mode has been finished. The appropriate mode callback
* method will have already been invoked.
*
* @param mode The mode that was just finished.
*/
public void onActionModeFinished(ActionMode mode);
}
从这些接口来看,发现有一些触屏相关的消息处理,推测是在Activity.java中实现了Window.Callback,通过查看Activity.java,确实是实现了Window.Callback。
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
// Update window manager if: we have a view, that view is
// attached to its parent (which will be a RootView), and
// this activity is not embedded.
if (mParent == null) {
View decor = mDecor;
if (decor != null && decor.getParent() != null) {
getWindowManager().updateViewLayout(decor, params);
}
}
}
接下来我们查看WindowManger.java中的updateViewLayout,发现没有,只在它的父类中发现了这样的一个接口。
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
看到这里面WindowManger只是个壳,具体实现应该在其他类中。在WindowManagerImpl.java中找到了
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
mGlobal.updateViewLayout(view, params);
}
继续查找代码,在WindowManagerGobal.java中找到updateViewLayout
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
再转到ViewRootImpl.java的setLayoutParams中
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
int oldSoftInputMode = mWindowAttributes.softInputMode;
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
// preserve compatible window flag if exists.
int compatibleWindowFlag = mWindowAttributes.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
// transfer over system UI visibility values as they carry current state.
attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
if ((mWindowAttributesChangesFlag
& WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
// Recompute system ui visibility.
mAttachInfo.mRecomputeGlobalAttributes = true;
}
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
applyKeepScreenOnFlag(mWindowAttributes);
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
// Don't lose the mode we last auto-computed.
if ((attrs.softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
== WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
& ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
| (oldSoftInputMode
& WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
}
mWindowAttributesChanged = true;
mUpdateTranformHint = true;
scheduleTraversals();
}
}
很是遗憾,在这个函数中,也是关于UI相关的设置,和屏幕亮度相关的设置始终没见着,得继续研究下。
那么我们从另外一个方向来分析,如何设置屏幕亮度。参考Settings中的BrightnessPreference.java中的setBrightness函数,如下
private void setBrightness(int brightness, boolean write) {
if (mAutomaticMode) {
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT) {
float valf = (((float)brightness*2)/SEEK_BAR_RANGE) - 1.0f;
try {
IPowerManager power = IPowerManager.Stub.asInterface(
ServiceManager.getService("power"));
if (power != null) {
power.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(valf);
}
if (write) {
final ContentResolver resolver = getContext().getContentResolver();
Settings.System.putFloat(resolver,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, valf);
}
} catch (RemoteException doe) {
}
}
} else {
int range = (mScreenBrightnessMaximum - mScreenBrightnessMinimum);
brightness = (brightness * range)/SEEK_BAR_RANGE + mScreenBrightnessMinimum;
try {
IPowerManager power = IPowerManager.Stub.asInterface(
ServiceManager.getService("power"));
if (power != null) {
power.setTemporaryScreenBrightnessSettingOverride(brightness);
}
if (write) {
mCurBrightness = -1;
final ContentResolver resolver = getContext().getContentResolver();
Settings.System.putInt(resolver,
Settings.System.SCREEN_BRIGHTNESS, brightness);
} else {
mCurBrightness = brightness;
}
} catch (RemoteException doe) {
}
}
}
在这个函数中可以看到调用PowerManagerService.java中的setTemporaryScreenBrightnessSettingOverride进行设置亮度。
@Override // Binder call
public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
接着转到
private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
synchronized (mLock) {
if (mTemporaryScreenBrightnessSettingOverride != brightness) {
mTemporaryScreenBrightnessSettingOverride = brightness;
mDirty |= DIRTY_SETTINGS;
updatePowerStateLocked();
}
}
}
再到updatePowerStateLocked()
private void updatePowerStateLocked() {
...
// Phase 2: Update dreams and display power state.
updateDreamLocked(dirtyPhase2);
updateDisplayPowerStateLocked(dirtyPhase2);
...
}
在该函数中调用updateDisplayPowerStateLocked(dirtyPhase2);
private void updateDisplayPowerStateLocked(int dirty) {
...
mDisplayReady = mDisplayPowerController.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
...
}
此时转到了DisplayPowerController.java的requestPowerState中
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
...
if (changed && !mPendingRequestChangedLocked) {
mPendingRequestChangedLocked = true;
sendUpdatePowerStateLocked();
}
...
}
接着调用 sendUpdatePowerStateLocked()
private void sendUpdatePowerStateLocked() {
if (!mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
接着跑到了 private void updatePowerState() 中
private void updatePowerState() {
...
animateScreenBrightness(clampScreenBrightness(target),
slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
...
}
接着咱们看一下animateScreenBrightness
private void animateScreenBrightness(int target, int rate) {
if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
mNotifier.onScreenBrightness(target);
}
}
待续中...
要实现控制屏幕亮度,流程应该如下:
PowerManagerService.java -> DisplayPowerController.java->LightsService.java(setLight_native)->com_android_server_LightsService.cpp(setLight_native)->liblights.so(lights.cpp set_backlight_light)
其中各自文件对应的目录如下:
PowerManagerService.java ---> SDK/frameworks/base/services/java/com/android/server/power
DisplayPowerController.java ---> SDK/frameworks/base/services/java/com/android/server/power
LightsService.java ---> SDK/frameworks/base/services/java/com/android/server
com_android_server_LightsService.cpp ---> SDK/frameworks/base/services/jni
lights.cpp 这个属于设备硬件相关,具体和采用哪个的平台有关系,我这边采用的是RK平台,如下:
lights.cpp ---> SDK/hardware/rk29/liblights
转载地址:http://inbwz.baihongyu.com/