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      * Inserts signals into the database.
60      *
61      * @param signals The signals to insert.
62      */
63     @Insert
insertSignals(@onNull List<DBProtectedSignal> signals)64     public abstract void insertSignals(@NonNull List<DBProtectedSignal> signals);
65 
66     /**
67      * Deletes signals from the database.
68      *
69      * @param signals The signals to delete.
70      */
71     @Delete
deleteSignals(@onNull List<DBProtectedSignal> signals)72     public abstract void deleteSignals(@NonNull List<DBProtectedSignal> signals);
73 
74     /**
75      * Inserts and deletes signals in a single transaction.
76      *
77      * @param signalsToInsert The signals to insert.
78      * @param signalsToDelete The signals to delete.
79      */
80     @Transaction
insertAndDelete( @onNull AdTechIdentifier buyer, @NonNull Instant now, @NonNull List<DBProtectedSignal> signalsToInsert, @NonNull List<DBProtectedSignal> signalsToDelete)81     public void insertAndDelete(
82             @NonNull AdTechIdentifier buyer,
83             @NonNull Instant now,
84             @NonNull List<DBProtectedSignal> signalsToInsert,
85             @NonNull List<DBProtectedSignal> signalsToDelete) {
86         insertSignals(signalsToInsert);
87         deleteSignals(signalsToDelete);
88         persistSignalsUpdateMetadata(
89                 DBSignalsUpdateMetadata.builder()
90                         .setBuyer(buyer)
91                         .setLastSignalsUpdatedTime(now)
92                         .build());
93     }
94 
95     /**
96      * Deletes all signals {@code expiryTime}.
97      *
98      * @return the number of deleted signals
99      */
100     @Query("DELETE FROM protected_signals WHERE creationTime < :expiryTime")
deleteSignalsBeforeTime(@onNull Instant expiryTime)101     protected abstract int deleteSignalsBeforeTime(@NonNull Instant expiryTime);
102 
103     /** Returns buyers with expired signals. */
104     @Query("SELECT DISTINCT buyer FROM protected_signals WHERE creationTime < :expiryTime")
getBuyersWithExpiredSignals( @onNull Instant expiryTime)105     protected abstract List<AdTechIdentifier> getBuyersWithExpiredSignals(
106             @NonNull Instant expiryTime);
107 
108     /**
109      * Deletes expired signals and updates buyer metadata.
110      *
111      * @return the number of deleted signals
112      */
113     @Transaction
deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( @onNull Instant expiryTime, @NonNull Instant now)114     public int deleteExpiredSignalsAndUpdateSignalsUpdateMetadata(
115             @NonNull Instant expiryTime, @NonNull Instant now) {
116         List<AdTechIdentifier> buyers = getBuyersWithExpiredSignals(expiryTime);
117         for (AdTechIdentifier buyer : buyers) {
118             persistSignalsUpdateMetadata(
119                     DBSignalsUpdateMetadata.builder()
120                             .setBuyer(buyer)
121                             .setLastSignalsUpdatedTime(now)
122                             .build());
123         }
124         return deleteSignalsBeforeTime(expiryTime);
125     }
126 
127     /**
128      * Deletes all signals belonging to disallowed buyer ad techs in a single transaction, where the
129      * buyer ad techs cannot be found in the enrollment database.
130      *
131      * @return the number of deleted signals
132      */
133     @Transaction
deleteDisallowedBuyerSignals(@onNull EnrollmentDao enrollmentDao)134     public int deleteDisallowedBuyerSignals(@NonNull EnrollmentDao enrollmentDao) {
135         Objects.requireNonNull(enrollmentDao);
136 
137         List<AdTechIdentifier> buyersToRemove = getAllBuyers();
138         if (buyersToRemove.isEmpty()) {
139             return 0;
140         }
141 
142         Set<AdTechIdentifier> enrolledAdTechs = enrollmentDao.getAllFledgeEnrolledAdTechs();
143         buyersToRemove.removeAll(enrolledAdTechs);
144 
145         int numDeletedEvents = 0;
146         if (!buyersToRemove.isEmpty()) {
147             numDeletedEvents = deleteByBuyers(buyersToRemove);
148             for (AdTechIdentifier buyer : buyersToRemove) {
149                 deleteSignalsUpdateMetadata(buyer);
150             }
151         }
152 
153         return numDeletedEvents;
154     }
155 
156     /**
157      * Helper method for {@link #deleteDisallowedBuyerSignals}
158      *
159      * @return All buyers with signals in the DB.
160      */
161     @Query("SELECT DISTINCT buyer FROM protected_signals")
getAllBuyers()162     protected abstract List<AdTechIdentifier> getAllBuyers();
163 
164     /**
165      * Deletes all signals for the list of buyers. Helper method for {@link
166      * #deleteDisallowedBuyerSignals}
167      *
168      * @return Number of buyers deleted.
169      */
170     @Query("DELETE FROM protected_signals where buyer in (:buyers)")
deleteByBuyers(@onNull List<AdTechIdentifier> buyers)171     protected abstract int deleteByBuyers(@NonNull List<AdTechIdentifier> buyers);
172 
173     /**
174      * Deletes all signals belonging to disallowed source apps in a single transaction, where the
175      * source apps cannot be found in the app package name allowlist or are not installed on the
176      * device.
177      *
178      * @return the number of deleted signals
179      */
180     @Transaction
deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( @onNull PackageManager packageManager, @NonNull Flags flags, @NonNull Instant now)181     public int deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata(
182             @NonNull PackageManager packageManager, @NonNull Flags flags, @NonNull Instant now) {
183         Objects.requireNonNull(packageManager);
184         Objects.requireNonNull(flags);
185         Objects.requireNonNull(now);
186 
187         List<String> sourceAppsToRemove = getAllPackages();
188         if (sourceAppsToRemove.isEmpty()) {
189             return 0;
190         }
191 
192         CleanupUtils.removeAllowedPackages(
193                 sourceAppsToRemove, packageManager, Arrays.asList(flags.getPasAppAllowList()));
194 
195         int numDeletedEvents = 0;
196         if (!sourceAppsToRemove.isEmpty()) {
197             List<AdTechIdentifier> buyers = getBuyersForPackages(sourceAppsToRemove);
198             for (AdTechIdentifier buyer : buyers) {
199                 persistSignalsUpdateMetadata(
200                         DBSignalsUpdateMetadata.builder()
201                                 .setBuyer(buyer)
202                                 .setLastSignalsUpdatedTime(now)
203                                 .build());
204             }
205             numDeletedEvents = deleteSignalsByPackage(sourceAppsToRemove);
206             // TODO(b/300661099): Collect and send telemetry on signal deletion
207         }
208         return numDeletedEvents;
209     }
210 
211     /**
212      * Returns the list of all unique packages in the signals table.
213      *
214      * <p>This method is not meant to be called externally, but is a helper for {@link
215      * #deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata(PackageManager, Flags,
216      * Instant)}
217      */
218     @Query("SELECT DISTINCT packageName FROM protected_signals")
getAllPackages()219     protected abstract List<String> getAllPackages();
220 
221     /**
222      * Deletes all signals generated from the given packages.
223      *
224      * @return the number of deleted signals
225      */
226     @Query("DELETE FROM protected_signals WHERE packageName in (:packages)")
deleteSignalsByPackage(@onNull List<String> packages)227     public abstract int deleteSignalsByPackage(@NonNull List<String> packages);
228 
229     /** Deletes all signals */
230     @Query("DELETE FROM protected_signals")
deleteAllSignals()231     public abstract int deleteAllSignals();
232 
233     /** Returns all buyers for the given packages. */
234     @Query("SELECT DISTINCT buyer FROM protected_signals WHERE packageName in (:packages)")
getBuyersForPackages(@onNull List<String> packages)235     protected abstract List<AdTechIdentifier> getBuyersForPackages(@NonNull List<String> packages);
236 
237     /** Create or update a buyer metadata entry. */
238     @Insert(entity = DBSignalsUpdateMetadata.class, onConflict = OnConflictStrategy.REPLACE)
239     @VisibleForTesting
persistSignalsUpdateMetadata( DBSignalsUpdateMetadata dbSignalsUpdateMetadata)240     protected abstract long persistSignalsUpdateMetadata(
241             DBSignalsUpdateMetadata dbSignalsUpdateMetadata);
242 
243     /** Returns a metadata entry according to the buyer. */
244     @Query("SELECT * FROM signals_update_metadata WHERE buyer=:buyer")
getSignalsUpdateMetadata(AdTechIdentifier buyer)245     public abstract DBSignalsUpdateMetadata getSignalsUpdateMetadata(AdTechIdentifier buyer);
246 
247     /** Delete the metadata for the buyer. */
248     @Query("DELETE FROM signals_update_metadata WHERE buyer=:buyer")
deleteSignalsUpdateMetadata(AdTechIdentifier buyer)249     public abstract void deleteSignalsUpdateMetadata(AdTechIdentifier buyer);
250 
251     /** Delete all metadata in the storage. */
252     @Query("DELETE FROM signals_update_metadata")
deleteAllSignalsUpdateMetadata()253     public abstract void deleteAllSignalsUpdateMetadata();
254 }
255