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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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"))) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -830,6 +830,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@EnforcePermission(BATTERY_STATS)
|
||||
public long computeBatteryScreenOffRealtimeMs() {
|
||||
synchronized (mStats) {
|
||||
final long curTimeUs = SystemClock.elapsedRealtimeNanos() / 1000;
|
||||
long timeUs = mStats.computeBatteryScreenOffRealtime(curTimeUs,
|
||||
BatteryStats.STATS_SINCE_CHARGED);
|
||||
return timeUs / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@EnforcePermission(BATTERY_STATS)
|
||||
public long getScreenOffDischargeMah() {
|
||||
synchronized (mStats) {
|
||||
long dischargeUah = mStats.getUahDischargeScreenOff(BatteryStats.STATS_SINCE_CHARGED);
|
||||
return dischargeUah / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@EnforcePermission(UPDATE_DEVICE_STATS)
|
||||
public void noteEvent(final int code, final String name, final int uid) {
|
||||
|
||||
@@ -140,6 +140,8 @@ public class ScribeTest {
|
||||
report1.numPositiveRegulations = 10;
|
||||
report1.cumulativeNegativeRegulations = 11;
|
||||
report1.numNegativeRegulations = 12;
|
||||
report1.screenOffDurationMs = 13;
|
||||
report1.screenOffDischargeMah = 14;
|
||||
mReports.add(report1);
|
||||
mScribeUnderTest.writeImmediatelyForTesting();
|
||||
mScribeUnderTest.loadFromDiskLocked();
|
||||
@@ -160,6 +162,8 @@ public class ScribeTest {
|
||||
report2.numPositiveRegulations = 100;
|
||||
report2.cumulativeNegativeRegulations = 110;
|
||||
report2.numNegativeRegulations = 120;
|
||||
report2.screenOffDurationMs = 130;
|
||||
report2.screenOffDischargeMah = 140;
|
||||
mReports.add(report2);
|
||||
mScribeUnderTest.writeImmediatelyForTesting();
|
||||
mScribeUnderTest.loadFromDiskLocked();
|
||||
@@ -385,6 +389,10 @@ public class ScribeTest {
|
||||
eReport.cumulativeNegativeRegulations, aReport.cumulativeNegativeRegulations);
|
||||
assertEquals("Reports #" + i + " numNegativeRegulations are not equal",
|
||||
eReport.numNegativeRegulations, aReport.numNegativeRegulations);
|
||||
assertEquals("Reports #" + i + " screenOffDurationMs are not equal",
|
||||
eReport.screenOffDurationMs, aReport.screenOffDurationMs);
|
||||
assertEquals("Reports #" + i + " screenOffDischargeMah are not equal",
|
||||
eReport.screenOffDischargeMah, aReport.screenOffDischargeMah);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,14 @@ package com.android.server.tare;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.app.IBatteryStats;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -45,20 +49,20 @@ public class AnalystTest {
|
||||
final Analyst analyst = new Analyst();
|
||||
|
||||
Analyst.Report expected = new Analyst.Report();
|
||||
expected.currentBatteryLevel = 55;
|
||||
analyst.noteBatteryLevelChange(55);
|
||||
expected.currentBatteryLevel = 75;
|
||||
analyst.noteBatteryLevelChange(75);
|
||||
assertEquals(1, analyst.getReports().size());
|
||||
assertReportsEqual(expected, analyst.getReports().get(0));
|
||||
|
||||
// Discharging
|
||||
analyst.noteBatteryLevelChange(54);
|
||||
expected.currentBatteryLevel = 54;
|
||||
expected.cumulativeBatteryDischarge = 1;
|
||||
expected.cumulativeBatteryDischarge = 21;
|
||||
assertEquals(1, analyst.getReports().size());
|
||||
assertReportsEqual(expected, analyst.getReports().get(0));
|
||||
analyst.noteBatteryLevelChange(50);
|
||||
expected.currentBatteryLevel = 50;
|
||||
expected.cumulativeBatteryDischarge = 5;
|
||||
expected.cumulativeBatteryDischarge = 25;
|
||||
assertEquals(1, analyst.getReports().size());
|
||||
assertReportsEqual(expected, analyst.getReports().get(0));
|
||||
|
||||
@@ -87,27 +91,53 @@ public class AnalystTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransaction_PeriodChange() {
|
||||
final Analyst analyst = new Analyst();
|
||||
public void testTransaction_PeriodChange() throws Exception {
|
||||
IBatteryStats iBatteryStats = mock(IBatteryStats.class);
|
||||
final Analyst analyst = new Analyst(iBatteryStats);
|
||||
|
||||
// Reset from enough discharge.
|
||||
Analyst.Report expected = new Analyst.Report();
|
||||
expected.currentBatteryLevel = 55;
|
||||
analyst.noteBatteryLevelChange(55);
|
||||
expected.currentBatteryLevel = 75;
|
||||
analyst.noteBatteryLevelChange(75);
|
||||
|
||||
runTestTransactions(analyst, expected, 1);
|
||||
|
||||
expected.currentBatteryLevel = 49;
|
||||
expected.cumulativeBatteryDischarge = 6;
|
||||
expected.cumulativeBatteryDischarge = 26;
|
||||
analyst.noteBatteryLevelChange(49);
|
||||
|
||||
runTestTransactions(analyst, expected, 1);
|
||||
|
||||
expected = new Analyst.Report();
|
||||
expected.currentBatteryLevel = 100;
|
||||
analyst.noteBatteryLevelChange(100);
|
||||
expected.currentBatteryLevel = 90;
|
||||
analyst.noteBatteryLevelChange(90);
|
||||
expected.cumulativeBatteryDischarge = 0;
|
||||
|
||||
runTestTransactions(analyst, expected, 2);
|
||||
|
||||
// Reset from report being long enough.
|
||||
doReturn(Analyst.MIN_REPORT_DURATION_FOR_RESET)
|
||||
.when(iBatteryStats).computeBatteryScreenOffRealtimeMs();
|
||||
expected.currentBatteryLevel = 85;
|
||||
analyst.noteBatteryLevelChange(85);
|
||||
expected.cumulativeBatteryDischarge = 5;
|
||||
expected.screenOffDurationMs = Analyst.MIN_REPORT_DURATION_FOR_RESET;
|
||||
|
||||
runTestTransactions(analyst, expected, 2);
|
||||
|
||||
expected.currentBatteryLevel = 79;
|
||||
analyst.noteBatteryLevelChange(79);
|
||||
expected.cumulativeBatteryDischarge = 11;
|
||||
|
||||
runTestTransactions(analyst, expected, 2);
|
||||
|
||||
expected = new Analyst.Report();
|
||||
expected.currentBatteryLevel = 80;
|
||||
analyst.noteBatteryLevelChange(80);
|
||||
expected.cumulativeBatteryDischarge = 0;
|
||||
expected.screenOffDurationMs = 0;
|
||||
|
||||
runTestTransactions(analyst, expected, 3);
|
||||
}
|
||||
|
||||
private void runTestTransactions(Analyst analyst, Analyst.Report lastExpectedReport,
|
||||
@@ -223,6 +253,8 @@ public class AnalystTest {
|
||||
assertEquals(expected.numPositiveRegulations, actual.numPositiveRegulations);
|
||||
assertEquals(expected.cumulativeNegativeRegulations, actual.cumulativeNegativeRegulations);
|
||||
assertEquals(expected.numNegativeRegulations, actual.numNegativeRegulations);
|
||||
assertEquals(expected.screenOffDurationMs, actual.screenOffDurationMs);
|
||||
assertEquals(expected.screenOffDischargeMah, actual.screenOffDischargeMah);
|
||||
}
|
||||
|
||||
private void assertReportListsEqual(List<Analyst.Report> expected,
|
||||
|
||||
Reference in New Issue
Block a user