diff options
Diffstat (limited to 'adservices/service-core/java/com/android/adservices/data/measurement/deletion/MeasurementDataDeleter.java')
-rw-r--r-- | adservices/service-core/java/com/android/adservices/data/measurement/deletion/MeasurementDataDeleter.java | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/deletion/MeasurementDataDeleter.java b/adservices/service-core/java/com/android/adservices/data/measurement/deletion/MeasurementDataDeleter.java new file mode 100644 index 0000000000..af13f872d5 --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/data/measurement/deletion/MeasurementDataDeleter.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.adservices.data.measurement.deletion; + +import android.adservices.measurement.DeletionParam; +import android.adservices.measurement.DeletionRequest; +import android.annotation.NonNull; +import android.net.Uri; + +import com.android.adservices.LogUtil; +import com.android.adservices.data.measurement.DatastoreException; +import com.android.adservices.data.measurement.DatastoreManager; +import com.android.adservices.data.measurement.IMeasurementDao; +import com.android.adservices.service.measurement.EventReport; +import com.android.adservices.service.measurement.Source; +import com.android.adservices.service.measurement.Trigger; +import com.android.adservices.service.measurement.aggregation.AggregateHistogramContribution; +import com.android.adservices.service.measurement.aggregation.AggregateReport; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; + +/** + * Facilitates deletion of measurement data from the database, for e.g. deletion of sources, + * triggers, reports, attributions. + */ +public class MeasurementDataDeleter { + static final String ANDROID_APP_SCHEME = "android-app"; + private static final int AGGREGATE_CONTRIBUTIONS_VALUE_MINIMUM_LIMIT = 0; + + private final DatastoreManager mDatastoreManager; + + public MeasurementDataDeleter(DatastoreManager datastoreManager) { + mDatastoreManager = datastoreManager; + } + + /** + * Deletes all measurement data owned by a registrant and optionally providing an origin uri + * and/or a range of dates. + * + * @param deletionParam contains registrant, time range, sites to consider for deletion + * @return true if deletion was successful, false otherwise + */ + public boolean delete(@NonNull DeletionParam deletionParam) { + return mDatastoreManager.runInTransaction( + (dao) -> { + List<String> sourceIds = + dao.fetchMatchingSources( + getRegistrant(deletionParam.getPackageName()), + deletionParam.getStart(), + deletionParam.getEnd(), + deletionParam.getOriginUris(), + deletionParam.getDomainUris(), + deletionParam.getMatchBehavior()); + List<String> triggerIds = + dao.fetchMatchingTriggers( + getRegistrant(deletionParam.getPackageName()), + deletionParam.getStart(), + deletionParam.getEnd(), + deletionParam.getOriginUris(), + deletionParam.getDomainUris(), + deletionParam.getMatchBehavior()); + + // Rest aggregate contributions and dedup keys on sources for triggers to be + // deleted. + List<AggregateReport> aggregateReports = + dao.fetchMatchingAggregateReports(sourceIds, triggerIds); + resetAggregateContributions(dao, aggregateReports); + + List<EventReport> eventReports = + dao.fetchMatchingEventReports(sourceIds, triggerIds); + resetDedupKeys(dao, eventReports); + + // Delete sources and triggers, that'll take care of deleting related reports + // and attributions + if (deletionParam.getDeletionMode() == DeletionRequest.DELETION_MODE_ALL) { + dao.deleteSources(sourceIds); + dao.deleteTriggers(triggerIds); + return; + } + + // Mark reports for deletion for DELETION_MODE_EXCLUDE_INTERNAL_DATA + for (EventReport eventReport : eventReports) { + dao.markEventReportStatus( + eventReport.getId(), EventReport.Status.MARKED_TO_DELETE); + } + + for (AggregateReport aggregateReport : aggregateReports) { + dao.markAggregateReportStatus( + aggregateReport.getId(), AggregateReport.Status.MARKED_TO_DELETE); + } + + // Finally mark sources and triggers for deletion + dao.updateSourceStatus(sourceIds, Source.Status.MARKED_TO_DELETE); + dao.updateTriggerStatus(triggerIds, Trigger.Status.MARKED_TO_DELETE); + }); + } + + @VisibleForTesting + void resetAggregateContributions( + @NonNull IMeasurementDao dao, @NonNull List<AggregateReport> aggregateReports) + throws DatastoreException { + for (AggregateReport report : aggregateReports) { + if (report.getSourceId() == null) { + LogUtil.e("SourceId is null on event report."); + return; + } + + Source source = dao.getSource(report.getSourceId()); + int aggregateHistogramContributionsSum = + report.getAggregateAttributionData().getContributions().stream() + .mapToInt(AggregateHistogramContribution::getValue) + .sum(); + + int newAggregateContributionsSum = + Math.max( + (source.getAggregateContributions() + - aggregateHistogramContributionsSum), + AGGREGATE_CONTRIBUTIONS_VALUE_MINIMUM_LIMIT); + + source.setAggregateContributions(newAggregateContributionsSum); + + // Update in the DB + dao.updateSourceAggregateContributions(source); + } + } + + @VisibleForTesting + void resetDedupKeys(@NonNull IMeasurementDao dao, @NonNull List<EventReport> eventReports) + throws DatastoreException { + for (EventReport report : eventReports) { + if (report.getSourceId() == null) { + LogUtil.e("SourceId on the event report is null."); + return; + } + + Source source = dao.getSource(report.getSourceId()); + source.getDedupKeys().remove(report.getTriggerDedupKey()); + dao.updateSourceDedupKeys(source); + } + } + + private Uri getRegistrant(String packageName) { + return Uri.parse(ANDROID_APP_SCHEME + "://" + packageName); + } +} |