今天测试给提了一个issue:刷机 - 设置设备的锁屏密码(pattern)- 重启设备,设备重启完成之后,首先看到的当然是解锁界面,会看到解锁界面的亮度比重启之前在 Settings 中设置的亮度亮了不少(即使在系统的 Settings 中设置了系统亮度是 0 ,重启之后首先看到的解锁界面也是很亮,解锁之后,设备屏幕的亮度恢复为重启之前亮度)。测试认为这是有问题的,把这个问题扔了出来。
首先,作为开发的直觉来说,我觉得这其实应该不是一个issue,而是Google有意为之,就像是平时我们给商户展示支付宝二维码的时候,会发现支付宝的二维码界面亮度亮了不少,等到扫码结束退到其他界面的时候,会看到亮度恢复为之前。但是空说没有什么证据,得看代码到底是怎么处理这个地方的逻辑的。
首先找到这个重启之后的输入密码界面是啥,通过打印堆栈信息,看到这个界面是:
realActivity=com.android.settings/.CryptKeeper
恩恩,这个界面是 Settings 中的某一个 Activity 界面,具体位置是:
folder\vendor\rockchip\hxxx\gxx\apps\Settings\src\com\android\settings\CryptKeeper.java
首先看一下这个 Activity 在初始化的时候做了什么,可以看到在它的 onCreate()里面没有做关于界面展示的逻辑,他把逻辑放到了onStart() 方法中。如下:
/**
* Note, we defer the state check and screen setup to onStart() because this will be
* re-run if the user clicks the power button (sleeping/waking the screen), and this is
* especially important if we were to lose the wakelock for any reason.
*/
@Override
public void onStart() {
super.onStart();
//关键代码
setupUi();
}
具体为啥把UI相关的逻辑放到 onStart() 方法,Google 工程师做了解释。那在 setupUi(); 中做了什么操作?如下:
/**
* Initializes the UI based on the current state of encryption.
* This is idempotent - calling repeatedly will simply re-initialize the UI.
*/
private void setupUi() {
if (mEncryptionGoneBad || isDebugView(FORCE_VIEW_ERROR)) {
setContentView(R.layout.crypt_keeper_progress);
showFactoryReset(mCorrupt);
return;
}
final String progress = SystemProperties.get("vold.encrypt_progress");
if (!"".equals(progress) || isDebugView(FORCE_VIEW_PROGRESS)) {
setContentView(R.layout.crypt_keeper_progress);
encryptionProgressInit();
} else if (mValidationComplete || isDebugView(FORCE_VIEW_PASSWORD)) {
new AsyncTask<Void, Void, Void>() {
int passwordType = StorageManager.CRYPT_TYPE_PASSWORD;
String owner_info;
boolean pattern_visible;
boolean password_visible;
@Override
public Void doInBackground(Void... v) {
try {
final IStorageManager service = getStorageManager();
passwordType = service.getPasswordType();
owner_info = service.getField(StorageManager.OWNER_INFO_KEY);
pattern_visible = !("0".equals(service.getField(StorageManager.PATTERN_VISIBLE_KEY)));
password_visible = !("0".equals(service.getField(StorageManager.PASSWORD_VISIBLE_KEY)));
} catch (Exception e) {
Log.e(TAG, "Error calling mount service " + e);
}
return null;
}
@Override
public void onPostExecute(java.lang.Void v) {
Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
password_visible ? 1 : 0);
//code...
//关键代码
passwordEntryInit();
//code...
}
}.execute();
} else if (!mValidationRequested) {
// We're supposed to be encrypted, but no validation has been done.
new ValidationTask().execute((Void[]) null);
mValidationRequested = true;
}
}
可以看到当系统检查过错误之后,会创建一个 AsyncTask,并在doInBackground()中获取到当前设备密码的类型,在onPostExecute()中调用 passwordEntryInit()方法进行密码实体化界面的初始化。看一下 passwordEntryInit()做了什么操作:
private void passwordEntryInit() {
// Password/pin case
// code...
// Pattern case
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
if (mLockPatternView != null) {
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
}
// Disable the Emergency call button if the device has no voice telephone capability
// code...
// We want to keep the screen on while waiting for input. In minimal boot mode, the device
// is completely non-functional, and we want the user to notice the device and enter a
// password.
if (mWakeLock == null) {
Log.d(TAG, "Acquiring wakelock.");
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (pm != null) {
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
//关键代码!
mWakeLock.acquire();
// Keep awake for 10 minutes - if the user hasn't been alerted by then
// best not to just drain their battery
mReleaseWakeLockCountdown = 96; // 96 * 5 secs per click + 120 secs before we show this = 600
}
}
// Make sure that the IME is shown when everything becomes ready.
// code...
// Notify the user in 120 seconds that we are waiting for him to enter the password.
mHandler.removeMessages(MESSAGE_NOTIFY);
mHandler.sendEmptyMessageDelayed(MESSAGE_NOTIFY, 120 * 1000);
// Dismiss secure & non-secure keyguards while this screen is showing.
// code...
}
这个方法很长,但是看起来其实一目了然,系统做了判断,对用户可能设置密码的三种可能做了初始化。我们关心一个方法就行:mWakeLock.acquire(); 我们可以看到这里是调用了 PowerManager 的 acquire();方法,这个方法是为了获取唤醒锁定。另外还有注释如下:
We want to keep the screen on while waiting for input. In minimal boot mode, the device is completely non-functional, and we want the user to notice the device and enter a password.
这里可以解释为,当设备重启完成之后,设备要保持 Screen on等待用户输入,并且保持屏幕是亮着的,用以提醒用户赶快输入密码解锁。
继续跟代码,在目录:
folder\XX\frameworks\base\core\java\android\os\
下有一个 PowerManager.java 类,刚刚调用的 acquire()就是调用的这个类中的方法。在这个类中可以看到:
/**
* Acquires the wake lock.
* <p>
* Ensures that the device is on at the level requested when
* the wake lock was created.
* </p>
*/
public void acquire() {
synchronized (mToken) {
//关键代码!
acquireLocked();
}
}
private void acquireLocked() {
mInternalCount++;
mExternalCount++;
if (!mRefCounted || mInternalCount == 1) {
for(int i=0;i<packageList.size();i++){
String pckname = packageList.get(i);
//Slog.d("lvjinhua","--------------------pckname111="+pckname+",mPackageName="+mPackageName);
if(mPackageName.equals(pckname) || mTag.equals(pckname)){
return;
}
}
// Do this even if the wake lock is already thought to be held (mHeld == true)
// because non-reference counted wake locks are not always properly released.
// For example, the keyguard's wake lock might be forcibly released by the
// power manager without the keyguard knowing. A subsequent call to acquire
// should immediately acquire the wake lock once again despite never having
// been explicitly released by the keyguard.
mHandler.removeCallbacks(mReleaser);
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
try {
//关键代码!
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
mHistoryTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld = true;
}
}
可以看到这里通过 IPowerManager 这个类型的代理对象 mService ,调用了 PowerManagerService 的acquireWakeLock();方法,很明显,这里是AIDL,最终执行的逻辑是Server 端的逻辑。
跟代码,找到 PowerManagerService.java 路径如下:
folder\XX\frameworks\base\services\core\java\com\android\server\power\
最终执行的 acquireWakeLock();方法是调用的 PowerManagerService.java 中的 acquireWakeLock();查看这个方法:
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
if (packageName == null) {
throw new IllegalArgumentException("packageName must not be null");
}
PowerManager.validateWakeLockParameters(flags, tag);
try {
//关键代码!
acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
可以看到这个关键代码调用了封装之后的方法 acquireWakeLockInternal(),即调用了获取唤醒锁的内部封装的逻辑,代码比较多,这里只展示关键代码:
private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag, int uid, int pid) {
synchronized (mLock) {
// code...
WakeLock wakeLock;
int index = findWakeLockIndexLocked(lock);
boolean notifyAcquire;
if (index >= 0) {
// code...
} else {
// code...
}
applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
mDirty |= DIRTY_WAKE_LOCKS;
// 关键代码!
updatePowerStateLocked();
// code...
}
}
可以看到这里又通过调用方法 updatePowerStateLocked()调用了更新电源状态锁。这个方法做了什么呢,如下:
/**
* Updates the global power state based on dirty bits recorded in mDirty.
*
* This is the main function that performs power state transitions.
* We centralize them here so that we can recompute the power state completely
* each time something important changes, and ensure that we do it the same
* way each time. The point is to gather all of the transition logic here.
*/
private void updatePowerStateLocked() {
// code check system status
// code...
Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
try {
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
// 关键代码!!
updateScreenBrightnessBoostLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update display power state.
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Update dream state (depends on display ready signal).
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Phase 4: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Phase 5: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
Google 工程师对这个方法的注释很明白了:方法中,根据mDirty中记录的数据更新全局电源状态。并且这是执行电源状态转换的主要功能。 我们将它们集中在这里,以便我们可以在每次重要变化时完全重新计算功率状态,并确保每次都以相同的方式进行。 关键是要在这里收集所有过渡逻辑。在这个方法中,分了5个阶段来更新处理全局电源状态 ,具体每个阶段做了什么我们就不一一看了,这不是我们追踪代码的目的,感兴趣的话,大家可以自己跟一下各个方法做了什么。我们只要关注第 0 个阶段和第 2 个阶段就可以了。
看第 0 个阶段中的标注出来的关键代码:updateScreenBrightnessBoostLocked(mDirty); 如下:
private void updateScreenBrightnessBoostLocked(int dirty) {
if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {
if (mScreenBrightnessBoostInProgress) {
final long now = SystemClock.uptimeMillis();
mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
final long boostTimeout = mLastScreenBrightnessBoostTime +
SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
if (boostTimeout > now) {
Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, boostTimeout);
return;
}
}
mScreenBrightnessBoostInProgress = false;
mNotifier.onScreenBrightnessBoostChanged();
userActivityNoUpdateLocked(now,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
}
}
}
这个方法中做的事情很简单,就是监测并处理屏幕亮度增加超时情况,如果在默认的时间内,屏幕的亮度一直没有增加,会通过mHandler 发送一个“屏幕亮度增加超时” 的广播出去。
看第 2 个阶段中的标注出来的关键代码:updateDisplayPowerStateLocked(dirtyPhase2); 如下:
/**
* 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 | DIRTY_VR_MODE_CHANGED |
DIRTY_QUIESCENT)) != 0) {
// code...
// Update display power request.
mDisplayPowerRequest.screenBrightness = screenBrightness;
mDisplayPowerRequest.screenAutoBrightnessAdjustment =
screenAutoBrightnessAdjustment;
mDisplayPowerRequest.brightnessSetByUser = brightnessSetByUser;
mDisplayPowerRequest.useAutoBrightness = autoBrightness;
mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest);
// code...
// 关键代码!!!
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
// code...
}
return mDisplayReady && !oldDisplayReady;
}
可以看到在代码中,把一些参数(其中就包含是否需要自动增加屏幕亮度 :boostScreenBrightness 这个参数)封装到 mDisplayPowerRequest 对象中,并通过调用 mDisplayManagerInternal 对象的 requestPowerState()方法将这些参数设置到 mDisplayManagerInternal 对象中。跟一下 mDisplayManagerInternal 对象的 requestPowerState()方法做了什么。
这个对象是 DisplayManagerInternal.java 的实例对象,查看代码发现这是一个抽象类,具体逻辑还是得看具体实现类(猜测实现类是 DMS,一会儿再跟),还是先看一下这个抽象类中抽象方法是怎么定义这个方法的吧:
/**
* Called by the power manager to request a new power state.
* <p>
* The display power controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
* </p>
*
* @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
* then try the request again later until the state converges.
*/
public abstract boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity);
可以看到 DMS 对这个方法是这么描述的:这个方法被 PowerManagerService 调用,并根据传递进来的封装之后的 request 对象更新状态。(不太想继续往下跟了。。)(刚刚项目上有事情需要做,先暂停一下,等下午有空了再继续跟)
抽空跟一下。。
上面说到这个地方是在 DMS,也就是 DisplayManagerService.java 中调用的 requestPowerState()方法。那看一下这个DisplayManagerService.java 类,目录:
folder\XX\frameworks\base\services\core\java\com\android\server\display\DisplayManagerService.java
看到在这个类中, requestPowerState()方法:
@Override
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
return mDisplayPowerController.requestPowerState(request,
waitForNegativeProximity);
}
其实是调用了 DisplayPowerController 代理对象 mDisplayPowerController 的 requestPowerState()方法。同级目录下找到 DisplayPowerController.java 这个类,查看这个 requestPowerState()方法:
/**
* Requests a new power state.
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
* @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
* negative proximity before turning the screen back on, assuming the screen
* was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
* then try the request again later until the state converges.
*/
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;
}
}
可以看到这个方法的描述:
请求新的 Power 状态。 控制器复制所提供的 request 对象,然后开始调整设备的 Power 状态以匹配请求的内容。
以上就是给设备设置了锁屏密码并开机之后,设备首次展示给用户解锁界面时,设备屏幕显示以及 Power 状态配置的大概流程。
基于以上流程跟踪,当调用 updateDisplayPowerStateLocked()时,PMS 会封装一个 DisplayPowerRequest 对象传递给 PMS,最终这个 DisplayPowerRequest 对象会被传递给 DMS,DMS 收到这个更新请求的时候,会按照 Request 对象中携带的数据进行状态以及 Power 的更新。
需要注意一点,DisplayPowerRequest 对象有一个boolean类型德尔属性:boostScreenBrightness,这个属性就是导致屏幕亮度变亮的关键。
// code...
mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
// code...
private boolean shouldBoostScreenBrightness() {
return !mIsVrModeEnabled && mScreenBrightnessBoostInProgress;
}
PMS 是怎么给boolean类型的 mIsVrModeEnabled 和 mScreenBrightnessBoostInProgress 进行赋值的呢?
对于变量 mIsVrModeEnabled 来说:True if we are currently in VR Mode.(所以这里一般为 false)
对于变量 mScreenBrightnessBoostInProgress 来说,当 设置屏幕亮度超时的时候,会置为 false,当调用内部逻辑提升屏幕亮度的时候,会置为 true。(变量变化如下两个方法所示)
private void updateScreenBrightnessBoostLocked(int dirty) {
if ((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) != 0) {
if (mScreenBrightnessBoostInProgress) {
final long now = SystemClock.uptimeMillis();
mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
final long boostTimeout = mLastScreenBrightnessBoostTime +
SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
if (boostTimeout > now) {
Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, boostTimeout);
return;
}
}
//关键代码!!
mScreenBrightnessBoostInProgress = false;
mNotifier.onScreenBrightnessBoostChanged();
userActivityNoUpdateLocked(now,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
}
}
}
private void boostScreenBrightnessInternal(long eventTime, int uid) {
synchronized (mLock) {
if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP
|| eventTime < mLastScreenBrightnessBoostTime) {
return;
}
Slog.i(TAG, "Brightness boost activated (uid " + uid +")...");
mLastScreenBrightnessBoostTime = eventTime;
if (!mScreenBrightnessBoostInProgress) {
// 关键代码!!
mScreenBrightnessBoostInProgress = true;
mNotifier.onScreenBrightnessBoostChanged();
}
mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
userActivityNoUpdateLocked(eventTime,
PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
updatePowerStateLocked();
}
}
并且通过跟踪代码看到,首先会调用 updateScreenBrightnessBoostLocked()方法,再调用 boostScreenBrightnessInternal()方法。所以只要不出意外情况,mScreenBrightnessBoostInProgress 最终会被置为 true。
所以,DisplayPowerRequest 对象中的 boolean 属性 boostScreenBrightness 不出意外情况,会是 true,这样当PMS 拿到 这个属性并传递给 DMS 的时候,DMS 就会去增加屏幕的亮度了。
所以基于代码跟踪,测试人员提出的这个不应该属于是issue,不需要修改。