• 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.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