| /* |
| * Copyright (C) 2022 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.net.module.util; |
| |
| import android.util.SparseIntArray; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| /** |
| * Keeps track of the counters under different uid, fire exception if the counter |
| * exceeded the specified maximum value. |
| * |
| * @hide |
| */ |
| public class PerUidCounter { |
| private final int mMaxCountPerUid; |
| |
| // Map from UID to count that UID has filed. |
| @VisibleForTesting |
| @GuardedBy("mUidToCount") |
| final SparseIntArray mUidToCount = new SparseIntArray(); |
| |
| /** |
| * Constructor |
| * |
| * @param maxCountPerUid the maximum count per uid allowed |
| */ |
| public PerUidCounter(final int maxCountPerUid) { |
| if (maxCountPerUid <= 0) { |
| throw new IllegalArgumentException("Maximum counter value must be positive"); |
| } |
| mMaxCountPerUid = maxCountPerUid; |
| } |
| |
| /** |
| * Increments the count of the given uid. Throws an exception if the number |
| * of the counter for the uid exceeds the value of maxCounterPerUid which is the value |
| * passed into the constructor. see: {@link #PerUidCounter(int)}. |
| * |
| * @throws IllegalStateException if the number of counter for the uid exceed |
| * the allowed number. |
| * |
| * @param uid the uid that the counter was made under |
| */ |
| public void incrementCountOrThrow(final int uid) { |
| incrementCountOrThrow(uid, 1 /* numToIncrement */); |
| } |
| |
| public synchronized void incrementCountOrThrow(final int uid, final int numToIncrement) { |
| if (numToIncrement <= 0) { |
| throw new IllegalArgumentException("Increment count must be positive"); |
| } |
| final long newCount = ((long) mUidToCount.get(uid, 0)) + numToIncrement; |
| if (newCount > mMaxCountPerUid) { |
| throw new IllegalStateException("Uid " + uid + " exceeded its allowed limit"); |
| } |
| // Since the count cannot be greater than Integer.MAX_VALUE here since mMaxCountPerUid |
| // is an integer, it is safe to cast to int. |
| mUidToCount.put(uid, (int) newCount); |
| } |
| |
| /** |
| * Decrements the count of the given uid. Throws an exception if the number |
| * of the counter goes below zero. |
| * |
| * @throws IllegalStateException if the number of counter for the uid goes below |
| * zero. |
| * |
| * @param uid the uid that the count was made under |
| */ |
| public void decrementCountOrThrow(final int uid) { |
| decrementCountOrThrow(uid, 1 /* numToDecrement */); |
| } |
| |
| public synchronized void decrementCountOrThrow(final int uid, final int numToDecrement) { |
| if (numToDecrement <= 0) { |
| throw new IllegalArgumentException("Decrement count must be positive"); |
| } |
| final int newCount = mUidToCount.get(uid, 0) - numToDecrement; |
| if (newCount < 0) { |
| throw new IllegalStateException("BUG: too small count " + newCount + " for UID " + uid); |
| } else if (newCount == 0) { |
| mUidToCount.delete(uid); |
| } else { |
| mUidToCount.put(uid, newCount); |
| } |
| } |
| } |