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