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.consent; 18 19 import static com.android.adservices.AdServicesCommon.ADEXTSERVICES_PACKAGE_NAME_SUFFIX; 20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_SEARCH_DATA_MIGRATION_FAILURE; 21 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_RESET_FAILURE; 22 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE; 23 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX; 24 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_WIPEOUT; 25 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU; 26 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; 27 28 import android.annotation.IntDef; 29 import android.annotation.Nullable; 30 import android.app.adservices.AdServicesManager; 31 import android.app.job.JobScheduler; 32 import android.content.Context; 33 import android.content.SharedPreferences; 34 import android.os.Build; 35 import android.os.SystemClock; 36 37 import androidx.annotation.RequiresApi; 38 39 import com.android.adservices.LogUtil; 40 import com.android.adservices.concurrency.AdServicesExecutors; 41 import com.android.adservices.data.adselection.AppInstallDao; 42 import com.android.adservices.data.adselection.FrequencyCapDao; 43 import com.android.adservices.data.adselection.SharedStorageDatabase; 44 import com.android.adservices.data.common.LegacyAtomicFileDatastoreFactory; 45 import com.android.adservices.data.consent.AppConsentDao; 46 import com.android.adservices.data.customaudience.CustomAudienceDao; 47 import com.android.adservices.data.customaudience.CustomAudienceDatabase; 48 import com.android.adservices.data.enrollment.EnrollmentDao; 49 import com.android.adservices.data.signals.ProtectedSignalsDao; 50 import com.android.adservices.data.signals.ProtectedSignalsDatabase; 51 import com.android.adservices.data.topics.Topic; 52 import com.android.adservices.data.topics.TopicsTables; 53 import com.android.adservices.errorlogging.AdServicesErrorLoggerImpl; 54 import com.android.adservices.errorlogging.ErrorLogUtil; 55 import com.android.adservices.service.DebugFlags; 56 import com.android.adservices.service.Flags; 57 import com.android.adservices.service.FlagsFactory; 58 import com.android.adservices.service.appsearch.AppSearchConsentStorageManager; 59 import com.android.adservices.service.common.BackgroundJobsManager; 60 import com.android.adservices.service.common.UserProfileIdManager; 61 import com.android.adservices.service.common.compat.FileCompatUtils; 62 import com.android.adservices.service.common.feature.PrivacySandboxFeatureType; 63 import com.android.adservices.service.measurement.MeasurementImpl; 64 import com.android.adservices.service.measurement.WipeoutStatus; 65 import com.android.adservices.service.stats.AdServicesLoggerImpl; 66 import com.android.adservices.service.stats.ConsentMigrationStats; 67 import com.android.adservices.service.stats.MeasurementWipeoutStats; 68 import com.android.adservices.service.stats.StatsdAdServicesLogger; 69 import com.android.adservices.service.stats.UiStatsLogger; 70 import com.android.adservices.service.topics.TopicsWorker; 71 import com.android.adservices.service.ui.data.UxStatesDao; 72 import com.android.adservices.service.ui.enrollment.collection.PrivacySandboxEnrollmentChannelCollection; 73 import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection; 74 import com.android.adservices.shared.common.ApplicationContextSingleton; 75 import com.android.adservices.shared.errorlogging.AdServicesErrorLogger; 76 import com.android.adservices.shared.storage.AtomicFileDatastore; 77 import com.android.internal.annotations.VisibleForTesting; 78 import com.android.modules.utils.build.SdkLevel; 79 80 import com.google.common.collect.ImmutableList; 81 82 import java.io.IOException; 83 import java.io.PrintWriter; 84 import java.lang.annotation.Retention; 85 import java.lang.annotation.RetentionPolicy; 86 import java.util.ArrayList; 87 import java.util.HashMap; 88 import java.util.List; 89 import java.util.Map; 90 import java.util.Objects; 91 import java.util.stream.Collectors; 92 93 /** 94 * Manager all critical user data such as per API consent. 95 * 96 * <p>For Beta the consent is given for all {@link AdServicesApiType} or for none. 97 * 98 * <p>Currently there are three types of source of truth to store consent data, 99 * 100 * <ul> 101 * <li>SYSTEM_SERVER_ONLY: Write and read consent from system server only. 102 * <li>PPAPI_ONLY: Write and read consent from PPAPI only. 103 * <li>PPAPI_AND_SYSTEM_SERVER: Write consent to both PPAPI and system server. Read consent from 104 * system server only. 105 * <li>APPSEARCH_ONLY: Write and read consent from appSearch only for back compat. 106 * </ul> 107 * 108 * IMPORTANT: Until ConsentManagerV2 is launched, keep in sync with ConsentManager. 109 */ 110 // TODO(b/279042385): move UI logs to UI. 111 @RequiresApi(Build.VERSION_CODES.S) 112 public final class ConsentManagerV2 { 113 114 // Used on dump() / log only 115 private static int sDataMigrationDurationMs; 116 private static int sInstantiationDurationMs; 117 118 private static volatile ConsentManagerV2 sConsentManager; 119 120 @IntDef(value = {NO_MANUAL_INTERACTIONS_RECORDED, UNKNOWN, MANUAL_INTERACTIONS_RECORDED}) 121 @Retention(RetentionPolicy.SOURCE) 122 public @interface UserManualInteraction {} 123 124 public static final int NO_MANUAL_INTERACTIONS_RECORDED = -1; 125 public static final int UNKNOWN = 0; 126 public static final int MANUAL_INTERACTIONS_RECORDED = 1; 127 128 private final Flags mFlags; 129 private final DebugFlags mDebugFlags; 130 private final TopicsWorker mTopicsWorker; 131 private final AtomicFileDatastore mDatastore; 132 private final EnrollmentDao mEnrollmentDao; 133 private final MeasurementImpl mMeasurementImpl; 134 private final CustomAudienceDao mCustomAudienceDao; 135 private final AppInstallDao mAppInstallDao; 136 private final ProtectedSignalsDao mProtectedSignalsDao; 137 private final FrequencyCapDao mFrequencyCapDao; 138 private final AdServicesStorageManager mAdServicesStorageManager; 139 private final AppSearchConsentStorageManager mAppSearchConsentStorageManager; 140 private final UserProfileIdManager mUserProfileIdManager; 141 142 private static final Object LOCK = new Object(); 143 144 private ConsentCompositeStorage mConsentCompositeStorage; 145 146 private AppConsentStorageManager mAppConsentStorageManager; 147 ConsentManagerV2( TopicsWorker topicsWorker, AppConsentDao appConsentDao, EnrollmentDao enrollmentDao, MeasurementImpl measurementImpl, CustomAudienceDao customAudienceDao, AppConsentStorageManager appConsentStorageManager, AppInstallDao appInstallDao, ProtectedSignalsDao protectedSignalsDao, FrequencyCapDao frequencyCapDao, AdServicesStorageManager adServicesStorageManager, AtomicFileDatastore atomicFileDatastore, AppSearchConsentStorageManager appSearchConsentStorageManager, UserProfileIdManager userProfileIdManager, Flags flags, DebugFlags debugFlags, @Flags.ConsentSourceOfTruth int consentSourceOfTruth, boolean enableAppsearchConsentData)148 ConsentManagerV2( 149 TopicsWorker topicsWorker, 150 AppConsentDao appConsentDao, 151 EnrollmentDao enrollmentDao, 152 MeasurementImpl measurementImpl, 153 CustomAudienceDao customAudienceDao, 154 AppConsentStorageManager appConsentStorageManager, 155 AppInstallDao appInstallDao, 156 ProtectedSignalsDao protectedSignalsDao, 157 FrequencyCapDao frequencyCapDao, 158 AdServicesStorageManager adServicesStorageManager, 159 AtomicFileDatastore atomicFileDatastore, 160 AppSearchConsentStorageManager appSearchConsentStorageManager, 161 UserProfileIdManager userProfileIdManager, 162 Flags flags, 163 DebugFlags debugFlags, 164 @Flags.ConsentSourceOfTruth int consentSourceOfTruth, 165 boolean enableAppsearchConsentData) { 166 Objects.requireNonNull(topicsWorker); 167 Objects.requireNonNull(appConsentDao); 168 Objects.requireNonNull(measurementImpl); 169 Objects.requireNonNull(customAudienceDao); 170 Objects.requireNonNull(appInstallDao); 171 Objects.requireNonNull(protectedSignalsDao); 172 Objects.requireNonNull(frequencyCapDao); 173 Objects.requireNonNull(atomicFileDatastore); 174 Objects.requireNonNull(userProfileIdManager); 175 176 if (consentSourceOfTruth != Flags.PPAPI_ONLY 177 && consentSourceOfTruth != Flags.APPSEARCH_ONLY) { 178 Objects.requireNonNull(adServicesStorageManager); 179 } 180 181 if (enableAppsearchConsentData) { 182 Objects.requireNonNull(appSearchConsentStorageManager); 183 } 184 185 mAdServicesStorageManager = adServicesStorageManager; 186 mTopicsWorker = topicsWorker; 187 mDatastore = atomicFileDatastore; 188 mEnrollmentDao = enrollmentDao; 189 mMeasurementImpl = measurementImpl; 190 mCustomAudienceDao = customAudienceDao; 191 mAppInstallDao = appInstallDao; 192 mProtectedSignalsDao = protectedSignalsDao; 193 mFrequencyCapDao = frequencyCapDao; 194 195 mAppSearchConsentStorageManager = appSearchConsentStorageManager; 196 mUserProfileIdManager = userProfileIdManager; 197 198 mFlags = flags; 199 mDebugFlags = debugFlags; 200 mAppConsentStorageManager = appConsentStorageManager; 201 202 mConsentCompositeStorage = 203 new ConsentCompositeStorage(getStorageListBySourceOfTruth(consentSourceOfTruth)); 204 } 205 getStorageListBySourceOfTruth( @lags.ConsentSourceOfTruth int consentSourceOfTruth)206 private ImmutableList<IConsentStorage> getStorageListBySourceOfTruth( 207 @Flags.ConsentSourceOfTruth int consentSourceOfTruth) { 208 switch (consentSourceOfTruth) { 209 case Flags.PPAPI_ONLY: 210 return ImmutableList.of(mAppConsentStorageManager); 211 case Flags.SYSTEM_SERVER_ONLY: 212 return ImmutableList.of(mAdServicesStorageManager); 213 case Flags.PPAPI_AND_SYSTEM_SERVER: 214 // System storage has higher priority 215 return ImmutableList.of(mAdServicesStorageManager, mAppConsentStorageManager); 216 case Flags.APPSEARCH_ONLY: 217 return ImmutableList.of(mAppSearchConsentStorageManager); 218 default: 219 LogUtil.e(ConsentConstants.ERROR_MESSAGE_INVALID_CONSENT_SOURCE_OF_TRUTH); 220 return ImmutableList.of(); 221 } 222 } 223 224 /** 225 * Gets an instance of {@link ConsentManagerV2} to be used. 226 * 227 * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the 228 * existing instance will be returned. 229 */ 230 // TODO: apply the lazy initialization to this class b/366283605 getInstance()231 public static ConsentManagerV2 getInstance() { 232 Context context = ApplicationContextSingleton.get(); 233 234 if (sConsentManager == null) { 235 synchronized (LOCK) { 236 if (sConsentManager == null) { 237 long startedTime = SystemClock.uptimeMillis(); 238 // Execute one-time consent migration if needed. 239 int consentSourceOfTruth = FlagsFactory.getFlags().getConsentSourceOfTruth(); 240 AtomicFileDatastore datastore = 241 createAndInitializeDataStore( 242 context, AdServicesErrorLoggerImpl.getInstance()); 243 AdServicesStorageManager adServicesManager = 244 AdServicesStorageManager.getInstance( 245 AdServicesManager.getInstance(context)); 246 AppConsentDao appConsentDao = AppConsentDao.getInstance(); 247 248 // It is possible that the old value of the flag lingers after OTA until the 249 // first PH sync. In that case, we should not use the stale value, but use the 250 // default instead. The next PH sync will restore the T+ value. 251 if (SdkLevel.isAtLeastT() && consentSourceOfTruth == Flags.APPSEARCH_ONLY) { 252 consentSourceOfTruth = Flags.DEFAULT_CONSENT_SOURCE_OF_TRUTH; 253 } 254 AppSearchConsentStorageManager appSearchConsentStorageManager = null; 255 StatsdAdServicesLogger statsdAdServicesLogger = 256 StatsdAdServicesLogger.getInstance(); 257 // Flag enable_appsearch_consent_data is true on S- and T+ only when we want to 258 // use AppSearch to write to or read from. 259 boolean enableAppsearchConsentData = 260 FlagsFactory.getFlags().getEnableAppsearchConsentData(); 261 if (enableAppsearchConsentData) { 262 appSearchConsentStorageManager = 263 AppSearchConsentStorageManager.getInstance(); 264 handleConsentMigrationFromAppSearchIfNeeded( 265 context, 266 datastore, 267 appConsentDao, 268 appSearchConsentStorageManager, 269 adServicesManager, 270 statsdAdServicesLogger); 271 } 272 UxStatesDao uxStatesDao = UxStatesDao.getInstance(); 273 274 // Attempt to migrate consent data from PPAPI to System server if needed. 275 handleConsentMigrationIfNeeded( 276 context, 277 datastore, 278 adServicesManager, 279 statsdAdServicesLogger, 280 consentSourceOfTruth); 281 282 AppConsentStorageManager appConsentStorageManager = 283 new AppConsentStorageManager(datastore, appConsentDao, uxStatesDao); 284 long postDataMigrationTime = SystemClock.uptimeMillis(); 285 sDataMigrationDurationMs = (int) (postDataMigrationTime - startedTime); 286 sConsentManager = 287 new ConsentManagerV2( 288 TopicsWorker.getInstance(), 289 appConsentDao, 290 EnrollmentDao.getInstance(), 291 MeasurementImpl.getInstance(), 292 CustomAudienceDatabase.getInstance().customAudienceDao(), 293 appConsentStorageManager, 294 SharedStorageDatabase.getInstance().appInstallDao(), 295 ProtectedSignalsDatabase.getInstance().protectedSignalsDao(), 296 SharedStorageDatabase.getInstance().frequencyCapDao(), 297 adServicesManager, 298 datastore, 299 appSearchConsentStorageManager, 300 UserProfileIdManager.getInstance(), 301 // TODO(b/260601944): Remove Flag Instance. 302 FlagsFactory.getFlags(), 303 DebugFlags.getInstance(), 304 consentSourceOfTruth, 305 enableAppsearchConsentData); 306 sInstantiationDurationMs = 307 (int) (postDataMigrationTime - sDataMigrationDurationMs); 308 LogUtil.d( 309 "finished consent manager initialization: data migration in %dms," 310 + " instantiation in %dms", 311 sDataMigrationDurationMs, sInstantiationDurationMs); 312 } 313 } 314 } 315 return sConsentManager; 316 } 317 318 /** 319 * Enables all PP API services. It gives consent to Topics, Fledge and Measurements services. 320 * 321 * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To 322 * write to system server consent if source of truth is system server or dual sources. 323 */ enable(Context context)324 public void enable(Context context) { 325 Objects.requireNonNull(context); 326 327 // Check current value, if it is already enabled, skip this enable process. so that the Api 328 // won't be reset. Only add this logic to "enable" not "disable", since if it already 329 // disabled, there is no harm to reset the api again. 330 if (mFlags.getConsentManagerLazyEnableMode() && getConsentFromSourceOfTruth()) { 331 LogUtil.d("CONSENT_KEY already enable. Skipping enable process."); 332 return; 333 } 334 UiStatsLogger.logOptInSelected(); 335 336 BackgroundJobsManager.scheduleAllBackgroundJobs(context); 337 try { 338 // reset all state data which should be removed 339 resetTopicsAndBlockedTopics(); 340 resetAppsAndBlockedApps(); 341 resetMeasurement(); 342 resetUserProfileId(); 343 mUserProfileIdManager.getOrCreateId(); 344 } catch (IOException e) { 345 throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); 346 } 347 setConsentToSourceOfTruth(/* isGiven */ true); 348 } 349 350 /** 351 * Disables all PP API services. It revokes consent to Topics, Fledge and Measurements services. 352 * 353 * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To 354 * write to system server consent if source of truth is system server or dual sources. 355 */ disable(Context context)356 public void disable(Context context) { 357 Objects.requireNonNull(context); 358 UiStatsLogger.logOptOutSelected(); 359 // Disable all the APIs 360 try { 361 // reset all data 362 resetTopicsAndBlockedTopics(); 363 resetAppsAndBlockedApps(); 364 resetMeasurement(); 365 resetEnrollment(); 366 resetUserProfileId(); 367 368 BackgroundJobsManager.unscheduleAllBackgroundJobs( 369 context.getSystemService(JobScheduler.class)); 370 } catch (IOException e) { 371 throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); 372 } 373 setConsentToSourceOfTruth(/* isGiven */ false); 374 } 375 376 /** 377 * Enables the {@code apiType} PP API service. It gives consent to an API which is provided in 378 * the parameter. 379 * 380 * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To 381 * write to system server consent if source of truth is system server or dual sources. 382 * 383 * @param context Context of the application. 384 * @param apiType Type of the API (Topics, Fledge, Measurement) which should be enabled. 385 */ enable(Context context, AdServicesApiType apiType)386 public void enable(Context context, AdServicesApiType apiType) { 387 Objects.requireNonNull(context); 388 // Check current value, if it is already enabled, skip this enable process. so that the Api 389 // won't be reset. 390 if (mFlags.getConsentManagerLazyEnableMode() 391 && getPerApiConsentFromSourceOfTruth(apiType)) { 392 LogUtil.d( 393 "ApiType: is %s already enable. Skipping enable process.", 394 apiType.toPpApiDatastoreKey()); 395 return; 396 } 397 398 UiStatsLogger.logOptInSelected(apiType); 399 400 BackgroundJobsManager.scheduleJobsPerApi(context, apiType); 401 402 try { 403 // reset all state data which should be removed 404 resetByApi(apiType); 405 406 if (AdServicesApiType.FLEDGE == apiType) { 407 mUserProfileIdManager.getOrCreateId(); 408 } 409 } catch (IOException e) { 410 throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); 411 } 412 413 setPerApiConsentToSourceOfTruth(/* isGiven */ true, apiType); 414 } 415 416 /** 417 * Disables {@code apiType} PP API service. It revokes consent to an API which is provided in 418 * the parameter. 419 * 420 * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To 421 * write to system server consent if source of truth is system server or dual sources. 422 */ disable(Context context, AdServicesApiType apiType)423 public void disable(Context context, AdServicesApiType apiType) { 424 Objects.requireNonNull(context); 425 426 UiStatsLogger.logOptOutSelected(apiType); 427 428 try { 429 resetByApi(apiType); 430 BackgroundJobsManager.unscheduleJobsPerApi( 431 context.getSystemService(JobScheduler.class), apiType); 432 } catch (IOException e) { 433 throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); 434 } 435 436 setPerApiConsentToSourceOfTruth(/* isGiven */ false, apiType); 437 438 if (areAllApisDisabled()) { 439 BackgroundJobsManager.unscheduleAllBackgroundJobs( 440 context.getSystemService(JobScheduler.class)); 441 } 442 } 443 areAllApisDisabled()444 private boolean areAllApisDisabled() { 445 if (getConsent(AdServicesApiType.TOPICS).isGiven() 446 || getConsent(AdServicesApiType.MEASUREMENTS).isGiven() 447 || getConsent(AdServicesApiType.FLEDGE).isGiven()) { 448 return false; 449 } 450 return true; 451 } 452 453 /** 454 * Retrieves the consent for all PP API services. 455 * 456 * <p>To read from PPAPI consent if source of truth is PPAPI. To read from system server consent 457 * if source of truth is system server or dual sources. 458 * 459 * @return AdServicesApiConsent the consent 460 */ getConsent()461 public AdServicesApiConsent getConsent() { 462 if (mDebugFlags.getConsentManagerDebugMode()) { 463 return AdServicesApiConsent.GIVEN; 464 } 465 return mConsentCompositeStorage.getConsent(AdServicesApiType.ALL_API); 466 } 467 468 /** 469 * Retrieves the consent per API. 470 * 471 * @param apiType apiType for which the consent should be provided 472 * @return {@link AdServicesApiConsent} providing information whether the consent was given or 473 * revoked. 474 */ getConsent(AdServicesApiType apiType)475 public AdServicesApiConsent getConsent(AdServicesApiType apiType) { 476 if (mDebugFlags.getConsentManagerDebugMode()) { 477 return AdServicesApiConsent.GIVEN; 478 } 479 return mConsentCompositeStorage.getConsent(apiType); 480 } 481 482 /** 483 * Proxy call to {@link TopicsWorker} to get {@link ImmutableList} of {@link Topic}s which could 484 * be returned to the {@link TopicsWorker} clients. 485 * 486 * @return {@link ImmutableList} of {@link Topic}s. 487 */ getKnownTopicsWithConsent()488 public ImmutableList<Topic> getKnownTopicsWithConsent() { 489 return mTopicsWorker.getKnownTopicsWithConsent(); 490 } 491 492 /** 493 * Proxy call to {@link TopicsWorker} to get {@link ImmutableList} of {@link Topic}s which were 494 * blocked by the user. 495 * 496 * @return {@link ImmutableList} of blocked {@link Topic}s. 497 */ getTopicsWithRevokedConsent()498 public ImmutableList<Topic> getTopicsWithRevokedConsent() { 499 return mTopicsWorker.getTopicsWithRevokedConsent(); 500 } 501 502 /** 503 * Proxy call to {@link TopicsWorker} to revoke consent for provided {@link Topic} (block 504 * topic). 505 * 506 * @param topic {@link Topic} to block. 507 */ revokeConsentForTopic(Topic topic)508 public void revokeConsentForTopic(Topic topic) { 509 mTopicsWorker.revokeConsentForTopic(topic); 510 } 511 512 /** 513 * Proxy call to {@link TopicsWorker} to restore consent for provided {@link Topic} (unblock the 514 * topic). 515 * 516 * @param topic {@link Topic} to restore consent for. 517 */ restoreConsentForTopic(Topic topic)518 public void restoreConsentForTopic(Topic topic) { 519 mTopicsWorker.restoreConsentForTopic(topic); 520 } 521 522 /** Wipes out all the data gathered by Topics API but blocked topics. */ resetTopics()523 public void resetTopics() { 524 ArrayList<String> tablesToBlock = new ArrayList<>(); 525 tablesToBlock.add(TopicsTables.BlockedTopicsContract.TABLE); 526 mTopicsWorker.clearAllTopicsData(tablesToBlock); 527 } 528 529 /** Wipes out all the data gathered by Topics API. */ resetTopicsAndBlockedTopics()530 public void resetTopicsAndBlockedTopics() { 531 mTopicsWorker.clearAllTopicsData(new ArrayList<>()); 532 } 533 534 /** 535 * @return an {@link ImmutableList} of all known apps in the database that have not had user 536 * consent revoked 537 */ getKnownAppsWithConsent()538 public ImmutableList<App> getKnownAppsWithConsent() { 539 return ImmutableList.copyOf( 540 mConsentCompositeStorage.getKnownAppsWithConsent().stream() 541 .map(App::create) 542 .collect(Collectors.toList())); 543 } 544 545 /** 546 * @return an {@link ImmutableList} of all known apps in the database that have had user consent 547 * revoked 548 */ getAppsWithRevokedConsent()549 public ImmutableList<App> getAppsWithRevokedConsent() { 550 return ImmutableList.copyOf( 551 mConsentCompositeStorage.getAppsWithRevokedConsent().stream() 552 .map(App::create) 553 .collect(Collectors.toList())); 554 } 555 556 /** 557 * Proxy call to {@link AppConsentDao} to revoke consent for provided {@link App}. 558 * 559 * <p>Also clears all app data related to the provided {@link App}. 560 * 561 * @param app {@link App} to block. 562 * @throws IOException if the operation fails 563 */ revokeConsentForApp(App app)564 public void revokeConsentForApp(App app) throws IOException { 565 mConsentCompositeStorage.setConsentForApp(app.getPackageName(), true); 566 567 asyncExecute( 568 () -> 569 mCustomAudienceDao.deleteCustomAudienceDataByOwner( 570 app.getPackageName(), 571 mFlags.getFledgeScheduleCustomAudienceUpdateEnabled())); 572 if (mFlags.getFledgeFrequencyCapFilteringEnabled()) { 573 asyncExecute( 574 () -> mFrequencyCapDao.deleteHistogramDataBySourceApp(app.getPackageName())); 575 } 576 if (mFlags.getFledgeAppInstallFilteringEnabled()) { 577 asyncExecute(() -> mAppInstallDao.deleteByPackageName(app.getPackageName())); 578 } 579 } 580 581 /** 582 * Proxy call to {@link AppConsentDao} to restore consent for provided {@link App}. 583 * 584 * @param app {@link App} to restore consent for. 585 * @throws IOException if the operation fails 586 */ restoreConsentForApp(App app)587 public void restoreConsentForApp(App app) throws IOException { 588 mConsentCompositeStorage.setConsentForApp(app.getPackageName(), false); 589 } 590 591 /** 592 * Deletes all app consent data and all app data gathered or generated by the Privacy Sandbox. 593 * 594 * <p>This should be called when the Privacy Sandbox has been disabled. 595 * 596 * @throws IOException if the operation fails 597 */ resetAppsAndBlockedApps()598 public void resetAppsAndBlockedApps() throws IOException { 599 mConsentCompositeStorage.clearAllAppConsentData(); 600 601 asyncExecute( 602 () -> 603 mCustomAudienceDao.deleteAllCustomAudienceData( 604 mFlags.getFledgeScheduleCustomAudienceUpdateEnabled())); 605 if (mFlags.getFledgeFrequencyCapFilteringEnabled()) { 606 asyncExecute(mFrequencyCapDao::deleteAllHistogramData); 607 } 608 if (mFlags.getFledgeAppInstallFilteringEnabled()) { 609 asyncExecute(mAppInstallDao::deleteAllAppInstallData); 610 } 611 if (mFlags.getProtectedSignalsCleanupEnabled()) { 612 asyncExecute(mProtectedSignalsDao::deleteAllSignals); 613 } 614 } 615 616 /** 617 * Deletes the list of known allowed apps as well as all app data from the Privacy Sandbox. 618 * 619 * <p>The list of blocked apps is not reset. 620 * 621 * @throws IOException if the operation fails 622 */ resetApps()623 public void resetApps() throws IOException { 624 mConsentCompositeStorage.clearKnownAppsWithConsent(); 625 asyncExecute( 626 () -> 627 mCustomAudienceDao.deleteAllCustomAudienceData( 628 mFlags.getFledgeScheduleCustomAudienceUpdateEnabled())); 629 if (mFlags.getFledgeFrequencyCapFilteringEnabled()) { 630 asyncExecute(mFrequencyCapDao::deleteAllHistogramData); 631 } 632 if (mFlags.getFledgeAppInstallFilteringEnabled()) { 633 asyncExecute(mAppInstallDao::deleteAllAppInstallData); 634 } 635 } 636 637 /** 638 * Checks whether a single given installed application (identified by its package name) has had 639 * user consent to use the FLEDGE APIs revoked. 640 * 641 * <p>This method also checks whether a user has opted out of the FLEDGE Privacy Sandbox 642 * initiative. 643 * 644 * @param packageName String package name that uniquely identifies an installed application to 645 * check 646 * @return {@code true} if either the FLEDGE Privacy Sandbox initiative has been opted out or if 647 * the user has revoked consent for the given application to use the FLEDGE APIs 648 * @throws IllegalArgumentException if the package name is invalid or not found as an installed 649 * application 650 */ isFledgeConsentRevokedForApp(String packageName)651 public boolean isFledgeConsentRevokedForApp(String packageName) 652 throws IllegalArgumentException { 653 AdServicesApiConsent consent = getConsent(AdServicesApiType.FLEDGE); 654 655 if (!consent.isGiven()) { 656 return true; 657 } 658 659 return mConsentCompositeStorage.isConsentRevokedForApp(packageName); 660 } 661 662 /** 663 * Persists the use of a FLEDGE API by a single given installed application (identified by its 664 * package name) if the app has not already had its consent revoked. 665 * 666 * <p>This method also checks whether a user has opted out of the FLEDGE Privacy Sandbox 667 * initiative. 668 * 669 * <p>This is only meant to be called by the FLEDGE APIs. 670 * 671 * @param packageName String package name that uniquely identifies an installed application that 672 * has used a FLEDGE API 673 * @return {@code true} if user consent has been revoked for the application or API, {@code 674 * false} otherwise 675 * @throws IllegalArgumentException if the package name is invalid or not found as an installed 676 * application 677 */ isFledgeConsentRevokedForAppAfterSettingFledgeUse(String packageName)678 public boolean isFledgeConsentRevokedForAppAfterSettingFledgeUse(String packageName) 679 throws IllegalArgumentException { 680 AdServicesApiConsent consent = getConsent(AdServicesApiType.FLEDGE); 681 682 if (!consent.isGiven()) { 683 return true; 684 } 685 686 return mConsentCompositeStorage.setConsentForAppIfNew(packageName, false); 687 } 688 689 /** 690 * Clear consent data after an app was uninstalled. 691 * 692 * @param packageName the package name that had been uninstalled. 693 */ clearConsentForUninstalledApp(String packageName, int packageUid)694 public void clearConsentForUninstalledApp(String packageName, int packageUid) { 695 mConsentCompositeStorage.clearConsentForUninstalledApp(packageName, packageUid); 696 } 697 698 /** 699 * Clear consent data after an app was uninstalled, but the package Uid is unavailable. This 700 * could happen because the INTERACT_ACROSS_USERS_FULL permission is not available on Android 701 * versions prior to T. 702 * 703 * <p><strong>This method should only be used for R/S back-compat scenarios.</strong> 704 * 705 * @param packageName the package name that had been uninstalled. 706 */ clearConsentForUninstalledApp(String packageName)707 public void clearConsentForUninstalledApp(String packageName) { 708 mConsentCompositeStorage.clearConsentForUninstalledApp(packageName); 709 } 710 711 /** Wipes out all the data gathered by Measurement API. */ resetMeasurement()712 public void resetMeasurement() { 713 mMeasurementImpl.deleteAllMeasurementData(List.of()); 714 // Log wipeout event triggered by consent flip to delete data of package 715 WipeoutStatus wipeoutStatus = new WipeoutStatus(); 716 wipeoutStatus.setWipeoutType(WipeoutStatus.WipeoutType.CONSENT_FLIP); 717 logWipeoutStats(wipeoutStatus); 718 } 719 720 /** Wipes out all the Enrollment data */ 721 @VisibleForTesting resetEnrollment()722 void resetEnrollment() { 723 mEnrollmentDao.deleteAll(); 724 } 725 726 /** 727 * Saves information to the storage that notification was displayed for the first time to the 728 * user. 729 */ recordNotificationDisplayed(boolean wasNotificationDisplayed)730 public void recordNotificationDisplayed(boolean wasNotificationDisplayed) { 731 mConsentCompositeStorage.recordNotificationDisplayed(wasNotificationDisplayed); 732 } 733 734 /** 735 * Retrieves if notification has been displayed. 736 * 737 * @return true if Consent Notification was displayed, otherwise false. 738 */ wasNotificationDisplayed()739 public boolean wasNotificationDisplayed() { 740 return mConsentCompositeStorage.wasNotificationDisplayed(); 741 } 742 743 /** 744 * Saves information to the storage that GA UX notification was displayed for the first time to 745 * the user. 746 */ recordGaUxNotificationDisplayed(boolean wasGaUxDisplayed)747 public void recordGaUxNotificationDisplayed(boolean wasGaUxDisplayed) { 748 mConsentCompositeStorage.recordGaUxNotificationDisplayed(wasGaUxDisplayed); 749 } 750 751 /** 752 * Retrieves if GA UX notification has been displayed. 753 * 754 * @return true if GA UX Consent Notification was displayed, otherwise false. 755 */ wasGaUxNotificationDisplayed()756 public boolean wasGaUxNotificationDisplayed() { 757 return mConsentCompositeStorage.wasGaUxNotificationDisplayed(); 758 } 759 760 /** Set the current privacy sandbox feature. */ setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType currentFeatureType)761 public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType currentFeatureType) { 762 mConsentCompositeStorage.setCurrentPrivacySandboxFeature(currentFeatureType); 763 } 764 765 /** Saves information to the storage that user interacted with consent manually. */ recordUserManualInteractionWithConsent(@serManualInteraction int interaction)766 public void recordUserManualInteractionWithConsent(@UserManualInteraction int interaction) { 767 mConsentCompositeStorage.recordUserManualInteractionWithConsent(interaction); 768 } 769 770 /** 771 * Get the current privacy sandbox feature. 772 * 773 * <p>To write to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To write to 774 * system server if consent source of truth is SYSTEM_SERVER_ONLY or dual sources. 775 */ getCurrentPrivacySandboxFeature()776 public PrivacySandboxFeatureType getCurrentPrivacySandboxFeature() { 777 return mConsentCompositeStorage.getCurrentPrivacySandboxFeature(); 778 } 779 780 /** 781 * Returns information whether user interacted with consent manually. 782 * 783 * @return true if the user interacted with the consent manually, otherwise false. 784 */ getUserManualInteractionWithConsent()785 public @UserManualInteraction int getUserManualInteractionWithConsent() { 786 return mConsentCompositeStorage.getUserManualInteractionWithConsent(); 787 } 788 789 @VisibleForTesting 790 // TODO(b/311183933): Remove passed in Context from static method. 791 @SuppressWarnings("AvoidStaticContext") createAndInitializeDataStore( Context context, AdServicesErrorLogger adServicesErrorLogger)792 static AtomicFileDatastore createAndInitializeDataStore( 793 Context context, AdServicesErrorLogger adServicesErrorLogger) { 794 @SuppressWarnings("deprecation") 795 AtomicFileDatastore atomicFileDatastore = 796 LegacyAtomicFileDatastoreFactory.createAtomicFileDatastore( 797 context, 798 ConsentConstants.STORAGE_XML_IDENTIFIER, 799 ConsentConstants.STORAGE_VERSION, 800 adServicesErrorLogger); 801 802 try { 803 atomicFileDatastore.initialize(); 804 // TODO(b/259607624): implement a method in the datastore which would support 805 // this exact scenario - if the value is null, return default value provided 806 // in the parameter (similar to SP apply etc.) 807 if (atomicFileDatastore.getBoolean(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE) 808 == null) { 809 atomicFileDatastore.putBoolean(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE, false); 810 } 811 if (atomicFileDatastore.getBoolean(ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE) 812 == null) { 813 atomicFileDatastore.putBoolean( 814 ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE, false); 815 } 816 } catch (IOException | IllegalArgumentException | NullPointerException e) { 817 throw new RuntimeException("Failed to initialize the File Datastore!", e); 818 } 819 820 return atomicFileDatastore; 821 } 822 823 // Handle different migration requests based on current consent source of Truth 824 // PPAPI_ONLY: reset the shared preference to reset status of migrating consent from PPAPI to 825 // system server. 826 // PPAPI_AND_SYSTEM_SERVER: migrate consent from PPAPI to system server. 827 // SYSTEM_SERVER_ONLY: migrate consent from PPAPI to system server and clear PPAPI consent 828 @VisibleForTesting 829 // TODO(b/311183933): Remove passed in Context from static method. 830 @SuppressWarnings("AvoidStaticContext") handleConsentMigrationIfNeeded( Context context, AtomicFileDatastore datastore, AdServicesStorageManager adServicesManager, StatsdAdServicesLogger statsdAdServicesLogger, @Flags.ConsentSourceOfTruth int consentSourceOfTruth)831 static void handleConsentMigrationIfNeeded( 832 Context context, 833 AtomicFileDatastore datastore, 834 AdServicesStorageManager adServicesManager, 835 StatsdAdServicesLogger statsdAdServicesLogger, 836 @Flags.ConsentSourceOfTruth int consentSourceOfTruth) { 837 Objects.requireNonNull(context); 838 // It is a T+ feature. On T+, this function should only execute if it's within the 839 // AdServices 840 // APK and not ExtServices. So check if it's within ExtServices, and bail out if that's the 841 // case on any platform. 842 String packageName = context.getPackageName(); 843 if (packageName != null && packageName.endsWith(ADEXTSERVICES_PACKAGE_NAME_SUFFIX)) { 844 LogUtil.d("Aborting attempt to migrate consent in ExtServices"); 845 return; 846 } 847 Objects.requireNonNull(datastore); 848 if (consentSourceOfTruth == Flags.PPAPI_AND_SYSTEM_SERVER 849 || consentSourceOfTruth == Flags.SYSTEM_SERVER_ONLY) { 850 Objects.requireNonNull(adServicesManager); 851 } 852 853 switch (consentSourceOfTruth) { 854 case Flags.PPAPI_ONLY: 855 // Technically we only need to reset the SHARED_PREFS_KEY_HAS_MIGRATED bit once. 856 // What we need is clearIfSet operation which is not available in SP. So here we 857 // always reset the bit since otherwise we need to read the SP to read the value and 858 // the clear the value. 859 // The only flow we would do are: 860 // Case 1: DUAL-> PPAPI if there is a bug in System Server 861 // Case 2: DUAL -> SYSTEM_SERVER_ONLY: if everything goes smoothly. 862 resetSharedPreference(context, ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED); 863 break; 864 case Flags.PPAPI_AND_SYSTEM_SERVER: 865 migratePpApiConsentToSystemService( 866 context, datastore, adServicesManager, statsdAdServicesLogger); 867 break; 868 case Flags.SYSTEM_SERVER_ONLY: 869 migratePpApiConsentToSystemService( 870 context, datastore, adServicesManager, statsdAdServicesLogger); 871 clearPpApiConsent(context, datastore); 872 break; 873 case Flags.APPSEARCH_ONLY: 874 // If this is an S- device, the consent source of truth is always APPSEARCH_ONLY. 875 break; 876 default: 877 break; 878 } 879 } 880 881 // Reset data for the specific AdServicesApiType 882 @VisibleForTesting resetByApi(AdServicesApiType apiType)883 void resetByApi(AdServicesApiType apiType) throws IOException { 884 switch (apiType) { 885 case TOPICS: 886 resetTopicsAndBlockedTopics(); 887 break; 888 case FLEDGE: 889 resetAppsAndBlockedApps(); 890 resetUserProfileId(); 891 break; 892 case MEASUREMENTS: 893 resetMeasurement(); 894 break; 895 default: 896 break; 897 } 898 } 899 resetUserProfileId()900 private void resetUserProfileId() { 901 mUserProfileIdManager.deleteId(); 902 } 903 904 // Perform a one-time migration to migrate existing PPAPI Consent 905 @VisibleForTesting 906 @SuppressWarnings({ 907 "NewApi", // Suppress lint warning for context.getUser in R since this code is unused in R 908 "AvoidStaticContext", // TODO(b/311183933): Remove passed in Context from static method. 909 }) migratePpApiConsentToSystemService( Context context, AtomicFileDatastore datastore, AdServicesStorageManager adServicesManager, StatsdAdServicesLogger statsdAdServicesLogger)910 static void migratePpApiConsentToSystemService( 911 Context context, 912 AtomicFileDatastore datastore, 913 AdServicesStorageManager adServicesManager, 914 StatsdAdServicesLogger statsdAdServicesLogger) { 915 Objects.requireNonNull(context); 916 Objects.requireNonNull(datastore); 917 Objects.requireNonNull(adServicesManager); 918 919 AppConsents appConsents = null; 920 try { 921 // Exit if migration has happened. 922 SharedPreferences sharedPreferences = 923 FileCompatUtils.getSharedPreferencesHelper( 924 context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); 925 // If we migrated data to system server either from PPAPI or from AppSearch, do not 926 // attempt another migration of data to system server. 927 boolean shouldSkipMigration = 928 sharedPreferences.getBoolean( 929 ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED, 930 /* default= */ false) 931 || sharedPreferences.getBoolean( 932 ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED, 933 /* default= */ false); 934 if (shouldSkipMigration) { 935 LogUtil.v( 936 "Consent migration has happened to user %d, skip...", 937 context.getUser().getIdentifier()); 938 return; 939 } 940 LogUtil.d("Started migrating Consent from PPAPI to System Service"); 941 942 Boolean consentKey = 943 Boolean.TRUE.equals(datastore.getBoolean(ConsentConstants.CONSENT_KEY)); 944 945 // Migrate Consent and Notification Displayed to System Service. 946 // Set consent enabled only when value is TRUE. FALSE and null are regarded as disabled. 947 adServicesManager.setConsent(AdServicesApiType.ALL_API, consentKey); 948 // Set notification displayed only when value is TRUE. FALSE and null are regarded as 949 // not displayed. 950 if (Boolean.TRUE.equals( 951 datastore.getBoolean(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE))) { 952 adServicesManager.recordNotificationDisplayed(true); 953 } 954 955 Boolean manualInteractionRecorded = 956 datastore.getBoolean(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED); 957 if (manualInteractionRecorded != null) { 958 adServicesManager.recordUserManualInteractionWithConsent( 959 manualInteractionRecorded ? 1 : -1); 960 } 961 962 // Save migration has happened into shared preferences. 963 SharedPreferences.Editor editor = sharedPreferences.edit(); 964 editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED, true); 965 appConsents = 966 AppConsents.builder() 967 .setMsmtConsent(consentKey) 968 .setFledgeConsent(consentKey) 969 .setTopicsConsent(consentKey) 970 .build(); 971 972 if (editor.commit()) { 973 LogUtil.d("Finished migrating Consent from PPAPI to System Service"); 974 statsdAdServicesLogger.logConsentMigrationStats( 975 getConsentManagerStatsForLogging( 976 appConsents, 977 ConsentMigrationStats.MigrationStatus 978 .SUCCESS_WITH_SHARED_PREF_UPDATED, 979 ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, 980 context)); 981 } else { 982 LogUtil.e( 983 "Finished migrating Consent from PPAPI to System Service but shared" 984 + " preference is not updated."); 985 ErrorLogUtil.e( 986 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE, 987 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); 988 statsdAdServicesLogger.logConsentMigrationStats( 989 getConsentManagerStatsForLogging( 990 appConsents, 991 ConsentMigrationStats.MigrationStatus 992 .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED, 993 ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, 994 context)); 995 } 996 } catch (Exception e) { 997 LogUtil.e("PPAPI consent data migration failed: ", e); 998 statsdAdServicesLogger.logConsentMigrationStats( 999 getConsentManagerStatsForLogging( 1000 appConsents, 1001 ConsentMigrationStats.MigrationStatus.FAILURE, 1002 ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, 1003 context)); 1004 } 1005 } 1006 1007 // Clear PPAPI Consent if fully migrated to use system server consent. This is because system 1008 // consent cannot be migrated back to PPAPI. This data clearing should only happen once. 1009 @VisibleForTesting 1010 // TODO(b/311183933): Remove passed in Context from static method. 1011 @SuppressWarnings("AvoidStaticContext") clearPpApiConsent(Context context, AtomicFileDatastore datastore)1012 static void clearPpApiConsent(Context context, AtomicFileDatastore datastore) { 1013 // Exit if PPAPI consent has cleared. 1014 SharedPreferences sharedPreferences = 1015 FileCompatUtils.getSharedPreferencesHelper( 1016 context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); 1017 if (sharedPreferences.getBoolean( 1018 ConsentConstants.SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, /* defValue */ false)) { 1019 return; 1020 } 1021 1022 LogUtil.d("Started clearing Consent in PPAPI."); 1023 1024 try { 1025 datastore.clear(); 1026 } catch (IOException e) { 1027 throw new RuntimeException("Failed to clear PPAPI Consent", e); 1028 } 1029 1030 // Save that PPAPI consent has cleared into shared preferences. 1031 SharedPreferences.Editor editor = sharedPreferences.edit(); 1032 editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, true); 1033 1034 if (editor.commit()) { 1035 LogUtil.d("Finished clearing Consent in PPAPI."); 1036 } else { 1037 LogUtil.e("Finished clearing Consent in PPAPI but shared preference is not updated."); 1038 ErrorLogUtil.e( 1039 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE, 1040 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); 1041 } 1042 } 1043 1044 // Set the shared preference to false for given key. 1045 @VisibleForTesting 1046 // TODO(b/311183933): Remove passed in Context from static method. 1047 @SuppressWarnings("AvoidStaticContext") resetSharedPreference(Context context, String sharedPreferenceKey)1048 static void resetSharedPreference(Context context, String sharedPreferenceKey) { 1049 Objects.requireNonNull(context); 1050 Objects.requireNonNull(sharedPreferenceKey); 1051 1052 SharedPreferences sharedPreferences = 1053 FileCompatUtils.getSharedPreferencesHelper( 1054 context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); 1055 SharedPreferences.Editor editor = sharedPreferences.edit(); 1056 editor.putBoolean(sharedPreferenceKey, false); 1057 1058 if (editor.commit()) { 1059 LogUtil.d("Finished resetting shared preference for " + sharedPreferenceKey); 1060 } else { 1061 LogUtil.e("Failed to reset shared preference for " + sharedPreferenceKey); 1062 ErrorLogUtil.e( 1063 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_RESET_FAILURE, 1064 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); 1065 } 1066 } 1067 1068 // To write to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. 1069 // To write to system server if consent source of truth is SYSTEM_SERVER_ONLY or dual sources. 1070 @VisibleForTesting setConsentToSourceOfTruth(boolean isGiven)1071 void setConsentToSourceOfTruth(boolean isGiven) { 1072 mConsentCompositeStorage.setConsent(AdServicesApiType.ALL_API, isGiven); 1073 } 1074 1075 @VisibleForTesting getConsentFromSourceOfTruth()1076 boolean getConsentFromSourceOfTruth() { 1077 return mConsentCompositeStorage.getConsent(AdServicesApiType.ALL_API).isGiven(); 1078 } 1079 1080 @VisibleForTesting getPerApiConsentFromSourceOfTruth(AdServicesApiType apiType)1081 boolean getPerApiConsentFromSourceOfTruth(AdServicesApiType apiType) { 1082 return mConsentCompositeStorage.getConsent(apiType).isGiven(); 1083 } 1084 1085 @VisibleForTesting setPerApiConsentToSourceOfTruth(boolean isGiven, AdServicesApiType apiType)1086 void setPerApiConsentToSourceOfTruth(boolean isGiven, AdServicesApiType apiType) { 1087 mConsentCompositeStorage.setConsent(apiType, isGiven); 1088 } 1089 storeUserManualInteractionToPpApi( @onsentManagerV2.UserManualInteraction int interaction, AtomicFileDatastore datastore)1090 private static void storeUserManualInteractionToPpApi( 1091 @ConsentManagerV2.UserManualInteraction int interaction, AtomicFileDatastore datastore) 1092 throws IOException { 1093 switch (interaction) { 1094 case NO_MANUAL_INTERACTIONS_RECORDED: 1095 datastore.putBoolean( 1096 ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED, false); 1097 break; 1098 case UNKNOWN: 1099 datastore.remove(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED); 1100 break; 1101 case MANUAL_INTERACTIONS_RECORDED: 1102 datastore.putBoolean( 1103 ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED, true); 1104 break; 1105 default: 1106 throw new IllegalArgumentException( 1107 String.format("InteractionId < %d > can not be handled.", interaction)); 1108 } 1109 } 1110 1111 /** 1112 * This method handles migration of consent data from AppSearch to AdServices. Consent data is 1113 * written to AppSearch on S- and ported to AdServices after OTA to T. If any new data is 1114 * written for consent, we need to make sure it is migrated correctly post-OTA in this method. 1115 */ 1116 @VisibleForTesting 1117 // TODO(b/311183933): Remove passed in Context from static method. 1118 @SuppressWarnings("AvoidStaticContext") handleConsentMigrationFromAppSearchIfNeeded( Context context, AtomicFileDatastore datastore, AppConsentDao appConsentDao, AppSearchConsentStorageManager appSearchConsentStorageManager, AdServicesStorageManager adServicesStorageManager, StatsdAdServicesLogger statsdAdServicesLogger)1119 static void handleConsentMigrationFromAppSearchIfNeeded( 1120 Context context, 1121 AtomicFileDatastore datastore, 1122 AppConsentDao appConsentDao, 1123 AppSearchConsentStorageManager appSearchConsentStorageManager, 1124 AdServicesStorageManager adServicesStorageManager, 1125 StatsdAdServicesLogger statsdAdServicesLogger) { 1126 Objects.requireNonNull(context); 1127 Objects.requireNonNull(appSearchConsentStorageManager); 1128 LogUtil.d("Check migrating Consent from AppSearch to PPAPI and System Service"); 1129 1130 // On R/S, this function should never be executed because AppSearch to PPAPI and 1131 // System Server migration is a T+ feature. On T+, this function should only execute 1132 // if it's within the AdServices APK and not ExtServices. So check if it's within 1133 // ExtServices, and bail out if that's the case on any platform. 1134 String packageName = context.getPackageName(); 1135 if (packageName != null && packageName.endsWith(ADEXTSERVICES_PACKAGE_NAME_SUFFIX)) { 1136 LogUtil.d( 1137 "Aborting attempt to migrate AppSearch to PPAPI and System Service in" 1138 + " ExtServices"); 1139 return; 1140 } 1141 1142 AppConsents appConsents = null; 1143 try { 1144 // This should be called only once after OTA (if flag is enabled). If we did not record 1145 // showing the notification on T+ yet and we have shown the notification on S- (as 1146 // recorded 1147 // in AppSearch), initialize T+ consent data so that we don't show notification twice 1148 // (after 1149 // OTA upgrade). 1150 SharedPreferences sharedPreferences = 1151 FileCompatUtils.getSharedPreferencesHelper( 1152 context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); 1153 // If we did not migrate notification data, we should not attempt to migrate anything. 1154 if (!appSearchConsentStorageManager.migrateConsentDataIfNeeded( 1155 sharedPreferences, datastore, adServicesStorageManager, appConsentDao)) { 1156 LogUtil.d("Skipping consent migration from AppSearch"); 1157 return; 1158 } 1159 // Migrate Consent for all APIs and per API to PP API and System Service. 1160 appConsents = 1161 migrateAppSearchConsents( 1162 appSearchConsentStorageManager, adServicesStorageManager, datastore); 1163 // Record interactions data only if we recorded an interaction in AppSearch. 1164 int manualInteractionRecorded = 1165 appSearchConsentStorageManager.getUserManualInteractionWithConsent(); 1166 if (manualInteractionRecorded == MANUAL_INTERACTIONS_RECORDED) { 1167 // Initialize PP API datastore. 1168 storeUserManualInteractionToPpApi(MANUAL_INTERACTIONS_RECORDED, datastore); 1169 // Initialize system service. 1170 adServicesStorageManager.recordUserManualInteractionWithConsent( 1171 manualInteractionRecorded); 1172 } 1173 1174 // Record that we migrated consent data from AppSearch. We write the notification data 1175 // to system server and perform migration only if system server did not record any 1176 // notification having been displayed. 1177 SharedPreferences.Editor editor = sharedPreferences.edit(); 1178 editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED, true); 1179 if (editor.commit()) { 1180 LogUtil.d("Finished migrating Consent from AppSearch to PPAPI + System Service"); 1181 statsdAdServicesLogger.logConsentMigrationStats( 1182 getConsentManagerStatsForLogging( 1183 appConsents, 1184 ConsentMigrationStats.MigrationStatus 1185 .SUCCESS_WITH_SHARED_PREF_UPDATED, 1186 ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, 1187 context)); 1188 } else { 1189 LogUtil.e( 1190 "Finished migrating Consent from AppSearch to PPAPI + System Service " 1191 + "but shared preference is not updated."); 1192 statsdAdServicesLogger.logConsentMigrationStats( 1193 getConsentManagerStatsForLogging( 1194 appConsents, 1195 ConsentMigrationStats.MigrationStatus 1196 .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED, 1197 ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, 1198 context)); 1199 } 1200 } catch (IOException e) { 1201 LogUtil.e("AppSearch consent data migration failed: ", e); 1202 ErrorLogUtil.e( 1203 e, 1204 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_SEARCH_DATA_MIGRATION_FAILURE, 1205 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); 1206 statsdAdServicesLogger.logConsentMigrationStats( 1207 getConsentManagerStatsForLogging( 1208 appConsents, 1209 ConsentMigrationStats.MigrationStatus.FAILURE, 1210 ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, 1211 context)); 1212 } 1213 } 1214 1215 /** 1216 * This method returns and migrates the consent states (opt in/out) for all PPAPIs, each API and 1217 * their default consent values. 1218 */ 1219 @VisibleForTesting migrateAppSearchConsents( AppSearchConsentStorageManager appSearchConsentManager, AdServicesStorageManager adServicesManager, AtomicFileDatastore datastore)1220 static AppConsents migrateAppSearchConsents( 1221 AppSearchConsentStorageManager appSearchConsentManager, 1222 AdServicesStorageManager adServicesManager, 1223 AtomicFileDatastore datastore) 1224 throws IOException { 1225 Map<String, Boolean> consentMap = new HashMap<>(); 1226 for (AdServicesApiType apiType : AdServicesApiType.values()) { 1227 if (apiType == AdServicesApiType.UNKNOWN) { 1228 continue; 1229 } 1230 boolean consented = appSearchConsentManager.getConsent(apiType).isGiven(); 1231 datastore.putBoolean(apiType.toPpApiDatastoreKey(), consented); 1232 adServicesManager.setConsent(apiType, consented); 1233 consentMap.put(apiType.toPpApiDatastoreKey(), consented); 1234 } 1235 return AppConsents.builder() 1236 .setMsmtConsent( 1237 consentMap.get(AdServicesApiType.MEASUREMENTS.toPpApiDatastoreKey())) 1238 .setTopicsConsent(consentMap.get(AdServicesApiType.TOPICS.toPpApiDatastoreKey())) 1239 .setFledgeConsent(consentMap.get(AdServicesApiType.FLEDGE.toPpApiDatastoreKey())) 1240 .build(); 1241 } 1242 1243 /** 1244 * Represents revoked consent as internally determined by the PP APIs. 1245 * 1246 * <p>This is an internal-only exception and is not meant to be returned to external callers. 1247 */ 1248 public static class RevokedConsentException extends IllegalStateException { 1249 public static final String REVOKED_CONSENT_ERROR_MESSAGE = 1250 "Error caused by revoked user consent"; 1251 1252 /** Creates an instance of a {@link RevokedConsentException}. */ RevokedConsentException()1253 public RevokedConsentException() { 1254 super(REVOKED_CONSENT_ERROR_MESSAGE); 1255 } 1256 } 1257 asyncExecute(Runnable runnable)1258 private void asyncExecute(Runnable runnable) { 1259 AdServicesExecutors.getBackgroundExecutor().execute(runnable); 1260 } 1261 logWipeoutStats(WipeoutStatus wipeoutStatus)1262 private void logWipeoutStats(WipeoutStatus wipeoutStatus) { 1263 AdServicesLoggerImpl.getInstance() 1264 .logMeasurementWipeoutStats( 1265 new MeasurementWipeoutStats.Builder() 1266 .setCode(AD_SERVICES_MEASUREMENT_WIPEOUT) 1267 .setWipeoutType(wipeoutStatus.getWipeoutType().getValue()) 1268 .build()); 1269 } 1270 1271 /** Returns whether the isAdIdEnabled bit is true based on consent_source_of_truth. */ isAdIdEnabled()1272 public boolean isAdIdEnabled() { 1273 return mConsentCompositeStorage.isAdIdEnabled(); 1274 } 1275 1276 /** Set the AdIdEnabled bit to storage based on consent_source_of_truth. */ setAdIdEnabled(boolean isAdIdEnabled)1277 public void setAdIdEnabled(boolean isAdIdEnabled) { 1278 mConsentCompositeStorage.setAdIdEnabled(isAdIdEnabled); 1279 } 1280 1281 /** Returns whether the isU18Account bit is true based on consent_source_of_truth. */ isU18Account()1282 public boolean isU18Account() { 1283 return mConsentCompositeStorage.isU18Account(); 1284 } 1285 1286 /** Set the U18Account bit to storage based on consent_source_of_truth. */ setU18Account(boolean isU18Account)1287 public void setU18Account(boolean isU18Account) { 1288 mConsentCompositeStorage.setU18Account(isU18Account); 1289 } 1290 1291 /** Returns whether the isEntryPointEnabled bit is true based on consent_source_of_truth. */ isEntryPointEnabled()1292 public boolean isEntryPointEnabled() { 1293 return mConsentCompositeStorage.isEntryPointEnabled(); 1294 } 1295 1296 /** Set the EntryPointEnabled bit to storage based on consent_source_of_truth. */ setEntryPointEnabled(boolean isEntryPointEnabled)1297 public void setEntryPointEnabled(boolean isEntryPointEnabled) { 1298 mConsentCompositeStorage.setEntryPointEnabled(isEntryPointEnabled); 1299 } 1300 1301 /** Returns whether the isAdultAccount bit is true based on consent_source_of_truth. */ isAdultAccount()1302 public boolean isAdultAccount() { 1303 return mConsentCompositeStorage.isAdultAccount(); 1304 } 1305 1306 /** Set the AdultAccount bit to storage based on consent_source_of_truth. */ setAdultAccount(boolean isAdultAccount)1307 public void setAdultAccount(boolean isAdultAccount) { 1308 mConsentCompositeStorage.setAdultAccount(isAdultAccount); 1309 } 1310 1311 /** 1312 * Returns whether the wasU18NotificationDisplayed bit is true based on consent_source_of_truth. 1313 */ wasU18NotificationDisplayed()1314 public boolean wasU18NotificationDisplayed() { 1315 return mConsentCompositeStorage.wasU18NotificationDisplayed(); 1316 } 1317 1318 /** Set the U18NotificationDisplayed bit to storage based on consent_source_of_truth. */ setU18NotificationDisplayed(boolean wasU18NotificationDisplayed)1319 public void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) { 1320 mConsentCompositeStorage.setU18NotificationDisplayed(wasU18NotificationDisplayed); 1321 } 1322 1323 /** Returns current UX based on consent_source_of_truth. */ getUx()1324 public PrivacySandboxUxCollection getUx() { 1325 return mConsentCompositeStorage.getUx(); 1326 } 1327 1328 /** Set the current UX to storage based on consent_source_of_truth. */ setUx(PrivacySandboxUxCollection ux)1329 public void setUx(PrivacySandboxUxCollection ux) { 1330 mConsentCompositeStorage.setUx(ux); 1331 } 1332 1333 /** Returns current enrollment channel based on consent_source_of_truth. */ getEnrollmentChannel( PrivacySandboxUxCollection ux)1334 public PrivacySandboxEnrollmentChannelCollection getEnrollmentChannel( 1335 PrivacySandboxUxCollection ux) { 1336 return mConsentCompositeStorage.getEnrollmentChannel(ux); 1337 } 1338 1339 /** Set the current enrollment channel to storage based on consent_source_of_truth. */ setEnrollmentChannel( PrivacySandboxUxCollection ux, PrivacySandboxEnrollmentChannelCollection channel)1340 public void setEnrollmentChannel( 1341 PrivacySandboxUxCollection ux, PrivacySandboxEnrollmentChannelCollection channel) { 1342 mConsentCompositeStorage.setEnrollmentChannel(ux, channel); 1343 } 1344 1345 @VisibleForTesting setConsentToPpApi(boolean isGiven)1346 void setConsentToPpApi(boolean isGiven) throws IOException { 1347 mDatastore.putBoolean(ConsentConstants.CONSENT_KEY, isGiven); 1348 } 1349 1350 /* Returns the region od the device */ getConsentRegion(Context context)1351 private static int getConsentRegion(Context context) { 1352 return DeviceRegionProvider.isEuDevice(context) 1353 ? AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU 1354 : AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; 1355 } 1356 1357 /* Returns an object of ConsentMigrationStats */ getConsentManagerStatsForLogging( AppConsents appConsents, ConsentMigrationStats.MigrationStatus migrationStatus, ConsentMigrationStats.MigrationType migrationType, Context context)1358 private static ConsentMigrationStats getConsentManagerStatsForLogging( 1359 AppConsents appConsents, 1360 ConsentMigrationStats.MigrationStatus migrationStatus, 1361 ConsentMigrationStats.MigrationType migrationType, 1362 Context context) { 1363 ConsentMigrationStats consentMigrationStats = 1364 ConsentMigrationStats.builder() 1365 .setMigrationType(migrationType) 1366 // When appConsents is null we log it as a failure 1367 .setMigrationStatus( 1368 appConsents != null 1369 ? migrationStatus 1370 : ConsentMigrationStats.MigrationStatus.FAILURE) 1371 .setMsmtConsent(appConsents == null || appConsents.getMsmtConsent()) 1372 .setTopicsConsent(appConsents == null || appConsents.getTopicsConsent()) 1373 .setFledgeConsent(appConsents == null || appConsents.getFledgeConsent()) 1374 .setRegion(getConsentRegion(context)) 1375 .build(); 1376 return consentMigrationStats; 1377 } 1378 1379 /** Returns whether the Fledge Consent is given. */ isPasFledgeConsentGiven()1380 public boolean isPasFledgeConsentGiven() { 1381 return mFlags.getPasUxEnabled() 1382 && mConsentCompositeStorage.wasPasNotificationDisplayed() 1383 && getConsent(AdServicesApiType.FLEDGE).isGiven(); 1384 } 1385 1386 /** Sets the isMeasurementDataReset bit to storage based on consent_source_of_truth. */ setMeasurementDataReset(boolean isMeasurementDataReset)1387 public void setMeasurementDataReset(boolean isMeasurementDataReset) { 1388 mConsentCompositeStorage.setMeasurementDataReset(isMeasurementDataReset); 1389 } 1390 1391 /** 1392 * Returns whether the measurement data reset activity happens based on consent_source_of_truth. 1393 */ isMeasurementDataReset()1394 public boolean isMeasurementDataReset() { 1395 return mConsentCompositeStorage.isMeasurementDataReset(); 1396 } 1397 1398 /** 1399 * Retrieves if PAS notification has been displayed. 1400 * 1401 * @return true if PAS Consent Notification was displayed, otherwise false. 1402 */ wasPasNotificationDisplayed()1403 public boolean wasPasNotificationDisplayed() { 1404 return mConsentCompositeStorage.wasPasNotificationDisplayed(); 1405 } 1406 1407 /** 1408 * Saves information to the storage that PAS UX notification was displayed for the first time to 1409 * the user. 1410 */ recordPasNotificationDisplayed(boolean wasPasDisplayed)1411 public void recordPasNotificationDisplayed(boolean wasPasDisplayed) { 1412 mConsentCompositeStorage.recordPasNotificationDisplayed(wasPasDisplayed); 1413 } 1414 1415 /** get pas conset for measurement */ isPasMeasurementConsentGiven()1416 public boolean isPasMeasurementConsentGiven() { 1417 if (mDebugFlags.getConsentManagerDebugMode()) { 1418 return true; 1419 } 1420 1421 return mFlags.getPasUxEnabled() 1422 && wasPasNotificationDisplayed() 1423 && getConsent(AdServicesApiType.MEASUREMENTS).isGiven(); 1424 } 1425 1426 /** 1427 * Retrieves the PP API default consent. 1428 * 1429 * @return true if the default consent is true, false otherwise. 1430 */ getDefaultConsent()1431 public boolean getDefaultConsent() { 1432 return mConsentCompositeStorage.getDefaultConsent(); 1433 } 1434 1435 /** 1436 * Retrieves the topics default consent. 1437 * 1438 * @return true if the topics default consent is true, false otherwise. 1439 */ getTopicsDefaultConsent()1440 boolean getTopicsDefaultConsent() { 1441 return mConsentCompositeStorage.getTopicsDefaultConsent(); 1442 } 1443 1444 /** 1445 * Retrieves the FLEDGE default consent. 1446 * 1447 * @return true if the FLEDGE default consent is true, false otherwise. 1448 */ getFledgeDefaultConsent()1449 boolean getFledgeDefaultConsent() { 1450 return mConsentCompositeStorage.getFledgeDefaultConsent(); 1451 } 1452 1453 /** 1454 * Retrieves the measurement default consent. 1455 * 1456 * @return true if the measurement default consent is true, false otherwise. 1457 */ getMeasurementDefaultConsent()1458 boolean getMeasurementDefaultConsent() { 1459 return mConsentCompositeStorage.getMeasurementDefaultConsent(); 1460 } 1461 1462 /** 1463 * Retrieves the default AdId state. 1464 * 1465 * @return true if the AdId is enabled by default, false otherwise. 1466 */ getDefaultAdIdState()1467 boolean getDefaultAdIdState() { 1468 return mConsentCompositeStorage.getDefaultAdIdState(); 1469 } 1470 1471 /** Saves the default consent bit to data stores based on source of truth. */ recordDefaultConsent(boolean defaultConsent)1472 void recordDefaultConsent(boolean defaultConsent) { 1473 mConsentCompositeStorage.recordDefaultConsent(defaultConsent); 1474 } 1475 1476 /** Saves the topics default consent bit to data stores based on source of truth. */ recordTopicsDefaultConsent(boolean defaultConsent)1477 void recordTopicsDefaultConsent(boolean defaultConsent) { 1478 mConsentCompositeStorage.recordTopicsDefaultConsent(defaultConsent); 1479 } 1480 1481 /** Saves the FLEDGE default consent bit to data stores based on source of truth. */ recordFledgeDefaultConsent(boolean defaultConsent)1482 void recordFledgeDefaultConsent(boolean defaultConsent) { 1483 mConsentCompositeStorage.recordFledgeDefaultConsent(defaultConsent); 1484 } 1485 1486 /** Saves the measurement default consent bit to data stores based on source of truth. */ recordMeasurementDefaultConsent(boolean defaultConsent)1487 void recordMeasurementDefaultConsent(boolean defaultConsent) { 1488 mConsentCompositeStorage.recordMeasurementDefaultConsent(defaultConsent); 1489 } 1490 1491 /** Saves the default AdId state bit to data stores based on source of truth. */ recordDefaultAdIdState(boolean defaultAdIdState)1492 void recordDefaultAdIdState(boolean defaultAdIdState) { 1493 mConsentCompositeStorage.recordDefaultAdIdState(defaultAdIdState); 1494 } 1495 1496 /** 1497 * Returns whether the measurement data reset activity happens based on consent_source_of_truth. 1498 */ isPaDataReset()1499 boolean isPaDataReset() { 1500 return mConsentCompositeStorage.isPaDataReset(); 1501 } 1502 1503 /** Set the isPaDataReset bit to storage based on consent_source_of_truth. */ setPaDataReset(boolean isPaDataReset)1504 void setPaDataReset(boolean isPaDataReset) { 1505 mConsentCompositeStorage.setPaDataReset(isPaDataReset); 1506 } 1507 1508 /** Dumps its internal state. */ dump(PrintWriter writer, @Nullable String[] args)1509 public void dump(PrintWriter writer, @Nullable String[] args) { 1510 writer.println("ConsentManagerV2"); 1511 String prefix = " "; 1512 1513 writer.printf("%ssDataMigrationDuration: %dms\n", prefix, sDataMigrationDurationMs); 1514 writer.printf("%ssInstantiationDuration: %dms\n", prefix, sInstantiationDurationMs); 1515 1516 writer.printf("%sDatastore:\n", prefix); 1517 String prefix2 = " "; 1518 mDatastore.dump(writer, prefix2, /* args= */ null); 1519 } 1520 } 1521