充电状态改变,是否震动与发出提示音

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;
}

此方法中规避了很多异常操作,只有当这些异常操作都不成立的时候才会去唤醒屏幕,当然还有更完整的解释

实施启发式方法来检测无线充电器的对接或断开。有些设备的无线充电电路无法检测到设备何时放在无线充电器上,除非设备实际从充电器接收电力。如果电池已快充满或太热,设备可能会停止供电。因此,我们不能总是依靠电池服务无线插头信号来准确指示设备是否已与无线充电器对接或断开对接。

这是一个问题,因为当设备插入无线充电器时,电源管理器通常会唤醒屏幕并播放提示音。对于系统来说,抑制虚假的对接和取消对接信号很重要,因为它们可能会干扰用户(尤其是当它们导致在深夜没有明显原因播放音调时)。

为了避免杂散信号,我们对无线充电器应用了一些特殊策略。

  1. 设备与无线充电器断开连接时不要唤醒设备,因为设备可能仍在无线充电器上,但由于电池已满而不再通电。理想情况下,如果我们可以确定用户已从无线充电器中取出设备,我们就会唤醒设备,但由于硬件限制,我们必须更加保守。

  2. 如果电池已基本充满,请勿在连接无线充电器时唤醒设备。这种情况可能表明设备一直放在充电器上,只是因为电池已经充满而没有通电。我们无法判断该设备是刚刚放在充电器上,还是已经在那里放了半夜慢慢放电,直到达到需要再次开始充电的程度。因此,当电池电量高于给定阈值时,我们会抑制对接信号。

  3. 如果设备自上次断开对接后似乎没有移动,请勿在接入无线充电器时唤醒该设备,因为之前的断开对接信号可能是虚假的。我们使用重力传感器来检测这种情况。

此段注释来自 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,所以这个状态下不会显示充电动画及铃声

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值