Android 12/11 连续插拔充电,出现充电提示音消失

现象: 连续插拔充电器,出现充电提示音消失,只能重启

插上充电器,PowerManagerService就会调用updatePowerStateLocked()方法来更新整个电源状态的改变,并进行重新计算。当电源状态发生改变时,如亮灭屏、电池状态改变、暗屏…都会调用该方法,在该方法中调用了其他同级方法进行更新。

源码路径: frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
 

	private void updatePowerStateLocked() {
        if (!mSystemReady || mDirty == 0) {
            return;
        }
        if (!Thread.holdsLock(mLock)) {
            Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
        }

        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 = mClock.uptimeMillis();
            int dirtyPhase2 = 0;
            for (;;) {
                int dirtyPhase1 = mDirty;
                dirtyPhase2 |= dirtyPhase1;
                mDirty = 0;

                updateWakeLockSummaryLocked(dirtyPhase1);
                updateUserActivitySummaryLocked(now, dirtyPhase1);
                updateAttentiveStateLocked(now, dirtyPhase1);
                if (!updateWakefulnessLocked(dirtyPhase1)) {
                    break;
                }
            }
            
            // Phase 2: Lock profiles that became inactive/not kept awake.
            updateProfilesLocked(now);

            // Phase 3: Update display power state.
            final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

            // Phase 4: Update dream state (depends on display ready signal).
            updateDreamLocked(dirtyPhase2, displayBecameReady);

            // Phase 5: Send notifications, if needed.
            finishWakefulnessChangeIfNeededLocked();

            // Phase 6: 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);
        }
    }

然后调用updateIsPoweredLocked(),此方法中会判断是否充电,充电类型,电池电量等级,会去唤醒屏幕,设置电池状态。

	/**
     * 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; //电池电量
            mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
            mPlugType = mBatteryManagerInternal.getPlugType();
            mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
            mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();

            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();
                // 插拔充电线是否唤醒屏幕PluggedOrUnplugged
                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
                // 播放充电提示音
                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);
        }
    }

充电提示音在Notifier.java中控制

  • 源码路径:frameworks/base/services/core/java/com/android/server/power/Notifier.java
    	// 无线充电
    	private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
            // play sounds + haptics
            playChargingStartedFeedback(userId, true /* wireless */);
    
            // show animation
            if (mShowWirelessChargingAnimationConfig && mStatusBarManagerInternal != null) {
                mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
            }
            mSuspendBlocker.release();
        }
        
        //有线充电
        private void showWiredChargingStarted(@UserIdInt int userId) {
            playChargingStartedFeedback(userId, false /* wireless */);
            mSuspendBlocker.release();
        }
        
        private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) {
            if (!isChargingFeedbackEnabled(userId)) {
                return;
            }
            
            // vibrate
    		final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                    Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
            if (vibrate) {
                mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
            }
            // play sound
            final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                    wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
                            : Settings.Global.CHARGING_STARTED_SOUND);
            final Uri soundUri = Uri.parse("file://" + soundPath);
            if (soundUri != null) {
                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
                if (sfx != null) {
                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
                    sfx.play();
                }
            }
        }
    

    上面的代码存在mediaPlayer未释放资源的问题,会导致其它模块播放media时出现(1,-19)

    Ringtone 持有一个对应的mediaplayer,虽然Ringtone 每次播放前都释放资源,但是因为RingtoneManager每次返回的是一个新的Ringtone 对象,所以上面代码中,会不断的创建新的nediaplayer,导致出现无法播放问题。

    	private Ringtone mRingtone; // add 
    	...
    	public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
                SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
                ...
            initChargingStartedSound(); // add       
    	}
    	// add start 
        private void initChargingStartedSound(){
            // play sound
            final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                    Settings.Global.CHARGING_STARTED_SOUND);
            if (soundPath != null) {
                final Uri soundUri = Uri.parse("file://" + soundPath);
                if (soundUri != null) {
                    mRingtone = RingtoneManager.getRingtone(mContext, soundUri);
                    if (mRingtone != null) {
                        mRingtone.setStreamType(AudioManager.STREAM_SYSTEM);
                    }
                }
            }
        }
    	// add end 
    	
    	private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) {
            if (!isChargingFeedbackEnabled(userId)) {
                return;
            }
            // add start
            // vibrate
    		/* final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                    Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
            if (vibrate) {
                mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
            }
    		*/
            // play sound
            /*final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
                    wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
                            : Settings.Global.CHARGING_STARTED_SOUND);
            final Uri soundUri = Uri.parse("file://" + soundPath);
            if (soundUri != null) {
                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
                if (sfx != null) {
                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
                    sfx.play();
                }
            }*/
            if (mRingtone != null) {
                mRingtone.stop();
                mRingtone.play();
            }
    		// add end
        }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值