Loading apex/jobscheduler/service/java/com/android/server/tare/Ledger.java 0 → 100644 +120 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.tare.TareUtils.narcToArc; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import java.util.ArrayList; import java.util.List; /** * Ledger to track the last recorded balance and recent activities of an app. */ class Ledger { static class Transaction { public final long startTimeMs; public final long endTimeMs; @NonNull public final String reason; @Nullable public final String tag; public final long delta; Transaction(long startTimeMs, long endTimeMs, @NonNull String reason, @Nullable String tag, long delta) { this.startTimeMs = startTimeMs; this.endTimeMs = endTimeMs; this.reason = reason; this.tag = tag; this.delta = delta; } } /** Last saved balance. This doesn't take currently ongoing events into account. */ private long mCurrentBalance = 0; private final List<Transaction> mTransactions = new ArrayList<>(); private final ArrayMap<String, Long> mCumulativeDeltaPerReason = new ArrayMap<>(); private long mEarliestSumTime; Ledger() { } long getCurrentBalance() { return mCurrentBalance; } void recordTransaction(@NonNull Transaction transaction) { mTransactions.add(transaction); mCurrentBalance += transaction.delta; Long sum = mCumulativeDeltaPerReason.get(transaction.reason); if (sum == null) { sum = 0L; } sum += transaction.delta; mCumulativeDeltaPerReason.put(transaction.reason, sum); mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs); } long get24HourSum(@NonNull String reason, final long now) { final long windowStartTime = now - 24 * HOUR_IN_MILLIS; if (mEarliestSumTime < windowStartTime) { // Need to redo sums mCumulativeDeltaPerReason.clear(); for (int i = mTransactions.size() - 1; i >= 0; --i) { final Transaction transaction = mTransactions.get(i); if (transaction.endTimeMs <= windowStartTime) { break; } final Long sumObj = mCumulativeDeltaPerReason.get(transaction.reason); long sum = sumObj == null ? 0 : sumObj; if (transaction.startTimeMs >= windowStartTime) { sum += transaction.delta; } else { // Pro-rate durationed deltas. Intentionally floor the result. sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime) * transaction.delta) / (transaction.endTimeMs - transaction.startTimeMs); } mCumulativeDeltaPerReason.put(transaction.reason, sum); } mEarliestSumTime = windowStartTime; } Long sum = mCumulativeDeltaPerReason.get(reason); if (sum == null) { return 0; } return sum; } void dump(IndentingPrintWriter pw) { pw.println("Ledger{"); pw.increaseIndent(); pw.print("cur balance", narcToArc(getCurrentBalance())).println(); pw.decreaseIndent(); pw.println("}"); } } apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.tare; class TareUtils { static long arcToNarc(int arcs) { return arcs * 1_000_000_000L; } static int narcToArc(long narcs) { return (int) (narcs / 1_000_000_000); } } services/tests/servicestests/src/com/android/server/tare/LedgerTest.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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 org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; /** Test that the ledger records transactions correctly. */ @RunWith(AndroidJUnit4.class) @SmallTest public class LedgerTest { @Test public void testInitialState() { final Ledger ledger = new Ledger(); assertEquals(0, ledger.getCurrentBalance()); assertEquals(0, ledger.get24HourSum("anything", 0)); } @Test public void testMultipleTransactions() { final Ledger ledger = new Ledger(); ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 5)); assertEquals(5, ledger.getCurrentBalance()); assertEquals(5, ledger.get24HourSum("test", 60_000)); ledger.recordTransaction(new Ledger.Transaction(2000, 2000, "test", null, 25)); assertEquals(30, ledger.getCurrentBalance()); assertEquals(30, ledger.get24HourSum("test", 60_000)); ledger.recordTransaction(new Ledger.Transaction(5000, 5500, "test", null, -10)); assertEquals(20, ledger.getCurrentBalance()); assertEquals(20, ledger.get24HourSum("test", 60_000)); } @Test public void test24HourSum() { final Ledger ledger = new Ledger(); ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 500)); assertEquals(500, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); ledger.recordTransaction( new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, "test", null, 2500)); assertEquals(3000, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); ledger.recordTransaction( new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, "test", null, 1)); assertEquals(3001, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); assertEquals(2501, ledger.get24HourSum("test", 25 * HOUR_IN_MILLIS)); assertEquals(2501, ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS)); // Pro-rated as the second transaction phases out assertEquals(1251, ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS)); assertEquals(1, ledger.get24HourSum("test", 27 * HOUR_IN_MILLIS)); assertEquals(0, ledger.get24HourSum("test", 28 * HOUR_IN_MILLIS)); } } Loading
apex/jobscheduler/service/java/com/android/server/tare/Ledger.java 0 → 100644 +120 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.tare.TareUtils.narcToArc; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import java.util.ArrayList; import java.util.List; /** * Ledger to track the last recorded balance and recent activities of an app. */ class Ledger { static class Transaction { public final long startTimeMs; public final long endTimeMs; @NonNull public final String reason; @Nullable public final String tag; public final long delta; Transaction(long startTimeMs, long endTimeMs, @NonNull String reason, @Nullable String tag, long delta) { this.startTimeMs = startTimeMs; this.endTimeMs = endTimeMs; this.reason = reason; this.tag = tag; this.delta = delta; } } /** Last saved balance. This doesn't take currently ongoing events into account. */ private long mCurrentBalance = 0; private final List<Transaction> mTransactions = new ArrayList<>(); private final ArrayMap<String, Long> mCumulativeDeltaPerReason = new ArrayMap<>(); private long mEarliestSumTime; Ledger() { } long getCurrentBalance() { return mCurrentBalance; } void recordTransaction(@NonNull Transaction transaction) { mTransactions.add(transaction); mCurrentBalance += transaction.delta; Long sum = mCumulativeDeltaPerReason.get(transaction.reason); if (sum == null) { sum = 0L; } sum += transaction.delta; mCumulativeDeltaPerReason.put(transaction.reason, sum); mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs); } long get24HourSum(@NonNull String reason, final long now) { final long windowStartTime = now - 24 * HOUR_IN_MILLIS; if (mEarliestSumTime < windowStartTime) { // Need to redo sums mCumulativeDeltaPerReason.clear(); for (int i = mTransactions.size() - 1; i >= 0; --i) { final Transaction transaction = mTransactions.get(i); if (transaction.endTimeMs <= windowStartTime) { break; } final Long sumObj = mCumulativeDeltaPerReason.get(transaction.reason); long sum = sumObj == null ? 0 : sumObj; if (transaction.startTimeMs >= windowStartTime) { sum += transaction.delta; } else { // Pro-rate durationed deltas. Intentionally floor the result. sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime) * transaction.delta) / (transaction.endTimeMs - transaction.startTimeMs); } mCumulativeDeltaPerReason.put(transaction.reason, sum); } mEarliestSumTime = windowStartTime; } Long sum = mCumulativeDeltaPerReason.get(reason); if (sum == null) { return 0; } return sum; } void dump(IndentingPrintWriter pw) { pw.println("Ledger{"); pw.increaseIndent(); pw.print("cur balance", narcToArc(getCurrentBalance())).println(); pw.decreaseIndent(); pw.println("}"); } }
apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.tare; class TareUtils { static long arcToNarc(int arcs) { return arcs * 1_000_000_000L; } static int narcToArc(long narcs) { return (int) (narcs / 1_000_000_000); } }
services/tests/servicestests/src/com/android/server/tare/LedgerTest.java 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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 org.junit.Assert.assertEquals; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; /** Test that the ledger records transactions correctly. */ @RunWith(AndroidJUnit4.class) @SmallTest public class LedgerTest { @Test public void testInitialState() { final Ledger ledger = new Ledger(); assertEquals(0, ledger.getCurrentBalance()); assertEquals(0, ledger.get24HourSum("anything", 0)); } @Test public void testMultipleTransactions() { final Ledger ledger = new Ledger(); ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 5)); assertEquals(5, ledger.getCurrentBalance()); assertEquals(5, ledger.get24HourSum("test", 60_000)); ledger.recordTransaction(new Ledger.Transaction(2000, 2000, "test", null, 25)); assertEquals(30, ledger.getCurrentBalance()); assertEquals(30, ledger.get24HourSum("test", 60_000)); ledger.recordTransaction(new Ledger.Transaction(5000, 5500, "test", null, -10)); assertEquals(20, ledger.getCurrentBalance()); assertEquals(20, ledger.get24HourSum("test", 60_000)); } @Test public void test24HourSum() { final Ledger ledger = new Ledger(); ledger.recordTransaction(new Ledger.Transaction(0, 1000, "test", null, 500)); assertEquals(500, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); ledger.recordTransaction( new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, "test", null, 2500)); assertEquals(3000, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); ledger.recordTransaction( new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, "test", null, 1)); assertEquals(3001, ledger.get24HourSum("test", 24 * HOUR_IN_MILLIS)); assertEquals(2501, ledger.get24HourSum("test", 25 * HOUR_IN_MILLIS)); assertEquals(2501, ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS)); // Pro-rated as the second transaction phases out assertEquals(1251, ledger.get24HourSum("test", 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS)); assertEquals(1, ledger.get24HourSum("test", 27 * HOUR_IN_MILLIS)); assertEquals(0, ledger.get24HourSum("test", 28 * HOUR_IN_MILLIS)); } }