1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.selinux; 17 18 import com.android.internal.annotations.VisibleForTesting; 19 import com.android.internal.os.Clock; 20 21 import java.time.Duration; 22 import java.time.Instant; 23 24 /** 25 * A QuotaLimiter allows to define a maximum number of Atom pushes within a specific time window. 26 * 27 * <p>The limiter divides the time line in windows of a fixed size. Every time a new permit is 28 * requested, the limiter checks whether the previous request was in the same time window as the 29 * current one. If the two windows are the same, it grants a permit only if the number of permits 30 * granted within the window does not exceed the quota. If the two windows are different, it resets 31 * the quota. 32 */ 33 public class QuotaLimiter { 34 35 private final Clock mClock; 36 private final Duration mWindowSize; 37 38 private int mMaxPermits; 39 private long mCurrentWindow; 40 private int mPermitsGranted; 41 42 @VisibleForTesting QuotaLimiter(Clock clock, Duration windowSize, int maxPermits)43 QuotaLimiter(Clock clock, Duration windowSize, int maxPermits) { 44 mClock = clock; 45 mWindowSize = windowSize; 46 mMaxPermits = maxPermits; 47 } 48 QuotaLimiter(Duration windowSize, int maxPermits)49 public QuotaLimiter(Duration windowSize, int maxPermits) { 50 this(Clock.SYSTEM_CLOCK, windowSize, maxPermits); 51 } 52 QuotaLimiter(int maxPermitsPerDay)53 public QuotaLimiter(int maxPermitsPerDay) { 54 this(Clock.SYSTEM_CLOCK, Duration.ofDays(1), maxPermitsPerDay); 55 } 56 57 /** 58 * Acquires a permit if there is one available in the current time window. 59 * 60 * @return true if a permit was acquired. 61 */ acquire()62 boolean acquire() { 63 long nowWindow = 64 Duration.between(Instant.EPOCH, Instant.ofEpochMilli(mClock.currentTimeMillis())) 65 .dividedBy(mWindowSize); 66 if (nowWindow > mCurrentWindow) { 67 mCurrentWindow = nowWindow; 68 mPermitsGranted = 0; 69 } 70 71 if (mPermitsGranted < mMaxPermits) { 72 mPermitsGranted++; 73 return true; 74 } 75 76 return false; 77 } 78 setMaxPermits(int maxPermits)79 public void setMaxPermits(int maxPermits) { 80 this.mMaxPermits = maxPermits; 81 } 82 } 83