• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.customaudience;
18 
19 import static android.adservices.common.AdServicesStatusUtils.STATUS_BACKGROUND_CALLER;
20 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED;
21 import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR;
22 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT;
23 import static android.adservices.common.AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED;
24 import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED;
25 import static android.adservices.common.AdServicesStatusUtils.StatusCode;
26 
27 import static com.android.adservices.service.common.Throttler.ApiKey.FLEDGE_API_JOIN_CUSTOM_AUDIENCE;
28 import static com.android.adservices.service.common.Throttler.ApiKey.FLEDGE_API_LEAVE_CUSTOM_AUDIENCE;
29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE;
30 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE;
31 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE;
32 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO;
33 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REMOVE_CUSTOM_AUDIENCE_REMOTE_INFO_OVERRIDE;
34 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES;
35 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE;
36 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_GET_CALLING_UID_ILLEGAL_STATE;
37 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_BACKGROUND_CALLER;
38 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_CALLER_NOT_ALLOWED;
39 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_INTERNAL_ERROR;
40 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_INVALID_ARGUMENT;
41 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_RATE_LIMIT_REACHED;
42 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_UNAUTHORIZED;
43 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_SUCCESS_TO_CALLER_FAILED;
44 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NULL_ARGUMENT;
45 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE;
46 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__JOIN_CUSTOM_AUDIENCE;
47 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__LEAVE_CUSTOM_AUDIENCE;
48 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE;
49 
50 import android.adservices.common.AdSelectionSignals;
51 import android.adservices.common.AdServicesPermissions;
52 import android.adservices.common.AdServicesStatusUtils;
53 import android.adservices.common.AdTechIdentifier;
54 import android.adservices.common.FledgeErrorResponse;
55 import android.adservices.customaudience.CustomAudience;
56 import android.adservices.customaudience.CustomAudienceOverrideCallback;
57 import android.adservices.customaudience.FetchAndJoinCustomAudienceCallback;
58 import android.adservices.customaudience.FetchAndJoinCustomAudienceInput;
59 import android.adservices.customaudience.ICustomAudienceCallback;
60 import android.adservices.customaudience.ICustomAudienceService;
61 import android.adservices.customaudience.ScheduleCustomAudienceUpdateCallback;
62 import android.adservices.customaudience.ScheduleCustomAudienceUpdateInput;
63 import android.annotation.NonNull;
64 import android.content.Context;
65 import android.os.Build;
66 import android.os.LimitExceededException;
67 import android.os.RemoteException;
68 
69 import androidx.annotation.RequiresApi;
70 
71 import com.android.adservices.LoggerFactory;
72 import com.android.adservices.concurrency.AdServicesExecutors;
73 import com.android.adservices.data.adselection.SharedStorageDatabase;
74 import com.android.adservices.data.customaudience.AdDataConversionStrategyFactory;
75 import com.android.adservices.data.customaudience.CustomAudienceDao;
76 import com.android.adservices.errorlogging.ErrorLogUtil;
77 import com.android.adservices.service.DebugFlags;
78 import com.android.adservices.service.Flags;
79 import com.android.adservices.service.FlagsFactory;
80 import com.android.adservices.service.adselection.AdFilteringFeatureFactory;
81 import com.android.adservices.service.common.AdRenderIdValidator;
82 import com.android.adservices.service.common.AppImportanceFilter;
83 import com.android.adservices.service.common.AppImportanceFilter.WrongCallingApplicationStateException;
84 import com.android.adservices.service.common.BinderFlagReader;
85 import com.android.adservices.service.common.CallingAppUidSupplier;
86 import com.android.adservices.service.common.CallingAppUidSupplierBinderImpl;
87 import com.android.adservices.service.common.CustomAudienceServiceFilter;
88 import com.android.adservices.service.common.FledgeAllowListsFilter;
89 import com.android.adservices.service.common.FledgeApiThrottleFilter;
90 import com.android.adservices.service.common.FledgeAuthorizationFilter;
91 import com.android.adservices.service.common.FledgeConsentFilter;
92 import com.android.adservices.service.common.Throttler;
93 import com.android.adservices.service.common.cache.CacheProviderFactory;
94 import com.android.adservices.service.common.httpclient.AdServicesHttpsClient;
95 import com.android.adservices.service.consent.ConsentManager;
96 import com.android.adservices.service.devapi.CustomAudienceOverrider;
97 import com.android.adservices.service.devapi.DevContext;
98 import com.android.adservices.service.devapi.DevContextFilter;
99 import com.android.adservices.service.stats.AdServicesLogger;
100 import com.android.adservices.service.stats.AdServicesLoggerImpl;
101 import com.android.adservices.service.stats.AdsRelevanceStatusUtils;
102 import com.android.internal.annotations.VisibleForTesting;
103 
104 import java.time.Clock;
105 import java.util.Objects;
106 import java.util.concurrent.ExecutorService;
107 
108 /** Implementation of the Custom Audience service. */
109 @RequiresApi(Build.VERSION_CODES.S)
110 public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub {
111     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
112     @NonNull private final Context mContext;
113     @NonNull private final CustomAudienceImpl mCustomAudienceImpl;
114     @NonNull private final FledgeAuthorizationFilter mFledgeAuthorizationFilter;
115     @NonNull private final ConsentManager mConsentManager;
116     @NonNull private final ExecutorService mExecutorService;
117     @NonNull private final DevContextFilter mDevContextFilter;
118     @NonNull private final AdServicesLogger mAdServicesLogger;
119     @NonNull private final AppImportanceFilter mAppImportanceFilter;
120     @NonNull private final Flags mFlags;
121     @NonNull private final DebugFlags mDebugFlags;
122     @NonNull private final CallingAppUidSupplier mCallingAppUidSupplier;
123 
124     @NonNull private final CustomAudienceServiceFilter mCustomAudienceServiceFilter;
125     @NonNull private final AdFilteringFeatureFactory mAdFilteringFeatureFactory;
126 
127     private static final String API_NOT_AUTHORIZED_MSG =
128             "This API is not enabled for the given app because either dev options are disabled or"
129                     + " the app is not debuggable.";
130 
CustomAudienceServiceImpl(@onNull Context context)131     private CustomAudienceServiceImpl(@NonNull Context context) {
132         this(
133                 context,
134                 CustomAudienceImpl.getInstance(),
135                 FledgeAuthorizationFilter.create(context, AdServicesLoggerImpl.getInstance()),
136                 ConsentManager.getInstance(),
137                 DevContextFilter.create(
138                         context,
139                         BinderFlagReader.readFlag(
140                                 () ->
141                                         DebugFlags.getInstance()
142                                                 .getDeveloperSessionFeatureEnabled())),
143                 AdServicesExecutors.getBackgroundExecutor(),
144                 AdServicesLoggerImpl.getInstance(),
145                 AppImportanceFilter.create(
146                         context,
147                         () -> FlagsFactory.getFlags().getForegroundStatuslLevelForValidation()),
148                 FlagsFactory.getFlags(),
149                 DebugFlags.getInstance(),
150                 CallingAppUidSupplierBinderImpl.create(),
151                 new CustomAudienceServiceFilter(
152                         context,
153                         new FledgeConsentFilter(
154                                 ConsentManager.getInstance(), AdServicesLoggerImpl.getInstance()),
155                         FlagsFactory.getFlags(),
156                         AppImportanceFilter.create(
157                                 context,
158                                 () ->
159                                         FlagsFactory.getFlags()
160                                                 .getForegroundStatuslLevelForValidation()),
161                         FledgeAuthorizationFilter.create(
162                                 context, AdServicesLoggerImpl.getInstance()),
163                         new FledgeAllowListsFilter(
164                                 FlagsFactory.getFlags(), AdServicesLoggerImpl.getInstance()),
165                         new FledgeApiThrottleFilter(
166                                 Throttler.getInstance(), AdServicesLoggerImpl.getInstance())),
167                 new AdFilteringFeatureFactory(
168                         SharedStorageDatabase.getInstance().appInstallDao(),
169                         SharedStorageDatabase.getInstance().frequencyCapDao(),
170                         FlagsFactory.getFlags()));
171     }
172 
173     /** Creates a new instance of {@link CustomAudienceServiceImpl}. */
174     // TODO(b/311183933): Remove passed in Context from static method.
175     @SuppressWarnings("AvoidStaticContext")
create(@onNull Context context)176     public static CustomAudienceServiceImpl create(@NonNull Context context) {
177         return new CustomAudienceServiceImpl(context);
178     }
179 
180     @VisibleForTesting
CustomAudienceServiceImpl( @onNull Context context, @NonNull CustomAudienceImpl customAudienceImpl, @NonNull FledgeAuthorizationFilter fledgeAuthorizationFilter, @NonNull ConsentManager consentManager, @NonNull DevContextFilter devContextFilter, @NonNull ExecutorService executorService, @NonNull AdServicesLogger adServicesLogger, @NonNull AppImportanceFilter appImportanceFilter, @NonNull Flags flags, @NonNull DebugFlags debugFlags, @NonNull CallingAppUidSupplier callingAppUidSupplier, @NonNull CustomAudienceServiceFilter customAudienceServiceFilter, @NonNull AdFilteringFeatureFactory adFilteringFeatureFactory)181     public CustomAudienceServiceImpl(
182             @NonNull Context context,
183             @NonNull CustomAudienceImpl customAudienceImpl,
184             @NonNull FledgeAuthorizationFilter fledgeAuthorizationFilter,
185             @NonNull ConsentManager consentManager,
186             @NonNull DevContextFilter devContextFilter,
187             @NonNull ExecutorService executorService,
188             @NonNull AdServicesLogger adServicesLogger,
189             @NonNull AppImportanceFilter appImportanceFilter,
190             @NonNull Flags flags,
191             @NonNull DebugFlags debugFlags,
192             @NonNull CallingAppUidSupplier callingAppUidSupplier,
193             @NonNull CustomAudienceServiceFilter customAudienceServiceFilter,
194             @NonNull AdFilteringFeatureFactory adFilteringFeatureFactory) {
195         Objects.requireNonNull(context);
196         Objects.requireNonNull(customAudienceImpl);
197         Objects.requireNonNull(fledgeAuthorizationFilter);
198         Objects.requireNonNull(consentManager);
199         Objects.requireNonNull(executorService);
200         Objects.requireNonNull(adServicesLogger);
201         Objects.requireNonNull(appImportanceFilter);
202         Objects.requireNonNull(customAudienceServiceFilter);
203 
204         mContext = context;
205         mCustomAudienceImpl = customAudienceImpl;
206         mFledgeAuthorizationFilter = fledgeAuthorizationFilter;
207         mConsentManager = consentManager;
208         mDevContextFilter = devContextFilter;
209         mExecutorService = executorService;
210         mAdServicesLogger = adServicesLogger;
211         mAppImportanceFilter = appImportanceFilter;
212         mFlags = flags;
213         mDebugFlags = debugFlags;
214         mCallingAppUidSupplier = callingAppUidSupplier;
215         mCustomAudienceServiceFilter = customAudienceServiceFilter;
216         mAdFilteringFeatureFactory = adFilteringFeatureFactory;
217     }
218 
219     /**
220      * Adds a user to a custom audience.
221      *
222      * @hide
223      */
224     @Override
joinCustomAudience( @onNull CustomAudience customAudience, @NonNull String ownerPackageName, @NonNull ICustomAudienceCallback callback)225     public void joinCustomAudience(
226             @NonNull CustomAudience customAudience,
227             @NonNull String ownerPackageName,
228             @NonNull ICustomAudienceCallback callback) {
229         sLogger.v("Entering joinCustomAudience");
230 
231         final int apiName = AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE;
232 
233         try {
234             Objects.requireNonNull(customAudience);
235             Objects.requireNonNull(ownerPackageName);
236             Objects.requireNonNull(callback);
237         } catch (NullPointerException exception) {
238             mAdServicesLogger.logFledgeApiCallStats(
239                     apiName, ownerPackageName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
240             AdsRelevanceStatusUtils.logCelInsideBinderThread(
241                     exception,
242                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NULL_ARGUMENT,
243                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__JOIN_CUSTOM_AUDIENCE);
244             // Rethrow because we want to fail fast
245             throw exception;
246         }
247 
248         // Caller permissions must be checked in the binder thread, before anything else
249         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
250                 mContext,
251                 ownerPackageName,
252                 apiName,
253                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
254 
255         final int callerUid = getCallingUid(apiName, ownerPackageName);
256         final DevContext devContext = mDevContextFilter.createDevContext();
257         sLogger.v("Running service");
258         mExecutorService.execute(
259                 () ->
260                         doJoinCustomAudience(
261                                 customAudience, ownerPackageName, callback, callerUid, devContext));
262     }
263 
264     /** Try to join the custom audience and signal back to the caller using the callback. */
doJoinCustomAudience( @onNull CustomAudience customAudience, @NonNull String ownerPackageName, @NonNull ICustomAudienceCallback callback, final int callerUid, @NonNull final DevContext devContext)265     private void doJoinCustomAudience(
266             @NonNull CustomAudience customAudience,
267             @NonNull String ownerPackageName,
268             @NonNull ICustomAudienceCallback callback,
269             final int callerUid,
270             @NonNull final DevContext devContext) {
271         Objects.requireNonNull(customAudience);
272         Objects.requireNonNull(ownerPackageName);
273         Objects.requireNonNull(callback);
274         Objects.requireNonNull(devContext);
275 
276         sLogger.v("Entering doJoinCustomAudience");
277 
278         final int apiName = AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE;
279         int resultCode = AdServicesStatusUtils.STATUS_UNSET;
280         // The filters log internally, so don't accidentally log again
281         boolean shouldLog = false;
282         try {
283             try {
284                 // Filter and validate request
285                 mCustomAudienceServiceFilter.filterRequest(
286                         customAudience.getBuyer(),
287                         ownerPackageName,
288                         mFlags.getEnforceForegroundStatusForFledgeCustomAudience(),
289                         false,
290                         !mDebugFlags.getConsentNotificationDebugMode(),
291                         callerUid,
292                         apiName,
293                         FLEDGE_API_JOIN_CUSTOM_AUDIENCE,
294                         devContext);
295 
296                 shouldLog = true;
297 
298                 // Fail silently for revoked user consent
299                 if (!mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse(
300                         ownerPackageName)) {
301                     sLogger.v("Joining custom audience");
302                     mCustomAudienceImpl.joinCustomAudience(
303                             customAudience, ownerPackageName, devContext);
304                     BackgroundFetchJob.schedule(mFlags);
305                     resultCode = AdServicesStatusUtils.STATUS_SUCCESS;
306                 } else {
307                     sLogger.v("Consent revoked");
308                     resultCode = AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED;
309                 }
310             } catch (Exception exception) {
311                 sLogger.d(exception, "Error encountered in joinCustomAudience, notifying caller");
312                 resultCode =
313                         notifyFailure(
314                                 callback,
315                                 exception,
316                                 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__JOIN_CUSTOM_AUDIENCE);
317                 return;
318             }
319 
320             callback.onSuccess();
321         } catch (Exception exception) {
322             sLogger.e(exception, "Unable to send result to the callback");
323             resultCode = STATUS_INTERNAL_ERROR;
324             ErrorLogUtil.e(
325                     exception,
326                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_SUCCESS_TO_CALLER_FAILED,
327                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__JOIN_CUSTOM_AUDIENCE);
328         } finally {
329             if (shouldLog) {
330                 mAdServicesLogger.logFledgeApiCallStats(
331                         apiName, ownerPackageName, resultCode, /* latencyMs= */ 0);
332             }
333         }
334     }
335 
336     /**
337      * Adds the user to the {@link CustomAudience} fetched from a {@code fetchUri}
338      *
339      * @hide
340      */
341     @Override
fetchAndJoinCustomAudience( @onNull FetchAndJoinCustomAudienceInput input, @NonNull FetchAndJoinCustomAudienceCallback callback)342     public void fetchAndJoinCustomAudience(
343             @NonNull FetchAndJoinCustomAudienceInput input,
344             @NonNull FetchAndJoinCustomAudienceCallback callback) {
345         sLogger.v("Executing fetchAndJoinCustomAudience.");
346         final int apiName = AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE;
347 
348         // Failing fast if parameters are null.
349         try {
350             Objects.requireNonNull(input);
351             Objects.requireNonNull(callback);
352         } catch (NullPointerException exception) {
353             mAdServicesLogger.logFledgeApiCallStats(
354                     apiName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
355             AdsRelevanceStatusUtils.logCelInsideBinderThread(
356                     exception,
357                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NULL_ARGUMENT,
358                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE);
359             // Rethrow because we want to fail fast
360             throw exception;
361         }
362 
363         // Caller permissions must be checked in the binder thread, before anything else
364         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
365                 mContext,
366                 input.getCallerPackageName(),
367                 apiName,
368                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
369 
370         final int callerUid = getCallingUid(apiName, input.getCallerPackageName());
371         final DevContext devContext = mDevContextFilter.createDevContext();
372         mExecutorService.execute(
373                 () -> {
374                     FetchCustomAudienceImpl impl =
375                             new FetchCustomAudienceImpl(
376                                     mFlags,
377                                     mDebugFlags,
378                                     // TODO(b/235841960): Align on internal Clock usage.
379                                     Clock.systemUTC(),
380                                     mAdServicesLogger,
381                                     mExecutorService,
382                                     mCustomAudienceImpl.getCustomAudienceDao(),
383                                     callerUid,
384                                     mCustomAudienceServiceFilter,
385                                     new AdServicesHttpsClient(
386                                             AdServicesExecutors.getBlockingExecutor(),
387                                             CacheProviderFactory.createNoOpCache()),
388                                     mAdFilteringFeatureFactory.getFrequencyCapAdDataValidator(),
389                                     AdRenderIdValidator.createInstance(mFlags),
390                                     AdDataConversionStrategyFactory.getAdDataConversionStrategy(
391                                             mFlags.getFledgeFrequencyCapFilteringEnabled(),
392                                             mFlags.getFledgeAppInstallFilteringEnabled(),
393                                             mFlags.getFledgeAuctionServerAdRenderIdEnabled()),
394                                     ComponentAdsStrategy.createInstance(
395                                             mFlags.getEnableCustomAudienceComponentAds(),
396                                             new ComponentAdsListValidator(
397                                                     mFlags.getComponentAdRenderIdMaxLengthBytes(),
398                                                     mFlags.getMaxComponentAdsPerCustomAudience())));
399 
400                     impl.doFetchCustomAudience(input, callback, devContext);
401                 });
402     }
403 
notifyFailure( ICustomAudienceCallback callback, Exception exception, int celPpapiNameId)404     private int notifyFailure(
405             ICustomAudienceCallback callback, Exception exception, int celPpapiNameId)
406             throws RemoteException {
407         int resultCode;
408         if (exception instanceof NullPointerException
409                 || exception instanceof IllegalArgumentException) {
410             resultCode = STATUS_INVALID_ARGUMENT;
411         } else if (exception instanceof WrongCallingApplicationStateException) {
412             resultCode = AdServicesStatusUtils.STATUS_BACKGROUND_CALLER;
413         } else if (exception instanceof FledgeAuthorizationFilter.CallerMismatchException) {
414             resultCode = AdServicesStatusUtils.STATUS_UNAUTHORIZED;
415         } else if (exception instanceof FledgeAuthorizationFilter.AdTechNotAllowedException
416                 || exception instanceof FledgeAllowListsFilter.AppNotAllowedException) {
417             resultCode = AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED;
418         } else if (exception instanceof LimitExceededException) {
419             resultCode = AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED;
420         } else if (exception instanceof IllegalStateException) {
421             resultCode = STATUS_INTERNAL_ERROR;
422         } else {
423             sLogger.e(exception, "Unexpected error during operation");
424             resultCode = STATUS_INTERNAL_ERROR;
425         }
426         logExceptionCel(exception, resultCode, celPpapiNameId);
427         callback.onFailure(
428                 new FledgeErrorResponse.Builder()
429                         .setStatusCode(resultCode)
430                         .setErrorMessage(exception.getMessage())
431                         .build());
432         return resultCode;
433     }
434 
435     @Override
scheduleCustomAudienceUpdate( ScheduleCustomAudienceUpdateInput input, ScheduleCustomAudienceUpdateCallback callback)436     public void scheduleCustomAudienceUpdate(
437             ScheduleCustomAudienceUpdateInput input,
438             ScheduleCustomAudienceUpdateCallback callback) {
439         final int apiName = AD_SERVICES_API_CALLED__API_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE;
440         try {
441             Objects.requireNonNull(input);
442             Objects.requireNonNull(callback);
443         } catch (NullPointerException exception) {
444             mAdServicesLogger.logFledgeApiCallStats(
445                     apiName,
446                     input.getCallerPackageName(),
447                     STATUS_INVALID_ARGUMENT,
448                     /* latencyMs= */ 0);
449             AdsRelevanceStatusUtils.logCelInsideBinderThread(
450                     exception,
451                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NULL_ARGUMENT,
452                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__SCHEDULE_CUSTOM_AUDIENCE_UPDATE);
453 
454             // Rethrow because we want to fail fast
455             throw exception;
456         }
457 
458         // Caller permissions must be checked in the binder thread, before anything else
459         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
460                 mContext,
461                 input.getCallerPackageName(),
462                 apiName,
463                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
464 
465         final int callerUid = getCallingUid(apiName, input.getCallerPackageName());
466         final DevContext devContext = mDevContextFilter.createDevContext();
467 
468         mExecutorService.execute(
469                 () -> {
470                     ScheduleCustomAudienceUpdateImpl impl =
471                             new ScheduleCustomAudienceUpdateImpl(
472                                     mContext,
473                                     mConsentManager,
474                                     callerUid,
475                                     mFlags,
476                                     mDebugFlags,
477                                     mAdServicesLogger,
478                                     AdServicesExecutors.getBackgroundExecutor(),
479                                     mCustomAudienceServiceFilter,
480                                     mCustomAudienceImpl.getCustomAudienceDao());
481 
482                     impl.doScheduleCustomAudienceUpdate(input, callback, devContext);
483                 });
484     }
485 
486     /**
487      * Attempts to remove a user from a custom audience.
488      *
489      * @hide
490      */
491     @Override
leaveCustomAudience( @onNull String ownerPackageName, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull ICustomAudienceCallback callback)492     public void leaveCustomAudience(
493             @NonNull String ownerPackageName,
494             @NonNull AdTechIdentifier buyer,
495             @NonNull String name,
496             @NonNull ICustomAudienceCallback callback) {
497         final int apiName = AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE;
498 
499         try {
500             Objects.requireNonNull(ownerPackageName);
501             Objects.requireNonNull(buyer);
502             Objects.requireNonNull(name);
503             Objects.requireNonNull(callback);
504         } catch (NullPointerException exception) {
505             mAdServicesLogger.logFledgeApiCallStats(
506                     apiName, ownerPackageName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
507             AdsRelevanceStatusUtils.logCelInsideBinderThread(
508                     exception,
509                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NULL_ARGUMENT,
510                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__LEAVE_CUSTOM_AUDIENCE);
511             // Rethrow because we want to fail fast
512             throw exception;
513         }
514 
515         // Caller permissions must be checked in the binder thread, before anything else
516         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
517                 mContext,
518                 ownerPackageName,
519                 apiName,
520                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
521 
522         final int callerUid = getCallingUid(apiName, ownerPackageName);
523         final DevContext devContext = mDevContextFilter.createDevContext();
524         mExecutorService.execute(
525                 () ->
526                         doLeaveCustomAudience(
527                                 ownerPackageName, buyer, name, callback, callerUid, devContext));
528     }
529 
530     /** Try to leave the custom audience and signal back to the caller using the callback. */
doLeaveCustomAudience( @onNull String ownerPackageName, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull ICustomAudienceCallback callback, final int callerUid, @NonNull final DevContext devContext)531     private void doLeaveCustomAudience(
532             @NonNull String ownerPackageName,
533             @NonNull AdTechIdentifier buyer,
534             @NonNull String name,
535             @NonNull ICustomAudienceCallback callback,
536             final int callerUid,
537             @NonNull final DevContext devContext) {
538         Objects.requireNonNull(ownerPackageName);
539         Objects.requireNonNull(buyer);
540         Objects.requireNonNull(name);
541         Objects.requireNonNull(callback);
542         Objects.requireNonNull(devContext);
543 
544         final int apiName = AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE;
545         int resultCode = AdServicesStatusUtils.STATUS_UNSET;
546         // The filters log internally, so don't accidentally log again
547         boolean shouldLog = false;
548         try {
549             try {
550                 // Filter and validate request
551                 mCustomAudienceServiceFilter.filterRequest(
552                         buyer,
553                         ownerPackageName,
554                         mFlags.getEnforceForegroundStatusForLeaveCustomAudience(),
555                         false,
556                         !mDebugFlags.getConsentNotificationDebugMode(),
557                         callerUid,
558                         apiName,
559                         FLEDGE_API_LEAVE_CUSTOM_AUDIENCE,
560                         devContext);
561 
562                 shouldLog = true;
563 
564                 // Fail silently for revoked user consent
565                 if (!mConsentManager.isFledgeConsentRevokedForApp(ownerPackageName)) {
566                     sLogger.v("Leaving custom audience");
567                     mCustomAudienceImpl.leaveCustomAudience(ownerPackageName, buyer, name);
568                     resultCode = AdServicesStatusUtils.STATUS_SUCCESS;
569                 } else {
570                     sLogger.v("Consent revoked");
571                     resultCode = AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED;
572                 }
573             } catch (WrongCallingApplicationStateException
574                     | LimitExceededException
575                     | FledgeAuthorizationFilter.CallerMismatchException
576                     | FledgeAuthorizationFilter.AdTechNotAllowedException
577                     | FledgeAllowListsFilter.AppNotAllowedException exception) {
578                 // Catch these specific exceptions, but report them back to the caller
579                 sLogger.d(exception, "Error encountered in leaveCustomAudience, notifying caller");
580                 resultCode =
581                         notifyFailure(
582                                 callback,
583                                 exception,
584                                 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__LEAVE_CUSTOM_AUDIENCE);
585                 return;
586             } catch (Exception exception) {
587                 // For all other exceptions, report success
588                 sLogger.e(exception, "Unexpected error leaving custom audience");
589                 resultCode = STATUS_INTERNAL_ERROR;
590             }
591 
592             callback.onSuccess();
593         } catch (Exception exception) {
594             sLogger.e(exception, "Unable to send result to the callback");
595             resultCode = STATUS_INTERNAL_ERROR;
596             ErrorLogUtil.e(
597                     exception,
598                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_SUCCESS_TO_CALLER_FAILED,
599                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__LEAVE_CUSTOM_AUDIENCE);
600         } finally {
601             if (shouldLog) {
602                 mAdServicesLogger.logFledgeApiCallStats(
603                         apiName, ownerPackageName, resultCode, /* latencyMs= */ 0);
604             }
605         }
606     }
607 
608     /**
609      * Adds a custom audience override with the given information.
610      *
611      * <p>If the owner does not match the calling package name, fail silently.
612      *
613      * @hide
614      */
615     @Override
overrideCustomAudienceRemoteInfo( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull String biddingLogicJS, long biddingLogicJsVersion, @NonNull AdSelectionSignals trustedBiddingSignals, @NonNull CustomAudienceOverrideCallback callback)616     public void overrideCustomAudienceRemoteInfo(
617             @NonNull String owner,
618             @NonNull AdTechIdentifier buyer,
619             @NonNull String name,
620             @NonNull String biddingLogicJS,
621             long biddingLogicJsVersion,
622             @NonNull AdSelectionSignals trustedBiddingSignals,
623             @NonNull CustomAudienceOverrideCallback callback) {
624         final int apiName = AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO;
625 
626         try {
627             Objects.requireNonNull(owner);
628             Objects.requireNonNull(buyer);
629             Objects.requireNonNull(name);
630             Objects.requireNonNull(biddingLogicJS);
631             Objects.requireNonNull(trustedBiddingSignals);
632             Objects.requireNonNull(callback);
633         } catch (NullPointerException exception) {
634             mAdServicesLogger.logFledgeApiCallStats(
635                     apiName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
636             // Rethrow to fail fast
637             throw exception;
638         }
639 
640         DevContext devContext = mDevContextFilter.createDevContext();
641 
642         if (!devContext.getDeviceDevOptionsEnabled()) {
643             mAdServicesLogger.logFledgeApiCallStats(
644                     apiName,
645                     devContext.getCallingAppPackageName(),
646                     STATUS_INTERNAL_ERROR,
647                     /* latencyMs= */ 0);
648             throw new SecurityException(API_NOT_AUTHORIZED_MSG);
649         }
650 
651         // Caller permissions must be checked with a non-null callingAppPackageName
652         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
653                 mContext,
654                 devContext.getCallingAppPackageName(),
655                 apiName,
656                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
657 
658         CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao();
659 
660         CustomAudienceOverrider overrider =
661                 new CustomAudienceOverrider(
662                         devContext,
663                         customAudienceDao,
664                         mExecutorService,
665                         mContext.getPackageManager(),
666                         mConsentManager,
667                         mAdServicesLogger,
668                         mAppImportanceFilter,
669                         mFlags);
670 
671         overrider.addOverride(
672                 owner,
673                 buyer,
674                 name,
675                 biddingLogicJS,
676                 biddingLogicJsVersion,
677                 trustedBiddingSignals,
678                 callback);
679     }
680 
681     /**
682      * Removes a custom audience override with the given information.
683      *
684      * @hide
685      */
686     @Override
removeCustomAudienceRemoteInfoOverride( @onNull String owner, @NonNull AdTechIdentifier buyer, @NonNull String name, @NonNull CustomAudienceOverrideCallback callback)687     public void removeCustomAudienceRemoteInfoOverride(
688             @NonNull String owner,
689             @NonNull AdTechIdentifier buyer,
690             @NonNull String name,
691             @NonNull CustomAudienceOverrideCallback callback) {
692         final int apiName =
693                 AD_SERVICES_API_CALLED__API_NAME__REMOVE_CUSTOM_AUDIENCE_REMOTE_INFO_OVERRIDE;
694 
695         try {
696             Objects.requireNonNull(owner);
697             Objects.requireNonNull(buyer);
698             Objects.requireNonNull(name);
699             Objects.requireNonNull(callback);
700         } catch (NullPointerException exception) {
701             mAdServicesLogger.logFledgeApiCallStats(
702                     apiName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
703             // Rethrow to fail fast
704             throw exception;
705         }
706 
707         DevContext devContext = mDevContextFilter.createDevContext();
708 
709         if (!devContext.getDeviceDevOptionsEnabled()) {
710             mAdServicesLogger.logFledgeApiCallStats(
711                     apiName,
712                     devContext.getCallingAppPackageName(),
713                     STATUS_INTERNAL_ERROR,
714                     /* latencyMs= */ 0);
715             throw new SecurityException(API_NOT_AUTHORIZED_MSG);
716         }
717 
718         // Caller permissions must be checked with a non-null callingAppPackageName
719         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
720                 mContext,
721                 devContext.getCallingAppPackageName(),
722                 apiName,
723                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
724 
725         CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao();
726 
727         CustomAudienceOverrider overrider =
728                 new CustomAudienceOverrider(
729                         devContext,
730                         customAudienceDao,
731                         mExecutorService,
732                         mContext.getPackageManager(),
733                         mConsentManager,
734                         mAdServicesLogger,
735                         mAppImportanceFilter,
736                         mFlags);
737 
738         overrider.removeOverride(owner, buyer, name, callback);
739     }
740 
741     /**
742      * Resets all custom audience overrides for a given caller.
743      *
744      * @hide
745      */
746     @Override
resetAllCustomAudienceOverrides(@onNull CustomAudienceOverrideCallback callback)747     public void resetAllCustomAudienceOverrides(@NonNull CustomAudienceOverrideCallback callback) {
748         final int apiName = AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES;
749 
750         try {
751             Objects.requireNonNull(callback);
752         } catch (NullPointerException exception) {
753             mAdServicesLogger.logFledgeApiCallStats(
754                     apiName, STATUS_INVALID_ARGUMENT, /* latencyMs= */ 0);
755             // Rethrow to fail fast
756             throw exception;
757         }
758 
759         final int callerUid = getCallingUid(apiName);
760 
761         DevContext devContext = mDevContextFilter.createDevContext();
762 
763         if (!devContext.getDeviceDevOptionsEnabled()) {
764             mAdServicesLogger.logFledgeApiCallStats(
765                     apiName,
766                     devContext.getCallingAppPackageName(),
767                     STATUS_INTERNAL_ERROR,
768                     /* latencyMs= */ 0);
769             throw new SecurityException(API_NOT_AUTHORIZED_MSG);
770         }
771 
772         // Caller permissions must be checked with a non-null callingAppPackageName
773         mFledgeAuthorizationFilter.assertAppDeclaredPermission(
774                 mContext,
775                 devContext.getCallingAppPackageName(),
776                 apiName,
777                 AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE);
778 
779         CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao();
780 
781         CustomAudienceOverrider overrider =
782                 new CustomAudienceOverrider(
783                         devContext,
784                         customAudienceDao,
785                         mExecutorService,
786                         mContext.getPackageManager(),
787                         mConsentManager,
788                         mAdServicesLogger,
789                         mAppImportanceFilter,
790                         mFlags);
791 
792         overrider.removeAllOverrides(callback, callerUid);
793     }
794 
getCallingUid(int apiNameLoggingId)795     private int getCallingUid(int apiNameLoggingId) throws IllegalStateException {
796         return getCallingUid(apiNameLoggingId, null);
797     }
798 
getCallingUid(int apiNameLoggingId, String callerAppPackageName)799     private int getCallingUid(int apiNameLoggingId, String callerAppPackageName)
800             throws IllegalStateException {
801         try {
802             return mCallingAppUidSupplier.getCallingAppUid();
803         } catch (IllegalStateException illegalStateException) {
804             mAdServicesLogger.logFledgeApiCallStats(
805                     apiNameLoggingId,
806                     callerAppPackageName,
807                     STATUS_INTERNAL_ERROR,
808                     /* latencyMs= */ 0);
809             AdsRelevanceStatusUtils.checkAndLogCelByApiNameLoggingId(
810                     illegalStateException,
811                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_GET_CALLING_UID_ILLEGAL_STATE,
812                     apiNameLoggingId);
813             throw illegalStateException;
814         }
815     }
816 
logExceptionCel( Exception exception, @StatusCode int resultCode, int celPpapiNameId)817     private void logExceptionCel(
818             Exception exception, @StatusCode int resultCode, int celPpapiNameId) {
819         int celEnum =
820                 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_INTERNAL_ERROR;
821         switch (resultCode) {
822             case STATUS_INVALID_ARGUMENT:
823                 celEnum =
824                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_INVALID_ARGUMENT;
825                 break;
826             case STATUS_BACKGROUND_CALLER:
827                 celEnum =
828                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_BACKGROUND_CALLER;
829                 break;
830             case STATUS_CALLER_NOT_ALLOWED:
831                 celEnum =
832                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_CALLER_NOT_ALLOWED;
833                 break;
834             case STATUS_UNAUTHORIZED:
835                 celEnum =
836                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_UNAUTHORIZED;
837                 break;
838             case STATUS_RATE_LIMIT_REACHED:
839                 celEnum =
840                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CUSTOM_AUDIENCE_SERVICE_NOTIFY_FAILURE_RATE_LIMIT_REACHED;
841                 break;
842         }
843         ErrorLogUtil.e(exception, celEnum, celPpapiNameId);
844     }
845 }
846