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.signals; 18 19 import android.adservices.common.AdTechIdentifier; 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.enrollment.EnrollmentDao; 26 import com.android.adservices.data.signals.EncodedPayloadDao; 27 import com.android.adservices.data.signals.EncoderLogicHandler; 28 import com.android.adservices.data.signals.ProtectedSignalsDao; 29 import com.android.adservices.data.signals.ProtectedSignalsDatabase; 30 import com.android.adservices.service.Flags; 31 import com.android.adservices.service.FlagsFactory; 32 import com.android.internal.annotations.VisibleForTesting; 33 34 import java.time.Clock; 35 import java.time.Instant; 36 import java.util.HashSet; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.Set; 40 41 /** Utility class to perform Protected Signals maintenance tasks. */ 42 public class SignalsMaintenanceTasksWorker { 43 44 private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 45 @NonNull private final ProtectedSignalsDao mProtectedSignalsDao; 46 @NonNull private final EnrollmentDao mEnrollmentDao; 47 @NonNull private final EncodedPayloadDao mEncodedPayloadDao; 48 @NonNull private final Flags mFlags; 49 @NonNull private final Clock mClock; 50 @NonNull private final PackageManager mPackageManager; 51 @NonNull private final EncoderLogicHandler mEncoderLogicHandler; 52 53 @VisibleForTesting SignalsMaintenanceTasksWorker( @onNull Flags flags, @NonNull ProtectedSignalsDao protectedSignalsDao, @NonNull EncoderLogicHandler encoderLogicHandler, @NonNull EncodedPayloadDao encodedPayloadDao, @NonNull EnrollmentDao enrollmentDao, @NonNull Clock clock, @NonNull PackageManager packageManager)54 public SignalsMaintenanceTasksWorker( 55 @NonNull Flags flags, 56 @NonNull ProtectedSignalsDao protectedSignalsDao, 57 @NonNull EncoderLogicHandler encoderLogicHandler, 58 @NonNull EncodedPayloadDao encodedPayloadDao, 59 @NonNull EnrollmentDao enrollmentDao, 60 @NonNull Clock clock, 61 @NonNull PackageManager packageManager) { 62 Objects.requireNonNull(flags); 63 Objects.requireNonNull(protectedSignalsDao); 64 Objects.requireNonNull(enrollmentDao); 65 Objects.requireNonNull(clock); 66 Objects.requireNonNull(packageManager); 67 68 mFlags = flags; 69 mProtectedSignalsDao = protectedSignalsDao; 70 mEnrollmentDao = enrollmentDao; 71 mEncodedPayloadDao = encodedPayloadDao; 72 mEncoderLogicHandler = encoderLogicHandler; 73 mClock = clock; 74 mPackageManager = packageManager; 75 } 76 SignalsMaintenanceTasksWorker(@onNull Context context)77 private SignalsMaintenanceTasksWorker(@NonNull Context context) { 78 Objects.requireNonNull(context); 79 mFlags = FlagsFactory.getFlags(); 80 mProtectedSignalsDao = ProtectedSignalsDatabase.getInstance().protectedSignalsDao(); 81 mEnrollmentDao = EnrollmentDao.getInstance(); 82 mEncoderLogicHandler = new EncoderLogicHandler(context); 83 mEncodedPayloadDao = ProtectedSignalsDatabase.getInstance().getEncodedPayloadDao(); 84 mClock = Clock.systemUTC(); 85 mPackageManager = context.getPackageManager(); 86 } 87 88 /** Creates a new instance of {@link SignalsMaintenanceTasksWorker}. */ create(@onNull Context context)89 public static SignalsMaintenanceTasksWorker create(@NonNull Context context) { 90 Objects.requireNonNull(context); 91 return new SignalsMaintenanceTasksWorker(context); 92 } 93 94 /** 95 * Clears invalid signals from the protected signals table. Not flagged since the job will only 96 * run if protected signals is enabled. 97 * 98 * <ul> 99 * <li>Expired signals 100 * <li>Disallowed buyer signals 101 * <li>Disallowed source app signals 102 * <li>Uninstalled source app signals 103 * <p>Also clears data for disallowed buyers and expired encoding related information such 104 * as: 105 * <ul> 106 * <li>Encoder end-point 107 * <li>Encoding logic 108 * <li>Encoded Signals payload 109 * </ul> 110 */ clearInvalidProtectedSignalsData()111 public void clearInvalidProtectedSignalsData() { 112 Instant now = mClock.instant(); 113 Instant expirationInstant = now.minusSeconds(ProtectedSignal.EXPIRATION_SECONDS); 114 clearInvalidSignals(expirationInstant, now); 115 clearInvalidEncoders(expirationInstant); 116 clearInvalidEncodedPayloads(expirationInstant); 117 } 118 119 @VisibleForTesting clearInvalidSignals(Instant expirationInstant, Instant now)120 void clearInvalidSignals(Instant expirationInstant, Instant now) { 121 122 sLogger.v("Clearing expired signals older than %s", expirationInstant); 123 int numExpiredSignals = 124 mProtectedSignalsDao.deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( 125 expirationInstant, now); 126 sLogger.v("Cleared %d expired signals", numExpiredSignals); 127 128 // Read from flags directly, since this maintenance task worker is attached to a background 129 // job with unknown lifetime 130 if (mFlags.getDisableFledgeEnrollmentCheck()) { 131 sLogger.v( 132 "FLEDGE enrollment check disabled; skipping disallowed buyer signal" 133 + " maintenance"); 134 } else { 135 sLogger.v("Clearing signals for disallowed buyer ad techs"); 136 int numDisallowedBuyerEvents = 137 mProtectedSignalsDao.deleteDisallowedBuyerSignals(mEnrollmentDao); 138 sLogger.v("Cleared %d signals for disallowed buyer ad techs", numDisallowedBuyerEvents); 139 } 140 141 sLogger.v("Clearing signals for disallowed source apps"); 142 int numDisallowedSourceAppSignals = 143 mProtectedSignalsDao.deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( 144 mPackageManager, mFlags, now); 145 sLogger.v("Cleared %d signals for disallowed source apps", numDisallowedSourceAppSignals); 146 } 147 148 @VisibleForTesting clearInvalidEncoders(Instant expirationInstant)149 void clearInvalidEncoders(Instant expirationInstant) { 150 151 Set<AdTechIdentifier> buyersWithConsentRevoked = new HashSet<>(); 152 if (mFlags.getDisableFledgeEnrollmentCheck()) { 153 sLogger.v( 154 "FLEDGE enrollment check disabled; skipping disallowed buyer encoding logic" 155 + " maintenance"); 156 } else { 157 sLogger.v("Gathering buyers with consent revoked"); 158 List<AdTechIdentifier> registeredBuyers = mEncoderLogicHandler.getBuyersWithEncoders(); 159 buyersWithConsentRevoked = getBuyersWithRevokedConsent(new HashSet<>(registeredBuyers)); 160 } 161 162 sLogger.v("Clearing expired encoders older than %s", expirationInstant); 163 Set<AdTechIdentifier> buyersWithStaleEncoders = 164 new HashSet<>(mEncoderLogicHandler.getBuyersWithStaleEncoders(expirationInstant)); 165 166 // Union of two sets with revoked buyers and buyers with stale encoders 167 buyersWithStaleEncoders.addAll(buyersWithConsentRevoked); 168 mEncoderLogicHandler.deleteEncodersForBuyers(buyersWithStaleEncoders); 169 } 170 171 @VisibleForTesting clearInvalidEncodedPayloads(Instant expirationInstant)172 void clearInvalidEncodedPayloads(Instant expirationInstant) { 173 174 if (mFlags.getDisableFledgeEnrollmentCheck()) { 175 sLogger.v( 176 "FLEDGE enrollment check disabled; skipping disallowed buyer encoded payload" 177 + " maintenance"); 178 } else { 179 sLogger.v("Gathering buyers with consent revoked"); 180 List<AdTechIdentifier> buyersWithEncodedPayloads = 181 mEncodedPayloadDao.getAllBuyersWithEncodedPayloads(); 182 Set<AdTechIdentifier> buyersWithConsentRevoked = 183 getBuyersWithRevokedConsent(new HashSet<>(buyersWithEncodedPayloads)); 184 for (AdTechIdentifier revokedBuyer : buyersWithConsentRevoked) { 185 mEncodedPayloadDao.deleteEncodedPayload(revokedBuyer); 186 } 187 } 188 189 sLogger.v("Clearing expired encodings older than %s", expirationInstant); 190 mEncodedPayloadDao.deleteEncodedPayloadsBeforeTime(expirationInstant); 191 } 192 getBuyersWithRevokedConsent(Set<AdTechIdentifier> buyers)193 private Set<AdTechIdentifier> getBuyersWithRevokedConsent(Set<AdTechIdentifier> buyers) { 194 Set<AdTechIdentifier> enrolledAdTechs = mEnrollmentDao.getAllFledgeEnrolledAdTechs(); 195 buyers.removeAll(enrolledAdTechs); 196 return buyers; 197 } 198 } 199