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.adservices.common.KeyedFrequencyCap; 20 import android.annotation.NonNull; 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 24 import com.android.adservices.LoggerFactory; 25 import com.android.adservices.data.adselection.AdSelectionDatabase; 26 import com.android.adservices.data.adselection.AdSelectionDebugReportDao; 27 import com.android.adservices.data.adselection.AdSelectionDebugReportingDatabase; 28 import com.android.adservices.data.adselection.AdSelectionEntryDao; 29 import com.android.adservices.data.adselection.AdSelectionServerDatabase; 30 import com.android.adservices.data.adselection.EncryptionContextDao; 31 import com.android.adservices.data.adselection.FrequencyCapDao; 32 import com.android.adservices.data.adselection.SharedStorageDatabase; 33 import com.android.adservices.data.enrollment.EnrollmentDao; 34 import com.android.adservices.data.kanon.KAnonDatabase; 35 import com.android.adservices.data.kanon.KAnonMessageDao; 36 import com.android.adservices.service.Flags; 37 import com.android.adservices.service.FlagsFactory; 38 import com.android.adservices.service.stats.AdServicesLogger; 39 import com.android.adservices.service.stats.InteractionReportingTableClearedStats; 40 import com.android.adservices.service.stats.StatsdAdServicesLogger; 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import java.time.Clock; 44 import java.time.Instant; 45 import java.util.Objects; 46 47 /** Utility class to perform Fledge maintenance tasks */ 48 public class FledgeMaintenanceTasksWorker { 49 private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 50 @NonNull private final AdSelectionEntryDao mAdSelectionEntryDao; 51 @NonNull private final AdSelectionDebugReportDao mAdSelectionDebugReportDao; 52 @NonNull private final FrequencyCapDao mFrequencyCapDao; 53 @NonNull private final EnrollmentDao mEnrollmentDao; 54 @NonNull private final EncryptionContextDao mEncryptionContextDao; 55 @NonNull private final Flags mFlags; 56 @NonNull private final Clock mClock; 57 @NonNull private final KAnonMessageDao mKAnonMessageDao; 58 59 @NonNull private final AdServicesLogger mAdServicesLogger; 60 61 @VisibleForTesting FledgeMaintenanceTasksWorker( @onNull Flags flags, @NonNull AdSelectionEntryDao adSelectionEntryDao, @NonNull FrequencyCapDao frequencyCapDao, @NonNull EnrollmentDao enrollmentDao, @NonNull EncryptionContextDao encryptionContextDao, @NonNull AdSelectionDebugReportDao adSelectionDebugReportDao, @NonNull Clock clock, @NonNull AdServicesLogger adServicesLogger, @NonNull KAnonMessageDao kAnonMessageDao)62 public FledgeMaintenanceTasksWorker( 63 @NonNull Flags flags, 64 @NonNull AdSelectionEntryDao adSelectionEntryDao, 65 @NonNull FrequencyCapDao frequencyCapDao, 66 @NonNull EnrollmentDao enrollmentDao, 67 @NonNull EncryptionContextDao encryptionContextDao, 68 @NonNull AdSelectionDebugReportDao adSelectionDebugReportDao, 69 @NonNull Clock clock, 70 @NonNull AdServicesLogger adServicesLogger, 71 @NonNull KAnonMessageDao kAnonMessageDao) { 72 Objects.requireNonNull(flags); 73 Objects.requireNonNull(adSelectionEntryDao); 74 Objects.requireNonNull(frequencyCapDao); 75 Objects.requireNonNull(enrollmentDao); 76 Objects.requireNonNull(clock); 77 Objects.requireNonNull(encryptionContextDao); 78 Objects.requireNonNull(adSelectionDebugReportDao); 79 Objects.requireNonNull(adServicesLogger); 80 Objects.requireNonNull(kAnonMessageDao); 81 82 mFlags = flags; 83 mAdSelectionEntryDao = adSelectionEntryDao; 84 mFrequencyCapDao = frequencyCapDao; 85 mEnrollmentDao = enrollmentDao; 86 mEncryptionContextDao = encryptionContextDao; 87 mClock = clock; 88 mAdSelectionDebugReportDao = adSelectionDebugReportDao; 89 mAdServicesLogger = adServicesLogger; 90 mKAnonMessageDao = kAnonMessageDao; 91 } 92 FledgeMaintenanceTasksWorker(@onNull Context context)93 private FledgeMaintenanceTasksWorker(@NonNull Context context) { 94 Objects.requireNonNull(context); 95 mFlags = FlagsFactory.getFlags(); 96 mAdSelectionEntryDao = AdSelectionDatabase.getInstance(context).adSelectionEntryDao(); 97 mFrequencyCapDao = SharedStorageDatabase.getInstance(context).frequencyCapDao(); 98 mEnrollmentDao = EnrollmentDao.getInstance(); 99 mEncryptionContextDao = 100 AdSelectionServerDatabase.getInstance(context).encryptionContextDao(); 101 mClock = Clock.systemUTC(); 102 mAdSelectionDebugReportDao = 103 AdSelectionDebugReportingDatabase.getInstance(context) 104 .getAdSelectionDebugReportDao(); 105 mAdServicesLogger = StatsdAdServicesLogger.getInstance(); 106 mKAnonMessageDao = KAnonDatabase.getInstance(context).kAnonMessageDao(); 107 } 108 109 /** Creates a new instance of {@link FledgeMaintenanceTasksWorker}. */ create(@onNull Context context)110 public static FledgeMaintenanceTasksWorker create(@NonNull Context context) { 111 Objects.requireNonNull(context); 112 return new FledgeMaintenanceTasksWorker(context); 113 } 114 115 /** 116 * Clears all entries in the {@code ad_selection} table that are older than {@code 117 * expirationTime}. Then, clears all expired entries in the {@code buyer_decision_logic} as well 118 * as the {@code registered_ad_interactions} table. 119 */ clearExpiredAdSelectionData()120 public void clearExpiredAdSelectionData() { 121 // Read from flags directly, since this maintenance task worker is attached to a background 122 // job with unknown lifetime 123 Instant expirationTime = 124 mClock.instant().minusSeconds(mFlags.getAdSelectionExpirationWindowS()); 125 126 sLogger.v("Clearing expired Ad Selection data"); 127 mAdSelectionEntryDao.removeExpiredAdSelection(expirationTime); 128 129 sLogger.v("Clearing expired Buyer Decision Logic data "); 130 mAdSelectionEntryDao.removeExpiredBuyerDecisionLogic(); 131 132 if (mFlags.getFledgeBeaconReportingMetricsEnabled()) { 133 long numUrisBeforeClearing = mAdSelectionEntryDao.getTotalNumRegisteredAdInteractions(); 134 135 sLogger.v("Clearing expired Registered Ad Interaction data "); 136 mAdSelectionEntryDao.removeExpiredRegisteredAdInteractions(); 137 138 long numUrisAfterClearing = mAdSelectionEntryDao.getTotalNumRegisteredAdInteractions(); 139 140 mAdServicesLogger.logInteractionReportingTableClearedStats( 141 InteractionReportingTableClearedStats.builder() 142 .setNumUrisCleared((int) (numUrisAfterClearing - numUrisBeforeClearing)) 143 .build()); 144 } else { 145 sLogger.v("Clearing expired Registered Ad Interaction data "); 146 mAdSelectionEntryDao.removeExpiredRegisteredAdInteractions(); 147 } 148 149 if (mFlags.getFledgeAuctionServerEnabled() 150 || mFlags.getFledgeOnDeviceAuctionShouldUseUnifiedTables()) { 151 sLogger.v("Clearing expired Ad Selection Initialization data"); 152 mAdSelectionEntryDao.removeExpiredAdSelectionInitializations(expirationTime); 153 } 154 155 if (mFlags.getFledgeOnDeviceAuctionShouldUseUnifiedTables()) { 156 sLogger.v("Clearing expired Registered Ad Interaction data from unified table "); 157 mAdSelectionEntryDao.removeExpiredRegisteredAdInteractionsFromUnifiedTable(); 158 } 159 160 if (mFlags.getFledgeAuctionServerEnabled()) { 161 sLogger.v("Clearing expired Encryption Context"); 162 mEncryptionContextDao.removeExpiredEncryptionContext(expirationTime); 163 } 164 165 if (mFlags.getFledgeEventLevelDebugReportingEnabled()) { 166 sLogger.v("Clearing expired debug reports "); 167 mAdSelectionDebugReportDao.deleteDebugReportsBeforeTime(expirationTime); 168 } 169 } 170 171 /** 172 * Clears invalid histogram data from the frequency cap database if the ad filtering feature is 173 * enabled. 174 * 175 * <p>Invalid histogram data includes: 176 * 177 * <ul> 178 * <li>Expired histogram data 179 * <li>Disallowed buyer histogram data 180 * <li>Disallowed source app histogram data 181 * <li>Uninstalled source app histogram data 182 * </ul> 183 */ clearInvalidFrequencyCapHistogramData(@onNull PackageManager packageManager)184 public void clearInvalidFrequencyCapHistogramData(@NonNull PackageManager packageManager) { 185 Objects.requireNonNull(packageManager); 186 187 // Read from flags directly, since this maintenance task worker is attached to a background 188 // job with unknown lifetime 189 if (!mFlags.getFledgeFrequencyCapFilteringEnabled()) { 190 sLogger.v( 191 "Frequency cap filtering disabled; skipping Frequency Cap histogram" 192 + " maintenance"); 193 return; 194 } 195 196 Instant expirationInstant = 197 mClock.instant().minusSeconds(KeyedFrequencyCap.MAX_INTERVAL.getSeconds()); 198 199 sLogger.v( 200 "Clearing expired Frequency Cap histogram events older than %s", expirationInstant); 201 int numExpiredEvents = mFrequencyCapDao.deleteAllExpiredHistogramData(expirationInstant); 202 sLogger.v("Cleared %d expired Frequency Cap histogram events", numExpiredEvents); 203 204 // Read from flags directly, since this maintenance task worker is attached to a background 205 // job with unknown lifetime 206 if (mFlags.getDisableFledgeEnrollmentCheck()) { 207 sLogger.v( 208 "FLEDGE enrollment check disabled; skipping disallowed buyer Frequency Cap " 209 + "histogram maintenance"); 210 } else { 211 sLogger.v("Clearing Frequency Cap histogram events for disallowed buyer ad techs"); 212 int numDisallowedBuyerEvents = 213 mFrequencyCapDao.deleteAllDisallowedBuyerHistogramData(mEnrollmentDao); 214 sLogger.v( 215 "Cleared %d Frequency Cap histogram events for disallowed buyer ad techs", 216 numDisallowedBuyerEvents); 217 } 218 219 // Read from flags directly, since this maintenance task worker is attached to a background 220 // job with unknown lifetime 221 sLogger.v("Clearing Frequency Cap histogram events for disallowed source apps"); 222 int numDisallowedSourceAppEvents = 223 mFrequencyCapDao.deleteAllDisallowedSourceAppHistogramData(packageManager, mFlags); 224 sLogger.v( 225 "Cleared %d Frequency Cap histogram events for disallowed source apps", 226 numDisallowedSourceAppEvents); 227 } 228 229 /** Deletes the expired {@link com.android.adservices.service.kanon.KAnonMessageEntity} */ clearExpiredKAnonMessageEntities()230 public void clearExpiredKAnonMessageEntities() { 231 if (mFlags.getFledgeKAnonSignJoinFeatureEnabled()) { 232 sLogger.v("Clearing expired KAnon message entities"); 233 mKAnonMessageDao.removeExpiredEntities(mClock.instant()); 234 sLogger.v("Cleared expired KAnon message entities"); 235 } 236 } 237 } 238