Settings中电池选项-Android13

[电池]Android 9.0 电池未充电与充电字符串提示信息[通俗易懂]


1、设置中界面

packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java
packages/apps/Settings/res/xml/power_usage_summary.xml
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java

在这里插入图片描述

12-23 13:13:23.184   602   715 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cmp=com.android.settings/.SubSettings (has extras)} from uid 1000
12-23 13:13:23.285   994  2135 D BatteryTipLoader: BatteryInfoLoader post query: 5ms
12-23 13:13:23.356   994  2135 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:23.357   994  2135 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 82ms
12-23 13:13:23.950   994   994 D SettingsActivity: Switching to fragment com.android.settings.fuelgauge.batteryusage.PowerUsageSummary

2、电池计算

packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java

    public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
            @NonNull BatteryUsageStats batteryUsageStats, Estimate estimate,
            long elapsedRealtimeUs, boolean shortString) {
        final long startTime = System.currentTimeMillis();
        final boolean isCompactStatus = context.getResources().getBoolean(
                com.android.settings.R.bool.config_use_compact_battery_status);
        BatteryInfo info = new BatteryInfo();
        info.mBatteryUsageStats = batteryUsageStats;
        info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
        info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
        info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
        info.averageTimeToDischarge = estimate.getAverageDischargeTime();
        info.isOverheated = batteryBroadcast.getIntExtra(
                BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
                == BatteryManager.BATTERY_HEALTH_OVERHEAT;

        info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
        info.batteryStatus = batteryBroadcast.getIntExtra(
                BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
        if (!info.mCharging) {
            updateBatteryInfoDischarging(context, shortString, estimate, info);
        } else {
            updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats,
                    info, isCompactStatus);
        }
        BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
        return info;
    }

    private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast,
            BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) {
        final Resources resources = context.getResources();
        final long chargeTimeMs = stats.getChargeTimeRemainingMs();
        final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
                BatteryManager.BATTERY_STATUS_UNKNOWN);
        info.discharging = false;
        info.suggestionLabel = null;
        if (info.isOverheated && status != BatteryManager.BATTERY_STATUS_FULL) {
            info.remainingLabel = null;
            int chargingLimitedResId = R.string.power_charging_limited;
            info.chargeLabel =
                context.getString(chargingLimitedResId, info.batteryPercentString);
        } else if (chargeTimeMs > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
            info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
            final CharSequence timeString = StringUtil.formatElapsedTime(
                    context,
                    PowerUtil.convertUsToMs(info.remainingTimeUs),
                    false /* withSeconds */,
                    true /* collapseTimeUnit */);
            int resId = R.string.power_charging_duration;
            info.remainingLabel = context.getString(
                    R.string.power_remaining_charging_duration_only, timeString);
            info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString);
        } else {
            final String chargeStatusLabel =
                    Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
            info.remainingLabel = null;
            info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString :
                    resources.getString(R.string.power_charging, info.batteryPercentString,
                            chargeStatusLabel.toLowerCase());
        }
    }

    private static void updateBatteryInfoDischarging(Context context, boolean shortString,
            Estimate estimate, BatteryInfo info) {
        final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());
        if (drainTimeUs > 0) {
            info.remainingTimeUs = drainTimeUs;
            info.remainingLabel = PowerUtil.getBatteryRemainingStringFormatted(
                    context,
                    PowerUtil.convertUsToMs(drainTimeUs),
                    null /* percentageString */,
                    false /* basedOnUsage */
            );
            info.chargeLabel = PowerUtil.getBatteryRemainingStringFormatted(
                    context,
                    PowerUtil.convertUsToMs(drainTimeUs),
                    info.batteryPercentString,
                    estimate.isBasedOnUsage() && !shortString
            );
            info.suggestionLabel = PowerUtil.getBatteryTipStringFormatted(
                    context, PowerUtil.convertUsToMs(drainTimeUs));
        } else {
            info.remainingLabel = null;
            info.suggestionLabel = null;
            info.chargeLabel = info.batteryPercentString;
        }
    }

2.1 充电时间计算

2.1.1 BatteryUsageStats获取

  • updateBatteryInfoCharging 中获取 BatteryUsageStatsmChargeTimeRemainingMs还需的充电时间;显示<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
  • BatteryUsageStats对象是BatteryStatsService通过BatteryUsageStatsProvider获取
  • BatteryUsageStatsProviderBatteryChargeCalculator.java#calculate最终调用到batteryStats.computeChargeTimeRemaining(rawRealtimeUs)计算

packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java

    public BatteryInfo getBatteryInfo(final String tag) {
        final BatteryStatsManager systemService = mContext.getSystemService(
                BatteryStatsManager.class);
        BatteryUsageStats batteryUsageStats;
        try {
            batteryUsageStats = systemService.getBatteryUsageStats(
                    new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
        } catch (RuntimeException e) {
            Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e);
            // Use default BatteryUsageStats.
            batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build();
        }

        final long startTime = System.currentTimeMillis();

        // Stuff we always need to get BatteryInfo
        final Intent batteryBroadcast = mContext.registerReceiver(null,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

        final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
                SystemClock.elapsedRealtime());

        BatteryInfo batteryInfo;
        Estimate estimate = getEnhancedEstimate();

        // couldn't get estimate from cache or provider, use fallback
        if (estimate == null) {
            estimate = new Estimate(
                    batteryUsageStats.getBatteryTimeRemainingMs(),
                    false /* isBasedOnUsage */,
                    EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
        }

        BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime);
        batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast,
                batteryUsageStats, estimate, elapsedRealtimeUs, false /* shortString */);
        BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);

        try {
            batteryUsageStats.close();
        } catch (Exception e) {
            Log.e(TAG, "BatteryUsageStats.close() failed", e);
        }
        return batteryInfo;
    }

frameworks/base/core/java/com/android/internal/os/BatteryUsageStatsProvider.java

    private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
            long currentTimeMs) {
        final long realtimeUs = elapsedRealtime() * 1000;
        final long uptimeUs = uptimeMillis() * 1000;

        final boolean includePowerModels = (query.getFlags()
                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
        final boolean includeProcessStateData = ((query.getFlags()
                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
                && mStats.isProcessStateDataAvailable();
        final boolean includeVirtualUids =  ((query.getFlags()
                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);

        final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
                mStats.getCustomEnergyConsumerNames(), includePowerModels,
                includeProcessStateData);
        // TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
        // of stats sessions to wall-clock adjustments
        batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
        batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);

        SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
        for (int i = uidStats.size() - 1; i >= 0; i--) {
            final BatteryStats.Uid uid = uidStats.valueAt(i);
            if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
                continue;
            }

            batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
                    .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND,
                            getProcessBackgroundTimeMs(uid, realtimeUs))
                    .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND,
                            getProcessForegroundTimeMs(uid, realtimeUs));
        }

        final int[] powerComponents = query.getPowerComponents();
        final List<PowerCalculator> powerCalculators = getPowerCalculators();
        for (int i = 0, count = powerCalculators.size(); i < count; i++) {
            PowerCalculator powerCalculator = powerCalculators.get(i);
            if (powerComponents != null) {
                boolean include = false;
                for (int j = 0; j < powerComponents.length; j++) {
                    if (powerCalculator.isPowerComponentSupported(powerComponents[j])) {
                        include = true;
                        break;
                    }
                }
                if (!include) {
                    continue;
                }
            }
            powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
                    query);
        }

        if ((query.getFlags()
                & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
            if (!(mStats instanceof BatteryStatsImpl)) {
                throw new UnsupportedOperationException(
                        "History cannot be included for " + getClass().getName());
            }

            BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;

            // Make a copy of battery history to avoid concurrent modification.
            Parcel historyBuffer = Parcel.obtain();
            historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
                    batteryStatsImpl.mHistoryBuffer.dataSize());

            final File systemDir =
                    batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
            final BatteryStatsHistory batteryStatsHistory =
                    new BatteryStatsHistory(batteryStatsImpl, systemDir, historyBuffer);

            batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
        }

        BatteryUsageStats stats = batteryUsageStatsBuilder.build();
        if (includeProcessStateData) {
            verify(stats);
        }
        return stats;
    }

    // STOPSHIP(b/229906525): remove verification before shipping
    private static boolean sErrorReported;
    private void verify(BatteryUsageStats stats) {
        if (sErrorReported) {
            return;
        }

        final double precision = 2.0;   // Allow rounding errors up to 2 mAh
        final int[] components =
                {BatteryConsumer.POWER_COMPONENT_CPU,
                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                        BatteryConsumer.POWER_COMPONENT_WIFI,
                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH};
        final int[] states =
                {BatteryConsumer.PROCESS_STATE_FOREGROUND,
                        BatteryConsumer.PROCESS_STATE_BACKGROUND,
                        BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
                        BatteryConsumer.PROCESS_STATE_CACHED};
        for (UidBatteryConsumer ubc : stats.getUidBatteryConsumers()) {
            for (int component : components) {
                double consumedPower = ubc.getConsumedPower(ubc.getKey(component));
                double sumStates = 0;
                for (int state : states) {
                    sumStates += ubc.getConsumedPower(ubc.getKey(component, state));
                }
                if (sumStates > consumedPower + precision) {
                    String error = "Sum of states exceeds total. UID = " + ubc.getUid() + " "
                            + BatteryConsumer.powerComponentIdToString(component)
                            + " total = " + consumedPower + " states = " + sumStates;
                    if (!sErrorReported) {
                        Slog.wtf(TAG, error);
                        sErrorReported = true;
                    } else {
                        Slog.e(TAG, error);
                    }
                    return;
                }
            }
        }
    }

frameworks/base/core/java/com/android/internal/os/BatteryChargeCalculator.java

    @Override
    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
        builder.setDischargePercentage(
                batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED));

        int batteryCapacityMah = batteryStats.getLearnedBatteryCapacity() / 1000;
        if (batteryCapacityMah <= 0) {
            batteryCapacityMah = batteryStats.getMinLearnedBatteryCapacity() / 1000;
            if (batteryCapacityMah <= 0) {
                batteryCapacityMah = batteryStats.getEstimatedBatteryCapacity();
            }
        }
        builder.setBatteryCapacity(batteryCapacityMah);

        final double dischargedPowerLowerBoundMah =
                batteryStats.getLowDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
        final double dischargedPowerUpperBoundMah =
                batteryStats.getHighDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
        builder.setDischargePercentage(
                batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED))
                .setDischargedPowerRange(dischargedPowerLowerBoundMah,
                        dischargedPowerUpperBoundMah)
                .setDischargeDurationMs(batteryStats.getBatteryRealtime(rawRealtimeUs) / 1000);

        final long batteryTimeRemainingMs = batteryStats.computeBatteryTimeRemaining(rawRealtimeUs);
        if (batteryTimeRemainingMs != -1) {
            builder.setBatteryTimeRemainingMs(batteryTimeRemainingMs / 1000);
        }

        final long chargeTimeRemainingMs = batteryStats.computeChargeTimeRemaining(rawRealtimeUs);
        if (chargeTimeRemainingMs != -1) {
            builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
        }

        long dischargeMah = batteryStats.getUahDischarge(BatteryStats.STATS_SINCE_CHARGED) / 1000;
        if (dischargeMah == 0) {
            dischargeMah = (long) ((dischargedPowerLowerBoundMah + dischargedPowerUpperBoundMah) / 2
                    + 0.5);
        }

        builder.getAggregateBatteryConsumerBuilder(
                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                .setConsumedPower(dischargeMah);
    }

2.1.2 BatteryStatsImpl计算

  • mBatteryTimeToFullSeconds 这个是底层支持计算,由底层health上报。
  • 计算逻辑 final LevelStepTracker mChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
    1. 电池状态信息变化调用**setBatteryStateLocked**,这里mChargeStepTrackerinit()初始化或level增大变化记录计算addLevelSteps
    2. mChargeStepTracker.mNumStepDurations < 1 充满一格电不会显示,至少连续充满两格电
    3. mChargeStepTracker.computeTimePerLevel() 平均addLevelSteps记录充电每个格电的总时间
    4. (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000; 连续充电的平均得到的每格电充电时间 * 需要充电level

frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java

    @Override
    public long computeChargeTimeRemaining(long curTime) {
        if (mOnBattery) {
            // Not yet working.
            return -1;
        }
        if (mBatteryTimeToFullSeconds >= 0) {
            return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
        }
        // Else use algorithmic approach
        if (mChargeStepTracker.mNumStepDurations < 1) {
            return -1;
        }
        long msPerLevel = mChargeStepTracker.computeTimePerLevel();
        if (msPerLevel <= 0) {
            return -1;
        }
        return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
    }

frameworks/base/core/java/android/os/BatteryStats.java

    public static final class LevelStepTracker {
        public long mLastStepTime = -1;
        public int mNumStepDurations;
        public final long[] mStepDurations;

        public LevelStepTracker(int maxLevelSteps) {
            mStepDurations = new long[maxLevelSteps];
        }

        public LevelStepTracker(int numSteps, long[] steps) {
            mNumStepDurations = numSteps;
            mStepDurations = new long[numSteps];
            System.arraycopy(steps, 0, mStepDurations, 0, numSteps);
        }
        // ... ...
        public void init() {
            mLastStepTime = -1;
            mNumStepDurations = 0;
        }
        // ... ...
        public long computeTimePerLevel() {
            final long[] steps = mStepDurations;
            final int numSteps = mNumStepDurations;

            // For now we'll do a simple average across all steps.
            if (numSteps <= 0) {
                return -1;
            }
            long total = 0;
            for (int i=0; i<numSteps; i++) {
                total += steps[i] & STEP_LEVEL_TIME_MASK;
            }
            return total / numSteps;
        }
         // ... ...
        public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
            int stepCount = mNumStepDurations;
            final long lastStepTime = mLastStepTime;
            if (lastStepTime >= 0 && numStepLevels > 0) {
                final long[] steps = mStepDurations;
                long duration = elapsedRealtime - lastStepTime;
                for (int i=0; i<numStepLevels; i++) {
                    System.arraycopy(steps, 0, steps, 1, steps.length-1);
                    long thisDuration = duration / (numStepLevels-i);
                    duration -= thisDuration;
                    if (thisDuration > STEP_LEVEL_TIME_MASK) {
                        thisDuration = STEP_LEVEL_TIME_MASK;
                    }
                    steps[0] = thisDuration | modeBits;
                }
                stepCount += numStepLevels;
                if (stepCount > steps.length) {
                    stepCount = steps.length;
                }
            }
            mNumStepDurations = stepCount;
            mLastStepTime = elapsedRealtime;
        }
        // ... ...
    }

2.2 电池剩余使用时间

2.2.1 Estimate获取

  • updateBatteryInfoDischarging 获取EstimateestimateMillis ,由 2.1.1BatteryUsageStats 获取中查看实际获取batteryUsageStats.getBatteryTimeRemainingMs()
  • BatteryUsageStatsmBatteryTimeRemainingMs查看上面BatteryChargeCalculator.java#calculate最终调用到batteryStats.computeBatteryTimeRemaining(rawRealtimeUs)计算
  • Estimate.kt 如果缓存的估计值可用,则返回该估计值。如果估计不可用或超过2分钟,将返回null。相关属性Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME、Settings.Global.TIME_REMAINING_ESTIMATE_MILLIS、Settings.Global.TIME_REMAINING_ESTIMATE_BASED_ON_USAGE、Settings.Global.AVERAGE_TIME_TO_DISCHARGE

frameworks/base/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java

    Estimate getEnhancedEstimate() {
        Estimate estimate = null;
        // Get enhanced prediction if available
        if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now())
                .compareTo(Duration.ofSeconds(10)) < 0) {
            estimate = Estimate.getCachedEstimateIfAvailable(mContext);
        } else if (mPowerUsageFeatureProvider != null &&
                mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
            estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
            if (estimate != null) {
                Estimate.storeCachedEstimate(mContext, estimate);
            }
        }
        return estimate;
    }

2.2.2 BatteryStatsImpl计算

  • 计算逻辑 final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
    1. 电池状态信息变化调用**setBatteryStateLocked**,这里mDischargeStepTracker init()初始化或level减小变化记录计算addLevelSteps
    2. mDischargeStepTracker.mNumStepDurations < 1 耗电一格电不会显示,至少连续耗电两格电
    3. mChargeStepTracker.computeTimePerLevel() 平均addLevelSteps记录耗电每个格电的总时间
    4. (msPerLevel * mCurrentBatteryLevel) * 1000 连续耗电的平均得到的每格电充电时间 * 当前电量level

frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java

    public long computeBatteryTimeRemaining(long curTime) {
        if (!mOnBattery) {
            return -1;
        }
        if (mDischargeStepTracker.mNumStepDurations < 1) {
            return -1;
        }
        long msPerLevel = mDischargeStepTracker.computeTimePerLevel();
        if (msPerLevel <= 0) {
            return -1;
        }
        return (msPerLevel * mCurrentBatteryLevel) * 1000;
    }

3、电池信息来源

  • mHealthServiceWrapper = HealthServiceWrapper.create(this::update);注册监听IHealthInfoCallback.hal,回调执行 update > processValuesLocked
  • android.hardware.health@2.0-service 中 轮询更新Health::update(),其中battery_monitor_->logValues()输出日志healthd : battery l=
  • BatteryService.java#processValuesLocked 通过BatteryStatsService.java最终通知到 BatteryStatsImpl.java#setBatteryStateLocked;这里输出Event日志battery_level、battery_status、battery_discharge、BatteryService

frameworks/base/services/core/java/com/android/server/BatteryService.java

    private void registerHealthCallback() {
        traceBegin("HealthInitWrapper");
        // IHealth is lazily retrieved.
        try {
            mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
        } catch (RemoteException ex) {
            Slog.e(TAG, "health: cannot register callback. (RemoteException)");
            throw ex.rethrowFromSystemServer();
        } catch (NoSuchElementException ex) {
            Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
            throw ex;
        } finally {
            traceEnd();
        }

        traceBegin("HealthInitWaitUpdate");
        // init register for new service notifications, and IServiceManager should return the
        // existing service in a near future. Wait for this.update() to instantiate
        // the initial mHealthInfo.
        long beforeWait = SystemClock.uptimeMillis();
        synchronized (mLock) {
            while (mHealthInfo == null) {
                Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
                        "ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
                try {
                    mLock.wait(HEALTH_HAL_WAIT_MS);
                } catch (InterruptedException ex) {
                    Slog.i(TAG, "health: InterruptedException when waiting for update. "
                        + " Continuing...");
                }
            }
        }

        Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
                + "ms and received the update.");
        traceEnd();
    }

    private void update(android.hardware.health.HealthInfo info) {
        traceBegin("HealthInfoUpdate");

        Trace.traceCounter(
                Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
        Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);

        synchronized (mLock) {
            if (!mUpdatesStopped) {
                mHealthInfo = info;
                // Process the new values.
                processValuesLocked(false);
                mLock.notifyAll(); // for any waiters on new info
            } else {
                copyV1Battery(mLastHealthInfo, info);
            }
        }
        traceEnd();
    }

    private void processValuesLocked(boolean force) {
        boolean logOutlier = false;
        long dischargeDuration = 0;

        mBatteryLevelCritical =
            mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
            && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
        mPlugType = plugType(mHealthInfo);

        if (DEBUG) {
            Slog.d(TAG, "Processing new values: "
                    + "info=" + mHealthInfo
                    + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                    + ", mPlugType=" + mPlugType);
        }

        // Let the battery stats keep track of the current level.
        try {
            mBatteryStats.setBatteryState(
                    mHealthInfo.batteryStatus,
                    mHealthInfo.batteryHealth,
                    mPlugType,
                    mHealthInfo.batteryLevel,
                    mHealthInfo.batteryTemperatureTenthsCelsius,
                    mHealthInfo.batteryVoltageMillivolts,
                    mHealthInfo.batteryChargeCounterUah,
                    mHealthInfo.batteryFullChargeUah,
                    mHealthInfo.batteryChargeTimeToFullNowSeconds);
        } catch (RemoteException e) {
            // Should never happen.
        }

        shutdownIfNoPowerLocked();
        shutdownIfOverTempLocked();

        if (force
                || (mHealthInfo.batteryStatus != mLastBatteryStatus
                        || mHealthInfo.batteryHealth != mLastBatteryHealth
                        || mHealthInfo.batteryPresent != mLastBatteryPresent
                        || mHealthInfo.batteryLevel != mLastBatteryLevel
                        || mPlugType != mLastPlugType
                        || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
                        || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
                        || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
                        || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
                        || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
                        || mInvalidCharger != mLastInvalidCharger)) {

            if (mPlugType != mLastPlugType) {
                if (mLastPlugType == BATTERY_PLUGGED_NONE) {
                    // discharging -> charging
                    mChargeStartLevel = mHealthInfo.batteryLevel;
                    mChargeStartTime = SystemClock.elapsedRealtime();

                    final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
                    builder.setType(MetricsEvent.TYPE_ACTION);
                    builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType);
                    builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
                            mHealthInfo.batteryLevel);
                    mMetricsLogger.write(builder);

                    // There's no value in this data unless we've discharged at least once and the
                    // battery level has changed; so don't log until it does.
                    if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
                        dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                        logOutlier = true;
                        EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
                                mDischargeStartLevel, mHealthInfo.batteryLevel);
                        // make sure we see a discharge event before logging again
                        mDischargeStartTime = 0;
                    }
                } else if (mPlugType == BATTERY_PLUGGED_NONE) {
                    // charging -> discharging or we just powered up
                    mDischargeStartTime = SystemClock.elapsedRealtime();
                    mDischargeStartLevel = mHealthInfo.batteryLevel;

                    long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime;
                    if (mChargeStartTime != 0 && chargeDuration != 0) {
                        final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
                        builder.setType(MetricsEvent.TYPE_DISMISS);
                        builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
                        builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
                                chargeDuration);
                        builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
                                mChargeStartLevel);
                        builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END,
                                mHealthInfo.batteryLevel);
                        mMetricsLogger.write(builder);
                    }
                    mChargeStartTime = 0;
                }
            }
            if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
                    mHealthInfo.batteryHealth != mLastBatteryHealth ||
                    mHealthInfo.batteryPresent != mLastBatteryPresent ||
                    mPlugType != mLastPlugType) {
                EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
                        mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
                        mPlugType, mHealthInfo.batteryTechnology);
            }
            if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
                // Don't do this just from voltage or temperature changes, that is
                // too noisy.
                EventLog.writeEvent(
                        EventLogTags.BATTERY_LEVEL,
                        mHealthInfo.batteryLevel,
                        mHealthInfo.batteryVoltageMillivolts,
                        mHealthInfo.batteryTemperatureTenthsCelsius);
            }
            if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                    mPlugType == BATTERY_PLUGGED_NONE) {
                // We want to make sure we log discharge cycle outliers
                // if the battery is about to die.
                dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                logOutlier = true;
            }

            if (!mBatteryLevelLow) {
                // Should we now switch in to low battery mode?
                if (mPlugType == BATTERY_PLUGGED_NONE
                        && mHealthInfo.batteryStatus !=
                           BatteryManager.BATTERY_STATUS_UNKNOWN
                        && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
                    mBatteryLevelLow = true;
                }
            } else {
                // Should we now switch out of low battery mode?
                if (mPlugType != BATTERY_PLUGGED_NONE) {
                    mBatteryLevelLow = false;
                } else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel)  {
                    mBatteryLevelLow = false;
                } else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
                    // If being forced, the previous state doesn't matter, we will just
                    // absolutely check to see if we are now above the warning level.
                    mBatteryLevelLow = false;
                }
            }

            mSequence++;

            // Separate broadcast is sent for power connected / not connected
            // since the standard intent will not wake any applications and some
            // applications may want to have smart behavior based on this.
            if (mPlugType != 0 && mLastPlugType == 0) {
                final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
                statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                    }
                });
            }
            else if (mPlugType == 0 && mLastPlugType != 0) {
                final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
                statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                    }
                });
            }

            if (shouldSendBatteryLowLocked()) {
                mSentLowBatteryBroadcast = true;
                final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
                statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                    }
                });
            } else if (mSentLowBatteryBroadcast &&
                    mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
                mSentLowBatteryBroadcast = false;
                final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
                statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
                    }
                });
            }

            // We are doing this after sending the above broadcasts, so anything processing
            // them will get the new sequence number at that point.  (See for example how testing
            // of JobScheduler's BatteryController works.)
            sendBatteryChangedIntentLocked();
            if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
                sendBatteryLevelChangedIntentLocked();
            }


            // Update the battery LED
            mLed.updateLightsLocked();

            // This needs to be done after sendIntent() so that we get the lastest battery stats.
            if (logOutlier && dischargeDuration != 0) {
                logOutlierLocked(dischargeDuration);
            }

            mLastBatteryStatus = mHealthInfo.batteryStatus;
            mLastBatteryHealth = mHealthInfo.batteryHealth;
            mLastBatteryPresent = mHealthInfo.batteryPresent;
            mLastBatteryLevel = mHealthInfo.batteryLevel;
            mLastPlugType = mPlugType;
            mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
            mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
            mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
            mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
            mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
            mLastBatteryLevelCritical = mBatteryLevelCritical;
            mLastInvalidCharger = mInvalidCharger;
        }
    }

hardware/interfaces/health/2.0/utils/libhealthservice/HealthServiceCommon.cpp

void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
    HealthInfo info;
    convertToHealthInfo(prop, info.legacy);
    Health::getImplementation()->notifyListeners(&info);
}

static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
    .init = healthd_mode_service_2_0_init,
    .preparetowait = healthd_mode_service_2_0_preparetowait,
    .heartbeat = healthd_mode_service_2_0_heartbeat,
    .battery_update = healthd_mode_service_2_0_battery_update,
};

int health_service_main(const char* instance) {
    gInstanceName = instance;
    if (gInstanceName.empty()) {
        gInstanceName = "default";
    }
    healthd_mode_ops = &healthd_mode_service_2_0_ops;
    LOG(INFO) << LOG_TAG << gInstanceName << ": Hal starting main loop...";
    return healthd_main();
}

hardware/interfaces/health/2.0/default/healthd_common_adapter.cpp

// Adapter of HealthLoop to use legacy healthd_mode_ops.
class HealthLoopAdapter : public HealthLoop {
   public:
    // Expose internal functions, assuming clients calls them in the same thread
    // where StartLoop is called.
    int RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
        return HealthLoop::RegisterEvent(fd, func, wakeup);
    }
    void AdjustWakealarmPeriods(bool charger_online) {
        return HealthLoop::AdjustWakealarmPeriods(charger_online);
    }
   protected:
    void Init(healthd_config* config) override { healthd_mode_ops->init(config); }
    void Heartbeat() override { healthd_mode_ops->heartbeat(); }
    int PrepareToWait() override { return healthd_mode_ops->preparetowait(); }
    void ScheduleBatteryUpdate() override { Health::getImplementation()->update(); }
};

hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp

void HealthLoop::PeriodicChores() {
    ScheduleBatteryUpdate();
}

void HealthLoop::MainLoop(void) {
    int nevents = 0;
    while (1) {
        reject_event_register_ = true;
        size_t eventct = event_handlers_.size();
        struct epoll_event events[eventct];
        int timeout = awake_poll_interval_;

        int mode_timeout;

        /* Don't wait for first timer timeout to run periodic chores */
        if (!nevents) PeriodicChores();

        Heartbeat();

        mode_timeout = PrepareToWait();
        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
        nevents = epoll_wait(epollfd_, events, eventct, timeout);
        if (nevents == -1) {
            if (errno == EINTR) continue;
            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
            break;
        }

        for (int n = 0; n < nevents; ++n) {
            if (events[n].data.ptr) {
                auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
                event_handler->func(event_handler->object, events[n].events);
            }
        }
    }

    return;
}

hardware/interfaces/health/2.0/default/Health.cpp

Return<Result> Health::update() {
    if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
        LOG(WARNING) << "health@2.0: update: not initialized. "
                     << "update() should not be called in charger";
        return Result::UNKNOWN;
    }

    // Retrieve all information and call healthd_mode_ops->battery_update, which calls
    // notifyListeners.
    battery_monitor_->updateValues();
    const HealthInfo_1_0& health_info = battery_monitor_->getHealthInfo_1_0();
    struct BatteryProperties props;
    convertFromHealthInfo(health_info, &props);
    bool log = (healthd_board_battery_update(&props) == 0);
    if (log) {
        battery_monitor_->logValues();
    }
    healthd_mode_ops->battery_update(&props);
    bool chargerOnline = battery_monitor_->isChargerOnline();

    // adjust uevent / wakealarm periods
    healthd_battery_update_internal(chargerOnline);

    return Result::SUCCESS;
}

void Health::notifyListeners(HealthInfo* healthInfo) {
    std::vector<StorageInfo> info;
    get_storage_info(info);

    std::vector<DiskStats> stats;
    get_disk_stats(stats);

    int32_t currentAvg = 0;

    struct BatteryProperty prop;
    status_t ret = battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop);
    if (ret == OK) {
        currentAvg = static_cast<int32_t>(prop.valueInt64);
    }

    healthInfo->batteryCurrentAverage = currentAvg;
    healthInfo->diskStats = stats;
    healthInfo->storageInfos = info;

    std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
    for (auto it = callbacks_.begin(); it != callbacks_.end();) {
        auto ret = (*it)->healthInfoChanged(*healthInfo);
        if (!ret.isOk() && ret.isDeadObject()) {
            it = callbacks_.erase(it);
        } else {
            ++it;
        }
    }
}

4、命令模拟

frameworks/base/services/core/java/com/android/server/BatteryService.java

C:\Users\Administrator\Desktop>adb shell cmd battery
Battery service (battery) commands:
  help
    Print this help text.
  get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]
  set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>
    Force a battery property value, freezing battery state.
    -f: force a battery change broadcast be sent, prints new sequence.
  unplug [-f]
    Force battery unplugged, freezing battery state.
    -f: force a battery change broadcast be sent, prints new sequence.
  reset [-f]
    Unfreeze battery state, returning to current hardware values.
    -f: force a battery change broadcast be sent, prints new sequence.
  suspend_input
    Suspend charging even if plugged in.

* 日志

  • Event日志
# ---------------------------
# BatteryService.java
# ---------------------------
2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)
2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3)
# This is logged when battery goes from discharging to charging.
# It lets us count the total amount of time between charges and the discharge level
2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6)
12-23 05:47:45.420   602   764 I battery_status: [2,2,1,1,Li-ion]
12-23 13:13:43.762     0     0 W healthd : battery l=57 v=5000 t=25.0 h=2 st=2 c=900000 fc=3000000 cc=10 chg=a
12-23 13:13:51.756   602   623 I battery_level: [58,5000,250]
12-23 13:13:52.156   994  2461 D BatteryInfoLoader: BatteryInfoLoader post query: 5ms
12-23 13:13:52.168   994  2461 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:52.170   994  2461 D BatteryInfoLoader: BatteryInfoLoader.loadInBackground: 19ms
12-23 13:13:52.300   994  2131 D BatteryTipLoader: BatteryInfoLoader post query: 6ms
12-23 13:13:52.308   994  2131 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:52.308   994  2131 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 15ms
12-23 13:13:57.300   602   623 I battery_level: [59,5000,250]
12-23 13:13:57.706   994  2135 D BatteryTipLoader: BatteryInfoLoader post query: 14ms
12-23 13:13:57.713   994  2135 D BatteryInfo: time for getBatteryInfo: 7ms
12-23 13:13:57.714   994  2135 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 21ms
12-23 13:13:57.789   994  2461 D BatteryInfoLoader: BatteryInfoLoader post query: 4ms
12-23 13:13:57.792   994  2461 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:57.793   994  2461 D BatteryInfoLoader: BatteryInfoLoader.loadInBackground: 8ms