updatePowerStateLocked()方法是整个PMS中的核心方法,也是整个PMS中最重要的一个方法,它用来更新整个电源状态的改变,并进行重新计算。PMS中使用一个int值mDirty作为标志位判断电源状态是否发生变化,当电源状态发生改变时,如亮灭屏、电池状态改变、暗屏…都会调用该方法,在该方法中调用了其他同级方法进行更新,其中以下方法就包含今天的标题。
//更新电池信息
updateIsPoweredLocked(mDirty);
/**
* Updates the value of mIsPowered.
* Sets DIRTY_IS_POWERED if a change occurred.
*/
private void updateIsPoweredLocked(int dirty) {
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
//是否充电
final boolean wasPowered = mIsPowered;
//充电类型
final int oldPlugType = mPlugType;
//是否处于低电量
final boolean oldLevelLow = mBatteryLevelLow;
/*---------------------BatteryService交互Begin-----------------------------*/
mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryManagerInternal.getPlugType();
mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
/*---------------------BatteryService交互 End-----------------------------*/
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
+ ", mIsPowered=" + mIsPowered
+ ", oldPlugType=" + oldPlugType
+ ", mPlugType=" + mPlugType
+ ", mBatteryLevel=" + mBatteryLevel);
}
//充电状态改变
if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
mDirty |= DIRTY_IS_POWERED;
// Update wireless dock detection state.
//是否连接无线充电
final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
mIsPowered, mPlugType);
// Treat plugging and unplugging the devices as a user activity.
// Users find it disconcerting when they plug or unplug the device
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
final long now = mClock.uptimeMillis();
//插拔充电线是否唤醒屏幕
if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
dockedOnWirelessCharger)) {
//屏幕唤醒
wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
"android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
}
//更新用户活动
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// only play charging sounds if boot is completed so charging sounds don't play
// with potential notification sounds
// 仅在启动完成后播放充电声音,因此充电声音不会与潜在的通知声音一起播放,mBootCompleted此值在启动完成后会置为true
if (mBootCompleted) {
if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
&& BatteryManager.isPlugWired(mPlugType)) {
mNotifier.onWiredChargingStarted(mUserId);
} else if (dockedOnWirelessCharger) {
//通知无线充电状态
mNotifier.onWirelessChargingStarted(mBatteryLevel, mUserId);
}
}
}
mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow);
}
}
此方法会在多种情况下调用,接收电池改变广播,以及其他一些情况
最终如果dockedOnWirelessCharger值为true的话,会向下通知无线充电的状态
/**
633 * Called when wireless charging has started - to provide user feedback (sound and visual).
634 */
635 public void onWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
636 if (DEBUG) {
637 Slog.d(TAG, "onWirelessChargingStarted");
638 }
639
640 mSuspendBlocker.acquire();
641 Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
642 msg.setAsynchronous(true);
643 msg.arg1 = batteryLevel;
644 msg.arg2 = userId;
645 mHandler.sendMessage(msg);
646 }
handler接收处理
private final class NotifierHandler extends Handler {
859
860 public NotifierHandler(Looper looper) {
861 super(looper, null, true /*async*/);
862 }
863 @Override
864 public void handleMessage(Message msg) {
865 switch (msg.what) {
866 case MSG_USER_ACTIVITY:
867 sendUserActivity();
868 break;
869 case MSG_BROADCAST:
870 sendNextBroadcast();
871 break;
872 case MSG_WIRELESS_CHARGING_STARTED:
873 showWirelessChargingStarted(msg.arg1, msg.arg2);
874 break;
875 case MSG_PROFILE_TIMED_OUT:
876 lockProfile(msg.arg1);
877 break;
878 case MSG_WIRED_CHARGING_STARTED:
879 showWiredChargingStarted(msg.arg1);
880 break;
881 }
882 }
883 }
继续查看showWirelessChargingStarted方法
private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
830 // // 播放声音 + 触觉
831 playChargingStartedFeedback(userId, true /* wireless */);
832
833 // 显示动画
834 if (mShowWirelessChargingAnimationConfig && mStatusBarManagerInternal != null) {
835 mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
836 }
837 mSuspendBlocker.release();
838 }
839
private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) {
//此值查看当前是否为请勿打扰模式并且是否可以使用
804 if (!isChargingFeedbackEnabled(userId)) {
805 return;
806 }
807
808 // 震动
809 final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
810 Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
811 if (vibrate) {
812 mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
813 }
814
815 // 播放声音 不管是无线充电还是线充电,铃声的URI已经存储在数据库中
816 final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
817 wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
818 : Settings.Global.CHARGING_STARTED_SOUND);
819 final Uri soundUri = Uri.parse("file://" + soundPath);
820 if (soundUri != null) {
821 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
822 if (sfx != null) {
823 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
//播放充电提示音
824 sfx.play();
825 }
826 }
827 }
828
显示动画:
mShowWirelessChargingAnimationConfig 此值配置是否支持无线充电动画,可以进行客制化
mStatusBarManagerInternal.showChargingAnimation(batteryLevel); 及 StatusBarManagerService进行的实现
调用由StatusBarManagerInternal->StatusBarManagerService(其中private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() )中实现了 showChargingAnimation方法–>StatusBar
由StatusBarManagerService通知statusbar去显示无线充电动画
public void showWirelessChargingAnimation(int batteryLevel) {
2392 showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
2393 }
protected void showChargingAnimation(int batteryLevel, int transmittingBatteryLevel,
2396 long animationDelay) {
//此时处于Doze模式或锁屏界面,此时通知面板在手机界面显示
2397 if (mDozing || mKeyguardManager.isKeyguardLocked()) {
2398 // 在Doze或锁屏下,在动画开始前先隐藏通知面板
2399 WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
2400 transmittingBatteryLevel, batteryLevel,
2401 new WirelessChargingAnimation.Callback() {
2402 @Override
2403 public void onAnimationStarting() {
// 通过CrossFadeHelper.fadeOut隐藏通知面板,内部实现则是调用View.setVisibility
2404 mNotificationShadeWindowController.setRequestTopUi(true, TAG);
2405 CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
2406 }
2407
2408 @Override
2409 public void onAnimationEnded() {
// 通过CrossFadeHelper.fadeIn,将通知面板显示出来
2410 CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
2411 mNotificationShadeWindowController.setRequestTopUi(false, TAG);
2412 }
2413 }, mDozing).show(animationDelay);
2414 } else {
2415 // // 如果在桌面状态下则直接显示充电动画
2416 WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
2417 transmittingBatteryLevel, batteryLevel,
2418 new WirelessChargingAnimation.Callback() {
2419 @Override
2420 public void onAnimationStarting() {
2421 mNotificationShadeWindowController.setRequestTopUi(true, TAG);
2422 }
2423
2424 @Override
2425 public void onAnimationEnded() {
2426 mNotificationShadeWindowController.setRequestTopUi(false, TAG);
2427 }
2428 }, false).show(animationDelay);
2429 }
2430 }
调用WirelessChargingAnimation.makeWirelessChargingAnimation显示充电动画, makeWirelessChargingAnimation()方法返回一个
WirelessChargingAnimation对象,并且调用该对象的 show 方法
public void show(long delay) {
if (mCurrentWirelessChargingView == null ||
mCurrentWirelessChargingView.mNextView == null) {
throw new RuntimeException("setView must have been called");
}
// 先清除之前的动画状态
if (mPreviousWirelessChargingView != null) {
mPreviousWirelessChargingView.hide(0);
}
mPreviousWirelessChargingView = mCurrentWirelessChargingView;
// 显示现在的动画
mCurrentWirelessChargingView.show(delay);
// 设置动画结束时间
mCurrentWirelessChargingView.hide(delay + DURATION);
}
// 发送show消息
public void show(long delay) {
if (DEBUG) Slog.d(TAG, "SHOW: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, SHOW), delay);
}
// 发送hide消息
public void hide(long duration) {
mHandler.removeMessages(HIDE);
if (DEBUG) Slog.d(TAG, "HIDE: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
}
最终的处理方法在handleShow及handleHide
private void handleShow() {
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
String packageName = mView.getContext().getOpPackageName();
if (context == null) {
context = mView.getContext();
}
mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
if (mView.getParent() != null) {
mWM.removeView(mView);
}
try {
if (mCallback != null) {
mCallback.onAnimationStarting();
}
mWM.addView(mView, mParams);
} catch (WindowManager.BadTokenException e) {
Slog.d(TAG, "Unable to add wireless charging view. " + e);
}
}
}
private void handleHide() {
if (DEBUG) Slog.d(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
if (mView.getParent() != null) {
if (DEBUG) Slog.d(TAG, "REMOVE! " + mView + " in " + this);
if (mCallback != null) {
mCallback.onAnimationEnded();
}
mWM.removeViewImmediate(mView);
}
mView = null;
}
}
至此无线充电动画和铃声流程结束。
震动提示音以及充电动画相关问题牵扯到PowerManagerService中的流程,在我们上面分析的流程中,有一个shouldWakeUpWhenPluggedOrUnpluggedLocked方法,如果此方法返回true那么才会去WakeUp, 那么大概的方向已经确定,就是这个方法的返回值可能出现问题
private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(
boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) {
// 除非经过配置,否则在通电时不要唤醒。
if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
return false;
}
// 与无线充电器断开连接时不要唤醒。
if (wasPowered && !mIsPowered
&& oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
return false;
}
// 除非我们确定,否则不要在对接无线充电器时醒来。
if (!wasPowered && mIsPowered
&& mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
&& !dockedOnWirelessCharger) {
return false;
}
// 如果已经开始充电,并且获得了电量,那么就不要唤醒
if (mIsPowered && getWakefulnessLocked() == WAKEFULNESS_DREAMING) {
return false;
}
// 启用剧院模式时不要唤醒。
if (mTheaterModeEnabled && !mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig) {
return false;
}
// SystemUI 显示充电指示灯
if(mAlwaysOnEnabled && getWakefulnessLocked() == WAKEFULNESS_DOZING) {
return false;
}
// 否则唤醒!
return true;
}
此方法中规避了很多异常操作,只有当这些异常操作都不成立的时候才会去唤醒屏幕,当然还有更完整的解释
实施启发式方法来检测无线充电器的对接或断开。有些设备的无线充电电路无法检测到设备何时放在无线充电器上,除非设备实际从充电器接收电力。如果电池已快充满或太热,设备可能会停止供电。因此,我们不能总是依靠电池服务无线插头信号来准确指示设备是否已与无线充电器对接或断开对接。
这是一个问题,因为当设备插入无线充电器时,电源管理器通常会唤醒屏幕并播放提示音。对于系统来说,抑制虚假的对接和取消对接信号很重要,因为它们可能会干扰用户(尤其是当它们导致在深夜没有明显原因播放音调时)。
为了避免杂散信号,我们对无线充电器应用了一些特殊策略。
-
设备与无线充电器断开连接时不要唤醒设备,因为设备可能仍在无线充电器上,但由于电池已满而不再通电。理想情况下,如果我们可以确定用户已从无线充电器中取出设备,我们就会唤醒设备,但由于硬件限制,我们必须更加保守。
-
如果电池已基本充满,请勿在连接无线充电器时唤醒设备。这种情况可能表明设备一直放在充电器上,只是因为电池已经充满而没有通电。我们无法判断该设备是刚刚放在充电器上,还是已经在那里放了半夜慢慢放电,直到达到需要再次开始充电的程度。因此,当电池电量高于给定阈值时,我们会抑制对接信号。
-
如果设备自上次断开对接后似乎没有移动,请勿在接入无线充电器时唤醒该设备,因为之前的断开对接信号可能是虚假的。我们使用重力传感器来检测这种情况。
此段注释来自 WirelessChargerDetector.java
2. 在无线充电底座附近短距离抬起并放下设备,无充电动画显示,无充电提示音播放
如果要有无线充电动画显示,并且有提示音,无线充电的状态必须通知下去,通过复现并打印log发现,因为dockedOnWirelessCharger此值为false,所以不会去通知 无线充电的状态,所以需要分析此值为false的原因,此值的来源为mWirelessChargerDetector.update();
因此,对该方法进行添加log查看
/**
* 如果检测到对接,则更新充电状态并返回 true。
*
* @param isPowered 如果设备已通电,则为真。
* @param plugType 当前插头类型。
* @return 如果在抑制虚假对接或取消对接信号后确定设备刚刚对接在无线充电器上,则为真。
*/
public boolean update(boolean isPowered, int plugType) {
synchronized (mLock) {
final boolean wasPoweredWirelessly = mPoweredWirelessly;
if (isPowered && plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
// 设备正在从无线充电器接收电源。 异步更新静止位置。
mPoweredWirelessly = true;
mMustUpdateRestPosition = true;
startDetectionLocked();
} else {
// 设备可能在也可能不在无线充电器上,具体取决于我们收到的拔出信号是否是虚假的。
mPoweredWirelessly = false;
if (mAtRest) {
if (plugType != 0 && plugType != BatteryManager.BATTERY_PLUGGED_WIRELESS) {
// 该设备已插入新的非无线电源。 可以安全地假设它不再在无线充电器上。
mMustUpdateRestPosition = false;
clearAtRestLocked();
} else {
// 该设备可能仍在无线充电器上,但我们不知道。 检查设备是否在充电器上保持静止,
//以便我们知道在需要时忽略下一个无线插头事件。
startDetectionLocked();
}
}
}
// 仅当设备刚刚开始以无线方式接收电源并且不知道设备之前已经在无线充电器上静止时,才报告设备已对接。
return mPoweredWirelessly && !wasPoweredWirelessly && !mAtRest;
}
}
最终的返回值由三个值决定,添加log查看这三个值的状态
最终确定为wasPoweredWirelessly的值为true导致不会显示充电动画及铃声
因为短距离抬起手机此时手机的plugType还并没有刷新还是处于BatteryManager.BATTERY_PLUGGED_WIRELESS状态,因此wasPoweredWirelessly的值一直为true,所以这个状态下不会显示充电动画及铃声