1 /* 2 * Copyright (C) 2022 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 17 package com.android.adservices.service.common; 18 19 import android.annotation.NonNull; 20 import android.os.Binder; 21 import android.util.Pair; 22 23 import com.android.adservices.service.Flags; 24 import com.android.internal.annotations.VisibleForTesting; 25 26 import com.google.common.util.concurrent.RateLimiter; 27 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.Objects; 31 import java.util.concurrent.ConcurrentHashMap; 32 33 /** Class to throttle PPAPI requests. */ 34 public class Throttler { 35 // Enum for each PP API or entry point that will be throttled. 36 public enum ApiKey { 37 UNKNOWN, 38 39 // Key to throttle AdId API, based on app package name. 40 ADID_API_APP_PACKAGE_NAME, 41 42 // Key to throttle AppSetId API, based on app package name. 43 APPSETID_API_APP_PACKAGE_NAME, 44 45 // Key to throttle Join Custom Audience API 46 FLEDGE_API_JOIN_CUSTOM_AUDIENCE, 47 48 // Key to throttle Fetch Custom Audience API 49 FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 50 51 // Key to throttle Leave Custom Audience API 52 FLEDGE_API_LEAVE_CUSTOM_AUDIENCE, 53 54 // Key to throttle Report impressions API 55 FLEDGE_API_REPORT_IMPRESSIONS, 56 57 // Key to throttle Report impressions API 58 FLEDGE_API_REPORT_INTERACTION, 59 60 // Key to throttle Select Ads API 61 FLEDGE_API_SELECT_ADS, 62 63 // Key to throttle Get Ad Selection Data API 64 FLEDGE_API_GET_AD_SELECTION_DATA, 65 // Key to throttle Persist Ad Selection Result API 66 FLEDGE_API_PERSIST_AD_SELECTION_RESULT, 67 68 // Key to throttle Schedule Custom Audience Update API 69 FLEDGE_API_SCHEDULE_CUSTOM_AUDIENCE_UPDATE, 70 71 // Key to throttle Set App Install Advertisers API 72 FLEDGE_API_SET_APP_INSTALL_ADVERTISERS, 73 74 // Key to throttle FLEDGE updateAdCounterHistogram API 75 FLEDGE_API_UPDATE_AD_COUNTER_HISTOGRAM, 76 77 // Key to throttle Measurement Deletion Registration API 78 MEASUREMENT_API_DELETION_REGISTRATION, 79 80 // Key to throttle Measurement Register Source API 81 MEASUREMENT_API_REGISTER_SOURCE, 82 83 // Key to throttle Measurement Register Trigger API 84 MEASUREMENT_API_REGISTER_TRIGGER, 85 86 // Key to throttle Measurement Register Web Source API 87 MEASUREMENT_API_REGISTER_WEB_SOURCE, 88 89 // Key to throttle Measurement Register Web Trigger API 90 MEASUREMENT_API_REGISTER_WEB_TRIGGER, 91 92 // Key to throttle Measurement Register Sources API 93 MEASUREMENT_API_REGISTER_SOURCES, 94 // Key to throttle updateSignals API 95 PROTECTED_SIGNAL_API_UPDATE_SIGNALS, 96 97 // Key to throttle Topics API based on the App Package Name. 98 TOPICS_API_APP_PACKAGE_NAME, 99 100 // Key to throttle Topics API based on the Sdk Name. 101 TOPICS_API_SDK_NAME, 102 } 103 104 private static volatile Throttler sSingleton; 105 private static final double DEFAULT_RATE_LIMIT = 1d; 106 107 // A Map from a Pair<ApiKey, Requester> to its RateLimiter. 108 // The Requester could be a SdkName or an AppPackageName depending on the rate limiting needs. 109 // Example Pair<TOPICS_API, "SomeSdkName">, Pair<TOPICS_API, "SomePackageName">. 110 private final ConcurrentHashMap<Pair<ApiKey, String>, RateLimiter> mSdkRateLimitMap = 111 new ConcurrentHashMap<>(); 112 113 // Used as a configuration to determine the rate limit per API 114 // Example: 115 // - TOPICS_API_SDK_NAME, 1 request per second 116 // - MEASUREMENT_API_REGISTER_SOURCE, 5 requests per second 117 private final Map<ApiKey, Double> mRateLimitPerApiMap = new HashMap<>(); 118 119 /** Returns the singleton instance of the Throttler. */ 120 @NonNull getInstance(@onNull Flags flags)121 public static Throttler getInstance(@NonNull Flags flags) { 122 Objects.requireNonNull(flags); 123 synchronized (Throttler.class) { 124 if (null == sSingleton) { 125 // Clearing calling identity to check device config permission read by flags on the 126 // local process and not on the process called. Once the device configs are read, 127 // restore calling identity. 128 final long token = Binder.clearCallingIdentity(); 129 sSingleton = new Throttler(flags); 130 Binder.restoreCallingIdentity(token); 131 } 132 return sSingleton; 133 } 134 } 135 136 @VisibleForTesting Throttler(Flags flags)137 Throttler(Flags flags) { 138 setRateLimitPerApiMap(flags); 139 } 140 141 /** 142 * The throttler is a Singleton and does not allow changing the rate limits once initialised, 143 * therefore it is not feasible to test different throttling policies across tests without 144 * destroying the previous instance. Intended to be used in test cases only. 145 */ 146 @VisibleForTesting destroyExistingThrottler()147 public static void destroyExistingThrottler() { 148 sSingleton = null; 149 } 150 151 /** 152 * Acquires a permit for an API and a Requester if it can be acquired immediately without delay. 153 * Example: {@code tryAcquire(TOPICS_API, "SomeSdkName") } 154 * 155 * @return {@code true} if the permit was acquired, {@code false} otherwise 156 */ tryAcquire(ApiKey apiKey, String requester)157 public boolean tryAcquire(ApiKey apiKey, String requester) { 158 // Negative Permits Per Second turns off rate limiting. 159 final double permitsPerSecond = 160 mRateLimitPerApiMap.getOrDefault(apiKey, DEFAULT_RATE_LIMIT); 161 if (permitsPerSecond <= 0) { 162 return true; 163 } 164 165 final RateLimiter rateLimiter = 166 mSdkRateLimitMap.computeIfAbsent( 167 Pair.create(apiKey, requester), ignored -> create(permitsPerSecond)); 168 169 return rateLimiter.tryAcquire(); 170 } 171 172 /** Configures permits per second per {@link ApiKey} */ setRateLimitPerApiMap(Flags flags)173 private void setRateLimitPerApiMap(Flags flags) { 174 final double defaultPermitsPerSecond = flags.getSdkRequestPermitsPerSecond(); 175 final double adIdPermitsPerSecond = flags.getAdIdRequestPermitsPerSecond(); 176 final double appSetIdPermitsPerSecond = flags.getAppSetIdRequestPermitsPerSecond(); 177 final double registerSource = flags.getMeasurementRegisterSourceRequestPermitsPerSecond(); 178 final double registerWebSource = 179 flags.getMeasurementRegisterWebSourceRequestPermitsPerSecond(); 180 final double registerSources = flags.getMeasurementRegisterSourcesRequestPermitsPerSecond(); 181 final double registerTrigger = flags.getMeasurementRegisterTriggerRequestPermitsPerSecond(); 182 final double registerWebTrigger = 183 flags.getMeasurementRegisterWebTriggerRequestPermitsPerSecond(); 184 final double topicsApiAppRequestPermitsPerSecond = 185 flags.getTopicsApiAppRequestPermitsPerSecond(); 186 final double topicsApiSdkRequestPermitsPerSecond = 187 flags.getTopicsApiSdkRequestPermitsPerSecond(); 188 189 mRateLimitPerApiMap.put(ApiKey.UNKNOWN, defaultPermitsPerSecond); 190 191 mRateLimitPerApiMap.put(ApiKey.ADID_API_APP_PACKAGE_NAME, adIdPermitsPerSecond); 192 mRateLimitPerApiMap.put(ApiKey.APPSETID_API_APP_PACKAGE_NAME, appSetIdPermitsPerSecond); 193 194 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_JOIN_CUSTOM_AUDIENCE, defaultPermitsPerSecond); 195 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_LEAVE_CUSTOM_AUDIENCE, defaultPermitsPerSecond); 196 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_REPORT_IMPRESSIONS, defaultPermitsPerSecond); 197 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_REPORT_INTERACTION, defaultPermitsPerSecond); 198 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_SELECT_ADS, defaultPermitsPerSecond); 199 mRateLimitPerApiMap.put(ApiKey.FLEDGE_API_GET_AD_SELECTION_DATA, defaultPermitsPerSecond); 200 mRateLimitPerApiMap.put( 201 ApiKey.FLEDGE_API_UPDATE_AD_COUNTER_HISTOGRAM, defaultPermitsPerSecond); 202 203 mRateLimitPerApiMap.put( 204 ApiKey.PROTECTED_SIGNAL_API_UPDATE_SIGNALS, defaultPermitsPerSecond); 205 206 mRateLimitPerApiMap.put( 207 ApiKey.MEASUREMENT_API_DELETION_REGISTRATION, defaultPermitsPerSecond); 208 mRateLimitPerApiMap.put(ApiKey.MEASUREMENT_API_REGISTER_SOURCE, registerSource); 209 mRateLimitPerApiMap.put(ApiKey.MEASUREMENT_API_REGISTER_TRIGGER, registerTrigger); 210 mRateLimitPerApiMap.put(ApiKey.MEASUREMENT_API_REGISTER_WEB_SOURCE, registerWebSource); 211 mRateLimitPerApiMap.put(ApiKey.MEASUREMENT_API_REGISTER_WEB_TRIGGER, registerWebTrigger); 212 mRateLimitPerApiMap.put(ApiKey.MEASUREMENT_API_REGISTER_SOURCES, registerSources); 213 214 mRateLimitPerApiMap.put( 215 ApiKey.TOPICS_API_APP_PACKAGE_NAME, topicsApiAppRequestPermitsPerSecond); 216 mRateLimitPerApiMap.put(ApiKey.TOPICS_API_SDK_NAME, topicsApiSdkRequestPermitsPerSecond); 217 } 218 219 /** 220 * Creates a Burst RateLimiter. This is a workaround since Guava does not support RateLimiter 221 * with initial Burst. 222 * 223 * <p>The RateLimiter is created with {@link Double#POSITIVE_INFINITY} to open all permit slots 224 * immediately. It is immediately overridden to the expected rate based on the permitsPerSecond 225 * parameter. Then {@link RateLimiter#tryAcquire()} is called to use the first acquisition so 226 * the expected bursting rate could kick in on the following calls. This flow enables initial 227 * bursting, multiple simultaneous permits would be acquired as soon as RateLimiter is created. 228 * Otherwise, if only {@link RateLimiter#create(double)} is called, after the 1st call 229 * subsequent request would have to be spread out evenly over 1 second. 230 */ create(double permitsPerSecond)231 private RateLimiter create(double permitsPerSecond) { 232 RateLimiter rateLimiter = RateLimiter.create(Double.POSITIVE_INFINITY); 233 rateLimiter.setRate(permitsPerSecond); 234 boolean unused = rateLimiter.tryAcquire(); 235 return rateLimiter; 236 } 237 } 238