本文共 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/