1. 程式人生 > >android5.1 自動亮度調節簡析

android5.1 自動亮度調節簡析

android亮度調節在"設定"的"顯示"中,分為手動調節和自動調節。其中手動調節就是通過拖動"亮度調節"的拖動條來直接設定亮度值,自動調節則比較複雜,設定自動亮度模式後,再拖動"亮度調節"的拖動條,可以小範圍改變亮度值(前提是光照條件不劇烈變化),但這時不是直接設定的亮度值,而是需要通過一系列轉變轉成亮度值。

現在遇到問題是:在自動亮度調節模式下,調節亮度拖動條到最小,螢幕變得很黑。

設定中的亮度設定是在DisplaySettings.java(/Settings/src/com/android/settings/DisplaySettings.java)中,但這裡面沒有拖動條的相關操作,只有設定手動調節和自動調節模式的操作。其實彈出拖動條的操作在BrightnessPreference.java(/Settings/src/com/android/settings/BrightnessPreference.java)

中,它實現了Preference的onClick方法:

 protected void onClick() {
        Log.i("BrightnessPreference", "BrightnessPreference:brightness dialog open");
        getContext().startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
                UserHandle.CURRENT_OR_SELF);
    }

通過查詢這個action,發現其就是android.intent.action.SHOW_BRIGHTNESS_DIALOG

,在 SystemUI(frameworks\base\packages\SystemUI)的AndroidManifest.xml裡也出現了:

<activity
    android:name=".settings.BrightnessDialog"
      android:label="@string/quick_settings_brightness_dialog_title"
            android:theme="@android:style/Theme.DeviceDefault.Dialog"
            android:finishOnCloseSystemDialogs
="true" android:launchMode="singleInstance" android:excludeFromRecents="true" android:exported="true">
<intent-filter> <action android:name="android.intent.action.SHOW_BRIGHTNESS_DIALOG" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>

這裡就是通過action啟動BrightnessDialog.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessDialog.java),發現這個Activity就是亮度調節的彈出框,至此就沒有Settings什麼事了。

  • BrightnessDialog

    在BrightnessDialog中初始化拖動條等相關控制元件,並例項化了BrightnessController.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessController.java)

    setContentView(R.layout.quick_settings_brightness_dialog);
    final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
    final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
    mBrightnessController = new BrightnessController(this, icon, slider);

    並註冊和登出BrightnessController的回撥方法:

    @Override
      protected void onStart() {
          super.onStart();
          mBrightnessController.registerCallbacks();
      }
    
      @Override
      protected void onStop() {
          super.onStop();
          mBrightnessController.unregisterCallbacks();
      }
  • BrightnessController

    BrightnessController的回撥方法主要是在亮度調節模式改變時更新系統儲存的模式,在亮度調節時更新拖動條(此拖動條和下拉欄的亮度條保持一致)。這裡主要看亮度設定時的操作。
    因為BrightnessController實現了對ToggleSlider的監聽,在拖動條改變時設定亮度:

    @Override
      public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
          updateIcon(mAutomatic);
          if (mExternalChange) return;
    
          if (!mAutomatic) {
              final int val = value + mMinimumBacklight;
              Log.i(TAG, "brightness mode:"+mAutomatic+"\nvalue:"+value+"value set:"+val);
              setBrightness(val);
              if (!tracking) {
                  AsyncTask.execute(new Runnable() {
                          public void run() {
                              Settings.System.putIntForUser(mContext.getContentResolver(),
                                      Settings.System.SCREEN_BRIGHTNESS, val,
                                      UserHandle.USER_CURRENT);
                          }
                      });
              }
          } else {
              final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;   //original code
              setBrightnessAdj(adj);
              if (!tracking) {
                  AsyncTask.execute(new Runnable() {
                      public void run() {
                          Settings.System.putFloatForUser(mContext.getContentResolver(),
                                  Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj,
                                  UserHandle.USER_CURRENT);
                      }
                  });
              }
          }
    
          for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
              cb.onBrightnessLevelChanged();
          }
      }

    value就是拖動條的值,在初始化的時候,亮度條的範圍和值都被設定:

    private void updateSlider() {
          if (mAutomatic) {
              float value = Settings.System.getFloatForUser(mContext.getContentResolver(),Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
                      UserHandle.USER_CURRENT);
              mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION);
              mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f));
    
          } else {
              int value;
              value = Settings.System.getIntForUser(mContext.getContentResolver(),
                      Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
                      UserHandle.USER_CURRENT);
              mControl.setMax(mMaximumBacklight - mMinimumBacklight);
              mControl.setValue(value - mMinimumBacklight);
          }
      }

    可以看出自動模式下亮度條最大值為BRIGHTNESS_ADJ_RESOLUTION,該值為100,而手動模式下亮度條最大值為mMaximumBacklight - mMinimumBacklight,其中mMaximumBacklight為255,mMinimumBacklight為10。
    在拖動條的值改變時,onChange()方法被呼叫,若為手動模式,則:

    final int val = value + mMinimumBacklight;
    setBrightness(val);

    可以看出,手動模式直接在從拖動條獲取的基數value上加了個亮度的最小值,然後呼叫setBrightness(val),再來看 setBrightness(val):

    private void setBrightness(int brightness) {
          try {
              mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
          } catch (RemoteException ex) {
          }
      }

    mPower是IPowerManager的物件,這裡先放在一邊。
    再來看若為自動模式時拖動條改變,

    final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;   
    setBrightnessAdj(adj);

    其中BRIGHTNESS_ADJ_RESOLUTION為100。可以計算出adj的範圍隨著value的改變始終保持在[-1,1],暫且稱之為調節引數。然後呼叫setBrightnessAdj(adj)

    private void setBrightnessAdj(float adj) {
          try {
              mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj);
          } catch (RemoteException ex) {
          }
      }

    可以看出,兩種模式都最終交給IPowerManager處理了,然後基本就是由framework層來進行處理。
    3.IPowerManager
    IPowerManager開頭就有註釋:

    /*
    * This file is auto-generated.  DO NOT MODIFY.
    * Original file: frameworks/base/core/java/android/os/IPowerManager.aidl
    */

    所以這個檔案是aidl自動生成的。
    兩個方法:

    @Override 
    public void setTemporaryScreenBrightnessSettingOverride(int brightness) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeInt(brightness);
    mRemote.transact(Stub.TRANSACTION_setTemporaryScreenBrightnessSettingOverride, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    @Override 
    public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeFloat(adj);
    mRemote.transact(Stub.TRANSACTION_setTemporaryScreenAutoBrightnessAdjustmentSettingOverride, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }

    然後搜尋發現在PowerManagerService.java(com.android.server.power.PowerManagerService.class)中的BinderService類implements了IPowerManager的Stub類,並且實現了上述兩個方法:

    \/**
           * Used by the settings application and brightness control widgets to
           * temporarily override the current screen brightness setting so that the
           * user can observe the effect of an intended settings change without applying
           * it immediately.
           *
           * The override will be canceled when the setting value is next updated.
           *
           * @param brightness The overridden brightness.
           *
           * @see android.provider.Settings.System#SCREEN_BRIGHTNESS
           */
          @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);
              }
          }
    
          /**
           * Used by the settings application and brightness control widgets to
           * temporarily override the current screen auto-brightness adjustment setting so that the
           * user can observe the effect of an intended settings change without applying
           * it immediately.
           *
           * The override will be canceled when the setting value is next updated.
           *
           * @param adj The overridden brightness, or Float.NaN to disable the override.
           *
           * @see android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
           */
          @Override // Binder call
          public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
              mContext.enforceCallingOrSelfPermission(
                      android.Manifest.permission.DEVICE_POWER, null);
    
              final long ident = Binder.clearCallingIdentity();
              try {
                  setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
              } finally {
                  Binder.restoreCallingIdentity(ident);
              }
          }

    註釋裡說明,這兩個方法是Settings和亮度調節外掛使用的,再來看值傳進來後呼叫的兩個方法:

    private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
          synchronized (mLock) {
              if (mTemporaryScreenBrightnessSettingOverride != brightness) {
                  mTemporaryScreenBrightnessSettingOverride = brightness;
                  mDirty |= DIRTY_SETTINGS;
                  updatePowerStateLocked();
              }
          }
      }
    
      private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
          synchronized (mLock) {
              // Note: This condition handles NaN because NaN is not equal to any other
              // value, including itself.
              if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
                  mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
                  mDirty |= DIRTY_SETTINGS;
                  updatePowerStateLocked();
              }
          }
      }

    拿到值後賦值給一個全域性變數,然後馬上呼叫updatePowerStateLocked(),再來看此方法對傳入的值做了哪些操作:

    /**
       * Updates the display power state asynchronously.
       * When the update is finished, mDisplayReady will be set to true.  The display
       * controller posts a message to tell us when the actual display power state
       * has been updated so we come back here to double-check and finish up.
       *
       * This function recalculates the display power state each time.
       *
       * @return True if the display became ready.
       */
      private boolean updateDisplayPowerStateLocked(int dirty) {
          final boolean oldDisplayReady = mDisplayReady;
          if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                  | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                  | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
              mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
    
              // Determine appropriate screen brightness and auto-brightness adjustments.
              int screenBrightness = mScreenBrightnessSettingDefault;
              float screenAutoBrightnessAdjustment = 0.0f;
              boolean autoBrightness = (mScreenBrightnessModeSetting ==
                      Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
              if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                  screenBrightness = mScreenBrightnessOverrideFromWindowManager;
                  autoBrightness = false;
              } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
                  screenBrightness = mTemporaryScreenBrightnessSettingOverride;
              } else if (isValidBrightness(mScreenBrightnessSetting)) {
                  screenBrightness = mScreenBrightnessSetting;
              }
              if (autoBrightness) {
                  screenBrightness = mScreenBrightnessSettingDefault;
                  if (isValidAutoBrightnessAdjustment(
                          mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
                      screenAutoBrightnessAdjustment =
                              mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
                  } else if (isValidAutoBrightnessAdjustment(
                          mScreenAutoBrightnessAdjustmentSetting)) {
                      screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
                  }
              }
              screenBrightness = Math.max(Math.min(screenBrightness,
                      mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
              screenAutoBrightnessAdjustment = Math.max(Math.min(
                      screenAutoBrightnessAdjustment, 1.0f), -1.0f);
    
              // Update display power request.
              mDisplayPowerRequest.screenBrightness = screenBrightness;
              mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                      screenAutoBrightnessAdjustment;
              mDisplayPowerRequest.useAutoBrightness = autoBrightness;
              mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
              mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
              mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress;
    
              if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                  mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
                  mDisplayPowerRequest.dozeScreenBrightness =
                          mDozeScreenBrightnessOverrideFromDreamManager;
              } else {
                  mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
                  mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
              }
    
              mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                      mRequestWaitForNegativeProximity);
              mRequestWaitForNegativeProximity = false;
    
              if (DEBUG_SPEW) {
                  Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
                          + ", policy=" + mDisplayPowerRequest.policy
                          + ", mWakefulness=" + mWakefulness
                          + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                          + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
                          + ", mBootCompleted=" + mBootCompleted
                          + ", mScreenBrightnessBoostInProgress="
                                  + mScreenBrightnessBoostInProgress);
              }
          }
          return mDisplayReady && !oldDisplayReady;
      }

    發現最終值都被賦給mDisplayPowerRequest的屬性,然後呼叫mDisplayManagerInternal.requestPowerState將值傳出返回結果。mDisplayManagerInternalDisplayManagerInternal(android.hardware.display.DisplayManagerInternal.class)的物件,再來看看DisplayManagerInternal:
    4.DisplayManagerInternal

    public abstract boolean requestPowerState(DisplayPowerRequest request,
              boolean waitForNegativeProximity);

    發現並沒有具體的實現方法。通過搜尋發現DisplayManageService(com.android.server.display.DisplayManagerService.class)中的LocalService類繼承了DisplayManagerInternal並實現了requestPowerState()方法:

    @Override
          public boolean requestPowerState(DisplayPowerRequest request,
                  boolean waitForNegativeProximity) {
              return mDisplayPowerController.requestPowerState(request,
                      waitForNegativeProximity);
          }

    發現呼叫了DisplayPowerController(com.android.server.display.DisplayPowerController.class)requestPowerState()方法作為返回結果。
    5.DisplayPowerController

    public boolean requestPowerState(DisplayPowerRequest request,
              boolean waitForNegativeProximity) {
          if (DEBUG) {
              Slog.d(TAG, "requestPowerState: "
                      + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
          }
    
          synchronized (mLock) {
              boolean changed = false;
    
              if (waitForNegativeProximity
                      && !mPendingWaitForNegativeProximityLocked) {
                  mPendingWaitForNegativeProximityLocked = true;
                  changed = true;
              }
    
              if (mPendingRequestLocked == null) {
                  mPendingRequestLocked = new DisplayPowerRequest(request);
                  changed = true;
              } else if (!mPendingRequestLocked.equals(request)) {
                  mPendingRequestLocked.copyFrom(request);
                  changed = true;
              }
    
              if (changed) {
                  mDisplayReadyLocked = false;
              }
    
              if (changed && !mPendingRequestChangedLocked) {
                  mPendingRequestChangedLocked = true;
                  sendUpdatePowerStateLocked();
              }
    
              return mDisplayReadyLocked;
          }
      }

    可以看出,先是將request物件賦給本地,然後呼叫sendUpdatePowerStateLocked()

    private void sendUpdatePowerStateLocked() {
          if (!mPendingUpdatePowerStateLocked) {
              mPendingUpdatePowerStateLocked = true;
              Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
              msg.setAsynchronous(true);
              mHandler.sendMessage(msg);
          }
      }

    交給handler處理:

    case MSG_UPDATE_POWER_STATE:
                      updatePowerState();
                      break;

    呼叫updatePowerState()方法,其中有這樣一段程式碼:

    // Configure auto-brightness.
          boolean autoBrightnessEnabled = false;
          if (mAutomaticBrightnessController != null) {
              final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
                      && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
              autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                      && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                      && brightness < 0;
              mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                      mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON);
          }

    mAutomaticBrightnessControllerAutomaticBrightnessController(com.android.server.display.AutomaticBrightnessController.class)物件,下面來看AutomaticBrightnessController.
    6.AutomaticBrightnessController

    public void configure(boolean enable, float adjustment, boolean dozing) {
          // While dozing, the application processor may be suspended which will prevent us from
          // receiving new information from the light sensor. On some devices, we may be able to
          // switch to a wake-up light sensor instead but for now we will simply disable the sensor
          // and hold onto the last computed screen auto brightness.  We save the dozing flag for
          // debugging purposes.
          mDozing = dozing;
          boolean changed = setLightSensorEnabled(enable && !dozing);
          changed |= setScreenAutoBrightnessAdjustment(adjustment);
          if (changed) {
              updateAutoBrightness(false /*sendUpdate*/);
          }
      }
    private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
          if (adjustment != mScreenAutoBrightnessAdjustment) {
              mScreenAutoBrightnessAdjustment = adjustment;
              return true;
          }
          return false;
      }

    拿到亮度調節引數後:

    private void updateAutoBrightness(boolean sendUpdate) {
          if (!mAmbientLuxValid) {
              return;
          }
    
          float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
          float gamma = 1.0f;
    
          if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
                  && mScreenAutoBrightnessAdjustment != 0.0f) {
              final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
                      Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
              gamma *= adjGamma;
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
              }
          }
    
          if (USE_TWILIGHT_ADJUSTMENT) {
              TwilightState state = mTwilight.getCurrentState();
              if (state != null && state.isNight()) {
                  final long now = System.currentTimeMillis();
                  final float earlyGamma =
                          getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
                  final float lateGamma =
                          getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
                  gamma *= earlyGamma * lateGamma;
                  if (DEBUG) {
                      Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
                              + ", lateGamma=" + lateGamma);
                  }
              }
          }
    
          if (gamma != 1.0f) {
              final float in = value;
              value = MathUtils.pow(value, gamma);
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
                          + ", in=" + in + ", out=" + value);
              }
          }
    
          int newScreenAutoBrightness =
              clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
          if (mScreenAutoBrightness != newScreenAutoBrightness) {
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
                          + mScreenAutoBrightness + ", newScreenAutoBrightness="
                          + newScreenAutoBrightness);
              }
    
              mScreenAutoBrightness = newScreenAutoBrightness;
              mLastScreenAutoBrightnessGamma = gamma;
              if (sendUpdate) {
                  mCallbacks.updateBrightness();
              }
          }
      }

    這裡是一大段針對自動亮度調節,將亮度調節係數和一段時間內的光線感應器獲取的光強的加權平均通過一系列運算轉化為合適的螢幕背光亮度,具體的轉化模型這裡不深究。

在這裡來看一下,每次轉化好亮度後都會做如下處理:

private int clampScreenBrightness(int value) {
        return MathUtils.constrain(value,
                mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
    }

mScreenBrightnessRangeMinimum就是最小值,由建構函式傳過來,發現是DisplayPowerController例項化AutomaticBrightnessController時傳入的:

mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                        handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
                        lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
                        mScreenBrightnessRangeMaximum, dozeScaleFactor);

DisplayPowerController的建構函式中:

int screenBrightnessRangeMinimum = Math.min(Math.min(
                screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
                mScreenBrightnessDarkConfig);

這三個值均取自config.xml(frameworks\base\core\res\res\values\config.xml)分別為:

screenBrightnessSettingMinimum:config_screenBrightnessSettingMinimum
mScreenBrightnessDimConfig:config_screenBrig