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