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.measurement;
18 
19 import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR;
20 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT;
21 import static android.adservices.common.AdServicesStatusUtils.STATUS_IO_ERROR;
22 import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
23 
24 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_WIPEOUT;
25 
26 import android.adservices.adid.AdId;
27 import android.adservices.common.AdServicesStatusUtils;
28 import android.adservices.measurement.DeletionParam;
29 import android.adservices.measurement.RegistrationRequest;
30 import android.adservices.measurement.SourceRegistrationRequestInternal;
31 import android.adservices.measurement.WebSourceRegistrationRequest;
32 import android.adservices.measurement.WebSourceRegistrationRequestInternal;
33 import android.adservices.measurement.WebTriggerRegistrationRequest;
34 import android.adservices.measurement.WebTriggerRegistrationRequestInternal;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.WorkerThread;
38 import android.app.adservices.AdServicesManager;
39 import android.content.ComponentName;
40 import android.content.ContentResolver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.net.Uri;
44 import android.os.Build;
45 import android.os.SystemClock;
46 import android.view.InputEvent;
47 
48 import androidx.annotation.RequiresApi;
49 
50 import com.android.adservices.LoggerFactory;
51 import com.android.adservices.data.measurement.DatastoreManager;
52 import com.android.adservices.data.measurement.DatastoreManagerFactory;
53 import com.android.adservices.data.measurement.deletion.MeasurementDataDeleter;
54 import com.android.adservices.service.Flags;
55 import com.android.adservices.service.FlagsFactory;
56 import com.android.adservices.service.common.WebAddresses;
57 import com.android.adservices.service.measurement.inputverification.ClickVerifier;
58 import com.android.adservices.service.measurement.registration.EnqueueAsyncRegistration;
59 import com.android.adservices.service.measurement.rollback.MeasurementRollbackCompatManager;
60 import com.android.adservices.service.measurement.util.Applications;
61 import com.android.adservices.service.stats.AdServicesLoggerImpl;
62 import com.android.adservices.service.stats.MeasurementWipeoutStats;
63 import com.android.internal.annotations.VisibleForTesting;
64 import com.android.modules.utils.build.SdkLevel;
65 
66 import java.net.URISyntaxException;
67 import java.util.Collections;
68 import java.util.List;
69 import java.util.Objects;
70 import java.util.Optional;
71 import java.util.concurrent.locks.ReadWriteLock;
72 import java.util.concurrent.locks.ReentrantReadWriteLock;
73 
74 import javax.annotation.concurrent.ThreadSafe;
75 
76 /**
77  * This class is thread safe.
78  *
79  * @hide
80  */
81 // TODO(b/269798827): Enable for R.
82 @RequiresApi(Build.VERSION_CODES.S)
83 @ThreadSafe
84 @WorkerThread
85 public final class MeasurementImpl {
86     private static final String ANDROID_APP_SCHEME = "android-app";
87     private static volatile MeasurementImpl sMeasurementImpl;
88     private final Context mContext;
89     private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
90     private final DatastoreManager mDatastoreManager;
91     private final ContentResolver mContentResolver;
92     private final ClickVerifier mClickVerifier;
93     private final MeasurementDataDeleter mMeasurementDataDeleter;
94     private final Flags mFlags;
95 
96     @VisibleForTesting
MeasurementImpl(Context context)97     MeasurementImpl(Context context) {
98         mContext = context;
99         mDatastoreManager = DatastoreManagerFactory.getDatastoreManager(context);
100         mClickVerifier = new ClickVerifier(context);
101         mFlags = FlagsFactory.getFlags();
102         mMeasurementDataDeleter = new MeasurementDataDeleter(mDatastoreManager, mFlags);
103         mContentResolver = mContext.getContentResolver();
104         deleteOnRollback();
105     }
106 
107     @VisibleForTesting
MeasurementImpl( Context context, Flags flags, DatastoreManager datastoreManager, ClickVerifier clickVerifier, MeasurementDataDeleter measurementDataDeleter, ContentResolver contentResolver)108     public MeasurementImpl(
109             Context context,
110             Flags flags,
111             DatastoreManager datastoreManager,
112             ClickVerifier clickVerifier,
113             MeasurementDataDeleter measurementDataDeleter,
114             ContentResolver contentResolver) {
115         mContext = context;
116         mDatastoreManager = datastoreManager;
117         mClickVerifier = clickVerifier;
118         mMeasurementDataDeleter = measurementDataDeleter;
119         mFlags = flags;
120         mContentResolver = contentResolver;
121     }
122 
123     /**
124      * Gets an instance of MeasurementImpl to be used.
125      *
126      * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
127      * existing instance will be returned.
128      */
129     @NonNull
getInstance(Context context)130     public static MeasurementImpl getInstance(Context context) {
131         if (sMeasurementImpl == null) {
132             synchronized (MeasurementImpl.class) {
133                 if (sMeasurementImpl == null) {
134                     sMeasurementImpl = new MeasurementImpl(context);
135                 }
136             }
137         }
138         return sMeasurementImpl;
139     }
140 
141     /**
142      * Invoked when a package is installed.
143      *
144      * @param packageUri installed package {@link Uri}.
145      * @param eventTime  time when the package was installed.
146      */
doInstallAttribution(@onNull Uri packageUri, long eventTime)147     public void doInstallAttribution(@NonNull Uri packageUri, long eventTime) {
148         LoggerFactory.getMeasurementLogger().d("Attributing installation for: " + packageUri);
149         Uri appUri = getAppUri(packageUri);
150         mReadWriteLock.readLock().lock();
151         try {
152             mDatastoreManager.runInTransaction(
153                     (dao) -> dao.doInstallAttribution(appUri, eventTime));
154         } finally {
155             mReadWriteLock.readLock().unlock();
156         }
157     }
158 
159     /** Implement a registration request, returning a {@link AdServicesStatusUtils.StatusCode}. */
160     @AdServicesStatusUtils.StatusCode
register(@onNull RegistrationRequest request, boolean adIdPermission, long requestTime)161     int register(@NonNull RegistrationRequest request, boolean adIdPermission, long requestTime) {
162         mReadWriteLock.readLock().lock();
163         try {
164             switch (request.getRegistrationType()) {
165                 case RegistrationRequest.REGISTER_SOURCE:
166                 case RegistrationRequest.REGISTER_TRIGGER:
167                     return EnqueueAsyncRegistration.appSourceOrTriggerRegistrationRequest(
168                                     request,
169                                     adIdPermission,
170                                     getRegistrant(request.getAppPackageName()),
171                                     requestTime,
172                                     request.getRegistrationType()
173                                                     == RegistrationRequest.REGISTER_TRIGGER
174                                             ? null
175                                             : getSourceType(
176                                                     request.getInputEvent(),
177                                                     request.getRequestTime(),
178                                                     request.getAppPackageName()),
179                                     /* postBody */ null,
180                                     mDatastoreManager,
181                                     mContentResolver)
182                             ? STATUS_SUCCESS
183                             : STATUS_IO_ERROR;
184 
185                 default:
186                     return STATUS_INVALID_ARGUMENT;
187             }
188         } finally {
189             mReadWriteLock.readLock().unlock();
190         }
191     }
192 
193     /**
194      * Implement a sources registration request, returning a {@link
195      * AdServicesStatusUtils.StatusCode}.
196      */
197     @AdServicesStatusUtils.StatusCode
registerSources(@onNull SourceRegistrationRequestInternal request, long requestTime)198     int registerSources(@NonNull SourceRegistrationRequestInternal request, long requestTime) {
199         mReadWriteLock.readLock().lock();
200         try {
201             return EnqueueAsyncRegistration.appSourcesRegistrationRequest(
202                             request,
203                             isAdIdPermissionGranted(request.getAdIdValue()),
204                             getRegistrant(request.getAppPackageName()),
205                             requestTime,
206                             getSourceType(
207                                     request.getSourceRegistrationRequest().getInputEvent(),
208                                     request.getBootRelativeRequestTime(),
209                                     request.getAppPackageName()),
210                             /* postBody*/ null,
211                             mDatastoreManager,
212                             mContentResolver)
213                     ? STATUS_SUCCESS
214                     : STATUS_IO_ERROR;
215         } finally {
216             mReadWriteLock.readLock().unlock();
217         }
218     }
219 
220     /**
221      * Processes a source registration request delegated to OS from the caller, e.g. Chrome,
222      * returning a status code.
223      */
registerWebSource( @onNull WebSourceRegistrationRequestInternal request, boolean adIdPermission, long requestTime)224     int registerWebSource(
225             @NonNull WebSourceRegistrationRequestInternal request,
226             boolean adIdPermission,
227             long requestTime) {
228         WebSourceRegistrationRequest sourceRegistrationRequest =
229                 request.getSourceRegistrationRequest();
230         if (!isValid(sourceRegistrationRequest)) {
231             LoggerFactory.getMeasurementLogger().e("registerWebSource received invalid parameters");
232             return STATUS_INVALID_ARGUMENT;
233         }
234         mReadWriteLock.readLock().lock();
235         try {
236             boolean enqueueStatus =
237                     EnqueueAsyncRegistration.webSourceRegistrationRequest(
238                             sourceRegistrationRequest,
239                             adIdPermission,
240                             getRegistrant(request.getAppPackageName()),
241                             requestTime,
242                             getSourceType(
243                                     sourceRegistrationRequest.getInputEvent(),
244                                     request.getRequestTime(),
245                                     request.getAppPackageName()),
246                             mDatastoreManager,
247                             mContentResolver);
248             if (enqueueStatus) {
249                 return STATUS_SUCCESS;
250             } else {
251 
252                 return STATUS_IO_ERROR;
253             }
254         } finally {
255             mReadWriteLock.readLock().unlock();
256         }
257     }
258 
259     /**
260      * Processes a trigger registration request delegated to OS from the caller, e.g. Chrome,
261      * returning a status code.
262      */
registerWebTrigger( @onNull WebTriggerRegistrationRequestInternal request, boolean adIdPermission, long requestTime)263     int registerWebTrigger(
264             @NonNull WebTriggerRegistrationRequestInternal request,
265             boolean adIdPermission,
266             long requestTime) {
267         WebTriggerRegistrationRequest triggerRegistrationRequest =
268                 request.getTriggerRegistrationRequest();
269         if (!isValid(triggerRegistrationRequest)) {
270             LoggerFactory.getMeasurementLogger()
271                     .e("registerWebTrigger received invalid parameters");
272             return STATUS_INVALID_ARGUMENT;
273         }
274         mReadWriteLock.readLock().lock();
275         try {
276             boolean enqueueStatus =
277                     EnqueueAsyncRegistration.webTriggerRegistrationRequest(
278                             triggerRegistrationRequest,
279                             adIdPermission,
280                             getRegistrant(request.getAppPackageName()),
281                             requestTime,
282                             mDatastoreManager,
283                             mContentResolver);
284             if (enqueueStatus) {
285                 return STATUS_SUCCESS;
286             } else {
287 
288                 return STATUS_IO_ERROR;
289             }
290         } finally {
291             mReadWriteLock.readLock().unlock();
292         }
293     }
294 
295     /** Implement a source registration request from a report event */
registerEvent( @onNull Uri registrationUri, @NonNull String appPackageName, @NonNull String sdkPackageName, boolean isAdIdEnabled, @Nullable String postBody, @Nullable InputEvent inputEvent, @Nullable String adIdValue)296     public int registerEvent(
297             @NonNull Uri registrationUri,
298             @NonNull String appPackageName,
299             @NonNull String sdkPackageName,
300             boolean isAdIdEnabled,
301             @Nullable String postBody,
302             @Nullable InputEvent inputEvent,
303             @Nullable String adIdValue) {
304         Objects.requireNonNull(registrationUri);
305         Objects.requireNonNull(appPackageName);
306         Objects.requireNonNull(sdkPackageName);
307 
308         final long apiRequestTime = System.currentTimeMillis();
309         final RegistrationRequest.Builder builder =
310                 new RegistrationRequest.Builder(
311                                 RegistrationRequest.REGISTER_SOURCE,
312                                 registrationUri,
313                                 appPackageName,
314                                 sdkPackageName)
315                         .setAdIdPermissionGranted(isAdIdEnabled)
316                         .setRequestTime(SystemClock.uptimeMillis())
317                         .setAdIdValue(adIdValue);
318         RegistrationRequest request = builder.build();
319 
320         mReadWriteLock.readLock().lock();
321         try {
322             return EnqueueAsyncRegistration.appSourceOrTriggerRegistrationRequest(
323                             request,
324                             request.isAdIdPermissionGranted(),
325                             registrationUri,
326                             apiRequestTime,
327                             getSourceType(
328                                     inputEvent,
329                                     request.getRequestTime(),
330                                     request.getAppPackageName()),
331                             postBody,
332                             mDatastoreManager,
333                             mContentResolver)
334                     ? STATUS_SUCCESS
335                     : STATUS_IO_ERROR;
336         } finally {
337             mReadWriteLock.readLock().unlock();
338         }
339     }
340 
341     /**
342      * Implement a deleteRegistrations request, returning a r{@link
343      * AdServicesStatusUtils.StatusCode}.
344      */
345     @AdServicesStatusUtils.StatusCode
deleteRegistrations(@onNull DeletionParam request)346     int deleteRegistrations(@NonNull DeletionParam request) {
347         mReadWriteLock.readLock().lock();
348         try {
349             boolean deleteResult = mMeasurementDataDeleter.delete(request);
350             if (deleteResult) {
351                 markDeletion();
352             }
353             return deleteResult ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
354         } catch (NullPointerException | IllegalArgumentException e) {
355             LoggerFactory.getMeasurementLogger()
356                     .e(e, "Delete registration received invalid parameters");
357             return STATUS_INVALID_ARGUMENT;
358         } finally {
359             mReadWriteLock.readLock().unlock();
360         }
361     }
362 
363     /**
364      * Delete all records from a specific package and return a boolean value to indicate whether any
365      * data was deleted.
366      */
deletePackageRecords(Uri packageUri)367     public boolean deletePackageRecords(Uri packageUri) {
368         Uri appUri = getAppUri(packageUri);
369         LoggerFactory.getMeasurementLogger().d("Deleting records for " + appUri);
370         mReadWriteLock.writeLock().lock();
371         boolean didDeletionOccur = false;
372         try {
373             didDeletionOccur = mMeasurementDataDeleter.deleteAppUninstalledData(appUri);
374             if (didDeletionOccur) {
375                 markDeletion();
376             }
377         } catch (NullPointerException | IllegalArgumentException e) {
378             LoggerFactory.getMeasurementLogger()
379                     .e(e, "Delete package records received invalid parameters");
380         } finally {
381             mReadWriteLock.writeLock().unlock();
382         }
383         return didDeletionOccur;
384     }
385 
386     /**
387      * Delete all data generated by Measurement API, except for tables in the exclusion list.
388      *
389      * @param tablesToExclude a {@link List} of tables that won't be deleted.
390      */
deleteAllMeasurementData(@onNull List<String> tablesToExclude)391     public void deleteAllMeasurementData(@NonNull List<String> tablesToExclude) {
392         mReadWriteLock.writeLock().lock();
393         try {
394             mDatastoreManager.runInTransaction(
395                     (dao) -> dao.deleteAllMeasurementData(tablesToExclude));
396             LoggerFactory.getMeasurementLogger()
397                     .v(
398                             "All data is cleared for Measurement API except: %s",
399                             tablesToExclude.toString());
400             markDeletion();
401         } finally {
402             mReadWriteLock.writeLock().unlock();
403         }
404     }
405 
406     /** Delete all data generated from apps that are not currently installed. */
deleteAllUninstalledMeasurementData()407     public void deleteAllUninstalledMeasurementData() {
408         final List<Uri> installedAppList =
409                 Applications.getCurrentInstalledApplicationsList(mContext);
410 
411         final Optional<List<Uri>> uninstalledAppsOpt =
412                 mDatastoreManager.runInTransactionWithResult(
413                         (dao) -> dao.getUninstalledAppNamesHavingMeasurementData(installedAppList));
414 
415         if (uninstalledAppsOpt.isPresent()) {
416             for (Uri uninstalledAppName : uninstalledAppsOpt.get()) {
417                 deletePackageRecords(uninstalledAppName);
418             }
419         }
420     }
421 
isAdIdPermissionGranted(@ullable String adIdValue)422     private static boolean isAdIdPermissionGranted(@Nullable String adIdValue) {
423         return adIdValue != null && !adIdValue.isEmpty() && !AdId.ZERO_OUT.equals(adIdValue);
424     }
425 
426     @VisibleForTesting
getSourceType( InputEvent inputEvent, long requestTime, String sourceRegistrant)427     Source.SourceType getSourceType(
428             InputEvent inputEvent, long requestTime, String sourceRegistrant) {
429         // If click verification is enabled and the InputEvent is not null, but it cannot be
430         // verified, then the SourceType is demoted to EVENT.
431         if (mFlags.getMeasurementIsClickVerificationEnabled()
432                 && inputEvent != null
433                 && !mClickVerifier.isInputEventVerifiable(
434                         inputEvent, requestTime, sourceRegistrant)) {
435             return Source.SourceType.EVENT;
436         } else {
437             return inputEvent == null ? Source.SourceType.EVENT : Source.SourceType.NAVIGATION;
438         }
439     }
440 
getRegistrant(String packageName)441     private Uri getRegistrant(String packageName) {
442         return Uri.parse(ANDROID_APP_SCHEME + "://" + packageName);
443     }
444 
getAppUri(Uri packageUri)445     private Uri getAppUri(Uri packageUri) {
446         return packageUri.getScheme() == null
447                 ? Uri.parse(ANDROID_APP_SCHEME + "://" + packageUri.getEncodedSchemeSpecificPart())
448                 : packageUri;
449     }
450 
isValid(WebSourceRegistrationRequest sourceRegistrationRequest)451     private boolean isValid(WebSourceRegistrationRequest sourceRegistrationRequest) {
452         Uri verifiedDestination = sourceRegistrationRequest.getVerifiedDestination();
453         Uri webDestination = sourceRegistrationRequest.getWebDestination();
454 
455         if (verifiedDestination == null) {
456             return webDestination == null
457                     ? true
458                     : WebAddresses.topPrivateDomainAndScheme(webDestination).isPresent();
459         }
460 
461         return isVerifiedDestination(
462                 verifiedDestination, webDestination, sourceRegistrationRequest.getAppDestination());
463     }
464 
isVerifiedDestination( Uri verifiedDestination, Uri webDestination, Uri appDestination)465     private boolean isVerifiedDestination(
466             Uri verifiedDestination, Uri webDestination, Uri appDestination) {
467         String destinationPackage = null;
468         if (appDestination != null) {
469             destinationPackage = appDestination.getHost();
470         }
471         String verifiedScheme = verifiedDestination.getScheme();
472         String verifiedHost = verifiedDestination.getHost();
473 
474         // Verified destination matches appDestination value
475         if (destinationPackage != null
476                 && verifiedHost != null
477                 && (verifiedScheme == null || verifiedScheme.equals(ANDROID_APP_SCHEME))
478                 && verifiedHost.equals(destinationPackage)) {
479             return true;
480         }
481 
482         try {
483             Intent intent = Intent.parseUri(verifiedDestination.toString(), 0);
484             ComponentName componentName = intent.resolveActivity(mContext.getPackageManager());
485             if (componentName == null) {
486                 return false;
487             }
488 
489             // (ComponentName::getPackageName cannot be null)
490             String verifiedPackage = componentName.getPackageName();
491 
492             // Try to match an app vendor store and extract a target package
493             if (destinationPackage != null
494                     && verifiedPackage.equals(AppVendorPackages.PLAY_STORE)) {
495                 String targetPackage = getTargetPackageFromPlayStoreUri(verifiedDestination);
496                 return targetPackage != null && targetPackage.equals(destinationPackage);
497 
498             // Try to match web destination
499             } else if (webDestination == null) {
500                 return false;
501             } else {
502                 Optional<Uri> webDestinationTopPrivateDomainAndScheme =
503                         WebAddresses.topPrivateDomainAndScheme(webDestination);
504                 Optional<Uri> verifiedDestinationTopPrivateDomainAndScheme =
505                         WebAddresses.topPrivateDomainAndScheme(verifiedDestination);
506                 return webDestinationTopPrivateDomainAndScheme.isPresent()
507                         && verifiedDestinationTopPrivateDomainAndScheme.isPresent()
508                         && webDestinationTopPrivateDomainAndScheme.get().equals(
509                                 verifiedDestinationTopPrivateDomainAndScheme.get());
510             }
511         } catch (URISyntaxException e) {
512             LoggerFactory.getMeasurementLogger()
513                     .e(
514                             e,
515                             "MeasurementImpl::handleVerifiedDestination: failed to parse intent"
516                                     + " URI: %s",
517                             verifiedDestination.toString());
518             return false;
519         }
520     }
521 
isValid(WebTriggerRegistrationRequest triggerRegistrationRequest)522     private static boolean isValid(WebTriggerRegistrationRequest triggerRegistrationRequest) {
523         Uri destination = triggerRegistrationRequest.getDestination();
524         return WebAddresses.topPrivateDomainAndScheme(destination).isPresent();
525     }
526 
getTargetPackageFromPlayStoreUri(Uri uri)527     private static String getTargetPackageFromPlayStoreUri(Uri uri) {
528         return uri.getQueryParameter("id");
529     }
530 
531     private interface AppVendorPackages {
532         String PLAY_STORE = "com.android.vending";
533     }
534 
535     /**
536      * Checks if the module was rollback and if there was a deletion in the version rolled back
537      * from. If there was, delete all measurement data to prioritize user privacy.
538      */
deleteOnRollback()539     private void deleteOnRollback() {
540         if (FlagsFactory.getFlags().getMeasurementRollbackDeletionKillSwitch()) {
541             LoggerFactory.getMeasurementLogger()
542                     .e("Rollback deletion is disabled. Not checking system server for rollback.");
543             return;
544         }
545 
546         LoggerFactory.getMeasurementLogger().d("Checking rollback status.");
547         boolean needsToHandleRollbackReconciliation = checkIfNeedsToHandleReconciliation();
548         if (needsToHandleRollbackReconciliation) {
549             LoggerFactory.getMeasurementLogger()
550                     .d("Rollback and deletion detected, deleting all measurement data.");
551             mReadWriteLock.writeLock().lock();
552             boolean success;
553             try {
554                 success =
555                         mDatastoreManager.runInTransaction(
556                                 (dao) -> dao.deleteAllMeasurementData(Collections.emptyList()));
557             } finally {
558                 mReadWriteLock.writeLock().unlock();
559             }
560             if (success) {
561                 AdServicesLoggerImpl.getInstance()
562                         .logMeasurementWipeoutStats(
563                                 new MeasurementWipeoutStats.Builder()
564                                         .setCode(AD_SERVICES_MEASUREMENT_WIPEOUT)
565                                         .setWipeoutType(
566                                                 WipeoutStatus.WipeoutType.ROLLBACK_WIPEOUT_CAUSE
567                                                         .getValue())
568                                         .setSourceRegistrant("")
569                                         .build());
570             }
571         }
572     }
573 
574     @VisibleForTesting
checkIfNeedsToHandleReconciliation()575     boolean checkIfNeedsToHandleReconciliation() {
576         if (SdkLevel.isAtLeastT()) {
577             return AdServicesManager.getInstance(mContext)
578                     .needsToHandleRollbackReconciliation(AdServicesManager.MEASUREMENT_DELETION);
579         }
580 
581         // Not on Android T+. Check if flag is enabled if on R/S.
582         if (isMeasurementRollbackCompatDisabled()) {
583             LoggerFactory.getMeasurementLogger()
584                     .e("Rollback deletion disabled. Not checking compatible store for rollback.");
585             return false;
586         }
587 
588         return MeasurementRollbackCompatManager.getInstance(
589                         mContext, AdServicesManager.MEASUREMENT_DELETION)
590                 .needsToHandleRollbackReconciliation();
591     }
592 
593     /**
594      * Stores a bit in the system server indicating that a deletion happened for the current
595      * AdServices module version. This information is used for deleting data after it has been
596      * restored by a module rollback.
597      */
markDeletion()598     private void markDeletion() {
599         if (FlagsFactory.getFlags().getMeasurementRollbackDeletionKillSwitch()) {
600             LoggerFactory.getMeasurementLogger()
601                     .e("Rollback deletion is disabled. Not storing status in system server.");
602             return;
603         }
604 
605         if (SdkLevel.isAtLeastT()) {
606             LoggerFactory.getMeasurementLogger().d("Marking deletion in system server.");
607             AdServicesManager.getInstance(mContext)
608                     .recordAdServicesDeletionOccurred(AdServicesManager.MEASUREMENT_DELETION);
609             return;
610         }
611 
612         // If on Android R/S, check if the appropriate flag is enabled, otherwise do nothing.
613         if (isMeasurementRollbackCompatDisabled()) {
614             LoggerFactory.getMeasurementLogger()
615                     .e("Rollback deletion disabled. Not storing status in compatible store.");
616             return;
617         }
618 
619         MeasurementRollbackCompatManager.getInstance(
620                         mContext, AdServicesManager.MEASUREMENT_DELETION)
621                 .recordAdServicesDeletionOccurred();
622     }
623 
isMeasurementRollbackCompatDisabled()624     private boolean isMeasurementRollbackCompatDisabled() {
625         if (SdkLevel.isAtLeastT()) {
626             // This method should never be called on T+.
627             return true;
628         }
629 
630         Flags flags = FlagsFactory.getFlags();
631         return SdkLevel.isAtLeastS()
632                 ? flags.getMeasurementRollbackDeletionAppSearchKillSwitch()
633                 : !flags.getMeasurementRollbackDeletionREnabled();
634     }
635 }
636