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.data.signals; 18 19 import android.adservices.common.AdTechIdentifier; 20 import android.content.pm.PackageManager; 21 22 import androidx.annotation.NonNull; 23 import androidx.room.Dao; 24 import androidx.room.Delete; 25 import androidx.room.Insert; 26 import androidx.room.OnConflictStrategy; 27 import androidx.room.Query; 28 import androidx.room.Transaction; 29 30 import com.android.adservices.data.common.CleanupUtils; 31 import com.android.adservices.data.enrollment.EnrollmentDao; 32 import com.android.adservices.service.Flags; 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.time.Instant; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Objects; 39 import java.util.Set; 40 41 /** 42 * DAO abstract class used to access ProtectedSignal storage 43 * 44 * <p>Annotations will generate Room-based SQLite Dao impl. 45 */ 46 @Dao 47 public abstract class ProtectedSignalsDao { 48 49 /** 50 * Returns a list of all signals owned by the given buyer. 51 * 52 * @param buyer The buyer to retrieve signals for. 53 * @return A list of all protected signals owned by the input buyer. 54 */ 55 @Query("SELECT * FROM protected_signals WHERE buyer = :buyer") getSignalsByBuyer(AdTechIdentifier buyer)56 public abstract List<DBProtectedSignal> getSignalsByBuyer(AdTechIdentifier buyer); 57 58 /** 59 * Returns if any raw signals exist from the given buyer. 60 * 61 * @param buyer The buyer to check existence of signals for. 62 * @return If any signals exist from the given buyer. 63 */ 64 @Query("SELECT EXISTS(SELECT 1 FROM protected_signals WHERE buyer = :buyer)") hasSignalsFromBuyer(AdTechIdentifier buyer)65 public abstract boolean hasSignalsFromBuyer(AdTechIdentifier buyer); 66 67 /** 68 * Inserts signals into the database. 69 * 70 * <p>This method is not meant to be called externally. Instead, use {@link 71 * #insertAndDelete(AdTechIdentifier, Instant, List, List)}. 72 * 73 * @param signals The signals to insert. 74 */ 75 @Insert insertSignals(@onNull List<DBProtectedSignal> signals)76 protected abstract void insertSignals(@NonNull List<DBProtectedSignal> signals); 77 78 /** 79 * Deletes signals from the database. 80 * 81 * <p>This method is not meant to be called externally. Instead, use {@link 82 * #insertAndDelete(AdTechIdentifier, Instant, List, List)}. 83 * 84 * @param signals The signals to delete. 85 */ 86 @Delete deleteSignals(@onNull List<DBProtectedSignal> signals)87 protected abstract void deleteSignals(@NonNull List<DBProtectedSignal> signals); 88 89 /** 90 * Inserts and deletes signals in a single transaction. 91 * 92 * <p>Also writes metadata about the update and clears any orphaned encoded payloads. 93 * 94 * @param signalsToInsert The signals to insert. 95 * @param signalsToDelete The signals to delete. 96 */ 97 @Transaction insertAndDelete( @onNull AdTechIdentifier buyer, @NonNull Instant now, @NonNull List<DBProtectedSignal> signalsToInsert, @NonNull List<DBProtectedSignal> signalsToDelete)98 public void insertAndDelete( 99 @NonNull AdTechIdentifier buyer, 100 @NonNull Instant now, 101 @NonNull List<DBProtectedSignal> signalsToInsert, 102 @NonNull List<DBProtectedSignal> signalsToDelete) { 103 insertSignals(signalsToInsert); 104 deleteSignals(signalsToDelete); 105 persistSignalsUpdateMetadata( 106 DBSignalsUpdateMetadata.builder() 107 .setBuyer(buyer) 108 .setLastSignalsUpdatedTime(now) 109 .build()); 110 deleteEncodedPayloadsWithMissingRawSignals(); 111 } 112 113 /** 114 * Deletes all signals {@code expiryTime}. 115 * 116 * @return the number of deleted signals 117 */ 118 @Query("DELETE FROM protected_signals WHERE creationTime < :expiryTime") deleteSignalsBeforeTime(@onNull Instant expiryTime)119 protected abstract int deleteSignalsBeforeTime(@NonNull Instant expiryTime); 120 121 /** Returns buyers with expired signals. */ 122 @Query("SELECT DISTINCT buyer FROM protected_signals WHERE creationTime < :expiryTime") getBuyersWithExpiredSignals( @onNull Instant expiryTime)123 protected abstract List<AdTechIdentifier> getBuyersWithExpiredSignals( 124 @NonNull Instant expiryTime); 125 126 /** 127 * Deletes expired signals and updates buyer metadata. 128 * 129 * @return the number of deleted signals 130 */ 131 @Transaction deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( @onNull Instant expiryTime, @NonNull Instant now)132 public int deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( 133 @NonNull Instant expiryTime, @NonNull Instant now) { 134 List<AdTechIdentifier> buyers = getBuyersWithExpiredSignals(expiryTime); 135 for (AdTechIdentifier buyer : buyers) { 136 persistSignalsUpdateMetadata( 137 DBSignalsUpdateMetadata.builder() 138 .setBuyer(buyer) 139 .setLastSignalsUpdatedTime(now) 140 .build()); 141 } 142 int numDeletedSignals = deleteSignalsBeforeTime(expiryTime); 143 deleteEncodedPayloadsWithMissingRawSignals(); 144 return numDeletedSignals; 145 } 146 147 /** 148 * Deletes all signals belonging to disallowed buyer ad techs in a single transaction, where the 149 * buyer ad techs cannot be found in the enrollment database. 150 * 151 * @return the number of deleted signals 152 */ 153 @Transaction deleteDisallowedBuyerSignals(@onNull EnrollmentDao enrollmentDao)154 public int deleteDisallowedBuyerSignals(@NonNull EnrollmentDao enrollmentDao) { 155 Objects.requireNonNull(enrollmentDao); 156 157 List<AdTechIdentifier> buyersToRemove = getAllBuyers(); 158 if (buyersToRemove.isEmpty()) { 159 return 0; 160 } 161 162 Set<AdTechIdentifier> enrolledAdTechs = enrollmentDao.getAllFledgeEnrolledAdTechs(); 163 buyersToRemove.removeAll(enrolledAdTechs); 164 165 int numDeletedEvents = 0; 166 if (!buyersToRemove.isEmpty()) { 167 numDeletedEvents = deleteByBuyers(buyersToRemove); 168 for (AdTechIdentifier buyer : buyersToRemove) { 169 deleteSignalsUpdateMetadata(buyer); 170 } 171 deleteEncodedPayloadsWithMissingRawSignals(); 172 } 173 174 return numDeletedEvents; 175 } 176 177 /** 178 * Helper method for {@link #deleteDisallowedBuyerSignals} 179 * 180 * @return All buyers with signals in the DB. 181 */ 182 @Query("SELECT DISTINCT buyer FROM protected_signals") getAllBuyers()183 protected abstract List<AdTechIdentifier> getAllBuyers(); 184 185 /** 186 * Deletes all signals for the list of buyers. Helper method for {@link 187 * #deleteDisallowedBuyerSignals} 188 * 189 * @return Number of buyers deleted. 190 */ 191 @Query("DELETE FROM protected_signals where buyer in (:buyers)") deleteByBuyers(@onNull List<AdTechIdentifier> buyers)192 protected abstract int deleteByBuyers(@NonNull List<AdTechIdentifier> buyers); 193 194 /** 195 * Deletes all signals belonging to disallowed source apps in a single transaction, where the 196 * source apps cannot be found in the app package name allowlist or are not installed on the 197 * device. 198 * 199 * @return the number of deleted raw signals 200 */ 201 @Transaction deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( @onNull PackageManager packageManager, @NonNull Flags flags, @NonNull Instant now)202 public int deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( 203 @NonNull PackageManager packageManager, @NonNull Flags flags, @NonNull Instant now) { 204 Objects.requireNonNull(packageManager); 205 Objects.requireNonNull(flags); 206 Objects.requireNonNull(now); 207 208 List<String> sourceAppsToRemove = getAllPackages(); 209 if (sourceAppsToRemove.isEmpty()) { 210 return 0; 211 } 212 213 CleanupUtils.removeAllowedPackages( 214 sourceAppsToRemove, packageManager, Arrays.asList(flags.getPasAppAllowList())); 215 216 int numDeletedEvents = 0; 217 if (!sourceAppsToRemove.isEmpty()) { 218 List<AdTechIdentifier> buyers = getBuyersForPackages(sourceAppsToRemove); 219 for (AdTechIdentifier buyer : buyers) { 220 persistSignalsUpdateMetadata( 221 DBSignalsUpdateMetadata.builder() 222 .setBuyer(buyer) 223 .setLastSignalsUpdatedTime(now) 224 .build()); 225 } 226 numDeletedEvents = deleteSignalsByPackage(sourceAppsToRemove); 227 // TODO(b/300661099): Collect and send telemetry on signal deletion 228 } 229 return numDeletedEvents; 230 } 231 232 /** 233 * Returns the list of all unique packages in the signals table. 234 * 235 * <p>This method is not meant to be called externally, but is a helper for {@link 236 * #deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata(PackageManager, Flags, 237 * Instant)} 238 */ 239 @Query("SELECT DISTINCT packageName FROM protected_signals") getAllPackages()240 protected abstract List<String> getAllPackages(); 241 242 /** 243 * Deletes all raw signals generated from the given packages. 244 * 245 * <p>Also clears any encoded signals that are no longer linked to any raw signals. 246 * 247 * @return the number of deleted raw signals 248 */ 249 @Transaction deleteSignalsByPackage(@onNull List<String> packages)250 public int deleteSignalsByPackage(@NonNull List<String> packages) { 251 int numDeletedRawSignals = deleteRawSignalsByPackage(packages); 252 deleteEncodedPayloadsWithMissingRawSignals(); 253 return numDeletedRawSignals; 254 } 255 256 /** 257 * Deletes all raw signals generated from the given packages. 258 * 259 * <p>This is an internal method not meant to be called outside of {@link 260 * #deleteSignalsByPackage(List)}. 261 * 262 * @return the number of deleted raw signals 263 */ 264 @Query("DELETE FROM protected_signals WHERE packageName in (:packages)") deleteRawSignalsByPackage(@onNull List<String> packages)265 protected abstract int deleteRawSignalsByPackage(@NonNull List<String> packages); 266 267 /** 268 * Deletes encoded signals for all buyers who no longer have raw signals. 269 * 270 * <p>This method is not meant to be called externally. It should only be called from {@link 271 * #deleteSignalsByPackage(List)}. 272 */ 273 // TODO(b/372334616): Consider moving this cross-table deletion into the EncodedPayloadDao 274 @Query("DELETE FROM encoded_payload WHERE buyer NOT IN (SELECT buyer FROM protected_signals)") deleteEncodedPayloadsWithMissingRawSignals()275 protected abstract int deleteEncodedPayloadsWithMissingRawSignals(); 276 277 /** Deletes all signals */ 278 @Query("DELETE FROM protected_signals") deleteAllSignals()279 public abstract int deleteAllSignals(); 280 281 /** Returns all buyers for the given packages. */ 282 @Query("SELECT DISTINCT buyer FROM protected_signals WHERE packageName in (:packages)") getBuyersForPackages(@onNull List<String> packages)283 protected abstract List<AdTechIdentifier> getBuyersForPackages(@NonNull List<String> packages); 284 285 /** Create or update a buyer metadata entry. */ 286 @Insert(entity = DBSignalsUpdateMetadata.class, onConflict = OnConflictStrategy.REPLACE) 287 @VisibleForTesting persistSignalsUpdateMetadata( DBSignalsUpdateMetadata dbSignalsUpdateMetadata)288 protected abstract long persistSignalsUpdateMetadata( 289 DBSignalsUpdateMetadata dbSignalsUpdateMetadata); 290 291 /** Returns a metadata entry according to the buyer. */ 292 @Query("SELECT * FROM signals_update_metadata WHERE buyer=:buyer") getSignalsUpdateMetadata(AdTechIdentifier buyer)293 public abstract DBSignalsUpdateMetadata getSignalsUpdateMetadata(AdTechIdentifier buyer); 294 295 /** Delete the metadata for the buyer. */ 296 @Query("DELETE FROM signals_update_metadata WHERE buyer=:buyer") deleteSignalsUpdateMetadata(AdTechIdentifier buyer)297 public abstract void deleteSignalsUpdateMetadata(AdTechIdentifier buyer); 298 299 /** Delete all metadata in the storage. */ 300 @Query("DELETE FROM signals_update_metadata") deleteAllSignalsUpdateMetadata()301 public abstract void deleteAllSignalsUpdateMetadata(); 302 } 303