Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb17357d authored by Kweku Adams's avatar Kweku Adams
Browse files

Implement TIP3.

Use a target background battery drain rate to determine the consumption
limit (stock).

Bug: 249365572
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/tare
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/tare
Test: manually drain device and check calculation as it charges
Change-Id: I85879ed23365c388f2ff179c28973e8a2e72b827
parent 677d1884
Loading
Loading
Loading
Loading
+136 −5
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.tare;

import static android.text.format.DateUtils.HOUR_IN_MILLIS;

import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
import static com.android.server.tare.EconomicPolicy.TYPE_REGULATION;
import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
@@ -23,9 +25,16 @@ import static com.android.server.tare.EconomicPolicy.getEventType;
import static com.android.server.tare.TareUtils.cakeToString;

import android.annotation.NonNull;
import android.os.BatteryManagerInternal;
import android.os.RemoteException;
import android.util.IndentingPrintWriter;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;

import java.util.ArrayList;
import java.util.List;

@@ -38,6 +47,8 @@ public class Analyst {
            || Log.isLoggable(TAG, Log.DEBUG);

    private static final int NUM_PERIODS_TO_RETAIN = 8;
    @VisibleForTesting
    static final long MIN_REPORT_DURATION_FOR_RESET = 24 * HOUR_IN_MILLIS;

    static final class Report {
        /** How much the battery was discharged over the tracked period. */
@@ -73,6 +84,22 @@ public class Analyst {
        public long cumulativeNegativeRegulations = 0;
        public int numNegativeRegulations = 0;

        /**
         * The approximate amount of time the screen has been off while on battery while this
         * report has been active.
         */
        public long screenOffDurationMs = 0;
        /**
         * The approximate amount of battery discharge while this report has been active.
         */
        public long screenOffDischargeMah = 0;
        /** The offset used to get the delta when polling the screen off time from BatteryStats. */
        private long bsScreenOffRealtimeBase = 0;
        /**
         * The offset used to get the delta when polling the screen off discharge from BatteryStats.
         */
        private long bsScreenOffDischargeMahBase = 0;

        private void clear() {
            cumulativeBatteryDischarge = 0;
            currentBatteryLevel = 0;
@@ -86,13 +113,27 @@ public class Analyst {
            numPositiveRegulations = 0;
            cumulativeNegativeRegulations = 0;
            numNegativeRegulations = 0;
            screenOffDurationMs = 0;
            screenOffDischargeMah = 0;
            bsScreenOffRealtimeBase = 0;
            bsScreenOffDischargeMahBase = 0;
        }
    }

    private final IBatteryStats mIBatteryStats;

    private int mPeriodIndex = 0;
    /** How much the battery was discharged over the tracked period. */
    private final Report[] mReports = new Report[NUM_PERIODS_TO_RETAIN];

    Analyst() {
        this(BatteryStatsService.getService());
    }

    @VisibleForTesting Analyst(IBatteryStats iBatteryStats) {
        mIBatteryStats = iBatteryStats;
    }

    /** Returns the list of most recent reports, with the oldest report first. */
    @NonNull
    List<Report> getReports() {
@@ -107,13 +148,35 @@ public class Analyst {
        return list;
    }

    long getBatteryScreenOffDischargeMah() {
        long discharge = 0;
        for (Report report : mReports) {
            if (report == null) {
                continue;
            }
            discharge += report.screenOffDischargeMah;
        }
        return discharge;
    }

    long getBatteryScreenOffDurationMs() {
        long duration = 0;
        for (Report report : mReports) {
            if (report == null) {
                continue;
            }
            duration += report.screenOffDurationMs;
        }
        return duration;
    }

    /**
     * Tracks the given reports instead of whatever is currently saved. Reports should be ordered
     * oldest to most recent.
     */
    void loadReports(@NonNull List<Report> reports) {
        final int numReports = reports.size();
        mPeriodIndex = Math.max(0, numReports - 1);
        mPeriodIndex = Math.max(0, Math.min(NUM_PERIODS_TO_RETAIN, numReports) - 1);
        for (int i = 0; i < NUM_PERIODS_TO_RETAIN; ++i) {
            if (i < numReports) {
                mReports[i] = reports.get(i);
@@ -121,22 +184,38 @@ public class Analyst {
                mReports[i] = null;
            }
        }
        final Report latest = mReports[mPeriodIndex];
        if (latest != null) {
            latest.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs();
            latest.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah();
        }
    }

    void noteBatteryLevelChange(int newBatteryLevel) {
        if (newBatteryLevel == 100 && mReports[mPeriodIndex] != null
                && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel) {
        final boolean deviceDischargedEnough = mReports[mPeriodIndex] != null
                && newBatteryLevel >= 90
                // Battery level is increasing, so device is charging.
                && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel
                && mReports[mPeriodIndex].cumulativeBatteryDischarge >= 25;
        final boolean reportLongEnough = mReports[mPeriodIndex] != null
                // Battery level is increasing, so device is charging.
                && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel
                && mReports[mPeriodIndex].screenOffDurationMs >= MIN_REPORT_DURATION_FOR_RESET;
        final boolean shouldStartNewReport = deviceDischargedEnough || reportLongEnough;
        if (shouldStartNewReport) {
            mPeriodIndex = (mPeriodIndex + 1) % NUM_PERIODS_TO_RETAIN;
            if (mReports[mPeriodIndex] != null) {
                final Report report = mReports[mPeriodIndex];
                report.clear();
                report.currentBatteryLevel = newBatteryLevel;
                report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs();
                report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah();
                return;
            }
        }

        if (mReports[mPeriodIndex] == null) {
            Report report = new Report();
            Report report = initializeReport();
            mReports[mPeriodIndex] = report;
            report.currentBatteryLevel = newBatteryLevel;
            return;
@@ -145,13 +224,27 @@ public class Analyst {
        final Report report = mReports[mPeriodIndex];
        if (newBatteryLevel < report.currentBatteryLevel) {
            report.cumulativeBatteryDischarge += (report.currentBatteryLevel - newBatteryLevel);

            final long latestScreenOffRealtime = getLatestBatteryScreenOffRealtimeMs();
            final long latestScreenOffDischargeMah = getLatestScreenOffDischargeMah();
            if (report.bsScreenOffRealtimeBase > latestScreenOffRealtime) {
                // BatteryStats reset
                report.bsScreenOffRealtimeBase = 0;
                report.bsScreenOffDischargeMahBase = 0;
            }
            report.screenOffDurationMs +=
                    (latestScreenOffRealtime - report.bsScreenOffRealtimeBase);
            report.screenOffDischargeMah +=
                    (latestScreenOffDischargeMah - report.bsScreenOffDischargeMahBase);
            report.bsScreenOffRealtimeBase = latestScreenOffRealtime;
            report.bsScreenOffDischargeMahBase = latestScreenOffDischargeMah;
        }
        report.currentBatteryLevel = newBatteryLevel;
    }

    void noteTransaction(@NonNull Ledger.Transaction transaction) {
        if (mReports[mPeriodIndex] == null) {
            mReports[mPeriodIndex] = new Report();
            mReports[mPeriodIndex] = initializeReport();
        }
        final Report report = mReports[mPeriodIndex];
        switch (getEventType(transaction.eventId)) {
@@ -191,6 +284,32 @@ public class Analyst {
        mPeriodIndex = 0;
    }

    private long getLatestBatteryScreenOffRealtimeMs() {
        try {
            return mIBatteryStats.computeBatteryScreenOffRealtimeMs();
        } catch (RemoteException e) {
            // Shouldn't happen
            return 0;
        }
    }

    private long getLatestScreenOffDischargeMah() {
        try {
            return mIBatteryStats.getScreenOffDischargeMah();
        } catch (RemoteException e) {
            // Shouldn't happen
            return 0;
        }
    }

    @NonNull
    private Report initializeReport() {
        final Report report = new Report();
        report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs();
        report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah();
        return report;
    }

    @NonNull
    private String padStringWithSpaces(@NonNull String text, int targetLength) {
        // Make sure to have at least one space on either side.
@@ -199,6 +318,8 @@ public class Analyst {
    }

    void dump(IndentingPrintWriter pw) {
        final BatteryManagerInternal bmi = LocalServices.getService(BatteryManagerInternal.class);
        final long batteryCapacityMah = bmi.getBatteryFullCharge() / 1000;
        pw.println("Reports:");
        pw.increaseIndent();
        pw.print("      Total Discharge");
@@ -208,6 +329,7 @@ public class Analyst {
        pw.print(padStringWithSpaces("Rewards (avg/reward : avg/discharge)", statColsLength));
        pw.print(padStringWithSpaces("+Regs (avg/reg : avg/discharge)", statColsLength));
        pw.print(padStringWithSpaces("-Regs (avg/reg : avg/discharge)", statColsLength));
        pw.print(padStringWithSpaces("Bg drain estimate", statColsLength));
        pw.println();
        for (int r = 0; r < NUM_PERIODS_TO_RETAIN; ++r) {
            final int idx = (mPeriodIndex - r + NUM_PERIODS_TO_RETAIN) % NUM_PERIODS_TO_RETAIN;
@@ -283,6 +405,15 @@ public class Analyst {
            } else {
                pw.print(padStringWithSpaces("N/A", statColsLength));
            }
            if (report.screenOffDurationMs > 0) {
                pw.print(padStringWithSpaces(String.format("%d mAh (%.2f%%/hr)",
                                report.screenOffDischargeMah,
                                1.0 * report.screenOffDischargeMah * HOUR_IN_MILLIS
                                        / (batteryCapacityMah * report.screenOffDurationMs)),
                        statColsLength));
            } else {
                pw.print(padStringWithSpaces("N/A", statColsLength));
            }
            pw.println();
        }
        pw.decreaseIndent();
+99 −0
Original line number Diff line number Diff line
@@ -112,6 +112,19 @@ public class InternalResourceService extends SystemService {
     * limit).
     */
    private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50;
    /**
     * The battery level above which we may consider adjusting the desired stock level.
     */
    private static final int STOCK_RECALCULATION_BATTERY_THRESHOLD = 80;
    /**
     * The amount of time to wait before considering recalculating the desired stock level.
     */
    private static final long STOCK_RECALCULATION_DELAY_MS = 16 * HOUR_IN_MILLIS;
    /**
     * The minimum amount of time we must have background drain for before considering
     * recalculating the desired stock level.
     */
    private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS;
    private static final int PACKAGE_QUERY_FLAGS =
            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                    | PackageManager.MATCH_APEX;
@@ -177,6 +190,9 @@ public class InternalResourceService extends SystemService {
    @GuardedBy("mLock")
    private int mCurrentBatteryLevel;

    // TODO(250007395): make configurable per device
    private final int mTargetBackgroundBatteryLifeHours;

    private final IAppOpsCallback mApbListener = new IAppOpsCallback.Stub() {
        @Override
        public void opChanged(int op, int uid, String packageName) {
@@ -316,6 +332,11 @@ public class InternalResourceService extends SystemService {

        mConfigObserver = new ConfigObserver(mHandler, context);

        mTargetBackgroundBatteryLifeHours =
                mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
                        ? 200 // ~ 0.5%/hr
                        : 100; // ~ 1%/hr

        publishLocalService(EconomyManagerInternal.class, new LocalService());
    }

@@ -461,6 +482,9 @@ public class InternalResourceService extends SystemService {
            mAnalyst.noteBatteryLevelChange(newBatteryLevel);
            final boolean increased = newBatteryLevel > mCurrentBatteryLevel;
            if (increased) {
                if (newBatteryLevel >= STOCK_RECALCULATION_BATTERY_THRESHOLD) {
                    maybeAdjustDesiredStockLevelLocked();
                }
                mAgent.distributeBasicIncomeLocked(newBatteryLevel);
            } else if (newBatteryLevel == mCurrentBatteryLevel) {
                // The broadcast is also sent when the plug type changes...
@@ -623,6 +647,10 @@ public class InternalResourceService extends SystemService {
     */
    @GuardedBy("mLock")
    void maybePerformQuantitativeEasingLocked() {
        if (mConfigObserver.ENABLE_TIP3) {
            maybeAdjustDesiredStockLevelLocked();
            return;
        }
        // We don't need to increase the limit if the device runs out of consumable credits
        // when the battery is low.
        final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
@@ -643,6 +671,68 @@ public class InternalResourceService extends SystemService {
        }
    }

    /**
     * Adjust the consumption limit based on historical data and the target battery drain.
     */
    @GuardedBy("mLock")
    void maybeAdjustDesiredStockLevelLocked() {
        if (!mConfigObserver.ENABLE_TIP3) {
            return;
        }
        // Don't adjust the limit too often or while the battery is low.
        final long now = getCurrentTimeMillis();
        if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS
                || mCurrentBatteryLevel <= STOCK_RECALCULATION_BATTERY_THRESHOLD) {
            return;
        }

        // For now, use screen off battery drain as a proxy for background battery drain.
        // TODO: get more accurate background battery drain numbers
        final long totalScreenOffDurationMs = mAnalyst.getBatteryScreenOffDurationMs();
        if (totalScreenOffDurationMs < STOCK_RECALCULATION_MIN_DATA_DURATION_MS) {
            return;
        }
        final long totalDischargeMah = mAnalyst.getBatteryScreenOffDischargeMah();
        final long batteryCapacityMah = mBatteryManagerInternal.getBatteryFullCharge() / 1000;
        final long estimatedLifeHours = batteryCapacityMah * totalScreenOffDurationMs
                / totalDischargeMah / HOUR_IN_MILLIS;
        final long percentageOfTarget =
                100 * estimatedLifeHours / mTargetBackgroundBatteryLifeHours;
        if (DEBUG) {
            Slog.d(TAG, "maybeAdjustDesiredStockLevelLocked:"
                    + " screenOffMs=" + totalScreenOffDurationMs
                    + " dischargeMah=" + totalDischargeMah
                    + " capacityMah=" + batteryCapacityMah
                    + " estimatedLifeHours=" + estimatedLifeHours
                    + " %ofTarget=" + percentageOfTarget);
        }
        final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
        final long newConsumptionLimit;
        if (percentageOfTarget > 105) {
            // The stock is too low. We're doing pretty well. We can increase the stock slightly
            // to let apps do more work in the background.
            newConsumptionLimit = Math.min((long) (currentConsumptionLimit * 1.01),
                    mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
        } else if (percentageOfTarget < 100) {
            // The stock is too high IMO. We're below the target. Decrease the stock to reduce
            // background work.
            newConsumptionLimit = Math.max((long) (currentConsumptionLimit * .98),
                    mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit());
        } else {
            // The stock is just right.
            return;
        }
        // TODO(250007191): calculate and log implied service level
        if (newConsumptionLimit != currentConsumptionLimit) {
            Slog.i(TAG, "Adjusting consumption limit from " + cakeToString(currentConsumptionLimit)
                    + " to " + cakeToString(newConsumptionLimit)
                    + " because drain was " + percentageOfTarget + "% of target");
            mScribe.setConsumptionLimitLocked(newConsumptionLimit);
            adjustCreditSupplyLocked(/* allowIncrease */ true);
            mScribe.setLastStockRecalculationTimeLocked(now);
        }
    }

    void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
            @NonNull Agent.ActionAffordabilityNote affordabilityNote) {
        if (DEBUG) {
@@ -1214,6 +1304,12 @@ public class InternalResourceService extends SystemService {
    private class ConfigObserver extends ContentObserver
            implements DeviceConfig.OnPropertiesChangedListener {
        private static final String KEY_DC_ENABLE_TARE = "enable_tare";
        private static final String KEY_ENABLE_TIP3 = "enable_tip3";

        private static final boolean DEFAULT_ENABLE_TIP3 = true;

        /** Use a target background battery drain rate to determine consumption limits. */
        public boolean ENABLE_TIP3 = DEFAULT_ENABLE_TIP3;

        private final ContentResolver mContentResolver;

@@ -1264,6 +1360,9 @@ public class InternalResourceService extends SystemService {
                        case KEY_DC_ENABLE_TARE:
                            updateEnabledStatus();
                            break;
                        case KEY_ENABLE_TIP3:
                            ENABLE_TIP3 = properties.getBoolean(name, DEFAULT_ENABLE_TIP3);
                            break;
                        default:
                            if (!economicPolicyUpdated
                                    && (name.startsWith("am") || name.startsWith("js"))) {
+18 −3
Original line number Diff line number Diff line
@@ -80,9 +80,9 @@ consumption limit, then the available resources are decreased to match the scale
Regulations are unique events invoked by the ~~government~~ system in order to get the whole economy
moving smoothly.

# Previous Implementations
# Significant Changes

## V0
## Tare Improvement Proposal #1 (TIP1)

The initial implementation/proposal combined the supply of resources with the allocation in a single
mechanism. It defined the maximum number of resources (ARCs) available at a time, and then divided
@@ -98,10 +98,25 @@ allocated as part of the rewards. There were several problems with that mechanis
These problems effectively meant that misallocation was a big problem, demand wasn't well reflected,
and some apps may not have been able to perform work even though they otherwise should have been.

Tare Improvement Proposal #1 (TIP1) separated allocation (to apps) from supply (by the system) and
TIP1 separated allocation (to apps) from supply (by the system) and
allowed apps to accrue credits as appropriate while still limiting the total number of credits
consumed.

## Tare Improvement Proposal #3 (TIP3)

TIP1 introduced Consumption Limits, which control the total number of ARCs that can be used to
perform actions, based on the production costs of each action. The Consumption Limits were initially
determined manually, but could increase in the system if apps used the full consumption limit before
the device had drained to 50% battery. As with any system that relies on manually deciding
parameters, the only mechanism to identify an optimal value is through experimentation, which can
take many iterations and requires extended periods of time to observe results. The limits are also
chosen and adjusted without consideration of the resulting battery drain of each possible value. In
addition, having the system potentially increase the limit without considering a decrease introduced
potential for battery life to get worse as time goes on and the user installed more background-work
demanding apps.

TIP3 uses a target background battery drain rate to dynamically adjust the Consumption Limit.

# Potential Future Changes

These are some ideas for further changes. There's no guarantee that they'll be implemented.
+27 −1
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ public class Scribe {
    private static final String XML_ATTR_USER_ID = "userId";
    private static final String XML_ATTR_VERSION = "version";
    private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
    private static final String XML_ATTR_LAST_STOCK_RECALCULATION_TIME =
            "lastStockRecalculationTime";
    private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes";
    private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
    private static final String XML_ATTR_PR_DISCHARGE = "discharge";
@@ -98,6 +100,8 @@ public class Scribe {
    private static final String XML_ATTR_PR_NUM_POS_REGULATIONS = "numPosRegulations";
    private static final String XML_ATTR_PR_NEG_REGULATIONS = "negRegulations";
    private static final String XML_ATTR_PR_NUM_NEG_REGULATIONS = "numNegRegulations";
    private static final String XML_ATTR_PR_SCREEN_OFF_DURATION_MS = "screenOffDurationMs";
    private static final String XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH = "screenOffDischargeMah";

    /** Version of the file schema. */
    private static final int STATE_FILE_VERSION = 0;
@@ -111,6 +115,8 @@ public class Scribe {
    @GuardedBy("mIrs.getLock()")
    private long mLastReclamationTime;
    @GuardedBy("mIrs.getLock()")
    private long mLastStockRecalculationTime;
    @GuardedBy("mIrs.getLock()")
    private long mSatiatedConsumptionLimit;
    @GuardedBy("mIrs.getLock()")
    private long mRemainingConsumableCakes;
@@ -172,6 +178,11 @@ public class Scribe {
        return mLastReclamationTime;
    }

    @GuardedBy("mIrs.getLock()")
    long getLastStockRecalculationTimeLocked() {
        return mLastStockRecalculationTime;
    }

    @GuardedBy("mIrs.getLock()")
    @NonNull
    Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
@@ -281,6 +292,8 @@ public class Scribe {
                    case XML_TAG_HIGH_LEVEL_STATE:
                        mLastReclamationTime =
                                parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME);
                        mLastStockRecalculationTime = parser.getAttributeLong(null,
                                XML_ATTR_LAST_STOCK_RECALCULATION_TIME, 0);
                        mSatiatedConsumptionLimit =
                                parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
                                        mIrs.getInitialSatiatedConsumptionLimitLocked());
@@ -336,6 +349,12 @@ public class Scribe {
        postWrite();
    }

    @GuardedBy("mIrs.getLock()")
    void setLastStockRecalculationTimeLocked(long time) {
        mLastStockRecalculationTime = time;
        postWrite();
    }

    @GuardedBy("mIrs.getLock()")
    void tearDownLocked() {
        TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
@@ -504,7 +523,6 @@ public class Scribe {
        return earliestEndTime;
    }


    /**
     * @param parser Xml parser at the beginning of a {@link #XML_TAG_PERIOD_REPORT} tag. The next
     *               "parser.next()" call will take the parser into the body of the report tag.
@@ -531,6 +549,10 @@ public class Scribe {
                parser.getAttributeLong(null, XML_ATTR_PR_NEG_REGULATIONS);
        report.numNegativeRegulations =
                parser.getAttributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS);
        report.screenOffDurationMs =
                parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, 0);
        report.screenOffDischargeMah =
                parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, 0);

        return report;
    }
@@ -606,6 +628,8 @@ public class Scribe {

                out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
                out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
                out.attributeLong(null,
                        XML_ATTR_LAST_STOCK_RECALCULATION_TIME, mLastStockRecalculationTime);
                out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
                out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
                        mRemainingConsumableCakes);
@@ -718,6 +742,8 @@ public class Scribe {
        out.attributeInt(null, XML_ATTR_PR_NUM_POS_REGULATIONS, report.numPositiveRegulations);
        out.attributeLong(null, XML_ATTR_PR_NEG_REGULATIONS, report.cumulativeNegativeRegulations);
        out.attributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS, report.numNegativeRegulations);
        out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, report.screenOffDurationMs);
        out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, report.screenOffDischargeMah);
        out.endTag(null, XML_TAG_PERIOD_REPORT);
    }

+5 −0
Original line number Diff line number Diff line
@@ -84,6 +84,11 @@ interface IBatteryStats {
    @RequiresNoPermission
    long computeChargeTimeRemaining();

    @EnforcePermission("BATTERY_STATS")
    long computeBatteryScreenOffRealtimeMs();
    @EnforcePermission("BATTERY_STATS")
    long getScreenOffDischargeMah();

    @EnforcePermission("UPDATE_DEVICE_STATS")
    void noteEvent(int code, String name, int uid);

Loading