Fix bug in Ledger.
Avoid an IndexOutOfBoundsException by checking that the size of the transactions list is greater than 0 before trying to access the 0th element. Bug: 158300259 Test: atest FrameworksServicesTests:LedgerTest Change-Id: I76df357f0cf65f5577ed1c625579a6cb656264d9
This commit is contained in:
@@ -26,6 +26,7 @@ import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
|
||||
import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
|
||||
import static com.android.server.tare.EconomicPolicy.eventToString;
|
||||
import static com.android.server.tare.EconomicPolicy.getEventType;
|
||||
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
|
||||
import static com.android.server.tare.TareUtils.narcToString;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -183,7 +184,7 @@ class Agent {
|
||||
mCurrentOngoingEvents.get(userId, pkgName);
|
||||
if (ongoingEvents != null) {
|
||||
final long nowElapsed = SystemClock.elapsedRealtime();
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
mTotalDeltaCalculator.reset(ledger, nowElapsed, now);
|
||||
ongoingEvents.forEach(mTotalDeltaCalculator);
|
||||
balance += mTotalDeltaCalculator.mTotal;
|
||||
@@ -194,7 +195,7 @@ class Agent {
|
||||
@GuardedBy("mLock")
|
||||
void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
|
||||
final int eventId, @Nullable String tag) {
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
final Ledger ledger = getLedgerLocked(userId, pkgName);
|
||||
|
||||
final int eventType = getEventType(eventId);
|
||||
@@ -278,7 +279,7 @@ class Agent {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
void onDeviceStateChangedLocked() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
final long nowElapsed = SystemClock.elapsedRealtime();
|
||||
|
||||
mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
|
||||
@@ -326,7 +327,7 @@ class Agent {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
final long nowElapsed = SystemClock.elapsedRealtime();
|
||||
|
||||
for (int i = 0; i < pkgNames.size(); ++i) {
|
||||
@@ -477,7 +478,7 @@ class Agent {
|
||||
// The earliest transaction won't change until we clean up the ledger, so no point
|
||||
// continuing to reschedule an existing cleanup.
|
||||
final long cleanupAlarmElapsed = SystemClock.elapsedRealtime() + MAX_TRANSACTION_AGE_MS
|
||||
- (System.currentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
|
||||
- (getCurrentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
|
||||
mLedgerCleanupAlarmListener.addAlarmLocked(userId, pkgName, cleanupAlarmElapsed);
|
||||
}
|
||||
// TODO: save changes to disk in a background thread
|
||||
@@ -509,7 +510,7 @@ class Agent {
|
||||
@GuardedBy("mLock")
|
||||
void reclaimUnusedAssetsLocked(double percentage) {
|
||||
final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
for (int i = 0; i < pkgs.size(); ++i) {
|
||||
final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
|
||||
final String pkgName = pkgs.get(i).packageName;
|
||||
@@ -543,7 +544,7 @@ class Agent {
|
||||
@GuardedBy("mLock")
|
||||
void distributeBasicIncomeLocked(int batteryLevel) {
|
||||
List<PackageInfo> pkgs = mIrs.getInstalledPackages();
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
for (int i = 0; i < pkgs.size(); ++i) {
|
||||
final PackageInfo pkgInfo = pkgs.get(i);
|
||||
final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
|
||||
@@ -578,7 +579,7 @@ class Agent {
|
||||
List<PackageInfo> pkgs = packageManager.getInstalledPackagesAsUser(0, userId);
|
||||
final long maxBirthright =
|
||||
mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
|
||||
for (int i = 0; i < pkgs.size(); ++i) {
|
||||
final PackageInfo packageInfo = pkgs.get(i);
|
||||
@@ -609,7 +610,7 @@ class Agent {
|
||||
List<PackageInfo> pkgs = mIrs.getInstalledPackages();
|
||||
final int numPackages = pkgs.size();
|
||||
final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
|
||||
recordTransactionLocked(userId, pkgName, ledger,
|
||||
new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.server.tare;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
@@ -159,7 +161,7 @@ public class InternalResourceService extends SystemService {
|
||||
public void onAlarm() {
|
||||
synchronized (mLock) {
|
||||
mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
|
||||
mLastUnusedReclamationTime = System.currentTimeMillis();
|
||||
mLastUnusedReclamationTime = getCurrentTimeMillis();
|
||||
scheduleUnusedWealthReclamationLocked();
|
||||
}
|
||||
}
|
||||
@@ -342,7 +344,7 @@ public class InternalResourceService extends SystemService {
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void scheduleUnusedWealthReclamationLocked() {
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
final long nextReclamationTime =
|
||||
Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000);
|
||||
mHandler.post(() -> {
|
||||
@@ -581,7 +583,7 @@ public class InternalResourceService extends SystemService {
|
||||
return;
|
||||
}
|
||||
final long nowElapsed = SystemClock.elapsedRealtime();
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = getCurrentTimeMillis();
|
||||
synchronized (mLock) {
|
||||
mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.tare;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
|
||||
import static com.android.server.tare.TareUtils.dumpTime;
|
||||
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
|
||||
import static com.android.server.tare.TareUtils.narcToString;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -109,8 +110,8 @@ class Ledger {
|
||||
|
||||
/** Deletes transactions that are older than {@code minAgeMs}. */
|
||||
void removeOldTransactions(long minAgeMs) {
|
||||
final long cutoff = System.currentTimeMillis() - minAgeMs;
|
||||
while (mTransactions.get(0).endTimeMs <= cutoff) {
|
||||
final long cutoff = getCurrentTimeMillis() - minAgeMs;
|
||||
while (mTransactions.size() > 0 && mTransactions.get(0).endTimeMs <= cutoff) {
|
||||
mTransactions.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Clock;
|
||||
|
||||
class TareUtils {
|
||||
private static final long NARC_IN_ARC = 1_000_000_000L;
|
||||
@@ -29,6 +32,9 @@ class TareUtils {
|
||||
private static final SimpleDateFormat sDumpDateFormat =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
|
||||
@VisibleForTesting
|
||||
static Clock sSystemClock = Clock.systemUTC();
|
||||
|
||||
static long arcToNarc(int arcs) {
|
||||
return arcs * NARC_IN_ARC;
|
||||
}
|
||||
@@ -37,6 +43,10 @@ class TareUtils {
|
||||
pw.print(sDumpDateFormat.format(time));
|
||||
}
|
||||
|
||||
static long getCurrentTimeMillis() {
|
||||
return sSystemClock.millis();
|
||||
}
|
||||
|
||||
static int narcToArc(long narcs) {
|
||||
return (int) (narcs / NARC_IN_ARC);
|
||||
}
|
||||
|
||||
@@ -19,19 +19,31 @@ package com.android.server.tare;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
/** Test that the ledger records transactions correctly. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class LedgerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
TareUtils.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialState() {
|
||||
final Ledger ledger = new Ledger();
|
||||
@@ -72,4 +84,45 @@ public class LedgerTest {
|
||||
assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS));
|
||||
assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveOldTransactions() {
|
||||
final Ledger ledger = new Ledger();
|
||||
ledger.removeOldTransactions(24 * HOUR_IN_MILLIS);
|
||||
assertNull(ledger.getEarliestTransaction());
|
||||
|
||||
final long now = getCurrentTimeMillis();
|
||||
Ledger.Transaction transaction1 = new Ledger.Transaction(
|
||||
now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800);
|
||||
Ledger.Transaction transaction2 = new Ledger.Transaction(
|
||||
now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600);
|
||||
Ledger.Transaction transaction3 = new Ledger.Transaction(
|
||||
now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600);
|
||||
// Instant event
|
||||
Ledger.Transaction transaction4 = new Ledger.Transaction(
|
||||
now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500);
|
||||
// Recent event
|
||||
Ledger.Transaction transaction5 = new Ledger.Transaction(
|
||||
now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400);
|
||||
ledger.recordTransaction(transaction1);
|
||||
ledger.recordTransaction(transaction2);
|
||||
ledger.recordTransaction(transaction3);
|
||||
ledger.recordTransaction(transaction4);
|
||||
ledger.recordTransaction(transaction5);
|
||||
|
||||
assertEquals(transaction1, ledger.getEarliestTransaction());
|
||||
ledger.removeOldTransactions(24 * HOUR_IN_MILLIS);
|
||||
assertEquals(transaction2, ledger.getEarliestTransaction());
|
||||
ledger.removeOldTransactions(23 * HOUR_IN_MILLIS);
|
||||
assertEquals(transaction3, ledger.getEarliestTransaction());
|
||||
// Shouldn't delete transaction3 yet since there's still a piece of it within the min age
|
||||
// window.
|
||||
ledger.removeOldTransactions(21 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS);
|
||||
assertEquals(transaction3, ledger.getEarliestTransaction());
|
||||
// Instant event should be removed as soon as we hit the exact threshold.
|
||||
ledger.removeOldTransactions(20 * HOUR_IN_MILLIS);
|
||||
assertEquals(transaction5, ledger.getEarliestTransaction());
|
||||
ledger.removeOldTransactions(0);
|
||||
assertNull(ledger.getEarliestTransaction());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user