• 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.common;
18 
19 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED;
20 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND;
21 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION;
22 import static android.adservices.common.AdServicesStatusUtils.STATUS_PERMISSION_NOT_REQUESTED;
23 import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED;
24 
25 import static com.android.adservices.service.common.AppManifestConfigCall.API_AD_SELECTION;
26 import static com.android.adservices.service.common.AppManifestConfigCall.API_CUSTOM_AUDIENCES;
27 import static com.android.adservices.service.common.AppManifestConfigCall.API_PROTECTED_SIGNALS;
28 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_AD_TECH_NOT_AUTHORIZED_BY_APP;
29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ANY_PERMISSION_FAILURE;
30 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_DATA_MATCH_NOT_FOUND;
31 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_NOT_FOUND;
32 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_INVALID_API_TYPE;
33 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED;
34 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_FROM_URI_BLOCKED;
35 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NO_MATCH_PACKAGE_NAME;
36 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_PERMISSION_FAILURE;
37 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED;
38 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.checkAndLogCelByApiNameLoggingId;
39 
40 import android.adservices.common.AdServicesStatusUtils;
41 import android.adservices.common.AdTechIdentifier;
42 import android.annotation.NonNull;
43 import android.content.Context;
44 import android.content.pm.PackageManager;
45 import android.net.Uri;
46 import android.os.Build;
47 import android.util.Pair;
48 
49 import androidx.annotation.RequiresApi;
50 
51 import com.android.adservices.LoggerFactory;
52 import com.android.adservices.data.enrollment.EnrollmentDao;
53 import com.android.adservices.errorlogging.ErrorLogUtil;
54 import com.android.adservices.service.FlagsFactory;
55 import com.android.adservices.service.enrollment.EnrollmentData;
56 import com.android.adservices.service.enrollment.EnrollmentStatus;
57 import com.android.adservices.service.enrollment.EnrollmentUtil;
58 import com.android.adservices.service.stats.AdServicesLogger;
59 import com.android.adservices.service.stats.AdsRelevanceStatusUtils;
60 import com.android.internal.annotations.VisibleForTesting;
61 
62 import java.util.Collection;
63 import java.util.Locale;
64 import java.util.Objects;
65 
66 /** Verify caller of FLEDGE API has the permission of performing certain behaviour. */
67 @RequiresApi(Build.VERSION_CODES.S)
68 public class FledgeAuthorizationFilter {
69 
70     @VisibleForTesting public static final String INVALID_API_TYPE = "Invalid apiType: %d";
71     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
72     @NonNull private final PackageManager mPackageManager;
73     @NonNull private final EnrollmentDao mEnrollmentDao;
74     @NonNull private final AdServicesLogger mAdServicesLogger;
75     private EnrollmentUtil mEnrollmentUtil;
76 
77     @VisibleForTesting
FledgeAuthorizationFilter( @onNull PackageManager packageManager, @NonNull EnrollmentDao enrollmentDao, @NonNull AdServicesLogger adServicesLogger)78     public FledgeAuthorizationFilter(
79             @NonNull PackageManager packageManager,
80             @NonNull EnrollmentDao enrollmentDao,
81             @NonNull AdServicesLogger adServicesLogger) {
82         this(packageManager, enrollmentDao, adServicesLogger, null);
83     }
84 
85     @VisibleForTesting
FledgeAuthorizationFilter( @onNull PackageManager packageManager, @NonNull EnrollmentDao enrollmentDao, @NonNull AdServicesLogger adServicesLogger, EnrollmentUtil enrollmentUtil)86     public FledgeAuthorizationFilter(
87             @NonNull PackageManager packageManager,
88             @NonNull EnrollmentDao enrollmentDao,
89             @NonNull AdServicesLogger adServicesLogger,
90             EnrollmentUtil enrollmentUtil) {
91         Objects.requireNonNull(packageManager);
92         Objects.requireNonNull(enrollmentDao);
93         Objects.requireNonNull(adServicesLogger);
94 
95         mPackageManager = packageManager;
96         mEnrollmentDao = enrollmentDao;
97         mAdServicesLogger = adServicesLogger;
98         mEnrollmentUtil = enrollmentUtil;
99     }
100 
101     /** Creates an instance of {@link FledgeAuthorizationFilter}. */
102     // TODO(b/311183933): Remove passed in Context from static method.
103     @SuppressWarnings("AvoidStaticContext")
create( @onNull Context context, @NonNull AdServicesLogger adServicesLogger)104     public static FledgeAuthorizationFilter create(
105             @NonNull Context context, @NonNull AdServicesLogger adServicesLogger) {
106         Objects.requireNonNull(context);
107 
108         return new FledgeAuthorizationFilter(
109                 context.getPackageManager(),
110                 EnrollmentDao.getInstance(),
111                 adServicesLogger,
112                 EnrollmentUtil.getInstance());
113     }
114 
115     /**
116      * Check if the package name provided by the caller is one of the package of the calling uid.
117      *
118      * @param callingPackageName the caller-supplied package name
119      * @param callingUid the uid get from the Binder
120      * @param apiNameLoggingId the id of the api being called
121      * @throws CallerMismatchException if the package name provided does not associate with the uid.
122      */
assertCallingPackageName( @onNull String callingPackageName, int callingUid, int apiNameLoggingId)123     public void assertCallingPackageName(
124             @NonNull String callingPackageName, int callingUid, int apiNameLoggingId)
125             throws CallerMismatchException {
126         Objects.requireNonNull(callingPackageName);
127 
128         sLogger.v(
129                 "Asserting package name \"%s\" is valid for uid %d",
130                 callingPackageName, callingUid);
131 
132         String[] packageNamesForUid = mPackageManager.getPackagesForUid(callingUid);
133         for (String packageNameForUid : packageNamesForUid) {
134             sLogger.v("Candidate package name \"%s\"", packageNameForUid);
135             if (callingPackageName.equals(packageNameForUid)) return;
136         }
137 
138         sLogger.v("No match found, failing calling package name match in API %d", apiNameLoggingId);
139         mAdServicesLogger.logFledgeApiCallStats(
140                 apiNameLoggingId, callingPackageName, STATUS_UNAUTHORIZED, /*latencyMs=*/ 0);
141         int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId);
142         if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
143             ErrorLogUtil.e(
144                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NO_MATCH_PACKAGE_NAME,
145                     celApiNameId);
146         }
147         throw new CallerMismatchException();
148     }
149 
150     /**
151      * Check if the app had declared a permission, and throw an error if it has not
152      *
153      * @param context api service context
154      * @param apiNameLoggingId the id of the api being called
155      * @throws SecurityException if the package did not declare custom audience permission
156      */
assertAppDeclaredPermission( @onNull Context context, @NonNull String appPackageName, int apiNameLoggingId, @NonNull String permission)157     public void assertAppDeclaredPermission(
158             @NonNull Context context,
159             @NonNull String appPackageName,
160             int apiNameLoggingId,
161             @NonNull String permission)
162             throws SecurityException {
163         Objects.requireNonNull(context);
164         Objects.requireNonNull(appPackageName);
165         Objects.requireNonNull(permission);
166 
167         if (!PermissionHelper.hasPermission(context, appPackageName, permission)) {
168             logAndThrowPermissionFailure(appPackageName, apiNameLoggingId, permission);
169         }
170     }
171 
172     /**
173      * Check if the app had declared any of the listed permissions, and throw an error if it has not
174      *
175      * @param context api service context
176      * @param appPackageName the package name of the calling app
177      * @param apiNameLoggingId the id of the api being called
178      * @param permissions lists of permissions that allow calling the API
179      * @throws SecurityException if the package did not declare custom audience permission
180      */
assertAppDeclaredAnyPermission( @onNull Context context, @NonNull String appPackageName, int apiNameLoggingId, @NonNull Collection<String> permissions)181     public void assertAppDeclaredAnyPermission(
182             @NonNull Context context,
183             @NonNull String appPackageName,
184             int apiNameLoggingId,
185             @NonNull Collection<String> permissions)
186             throws SecurityException {
187         Objects.requireNonNull(context);
188         Objects.requireNonNull(appPackageName);
189         Objects.requireNonNull(permissions);
190 
191         for (String permission : permissions) {
192             if (PermissionHelper.hasPermission(context, appPackageName, permission)) {
193                 return; // Found a valid permission
194             }
195         }
196         logAndThrowMultiplePermissionFailure(appPackageName, apiNameLoggingId, permissions);
197     }
198 
logAndThrowPermissionFailure( String callerAppPackageName, int apiNameLoggingId, String permission)199     private void logAndThrowPermissionFailure(
200             String callerAppPackageName, int apiNameLoggingId, String permission) {
201         sLogger.v("Permission %s not declared by caller in API %d", permission, apiNameLoggingId);
202         mAdServicesLogger.logFledgeApiCallStats(
203                 apiNameLoggingId,
204                 callerAppPackageName,
205                 STATUS_PERMISSION_NOT_REQUESTED,
206                 /*latencyMs=*/ 0);
207         int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId);
208         if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
209             // TODO(b/376542959): replace this temporary solution for CEL inside Binder thread.
210             AdsRelevanceStatusUtils.logCelInsideBinderThread(
211                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_PERMISSION_FAILURE,
212                     celApiNameId);
213         }
214         throw new SecurityException(
215                 AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE);
216     }
217 
logAndThrowMultiplePermissionFailure( String callerAppPackageName, int apiNameLoggingId, Collection<String> permissions)218     private void logAndThrowMultiplePermissionFailure(
219             String callerAppPackageName, int apiNameLoggingId, Collection<String> permissions) {
220         sLogger.v("Permissions %s not declared by caller in API %d", permissions, apiNameLoggingId);
221         mAdServicesLogger.logFledgeApiCallStats(
222                 apiNameLoggingId,
223                 callerAppPackageName,
224                 STATUS_PERMISSION_NOT_REQUESTED,
225                 /*latencyMs=*/ 0);
226         int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId);
227         if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
228             // TODO(b/376542959): replace this temporary solution for CEL inside Binder thread.
229             AdsRelevanceStatusUtils.logCelInsideBinderThread(
230                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ANY_PERMISSION_FAILURE,
231                     celApiNameId);
232         }
233         throw new SecurityException(
234                 AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE);
235     }
236 
237     /**
238      * Check if a certain ad tech is enrolled and authorized to perform the operation for the
239      * package.
240      *
241      * @param context api service context
242      * @param appPackageName the package name to check against
243      * @param adTechIdentifier the ad tech to check against
244      * @param apiNameLoggingId the id of the api being called
245      * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation
246      */
assertAdTechAllowed( @onNull Context context, @NonNull String appPackageName, @NonNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)247     public void assertAdTechAllowed(
248             @NonNull Context context,
249             @NonNull String appPackageName,
250             @NonNull AdTechIdentifier adTechIdentifier,
251             int apiNameLoggingId,
252             @AppManifestConfigCall.ApiType int apiType)
253             throws AdTechNotAllowedException {
254         Objects.requireNonNull(context);
255         Objects.requireNonNull(appPackageName);
256         Objects.requireNonNull(adTechIdentifier);
257 
258         int buildId = -1;
259         int dataFileGroupStatus = 0;
260         if (mEnrollmentUtil != null) {
261             buildId = mEnrollmentUtil.getBuildId();
262             dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus();
263         }
264         int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging();
265         EnrollmentData enrollmentData = getEnrollmentData(adTechIdentifier, apiType);
266         int celApiNameId = AdsRelevanceStatusUtils.getCelPpApiNameId(apiNameLoggingId);
267 
268         if (enrollmentData == null) {
269             sLogger.v(
270                     "Enrollment data match not found for ad tech \"%s\" while calling API %d",
271                     adTechIdentifier.toString(), apiNameLoggingId);
272             mAdServicesLogger.logFledgeApiCallStats(
273                     apiNameLoggingId,
274                     appPackageName,
275                     STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND,
276                     /*latencyMs=*/ 0);
277             if (mEnrollmentUtil != null) {
278                 mEnrollmentUtil.logEnrollmentFailedStats(
279                         mAdServicesLogger,
280                         buildId,
281                         dataFileGroupStatus,
282                         enrollmentRecordsCount,
283                         adTechIdentifier.toString(),
284                         EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue());
285             }
286             if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
287                 ErrorLogUtil.e(
288                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_DATA_MATCH_NOT_FOUND,
289                         celApiNameId);
290             }
291             throw new AdTechNotAllowedException();
292         }
293 
294         if (!isAllowedAccess(appPackageName, apiType, enrollmentData)) {
295             sLogger.v(
296                     "App package name \"%s\" with ad tech identifier \"%s\" not authorized to call"
297                             + " API %d",
298                     appPackageName, adTechIdentifier.toString(), apiNameLoggingId);
299             mAdServicesLogger.logFledgeApiCallStats(
300                     apiNameLoggingId,
301                     appPackageName,
302                     STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION,
303                     /*latencyMs=*/ 0);
304             mEnrollmentUtil.logEnrollmentFailedStats(
305                     mAdServicesLogger,
306                     buildId,
307                     dataFileGroupStatus,
308                     enrollmentRecordsCount,
309                     adTechIdentifier.toString(),
310                     EnrollmentStatus.ErrorCause.UNKNOWN_ERROR_CAUSE.getValue());
311             if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
312                 ErrorLogUtil.e(
313                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_AD_TECH_NOT_AUTHORIZED_BY_APP,
314                         celApiNameId);
315             }
316             throw new AdTechNotAllowedException();
317         }
318 
319         // Check if enrollment is in blocklist.
320         if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) {
321             sLogger.v(
322                     "App package name \"%s\" with ad tech identifier \"%s\" not authorized to call"
323                             + " API %d",
324                     appPackageName, adTechIdentifier.toString(), apiNameLoggingId);
325             mAdServicesLogger.logFledgeApiCallStats(
326                     apiNameLoggingId,
327                     appPackageName,
328                     STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED,
329                     /*latencyMs=*/ 0);
330             mEnrollmentUtil.logEnrollmentFailedStats(
331                     mAdServicesLogger,
332                     buildId,
333                     dataFileGroupStatus,
334                     enrollmentRecordsCount,
335                     adTechIdentifier.toString(),
336                     EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue());
337             if (celApiNameId != AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__PPAPI_NAME_UNSPECIFIED) {
338                 ErrorLogUtil.e(
339                         AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED,
340                         celApiNameId);
341             }
342             throw new AdTechNotAllowedException();
343         }
344     }
345 
getEnrollmentData(AdTechIdentifier adTechIdentifier, int apiType)346     private EnrollmentData getEnrollmentData(AdTechIdentifier adTechIdentifier, int apiType) {
347         if (apiType == API_CUSTOM_AUDIENCES) {
348             return mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier);
349         } else if (apiType == API_PROTECTED_SIGNALS) {
350             return mEnrollmentDao.getEnrollmentDataForPASByAdTechIdentifier(adTechIdentifier);
351         } else if (apiType == API_AD_SELECTION) {
352             EnrollmentData caEnrollment =
353                     mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier);
354             if (caEnrollment == null) {
355                 return mEnrollmentDao.getEnrollmentDataForPASByAdTechIdentifier(adTechIdentifier);
356             }
357             return caEnrollment;
358         } else {
359             throw new IllegalStateException(String.format(INVALID_API_TYPE, apiType));
360         }
361     }
362 
getAdTechIdentifierEnrollmentDataPair( Uri uriForAdTech, int apiType, int apiNameLoggingId)363     private Pair<AdTechIdentifier, EnrollmentData> getAdTechIdentifierEnrollmentDataPair(
364             Uri uriForAdTech, int apiType, int apiNameLoggingId) {
365         if (apiType == API_CUSTOM_AUDIENCES) {
366             return mEnrollmentDao.getEnrollmentDataForFledgeByMatchingAdTechIdentifier(
367                     uriForAdTech);
368         } else if (apiType == API_PROTECTED_SIGNALS) {
369             return mEnrollmentDao.getEnrollmentDataForPASByMatchingAdTechIdentifier(uriForAdTech);
370         } else if (apiType == API_AD_SELECTION) {
371             Pair<AdTechIdentifier, EnrollmentData> caEnrollment =
372                     mEnrollmentDao.getEnrollmentDataForFledgeByMatchingAdTechIdentifier(
373                             uriForAdTech);
374             if (caEnrollment == null) {
375                 return mEnrollmentDao.getEnrollmentDataForPASByMatchingAdTechIdentifier(
376                         uriForAdTech);
377             }
378             return caEnrollment;
379         } else {
380             IllegalStateException exception =
381                     new IllegalStateException(
382                             String.format(Locale.ENGLISH, INVALID_API_TYPE, apiType));
383             checkAndLogCelByApiNameLoggingId(
384                     exception,
385                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_INVALID_API_TYPE,
386                     apiNameLoggingId);
387             throw exception;
388         }
389     }
390 
isAllowedAccess( String appPackageName, @AppManifestConfigCall.ApiType int apiType, EnrollmentData enrollmentData)391     private boolean isAllowedAccess(
392             String appPackageName,
393             @AppManifestConfigCall.ApiType int apiType,
394             EnrollmentData enrollmentData) {
395         if (apiType == API_CUSTOM_AUDIENCES) {
396             return AppManifestConfigHelper.isAllowedCustomAudiencesAccess(
397                     appPackageName, enrollmentData.getEnrollmentId());
398         } else if (apiType == API_PROTECTED_SIGNALS) {
399             return AppManifestConfigHelper.isAllowedProtectedSignalsAccess(
400                     appPackageName, enrollmentData.getEnrollmentId());
401         } else if (apiType == API_AD_SELECTION) {
402             return AppManifestConfigHelper.isAllowedAdSelectionAccess(
403                     appPackageName, enrollmentData.getEnrollmentId());
404         } else {
405             throw new IllegalStateException(
406                     String.format(Locale.ENGLISH, INVALID_API_TYPE, apiType));
407         }
408     }
409 
410     /**
411      * Extract and return an {@link AdTechIdentifier} from the given {@link Uri} after checking if
412      * the ad tech is enrolled and authorized to perform the operation for the package.
413      *
414      * @param context API service context
415      * @param appPackageName the package name to check against
416      * @param uriForAdTech a {@link Uri} matching the ad tech to check against
417      * @param apiNameLoggingId the logging ID of the API being called
418      * @return an {@link AdTechIdentifier} which is allowed to perform the operation
419      * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation
420      */
421     @NonNull
getAndAssertAdTechFromUriAllowed( @onNull Context context, @NonNull String appPackageName, @NonNull Uri uriForAdTech, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)422     public AdTechIdentifier getAndAssertAdTechFromUriAllowed(
423             @NonNull Context context,
424             @NonNull String appPackageName,
425             @NonNull Uri uriForAdTech,
426             int apiNameLoggingId,
427             @AppManifestConfigCall.ApiType int apiType)
428             throws AdTechNotAllowedException {
429         Objects.requireNonNull(context);
430         Objects.requireNonNull(appPackageName);
431         Objects.requireNonNull(uriForAdTech);
432 
433         int buildId = -1;
434         int dataFileGroupStatus = 0;
435         if (mEnrollmentUtil != null) {
436             buildId = mEnrollmentUtil.getBuildId();
437             dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus();
438         }
439         int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging();
440         Pair<AdTechIdentifier, EnrollmentData> enrollmentResult =
441                 getAdTechIdentifierEnrollmentDataPair(uriForAdTech, apiType, apiNameLoggingId);
442 
443         if (enrollmentResult == null) {
444             sLogger.v(
445                     "Enrollment data match not found for URI \"%s\" while calling API %d",
446                     uriForAdTech, apiNameLoggingId);
447             mAdServicesLogger.logFledgeApiCallStats(
448                     apiNameLoggingId,
449                     appPackageName,
450                     STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND,
451                     /*latencyMs=*/ 0);
452             mEnrollmentUtil.logEnrollmentFailedStats(
453                     mAdServicesLogger,
454                     buildId,
455                     dataFileGroupStatus,
456                     enrollmentRecordsCount,
457                     uriForAdTech.toString(),
458                     EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue());
459             throw new AdTechNotAllowedException();
460         }
461 
462         AdTechIdentifier adTechIdentifier = enrollmentResult.first;
463         EnrollmentData enrollmentData = enrollmentResult.second;
464 
465         boolean isAllowedAccess = isAllowedAccess(appPackageName, apiType, enrollmentData);
466 
467         boolean isEnrollmentBlocklisted =
468                 FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId());
469         int errorCause = EnrollmentStatus.ErrorCause.UNKNOWN_ERROR_CAUSE.getValue();
470         if (!isAllowedAccess || isEnrollmentBlocklisted) {
471             sLogger.v(
472                     "App package name \"%s\" with ad tech identifier \"%s\" from URI \"%s\" not"
473                             + " authorized to call API %d",
474                     appPackageName, adTechIdentifier.toString(), uriForAdTech, apiNameLoggingId);
475 
476             int resultCode;
477             if (!isAllowedAccess) {
478                 resultCode = STATUS_CALLER_NOT_ALLOWED_MANIFEST_ADSERVICES_CONFIG_NO_PERMISSION;
479             } else {
480                 resultCode = STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED;
481                 errorCause =
482                         EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue();
483             }
484             mAdServicesLogger.logFledgeApiCallStats(
485                     apiNameLoggingId, appPackageName, resultCode, /*latencyMs=*/ 0);
486             mEnrollmentUtil.logEnrollmentFailedStats(
487                     mAdServicesLogger,
488                     buildId,
489                     dataFileGroupStatus,
490                     enrollmentRecordsCount,
491                     uriForAdTech.toString(),
492                     errorCause);
493             throw new AdTechNotAllowedException();
494         }
495 
496         return adTechIdentifier;
497     }
498 
499     /**
500      * Asserts that an {@link AdTechIdentifier} extracted from the given {@link Uri} is enrolled.
501      *
502      * <p>This method does not check for any app-specific authorization.
503      *
504      * <p>This method does not log general API called stats, since URL checks are generally done
505      * after initial API enrollment validation.
506      *
507      * @param uriForAdTech a {@link Uri} matching the ad tech to check against
508      * @param apiNameLoggingId the logging ID of the API being called
509      * @throws AdTechNotAllowedException if the ad tech is not authorized to perform the operation
510      */
511     @NonNull
assertAdTechFromUriEnrolled( @onNull Uri uriForAdTech, int apiNameLoggingId, @AppManifestConfigCall.ApiType int apiType)512     public void assertAdTechFromUriEnrolled(
513             @NonNull Uri uriForAdTech,
514             int apiNameLoggingId,
515             @AppManifestConfigCall.ApiType int apiType)
516             throws AdTechNotAllowedException {
517         Objects.requireNonNull(uriForAdTech);
518 
519         int buildId = -1;
520         int dataFileGroupStatus = 0;
521         if (mEnrollmentUtil != null) {
522             buildId = mEnrollmentUtil.getBuildId();
523             dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus();
524         }
525         int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging();
526 
527         Pair<AdTechIdentifier, EnrollmentData> enrollmentResult =
528                 getAdTechIdentifierEnrollmentDataPair(uriForAdTech, apiType, apiNameLoggingId);
529 
530         if (enrollmentResult == null) {
531             sLogger.v(
532                     "Enrollment data match not found for URI \"%s\" while calling API %d",
533                     uriForAdTech, apiNameLoggingId);
534             mEnrollmentUtil.logEnrollmentFailedStats(
535                     mAdServicesLogger,
536                     buildId,
537                     dataFileGroupStatus,
538                     enrollmentRecordsCount,
539                     uriForAdTech.toString(),
540                     EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue());
541             AdTechNotAllowedException exception = new AdTechNotAllowedException();
542             checkAndLogCelByApiNameLoggingId(
543                     exception,
544                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_ENROLLMENT_NOT_FOUND,
545                     apiNameLoggingId);
546             throw exception;
547         }
548 
549         AdTechIdentifier adTechIdentifier = enrollmentResult.first;
550         EnrollmentData enrollmentData = enrollmentResult.second;
551 
552         if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) {
553             sLogger.v(
554                     "Enrollment for ad tech identifier \"%s\" from URI \"%s\" blocklisted while"
555                             + " calling API %d",
556                     adTechIdentifier, uriForAdTech, apiNameLoggingId);
557 
558             mEnrollmentUtil.logEnrollmentFailedStats(
559                     mAdServicesLogger,
560                     buildId,
561                     dataFileGroupStatus,
562                     enrollmentRecordsCount,
563                     uriForAdTech.toString(),
564                     EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue());
565             AdTechNotAllowedException exception = new AdTechNotAllowedException();
566             checkAndLogCelByApiNameLoggingId(
567                     exception,
568                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__FLEDGE_AUTHORIZATION_FILTER_NOT_ALLOWED_ENROLLMENT_FROM_URI_BLOCKED,
569                     apiNameLoggingId);
570             throw exception;
571         }
572     }
573 
574     /**
575      * Check if a certain ad tech is enrolled for FLEDGE.
576      *
577      * @param adTechIdentifier the ad tech to check against
578      * @param apiNameLoggingId the id of the api being called
579      * @throws AdTechNotAllowedException if the ad tech is not enrolled in FLEDGE
580      */
assertAdTechEnrolled( @onNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId)581     public void assertAdTechEnrolled(
582             @NonNull AdTechIdentifier adTechIdentifier, int apiNameLoggingId)
583             throws AdTechNotAllowedException {
584         Objects.requireNonNull(adTechIdentifier);
585 
586         int buildId = -1;
587         int dataFileGroupStatus = 0;
588         if (mEnrollmentUtil != null) {
589             buildId = mEnrollmentUtil.getBuildId();
590             dataFileGroupStatus = mEnrollmentUtil.getFileGroupStatus();
591         }
592         int enrollmentRecordsCount = mEnrollmentDao.getEnrollmentRecordCountForLogging();
593 
594         EnrollmentData enrollmentData =
595                 mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTechIdentifier);
596 
597         if (enrollmentData == null) {
598             sLogger.v(
599                     "Enrollment data match not found for ad tech \"%s\" while calling API %d",
600                     adTechIdentifier, apiNameLoggingId);
601             mAdServicesLogger.logFledgeApiCallStats(
602                     apiNameLoggingId,
603                     STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_MATCH_NOT_FOUND,
604                     /*latencyMs=*/ 0);
605 
606             mEnrollmentUtil.logEnrollmentFailedStats(
607                     mAdServicesLogger,
608                     buildId,
609                     dataFileGroupStatus,
610                     enrollmentRecordsCount,
611                     adTechIdentifier.toString(),
612                     EnrollmentStatus.ErrorCause.ENROLLMENT_NOT_FOUND_ERROR_CAUSE.getValue());
613             throw new AdTechNotAllowedException();
614         }
615 
616         if (FlagsFactory.getFlags().isEnrollmentBlocklisted(enrollmentData.getEnrollmentId())) {
617             sLogger.v(
618                     "Enrollment for ad tech identifier \"%s\" blocklisted while calling API %d",
619                     adTechIdentifier, apiNameLoggingId);
620             mAdServicesLogger.logFledgeApiCallStats(
621                     apiNameLoggingId,
622                     STATUS_CALLER_NOT_ALLOWED_ENROLLMENT_BLOCKLISTED,
623                     /* latencyMs= */ 0);
624 
625             mEnrollmentUtil.logEnrollmentFailedStats(
626                     mAdServicesLogger,
627                     buildId,
628                     dataFileGroupStatus,
629                     enrollmentRecordsCount,
630                     adTechIdentifier.toString(),
631                     EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.getValue());
632             throw new AdTechNotAllowedException();
633         }
634     }
635 
636     /**
637      * Internal exception for easy assertion catches specific to checking that a caller matches the
638      * given package name.
639      *
640      * <p>This exception is not meant to be exposed externally and should not be passed outside of
641      * the service.
642      */
643     public static class CallerMismatchException extends SecurityException {
644         /**
645          * Creates a {@link CallerMismatchException}, used in cases where a caller should match the
646          * package name provided to the API.
647          */
CallerMismatchException()648         public CallerMismatchException() {
649             super(
650                     AdServicesStatusUtils
651                             .SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE);
652         }
653     }
654 
655     /**
656      * Internal exception for easy assertion catches specific to checking that an ad tech is allowed
657      * to use the PP APIs.
658      *
659      * <p>This exception is not meant to be exposed externally and should not be passed outside of
660      * the service.
661      */
662     public static class AdTechNotAllowedException extends SecurityException {
663         /**
664          * Creates a {@link AdTechNotAllowedException}, used in cases where an ad tech should have
665          * been allowed to use the PP APIs.
666          */
AdTechNotAllowedException()667         public AdTechNotAllowedException() {
668             super(AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE);
669         }
670     }
671 }
672