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.download; 18 19 import static com.android.adservices.download.EnrollmentDataDownloadManager.DownloadStatus.SUCCESS; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.Mockito.when; 26 27 import static java.util.concurrent.TimeUnit.SECONDS; 28 29 import android.content.Context; 30 import android.database.DatabaseUtils; 31 import android.net.wifi.WifiManager; 32 import android.os.SystemClock; 33 34 import androidx.annotation.NonNull; 35 36 import com.android.adservices.common.AdServicesExtendedMockitoTestCase; 37 import com.android.adservices.data.DbTestUtil; 38 import com.android.adservices.data.encryptionkey.EncryptionKeyDao; 39 import com.android.adservices.data.encryptionkey.EncryptionKeyTables; 40 import com.android.adservices.data.enrollment.EnrollmentDao; 41 import com.android.adservices.data.enrollment.EnrollmentTables; 42 import com.android.adservices.data.shared.SharedDbHelper; 43 import com.android.adservices.service.FakeFlagsFactory; 44 import com.android.adservices.service.Flags; 45 import com.android.adservices.service.FlagsFactory; 46 import com.android.adservices.service.consent.AdServicesApiConsent; 47 import com.android.adservices.service.consent.AdServicesApiType; 48 import com.android.adservices.service.consent.ConsentManager; 49 import com.android.adservices.service.topics.classifier.CommonClassifierHelper; 50 import com.android.adservices.service.ui.data.UxStatesManager; 51 import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection; 52 import com.android.adservices.shared.testing.annotations.RequiresSdkLevelAtLeastS; 53 import com.android.adservices.shared.util.Clock; 54 import com.android.compatibility.common.util.ShellUtils; 55 import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; 56 57 import com.google.android.libraries.mobiledatadownload.AddFileGroupRequest; 58 import com.google.android.libraries.mobiledatadownload.DownloadFileGroupRequest; 59 import com.google.android.libraries.mobiledatadownload.FileGroupPopulator; 60 import com.google.android.libraries.mobiledatadownload.GetFileGroupRequest; 61 import com.google.android.libraries.mobiledatadownload.Logger; 62 import com.google.android.libraries.mobiledatadownload.MobileDataDownload; 63 import com.google.android.libraries.mobiledatadownload.MobileDataDownloadBuilder; 64 import com.google.android.libraries.mobiledatadownload.TaskScheduler; 65 import com.google.android.libraries.mobiledatadownload.TimeSource; 66 import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader; 67 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 68 import com.google.android.libraries.mobiledatadownload.monitor.NetworkUsageMonitor; 69 import com.google.common.base.Optional; 70 import com.google.common.collect.ImmutableList; 71 import com.google.mobiledatadownload.ClientConfigProto; 72 import com.google.mobiledatadownload.ClientConfigProto.ClientFileGroup; 73 import com.google.mobiledatadownload.DownloadConfigProto.DataFile; 74 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup; 75 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions; 76 import com.google.mobiledatadownload.DownloadConfigProto.DownloadConditions.DeviceNetworkPolicy; 77 78 import org.junit.After; 79 import org.junit.Assume; 80 import org.junit.Before; 81 import org.junit.Test; 82 import org.mockito.Mock; 83 84 import java.util.concurrent.ExecutionException; 85 import java.util.concurrent.TimeoutException; 86 import java.util.stream.Collectors; 87 88 /** Unit tests for {@link MobileDataDownloadFactory} */ 89 @RequiresSdkLevelAtLeastS 90 @SpyStatic(MddLogger.class) 91 @SpyStatic(FlagsFactory.class) 92 @SpyStatic(MobileDataDownloadFactory.class) 93 @SpyStatic(UxStatesManager.class) 94 @SpyStatic(EnrollmentDao.class) 95 @SpyStatic(EncryptionKeyDao.class) 96 @SpyStatic(ConsentManager.class) 97 @SpyStatic(CommonClassifierHelper.class) 98 public final class MobileDataDownloadTest extends AdServicesExtendedMockitoTestCase { 99 private static final int MAX_HANDLE_TASK_WAIT_TIME_SECS = 300; 100 101 // Two files are from cts_test_1 folder. 102 // https://source.corp.google.com/piper///depot/google3/wireless/android/adservices/mdd/topics_classifier/cts_test_1/ 103 private static final String FILE_GROUP_NAME_1 = "test-group-1"; 104 private static final String FILE_ID_1 = "classifier_assets_metadata.json"; 105 private static final String FILE_ID_2 = "stopwords.txt"; 106 private static final String FILE_CHECKSUM_1 = "52633ae715ead32ec6c8ae721ad34ea301336a8e"; 107 private static final String FILE_URL_1 = 108 "https://dl.google.com/mdi-serving/rubidium-adservices-topics-classifier/1489" 109 + "/52633ae715ead32ec6c8ae721ad34ea301336a8e"; 110 private static final int FILE_SIZE_1 = 1026; 111 112 private static final String FILE_CHECKSUM_2 = "042dc4512fa3d391c5170cf3aa61e6a638f84342"; 113 private static final String FILE_URL_2 = 114 "https://dl.google.com/mdi-serving/rubidium-adservices-topics-classifier/1489" 115 + "/042dc4512fa3d391c5170cf3aa61e6a638f84342"; 116 private static final int FILE_SIZE_2 = 1; 117 118 // TODO(b/263521464): Use the production topics classifier manifest URL. 119 private static final String TEST_MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL = 120 "https://www.gstatic.com/mdi-serving/rubidium-adservices-topics-classifier/922" 121 + "/217081737fd739c74dd3ca5c407813d818526577"; 122 private static final String MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL = 123 "https://www.gstatic.com/mdi-serving/rubidium-adservices-topics-classifier/1986" 124 + "/9e98784bcdb26a3eb2ab3f65ee811f43177c761f"; 125 private static final String PRODUCTION_ENROLLMENT_MANIFEST_FILE_URL = 126 "https://www.gstatic.com/mdi-serving/rubidium-adservices-adtech-enrollment/4503" 127 + "/fecd522d3dcfbe1b3b1f1054947be8528be43e97"; 128 private static final String PRODUCTION_ENCRYPTION_KEYS_MANIFEST_FILE_URL = 129 "https://www.gstatic.com/mdi-serving/rubidium-adservices-encryption-keys/4543" 130 + "/e9d118728752e6a6bfb5d7d8d1520807591f0717"; 131 132 // Prod Test Bed enrollment manifest URL 133 private static final String PTB_ENROLLMENT_MANIFEST_FILE_URL = 134 "https://www.gstatic.com/mdi-serving/rubidium-adservices-adtech-enrollment/3548" 135 + "/206afe932d6db2a87cad70421454a0c258297d77"; 136 private static final String OEM_ENROLLMENT_MANIFEST_FILE_URL = 137 "https://www.gstatic.com/mdi-serving/rubidium-adservices-adtech-enrollment/1760" 138 + "/1460e6aea598fe7a153100d6e2749f45313ef905"; 139 private static final String UI_OTA_STRINGS_MANIFEST_FILE_URL = 140 "https://www.gstatic.com/mdi-serving/rubidium-adservices-ui-ota-strings/1360" 141 + "/d428721d225582922a7fe9d5ad6db7b09cb03209"; 142 143 private static final String UI_OTA_RESOURCES_MANIFEST_FILE_URL = 144 "https://www.gstatic.com/mdi-serving/rubidium-adservices-ui-ota-strings/3150" 145 + "/672c83fa4aad630a360dc3b7ce43d94ab75852cd"; 146 147 private static final int PRODUCTION_ENROLLMENT_ENTRIES = 78; 148 149 /** Old PTB URL with a small number of enrollment records. */ 150 private static final String PTB_OLD_ENROLLMENT_FILE_URL = 151 "https://www.gstatic.com/mdi-serving/rubidium-adservices-adtech-enrollment/3156/9d9d99be0c6dc71fc329f5c02a0fac48d3b06e73"; 152 153 private static final int PTB_ENROLLMENT_ENTRIES = 6; 154 155 private static final int PTB_OLD_ENROLLMENT_ENTRIES = 4; 156 private static final int OEM_ENROLLMENT_ENTRIES = 114; 157 158 private static final int PRODUCTION_FILEGROUP_VERSION = 0; 159 private static final int PTB_FILEGROUP_VERSION = 0; 160 private static final int OEM_FILEGROUP_VERSION = 0; 161 162 public static final String TEST_TOPIC_FILE_GROUP_NAME = "topics-classifier-model"; 163 public static final String ENCRYPTION_KEYS_FILE_GROUP_NAME = "encryption-keys"; 164 public static final String ENROLLMENT_FILE_GROUP_NAME = "adtech_enrollment_data"; 165 public static final String ENROLLMENT_PROTO_FILE_GROUP_NAME = "adtech_enrollment_proto_data"; 166 public static final String UI_OTA_STRINGS_FILE_GROUP_NAME = "ui-ota-strings"; 167 private SynchronousFileStorage mFileStorage; 168 private FileDownloader mFileDownloader; 169 private SharedDbHelper mDbHelper; 170 private MobileDataDownload mMdd; 171 private WifiManager mWifiManager; 172 173 @Mock Flags mMockFlags; 174 @Mock ConsentManager mConsentManager; 175 @Mock UxStatesManager mUxStatesManager; 176 @Mock Clock mMockClock; 177 178 @Before setUp()179 public void setUp() throws Exception { 180 mWifiManager = mContext.getSystemService(WifiManager.class); 181 // The MDD integration tests require wifi connection. If the running device is not 182 // connected to the Wifi, tests should be skipped. 183 Assume.assumeTrue("Device must have wifi connection", mWifiManager.isWifiEnabled()); 184 185 mockMddFlags(); 186 187 mFileStorage = MobileDataDownloadFactory.getFileStorage(); 188 mFileDownloader = MobileDataDownloadFactory.getFileDownloader(mMockFlags, mFileStorage); 189 190 mDbHelper = DbTestUtil.getSharedDbHelperForTest(); 191 192 doReturn(AdServicesApiConsent.GIVEN).when(mConsentManager).getConsent(); 193 // Mock static method ConsentManager.getInstance() to return test ConsentManager 194 doReturn(mConsentManager).when(() -> ConsentManager.getInstance()); 195 doReturn(mUxStatesManager).when(() -> UxStatesManager.getInstance()); 196 overridingMddLoggingLevel("VERBOSE"); 197 } 198 199 @After teardown()200 public void teardown() throws ExecutionException, InterruptedException { 201 if (mMdd != null) { 202 mMdd.clear().get(); 203 } 204 overridingMddLoggingLevel("INFO"); 205 } 206 207 @Test testCreateMddManagerSuccessfully()208 public void testCreateMddManagerSuccessfully() throws ExecutionException, InterruptedException { 209 mMdd = 210 getMddForTesting( 211 mContext, 212 FakeFlagsFactory.getFlagsForTest(), 213 // Pass in an empty list of FileGroupPopulator. Add ad hoc DataFileGroup 214 // to MDD manually below. 215 ImmutableList.of()); 216 217 DataFileGroup dataFileGroup = 218 createDataFileGroup( 219 FILE_GROUP_NAME_1, 220 mContext.getPackageName(), 221 5 /* versionNumber */, 222 new String[] {FILE_ID_1, FILE_ID_2}, 223 new int[] {FILE_SIZE_1, FILE_SIZE_2}, 224 new String[] {FILE_CHECKSUM_1, FILE_CHECKSUM_2}, 225 new String[] {FILE_URL_1, FILE_URL_2}, 226 DeviceNetworkPolicy.DOWNLOAD_ONLY_ON_WIFI); 227 // Add the DataFileGroup to MDD 228 assertThat( 229 mMdd.addFileGroup( 230 AddFileGroupRequest.newBuilder() 231 .setDataFileGroup(dataFileGroup) 232 .build()) 233 .get()) 234 .isTrue(); 235 236 // Trigger the download immediately. 237 ClientFileGroup clientFileGroup = 238 mMdd.downloadFileGroup( 239 DownloadFileGroupRequest.newBuilder() 240 .setGroupName(FILE_GROUP_NAME_1) 241 .build()) 242 .get(); 243 244 // Verify the downloaded DataFileGroup. 245 assertThat(clientFileGroup.getGroupName()).isEqualTo(FILE_GROUP_NAME_1); 246 assertThat(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 247 assertThat(clientFileGroup.getVersionNumber()).isEqualTo(5); 248 assertThat(clientFileGroup.getFileCount()).isEqualTo(2); 249 assertThat(clientFileGroup.hasAccount()).isFalse(); 250 } 251 252 @Test testTopicsManifestFileGroupPopulator_ManifestConfigOverrider_NoFileGroup()253 public void testTopicsManifestFileGroupPopulator_ManifestConfigOverrider_NoFileGroup() 254 throws ExecutionException, InterruptedException, TimeoutException { 255 createMddForTopics(MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 256 // The server side test model build_id = 1986, which equals to bundled model build_id = 257 // 1986. ManifestConfigOverrider will not add the DataFileGroup in the 258 // TopicsManifestFileGroupPopulator and will not download either. 259 assertThat( 260 mMdd.getFileGroup( 261 GetFileGroupRequest.newBuilder() 262 .setGroupName(TEST_TOPIC_FILE_GROUP_NAME) 263 .build()) 264 .get()) 265 .isNull(); 266 } 267 268 /** 269 * This method tests topics manifest files. It downloads test classifier model and verifies 270 * files downloaded successfully. 271 */ 272 @Test testTopicsManifestFileGroupPopulator()273 public void testTopicsManifestFileGroupPopulator() 274 throws ExecutionException, InterruptedException, TimeoutException { 275 // Set the bundled build_id to 1 so the server side build_id will be bigger. This will 276 // trigger MDD download. 277 doReturn(1L).when(() -> CommonClassifierHelper.getBundledModelBuildId(any(), any())); 278 279 createMddForTopics(MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 280 281 ClientFileGroup clientFileGroup = 282 mMdd.getFileGroup( 283 GetFileGroupRequest.newBuilder() 284 .setGroupName(TEST_TOPIC_FILE_GROUP_NAME) 285 .build()) 286 .get(); 287 288 // Verify topics file group. 289 assertThat(clientFileGroup.getGroupName()).isEqualTo(TEST_TOPIC_FILE_GROUP_NAME); 290 assertThat(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 291 assertThat(clientFileGroup.getVersionNumber()) 292 .isEqualTo(/* Test filegroup version number */ 0); 293 assertThat(clientFileGroup.getFileCount()).isEqualTo(6); 294 assertThat(clientFileGroup.getStatus()).isEqualTo(ClientFileGroup.Status.DOWNLOADED); 295 assertThat(clientFileGroup.getBuildId()).isEqualTo(/* BuildID generated by Ingress */ 1986); 296 } 297 298 /** 299 * This method tests MDD production encryption keys data, verifies files downloaded successfully 300 * and data saved into DB correctly. 301 */ 302 @Test testEncryptionKeysDataDownload_production_featureEnabled()303 public void testEncryptionKeysDataDownload_production_featureEnabled() throws Exception { 304 doReturn(true).when(mMockFlags).getEnableMddEncryptionKeys(); 305 // All keys have greater expiration time than this timestamp. (Sep 2, 1996) 306 when(mMockClock.currentTimeMillis()).thenReturn(841622400000L); 307 createMddForEncryptionKeys(PRODUCTION_ENCRYPTION_KEYS_MANIFEST_FILE_URL); 308 309 ClientFileGroup clientFileGroup = 310 mMdd.getFileGroup( 311 GetFileGroupRequest.newBuilder() 312 .setGroupName(ENCRYPTION_KEYS_FILE_GROUP_NAME) 313 .build()) 314 .get(); 315 316 assertThat(clientFileGroup).isNotNull(); 317 verifyEncryptionKeysFileGroup(clientFileGroup, /* NumberOfKeysOnTestUrl */ 2); 318 } 319 320 /** Test disabling the feature flag does not create the manifest for download. */ 321 @Test testEncryptionKeysDataDownload_production_featureDisabled()322 public void testEncryptionKeysDataDownload_production_featureDisabled() throws Exception { 323 doReturn(false).when(mMockFlags).getEnableMddEncryptionKeys(); 324 createMddForEncryptionKeys(PRODUCTION_ENCRYPTION_KEYS_MANIFEST_FILE_URL); 325 326 ClientFileGroup clientFileGroup = 327 mMdd.getFileGroup( 328 GetFileGroupRequest.newBuilder() 329 .setGroupName(ENCRYPTION_KEYS_FILE_GROUP_NAME) 330 .build()) 331 .get(); 332 333 assertThat(clientFileGroup).isNull(); 334 } 335 336 /** 337 * This method tests MDD production enrollment data, verifies files downloaded successfully and 338 * data saved into DB correctly. 339 */ 340 @Test testEnrollmentDataDownload_Production()341 public void testEnrollmentDataDownload_Production() 342 throws ExecutionException, InterruptedException, TimeoutException { 343 when(mMockFlags.getEncryptionKeyNewEnrollmentFetchKillSwitch()).thenReturn(true); 344 createMddForEnrollment(PRODUCTION_ENROLLMENT_MANIFEST_FILE_URL, /* getProto= */ false); 345 346 ClientFileGroup clientFileGroup = 347 mMdd.getFileGroup( 348 GetFileGroupRequest.newBuilder() 349 .setGroupName(ENROLLMENT_FILE_GROUP_NAME) 350 .build()) 351 .get(); 352 353 verifyMeasurementFileGroup( 354 clientFileGroup, PRODUCTION_FILEGROUP_VERSION, PRODUCTION_ENROLLMENT_ENTRIES); 355 } 356 357 /** 358 * This method tests OEM enrollment data, verifies files downloaded successfully and data saved 359 * into DB correctly. 360 */ 361 @Test testEnrollmentDataDownload_OEM()362 public void testEnrollmentDataDownload_OEM() 363 throws ExecutionException, InterruptedException, TimeoutException { 364 when(mMockFlags.getEncryptionKeyNewEnrollmentFetchKillSwitch()).thenReturn(true); 365 createMddForEnrollment(OEM_ENROLLMENT_MANIFEST_FILE_URL, /* getProto= */ false); 366 367 ClientFileGroup clientFileGroup = 368 mMdd.getFileGroup( 369 GetFileGroupRequest.newBuilder() 370 .setGroupName(ENROLLMENT_FILE_GROUP_NAME) 371 .build()) 372 .get(); 373 374 verifyMeasurementFileGroup(clientFileGroup, OEM_FILEGROUP_VERSION, OEM_ENROLLMENT_ENTRIES); 375 } 376 377 /** 378 * This method tests Prod Test Bed enrollment data, verifies files downloaded successfully and 379 * data saved into DB correctly, additionally checks record deletion when flag is enabled. 380 */ 381 @Test testEnrollmentDataDownload_PTB()382 public void testEnrollmentDataDownload_PTB() 383 throws ExecutionException, InterruptedException, TimeoutException { 384 when(mMockFlags.getEncryptionKeyNewEnrollmentFetchKillSwitch()).thenReturn(true); 385 when(mMockFlags.getEnrollmentMddRecordDeletionEnabled()).thenReturn(true); 386 createMddForEnrollment(PTB_ENROLLMENT_MANIFEST_FILE_URL, /* getProto= */ false); 387 388 ClientFileGroup clientFileGroup = 389 mMdd.getFileGroup( 390 GetFileGroupRequest.newBuilder() 391 .setGroupName(ENROLLMENT_FILE_GROUP_NAME) 392 .build()) 393 .get(); 394 395 verifyMeasurementFileGroup( 396 clientFileGroup, 397 PTB_FILEGROUP_VERSION, 398 PTB_ENROLLMENT_ENTRIES, 399 /* clearExistingData= */ true, 400 /* clearDownloadedData= */ false); 401 createMddForEnrollment(PTB_OLD_ENROLLMENT_FILE_URL, /* getProto= */ false); 402 verifyMeasurementFileGroup( 403 clientFileGroup, 404 PTB_FILEGROUP_VERSION, 405 PTB_OLD_ENROLLMENT_ENTRIES, 406 /* clearExistingData= */ false, 407 /* clearDownloadedData= */ true); 408 } 409 410 // TODO (b/340891475): Add tests for Enrollment production proto files 411 412 /** This method verifies that the file group does not exist when an empty url is provided. */ 413 @Test testEnrollmentProtoDataDownload_emptyUrl()414 public void testEnrollmentProtoDataDownload_emptyUrl() throws Exception { 415 when(mMockFlags.getEncryptionKeyNewEnrollmentFetchKillSwitch()).thenReturn(true); 416 createMddForEnrollment("", /* getProto= */ true); 417 418 ClientFileGroup clientFileGroup = 419 mMdd.getFileGroup( 420 GetFileGroupRequest.newBuilder() 421 .setGroupName(ENROLLMENT_PROTO_FILE_GROUP_NAME) 422 .build()) 423 .get(); 424 425 assertThat(clientFileGroup).isNull(); 426 } 427 428 /** 429 * This method tests enrollment data, verifies that the file group doesn't exist if the consent 430 * is revoked. 431 */ 432 @Test testEnrollmentDataDownloadFailOnConsentRevoked_gaUxEnabled()433 public void testEnrollmentDataDownloadFailOnConsentRevoked_gaUxEnabled() 434 throws ExecutionException, InterruptedException, TimeoutException { 435 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 436 when(mConsentManager.getConsent(AdServicesApiType.MEASUREMENTS)) 437 .thenReturn(AdServicesApiConsent.REVOKED); 438 439 createMddForEnrollment(PRODUCTION_ENROLLMENT_MANIFEST_FILE_URL, /* getProto= */ false); 440 441 ClientFileGroup clientFileGroup = 442 mMdd.getFileGroup( 443 GetFileGroupRequest.newBuilder() 444 .setGroupName(ENROLLMENT_FILE_GROUP_NAME) 445 .build()) 446 .get(); 447 448 assertThat(clientFileGroup).isNull(); 449 } 450 451 /** 452 * This method tests enrollment data, verifies that the file group exists if the consent is 453 * given. 454 */ 455 @Test testEnrollmentDataDownloadOnConsentGiven_gaUxEnabled()456 public void testEnrollmentDataDownloadOnConsentGiven_gaUxEnabled() 457 throws ExecutionException, InterruptedException, TimeoutException { 458 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 459 when(mConsentManager.getConsent(AdServicesApiType.MEASUREMENTS)) 460 .thenReturn(AdServicesApiConsent.GIVEN); 461 462 createMddForEnrollment(PRODUCTION_ENROLLMENT_MANIFEST_FILE_URL, /* getProto= */ false); 463 464 ClientFileGroup clientFileGroup = 465 mMdd.getFileGroup( 466 GetFileGroupRequest.newBuilder() 467 .setGroupName(ENROLLMENT_FILE_GROUP_NAME) 468 .build()) 469 .get(); 470 471 assertThat(clientFileGroup).isNotNull(); 472 } 473 474 /** 475 * This method tests topics data, verifies that the file group doesn't exist if the consent is 476 * revoked. 477 */ 478 @Test testMddTopicsFailsOnConsentRevoked_gaUxEnabled()479 public void testMddTopicsFailsOnConsentRevoked_gaUxEnabled() 480 throws ExecutionException, InterruptedException, TimeoutException { 481 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 482 when(mConsentManager.getConsent(AdServicesApiType.TOPICS)) 483 .thenReturn(AdServicesApiConsent.REVOKED); 484 485 createMddForTopics(TEST_MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 486 487 ClientFileGroup clientFileGroup = 488 mMdd.getFileGroup( 489 GetFileGroupRequest.newBuilder() 490 .setGroupName(TEST_TOPIC_FILE_GROUP_NAME) 491 .build()) 492 .get(); 493 494 assertThat(clientFileGroup).isNull(); 495 } 496 497 /** 498 * This method tests topics data, verifies that the file group exists if the consent is given. 499 */ 500 @Test testMddTopicsOnConsentGiven_gaUxEnabled()501 public void testMddTopicsOnConsentGiven_gaUxEnabled() 502 throws ExecutionException, InterruptedException, TimeoutException { 503 doReturn(1L).when(() -> CommonClassifierHelper.getBundledModelBuildId(any(), any())); 504 505 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 506 when(mConsentManager.getConsent(AdServicesApiType.TOPICS)) 507 .thenReturn(AdServicesApiConsent.GIVEN); 508 509 createMddForTopics(TEST_MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 510 511 ClientFileGroup clientFileGroup = 512 mMdd.getFileGroup( 513 GetFileGroupRequest.newBuilder() 514 .setGroupName(TEST_TOPIC_FILE_GROUP_NAME) 515 .build()) 516 .get(); 517 518 assertThat(clientFileGroup).isNotNull(); 519 } 520 521 /** 522 * This method tests OTA data, verifies that the file group exists if the consent is given to at 523 * least one of the APIs. 524 */ 525 @Test testOtaOnTopicsConsentGiven_gaUxEnabled()526 public void testOtaOnTopicsConsentGiven_gaUxEnabled() 527 throws ExecutionException, InterruptedException, TimeoutException { 528 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 529 when(mConsentManager.getConsent(AdServicesApiType.TOPICS)) 530 .thenReturn(AdServicesApiConsent.GIVEN); 531 532 doReturn(UI_OTA_STRINGS_MANIFEST_FILE_URL) 533 .when(mMockFlags) 534 .getUiOtaStringsManifestFileUrl(); 535 doReturn(UI_OTA_RESOURCES_MANIFEST_FILE_URL) 536 .when(mMockFlags) 537 .getUiOtaResourcesManifestFileUrl(); 538 createMddForUiOTA(); 539 540 ClientFileGroup clientFileGroup = 541 mMdd.getFileGroup( 542 GetFileGroupRequest.newBuilder() 543 .setGroupName(UI_OTA_STRINGS_FILE_GROUP_NAME) 544 .build()) 545 .get(); 546 547 assertThat(clientFileGroup).isNotNull(); 548 } 549 550 /** 551 * This method tests OTA data, verifies that the file group doesn't exist if the consent is 552 * revoked for all the APIs. 553 */ 554 @Test testOtaFailsOnAggregatedConsentRevoked_gaUxEnabled()555 public void testOtaFailsOnAggregatedConsentRevoked_gaUxEnabled() 556 throws ExecutionException, InterruptedException, TimeoutException { 557 doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); 558 when(mConsentManager.getConsent(AdServicesApiType.TOPICS)) 559 .thenReturn(AdServicesApiConsent.REVOKED); 560 when(mConsentManager.getConsent(AdServicesApiType.MEASUREMENTS)) 561 .thenReturn(AdServicesApiConsent.REVOKED); 562 when(mConsentManager.getConsent(AdServicesApiType.FLEDGE)) 563 .thenReturn(AdServicesApiConsent.REVOKED); 564 565 createMddForTopics(TEST_MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 566 567 ClientFileGroup clientFileGroup = 568 mMdd.getFileGroup( 569 GetFileGroupRequest.newBuilder() 570 .setGroupName(UI_OTA_STRINGS_FILE_GROUP_NAME) 571 .build()) 572 .get(); 573 574 assertThat(clientFileGroup).isNull(); 575 } 576 577 // Topics MFGP should be disabled for U18 UX. 578 @Test topicsDownloadTest_U18UxEnabled()579 public void topicsDownloadTest_U18UxEnabled() 580 throws ExecutionException, InterruptedException, TimeoutException { 581 doReturn(PrivacySandboxUxCollection.U18_UX).when(mUxStatesManager).getUx(); 582 583 createMddForTopics(TEST_MDD_TOPICS_CLASSIFIER_MANIFEST_FILE_URL); 584 585 ClientFileGroup clientFileGroup = 586 mMdd.getFileGroup( 587 GetFileGroupRequest.newBuilder() 588 .setGroupName(TEST_TOPIC_FILE_GROUP_NAME) 589 .build()) 590 .get(); 591 592 assertThat(clientFileGroup).isNull(); 593 } 594 595 @Test testMddLoggerFeatureFlagIsOff()596 public void testMddLoggerFeatureFlagIsOff() { 597 // The feature flag is off. MddLogger should be disabled. 598 doReturn(false).when(mMockFlags).getMddLoggerEnabled(); 599 Optional<Logger> mddLogger = MobileDataDownloadFactory.getMddLogger(mMockFlags); 600 assertThat(mddLogger).isAbsent(); 601 } 602 603 @Test testMddLoggerFeatureFlagIsOn()604 public void testMddLoggerFeatureFlagIsOn() { 605 // The feature flag is on. MddLogger should be enabled. 606 doReturn(true).when(mMockFlags).getMddLoggerEnabled(); 607 Optional<Logger> mddLogger = MobileDataDownloadFactory.getMddLogger(mMockFlags); 608 assertThat(mddLogger).isPresent(); 609 } 610 611 /** 612 * This method tests UI OTA Strings manifest files. It downloads test UI Strings file and 613 * verifies files downloaded successfully. 614 */ 615 @Test testUiOtaStringsManifestFileGroupPopulator()616 public void testUiOtaStringsManifestFileGroupPopulator() 617 throws ExecutionException, InterruptedException, TimeoutException { 618 doReturn(false).when(mMockFlags).getUiOtaResourcesFeatureEnabled(); 619 doReturn(UI_OTA_STRINGS_MANIFEST_FILE_URL) 620 .when(mMockFlags) 621 .getUiOtaStringsManifestFileUrl(); 622 createMddForUiOTA(); 623 624 ClientFileGroup clientFileGroup = 625 mMdd.getFileGroup( 626 GetFileGroupRequest.newBuilder() 627 .setGroupName(UI_OTA_STRINGS_FILE_GROUP_NAME) 628 .build()) 629 .get(); 630 631 // Verify UI file group. 632 assertThat(clientFileGroup.getGroupName()).isEqualTo(UI_OTA_STRINGS_FILE_GROUP_NAME); 633 assertThat(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 634 assertThat(clientFileGroup.getFileCount()).isEqualTo(1); 635 assertThat(clientFileGroup.getStatus()).isEqualTo(ClientFileGroup.Status.DOWNLOADED); 636 assertThat(clientFileGroup.getBuildId()).isEqualTo(/* BuildID generated by Ingress */ 1360); 637 } 638 639 /** 640 * This method tests UI OTA resources manifest files. It downloads test UI apk file and verifies 641 * files downloaded successfully. 642 */ 643 @Test testUiOtaResourcesManifestFileGroupPopulator()644 public void testUiOtaResourcesManifestFileGroupPopulator() 645 throws ExecutionException, InterruptedException, TimeoutException { 646 doReturn(true).when(mMockFlags).getUiOtaResourcesFeatureEnabled(); 647 doReturn(UI_OTA_RESOURCES_MANIFEST_FILE_URL) 648 .when(mMockFlags) 649 .getUiOtaResourcesManifestFileUrl(); 650 createMddForUiOTA(); 651 652 ClientFileGroup clientFileGroup = 653 mMdd.getFileGroup( 654 GetFileGroupRequest.newBuilder() 655 .setGroupName(UI_OTA_STRINGS_FILE_GROUP_NAME) 656 .build()) 657 .get(); 658 659 // Verify UI file group. 660 assertThat(clientFileGroup.getGroupName()).isEqualTo(UI_OTA_STRINGS_FILE_GROUP_NAME); 661 assertThat(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 662 assertThat(clientFileGroup.getFileCount()).isEqualTo(1); 663 assertThat(clientFileGroup.getStatus()).isEqualTo(ClientFileGroup.Status.DOWNLOADED); 664 assertThat(clientFileGroup.getBuildId()).isEqualTo(/* BuildID generated by Ingress */ 3150); 665 } 666 667 // A helper function to create a DataFilegroup. createDataFileGroup( String groupName, String ownerPackage, int versionNumber, String[] fileId, int[] byteSize, String[] checksum, String[] url, DeviceNetworkPolicy deviceNetworkPolicy)668 private static DataFileGroup createDataFileGroup( 669 String groupName, 670 String ownerPackage, 671 int versionNumber, 672 String[] fileId, 673 int[] byteSize, 674 String[] checksum, 675 String[] url, 676 DeviceNetworkPolicy deviceNetworkPolicy) { 677 if (fileId.length != byteSize.length 678 || fileId.length != checksum.length 679 || fileId.length != url.length) { 680 throw new IllegalArgumentException(); 681 } 682 683 DataFileGroup.Builder dataFileGroupBuilder = 684 DataFileGroup.newBuilder() 685 .setGroupName(groupName) 686 .setOwnerPackage(ownerPackage) 687 .setFileGroupVersionNumber(versionNumber) 688 .setDownloadConditions( 689 DownloadConditions.newBuilder() 690 .setDeviceNetworkPolicy(deviceNetworkPolicy)); 691 692 for (int i = 0; i < fileId.length; ++i) { 693 DataFile file = 694 DataFile.newBuilder() 695 .setFileId(fileId[i]) 696 .setByteSize(byteSize[i]) 697 .setChecksum(checksum[i]) 698 .setUrlToDownload(url[i]) 699 .build(); 700 dataFileGroupBuilder.addFile(file); 701 } 702 703 return dataFileGroupBuilder.build(); 704 } 705 706 /** 707 * Returns a MobileDataDownload instance for testing. 708 * 709 * @param context the context 710 * @param flags the flags 711 * @param fileGroupPopulators a list of FileGroupPopulator that will be added to the MDD 712 * @return a MobileDataDownload instance. 713 */ 714 @NonNull getMddForTesting( @onNull Context context, @NonNull Flags flags, @NonNull ImmutableList<FileGroupPopulator> fileGroupPopulators)715 private static MobileDataDownload getMddForTesting( 716 @NonNull Context context, 717 @NonNull Flags flags, 718 @NonNull ImmutableList<FileGroupPopulator> fileGroupPopulators) { 719 context = context.getApplicationContext(); 720 SynchronousFileStorage fileStorage = MobileDataDownloadFactory.getFileStorage(); 721 FileDownloader fileDownloader = 722 MobileDataDownloadFactory.getFileDownloader(flags, fileStorage); 723 NetworkUsageMonitor networkUsageMonitor = 724 new NetworkUsageMonitor( 725 context, 726 new TimeSource() { 727 @Override 728 public long currentTimeMillis() { 729 return System.currentTimeMillis(); 730 } 731 732 @Override 733 public long elapsedRealtimeNanos() { 734 return SystemClock.elapsedRealtimeNanos(); 735 } 736 }); 737 738 return MobileDataDownloadBuilder.newBuilder() 739 .setContext(context) 740 .setControlExecutor(MobileDataDownloadFactory.getControlExecutor()) 741 .setNetworkUsageMonitor(networkUsageMonitor) 742 .setFileStorage(fileStorage) 743 .setFileDownloaderSupplier(() -> fileDownloader) 744 .addFileGroupPopulators(fileGroupPopulators) 745 .setLoggerOptional(MobileDataDownloadFactory.getMddLogger(flags)) 746 // Use default MDD flags so that it does not need to access DeviceConfig 747 // which is inaccessible from Unit Tests. 748 .setFlagsOptional( 749 Optional.of(new com.google.android.libraries.mobiledatadownload.Flags() {})) 750 .build(); 751 } 752 753 // Returns MobileDataDownload using passed in encryption keys manifest url. 754 @NonNull createMddForEncryptionKeys(String encryptionManifestFileUrl)755 private void createMddForEncryptionKeys(String encryptionManifestFileUrl) 756 throws ExecutionException, InterruptedException, TimeoutException { 757 doReturn(encryptionManifestFileUrl).when(mMockFlags).getMddEncryptionKeysManifestFileUrl(); 758 759 FileGroupPopulator fileGroupPopulator = 760 MobileDataDownloadFactory.getEncryptionKeysManifestPopulator( 761 mContext, mMockFlags, mFileStorage, mFileDownloader); 762 763 mMdd = 764 getMddForTesting( 765 mContext, 766 mMockFlags, 767 // List of FileGroupPopulator that contains Measurement FileGroupPopulator 768 // only. 769 ImmutableList.of(fileGroupPopulator)); 770 771 // Calling handleTask directly to trigger the MDD's background download on wifi. This should 772 // be done in tests only. 773 mMdd.handleTask(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK) 774 .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS); 775 } 776 777 // Returns MobileDataDownload using passed in enrollment manifest url. 778 @NonNull createMddForEnrollment(String enrollmentManifestFileUrl, boolean getProto)779 private void createMddForEnrollment(String enrollmentManifestFileUrl, boolean getProto) 780 throws ExecutionException, InterruptedException, TimeoutException { 781 if (getProto) { 782 doReturn(enrollmentManifestFileUrl).when(mMockFlags).getMddEnrollmentManifestFileUrl(); 783 } else { 784 doReturn(enrollmentManifestFileUrl).when(mMockFlags).getMeasurementManifestFileUrl(); 785 } 786 787 FileGroupPopulator fileGroupPopulator = 788 MobileDataDownloadFactory.getMeasurementManifestPopulator( 789 mMockFlags, mFileStorage, mFileDownloader, getProto); 790 791 mMdd = 792 getMddForTesting( 793 mContext, 794 mMockFlags, 795 // List of FileGroupPopulator that contains Measurement FileGroupPopulator 796 // only. 797 ImmutableList.of(fileGroupPopulator)); 798 799 // Calling handleTask directly to trigger the MDD's background download on wifi. This should 800 // be done in tests only. 801 mMdd.handleTask(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK) 802 .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS); 803 } 804 805 // Returns MobileDataDownload using passed in topics manifest url. 806 @NonNull createMddForTopics(String topicsManifestFileUrl)807 private void createMddForTopics(String topicsManifestFileUrl) 808 throws ExecutionException, InterruptedException, TimeoutException { 809 doReturn(topicsManifestFileUrl).when(mMockFlags).getMddTopicsClassifierManifestFileUrl(); 810 811 FileGroupPopulator fileGroupPopulator = 812 MobileDataDownloadFactory.getTopicsManifestPopulator( 813 mMockFlags, mFileStorage, mFileDownloader); 814 815 mMdd = 816 getMddForTesting( 817 mContext, 818 mMockFlags, 819 // List of FileGroupPopulator that contains Topics FileGroupPopulator only. 820 ImmutableList.of(fileGroupPopulator)); 821 822 // Calling handleTask directly to trigger the MDD's background download on wifi. This should 823 // be done in tests only. 824 mMdd.handleTask(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK) 825 .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS); 826 } 827 828 // Returns MobileDataDownload using passed in UI OTA manifest url. 829 @NonNull createMddForUiOTA()830 private void createMddForUiOTA() 831 throws ExecutionException, InterruptedException, TimeoutException { 832 FileGroupPopulator fileGroupPopulator = 833 MobileDataDownloadFactory.getUiOtaResourcesManifestPopulator( 834 mMockFlags, mFileStorage, mFileDownloader); 835 836 mMdd = 837 getMddForTesting( 838 mContext, 839 mMockFlags, 840 // List of FileGroupPopulator that contains UI OTA String FileGroupPopulator 841 // only. 842 ImmutableList.of(fileGroupPopulator)); 843 844 // Calling handleTask directly to trigger the MDD's background download on wifi. This should 845 // be done in tests only. 846 mMdd.handleTask(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK) 847 .get(MAX_HANDLE_TASK_WAIT_TIME_SECS, SECONDS); 848 } 849 getNumEntriesInEncryptionKeysTable()850 private long getNumEntriesInEncryptionKeysTable() { 851 return DatabaseUtils.queryNumEntries( 852 mDbHelper.getReadableDatabase(), 853 EncryptionKeyTables.EncryptionKeyContract.TABLE, 854 null); 855 } 856 getNumEntriesInEnrollmentTable()857 private long getNumEntriesInEnrollmentTable() { 858 return DatabaseUtils.queryNumEntries( 859 mDbHelper.getReadableDatabase(), 860 EnrollmentTables.EnrollmentDataContract.TABLE, 861 null); 862 } 863 verifyEncryptionKeysFileGroup( ClientFileGroup clientFileGroup, int numberOfExpectedKeys)864 private void verifyEncryptionKeysFileGroup( 865 ClientFileGroup clientFileGroup, int numberOfExpectedKeys) 866 throws InterruptedException, ExecutionException { 867 expect.that(clientFileGroup.getGroupName()).isEqualTo(ENCRYPTION_KEYS_FILE_GROUP_NAME); 868 expect.that(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 869 // Number of enrollment ids with provided encryption keys. 870 expect.that(clientFileGroup.getFileCount()).isEqualTo(numberOfExpectedKeys); 871 expect.that( 872 clientFileGroup.getFileList().stream() 873 .map(ClientConfigProto.ClientFile::getFileId) 874 .collect(Collectors.toList())) 875 .containsExactly("E4.json", "ptb.json"); 876 expect.that(clientFileGroup.getStatus()).isEqualTo(ClientFileGroup.Status.DOWNLOADED); 877 878 doReturn(mMdd).when(() -> MobileDataDownloadFactory.getMdd(any(Flags.class))); 879 880 EncryptionKeyDao encryptionKeyDao = new EncryptionKeyDao(mDbHelper); 881 EncryptionDataDownloadManager encryptionDataDownloadManager = 882 new EncryptionDataDownloadManager(mMockFlags, encryptionKeyDao, mMockClock); 883 884 // Verify encryption keys data file read from MDD and insert the data into the encryption 885 // keys database. 886 expect.that(encryptionDataDownloadManager.readAndInsertEncryptionDataFromMdd().get()) 887 .isEqualTo(EncryptionDataDownloadManager.DownloadStatus.SUCCESS); 888 expect.that(getNumEntriesInEncryptionKeysTable()).isEqualTo(numberOfExpectedKeys); 889 } 890 verifyEnrollmentMddDownloadStatus( ClientFileGroup clientFileGroup, int fileGroupVersion)891 private void verifyEnrollmentMddDownloadStatus( 892 ClientFileGroup clientFileGroup, int fileGroupVersion) { 893 assertThat(clientFileGroup.getGroupName()).isEqualTo(ENROLLMENT_FILE_GROUP_NAME); 894 assertThat(clientFileGroup.getOwnerPackage()).isEqualTo(mContext.getPackageName()); 895 assertThat(clientFileGroup.getFileCount()).isEqualTo(1); 896 assertThat(clientFileGroup.getStatus()).isEqualTo(ClientFileGroup.Status.DOWNLOADED); 897 assertThat(clientFileGroup.getVersionNumber()).isEqualTo(fileGroupVersion); 898 } 899 setupEnrollmentDaoForTest()900 private EnrollmentDao setupEnrollmentDaoForTest() { 901 EnrollmentDao enrollmentDao = new EnrollmentDao(mContext, mDbHelper, mMockFlags); 902 doReturn(enrollmentDao).when(() -> EnrollmentDao.getInstance()); 903 return enrollmentDao; 904 } 905 setupEnrollmentDownloadManagerForTest()906 private EnrollmentDataDownloadManager setupEnrollmentDownloadManagerForTest() { 907 EnrollmentDataDownloadManager enrollmentDataDownloadManager = 908 new EnrollmentDataDownloadManager(mContext, mMockFlags); 909 910 EncryptionKeyDao encryptionKeyDao = new EncryptionKeyDao(mDbHelper); 911 doReturn(encryptionKeyDao).when(EncryptionKeyDao::getInstance); 912 return enrollmentDataDownloadManager; 913 } 914 verifyMeasurementFileGroup( ClientFileGroup clientFileGroup, int fileGroupVersion, int enrollmentEntries)915 private void verifyMeasurementFileGroup( 916 ClientFileGroup clientFileGroup, int fileGroupVersion, int enrollmentEntries) 917 throws InterruptedException, ExecutionException { 918 verifyMeasurementFileGroup( 919 clientFileGroup, 920 fileGroupVersion, 921 enrollmentEntries, 922 /* clearExistingData= */ true, 923 /* clearDownloadedData= */ true); 924 } 925 verifyMeasurementFileGroup( ClientFileGroup clientFileGroup, int fileGroupVersion, int enrollmentEntries, boolean clearExistingData, boolean clearDownloadedData)926 private void verifyMeasurementFileGroup( 927 ClientFileGroup clientFileGroup, 928 int fileGroupVersion, 929 int enrollmentEntries, 930 boolean clearExistingData, 931 boolean clearDownloadedData) 932 throws InterruptedException, ExecutionException { 933 verifyEnrollmentMddDownloadStatus(clientFileGroup, fileGroupVersion); 934 935 doReturn(mMdd).when(() -> MobileDataDownloadFactory.getMdd(any(Flags.class))); 936 937 EnrollmentDao enrollmentDao = setupEnrollmentDaoForTest(); 938 if (clearExistingData) { 939 assertThat(enrollmentDao.deleteAll()).isTrue(); 940 // Verify no enrollment data after table cleared. 941 assertThat(getNumEntriesInEnrollmentTable()).isEqualTo(0); 942 } 943 944 EnrollmentDataDownloadManager enrollmentDataDownloadManager = 945 setupEnrollmentDownloadManagerForTest(); 946 // Verify enrollment data file read from MDD and insert the data into the enrollment 947 // database. 948 assertThat(enrollmentDataDownloadManager.readAndInsertEnrollmentDataFromMdd().get()) 949 .isEqualTo(SUCCESS); 950 assertThat(getNumEntriesInEnrollmentTable()).isEqualTo(enrollmentEntries); 951 if (clearDownloadedData) { 952 assertThat(enrollmentDao.deleteAll()).isTrue(); 953 } 954 } 955 mockMddFlags()956 private void mockMddFlags() { 957 mocker.mockGetFlags(mMockFlags); 958 959 doReturn(2).when(mMockFlags).getDownloaderMaxDownloadThreads(); 960 doReturn(false).when(mMockFlags).getEncryptionKeyNewEnrollmentFetchKillSwitch(); 961 doReturn(Flags.ENCRYPTION_KEY_NETWORK_CONNECT_TIMEOUT_MS) 962 .when(mMockFlags) 963 .getEncryptionKeyNetworkConnectTimeoutMs(); 964 doReturn(Flags.ENCRYPTION_KEY_NETWORK_READ_TIMEOUT_MS) 965 .when(mMockFlags) 966 .getEncryptionKeyNetworkReadTimeoutMs(); 967 } 968 overridingMddLoggingLevel(String loggingLevel)969 private static void overridingMddLoggingLevel(String loggingLevel) { 970 ShellUtils.runShellCommand("setprop log.tag.MDD %s", loggingLevel); 971 } 972 } 973