diff options
Diffstat (limited to 'adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests')
3 files changed, 284 insertions, 436 deletions
diff --git a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/AppUpdateTest.java b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/AppUpdateTest.java deleted file mode 100644 index 033c951d59..0000000000 --- a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/AppUpdateTest.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.adservices.tests.cts.topics; - -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; - -import static com.google.common.truth.Truth.assertThat; - -import android.adservices.clients.topics.AdvertisingTopicsClient; -import android.adservices.topics.GetTopicsResponse; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.util.Log; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.compatibility.common.util.ShellUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -/** - * This CTS test is to test app update flow for Topics API. It has two goals: - * - * <ul> - * <li>1. To test topic assignment for newly installed apps - * <li>2. To test data wiping-off for uninstalled apps - * </ul> - * - * <p>It communicates with test apps, which are installed/uninstalled, call Topic API, etc. The - * basic ideas are - * - * <ul> - * <li>Start test apps from this CTS test suite. - * <li>Test apps call Topics API in <code>onCreate()</code> method, and sends out broadcast to - * this CTS test suite. - * <li>This CTS test suite asserts the expected results in <code>onReceived()</code> - * <li>Test apps will be killed once it finishes calling Topic API. - * </ul> - * - * <p>Specific steps: - * - * <ul> - * <li>1. CTS Test Suite app calls topics API so that Topics API has usage at epoch T. Then - * trigger epoch computation and get top/returned topics at epoch T. - * <li>2. Install test app at epoch T+1 and it'll be assigned with returned topics at T. - * <li>3. Forces Maintenance job to run to reconcile app installation mismatching, in case - * broadcast is missed due to system delay. - * <li>4. Test app itself calls Topics API and it'll return the topics at T. Then test app calls - * Topics API through sdk, the sdk will be assigned with returned topic for epoch T at the - * serving flow. Both calls will send broadcast to this CTS test suite app and this test suite - * app will verify the results are expected - * <li>5. Uninstall test app and wait for 3 epochs. - * <li>6. Install test app again and it won't be assigned with topics as no usage in the past 3 - * epoch. Call topics API again via app only and sdk. Both calls should have empty response as - * all derived data been wiped off. - * <li>7. Finally verify the number of broadcast received is as expected. Then uninstall test app - * and unregister the listener. - * </ul> - * - * <p>Expected running time: ~48s. - */ -public class AppUpdateTest { - @SuppressWarnings("unused") - private static final String TAG = "AppUpdateTest"; - - private static final String TEST_APK_NAME = "CtsSampleTopicsApp1.apk"; - private static final String TEST_APK_PATH = "/data/local/tmp/cts/install/" + TEST_APK_NAME; - private static final String TEST_PKG_NAME = "com.android.adservices.tests.cts.topics.testapp1"; - private static final String TEST_ACTIVITY_NAME = TEST_PKG_NAME + ".MainActivity"; - private static final ComponentName COMPONENT = - new ComponentName(TEST_PKG_NAME, TEST_ACTIVITY_NAME); - - // Broadcast sent from test apps to test suite to pass GetTopicsResponse. Use two types of - // broadcast to make the result more explicit. - private static final String TOPIC_RESPONSE_BROADCAST_KEY = "topicIds"; - // Test app will send this broadcast to this CTS test suite if it receives non-empty topics. - private static final String NON_EMPTY_TOPIC_RESPONSE_BROADCAST = - "com.android.adservices.tests.cts.topics.NON_EMPTY_TOPIC_RESPONSE"; - // Test app will send this broadcast to this CTS test suite if it receives empty topics. - private static final String EMPTY_TOPIC_RESPONSE_BROADCAST = - "com.android.adservices.tests.cts.topics.EMPTY_TOPIC_RESPONSE"; - private static final String SDK_NAME = "sdk"; - - protected static final Context sContext = ApplicationProvider.getApplicationContext(); - private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); - - // Used to get the package name. Copied over from com.android.adservices.AdServicesCommon - private static final String TOPICS_SERVICE_NAME = "android.adservices.TOPICS_SERVICE"; - private static final String ADSERVICES_PACKAGE_NAME = getAdServicesPackageName(); - - // Expected topic responses used for assertion. This is the expected order of broadcasts that - // the test sample app will send back to the main test app. So the order is important. - private static final String[] EXPECTED_TOPIC_RESPONSE_BROADCASTS = { - NON_EMPTY_TOPIC_RESPONSE_BROADCAST, - NON_EMPTY_TOPIC_RESPONSE_BROADCAST, - EMPTY_TOPIC_RESPONSE_BROADCAST, - EMPTY_TOPIC_RESPONSE_BROADCAST, - }; - - // The JobId of the Epoch Computation and Maintenance job. - private static final int EPOCH_JOB_ID = 2; - private static final int MAINTENANCE_JOB_ID = 1; - - // Override the Epoch Job Period to this value to speed up the epoch computation. - private static final long TEST_EPOCH_JOB_PERIOD_MS = 5000; - - // As adb commands and broadcast processing require time to execute, add this waiting time to - // allow them to have enough time to be executed. This helps to reduce the test flaky. - private static final long EXECUTION_WAITING_TIME = 2000; - - // Default Epoch Period. - private static final long TOPICS_EPOCH_JOB_PERIOD_MS = 7 * 86_400_000; // 7 days. - - // Use 0 percent for random topic in the test so that we can verify the returned topic. - private static final int TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 0; - private static final int DEFAULT_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 5; - - private int mExpectedTopicResponseBroadCastIndex = 0; - private BroadcastReceiver mTopicsResponseReceiver; - - @Before - public void setup() throws InterruptedException { - // We need to skip 3 epochs so that if there is any usage from other test runs, it will - // not be used for epoch retrieval. - Thread.sleep(3 * TEST_EPOCH_JOB_PERIOD_MS); - - overridingBeforeTest(); - - registerTopicResponseReceiver(); - } - - @After - public void tearDown() { - overridingAfterTest(); - } - - @Test - public void testAppUpdate() throws Exception { - // Invoke Topics API once to compute top topics so that following installed test apps are - // able to get top topics assigned when getting installed. - AdvertisingTopicsClient advertisingTopicsClient = - new AdvertisingTopicsClient.Builder() - .setContext(sContext) - .setSdkName(SDK_NAME) - .setExecutor(CALLBACK_EXECUTOR) - .build(); - - // At beginning, Sdk1 receives no topic. - GetTopicsResponse sdkResponse = advertisingTopicsClient.getTopics().get(); - assertThat(sdkResponse.getTopics()).isEmpty(); - - // Now force the Epoch Computation Job. This should be done in the same epoch for - // callersCanLearnMap to have the entry for processing. - forceEpochComputationJob(); - - // Wait to the next epoch. We will not need to do this after we implement the fix in - // go/rb-topics-epoch-scheduling - Thread.sleep(TEST_EPOCH_JOB_PERIOD_MS); - - // Install test app1. - installTestSampleApp(); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Forces Maintenance job if broadcast for installation is missed or delayed. - forceMaintenanceJob(); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Invoke test app1. The test app1 should be assigned with topics as there are usages - // and top topics in the last 3 epochs. - Intent testAppIntent = - new Intent().setComponent(COMPONENT).addFlags(FLAG_ACTIVITY_NEW_TASK); - sContext.startActivity(testAppIntent); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Uninstall test app1. All derived data for test app1 should be wiped off. - uninstallTestSampleApp(); - - // Skip 3 epochs so that newly installed apps won't be assigned with topics. - Thread.sleep(3 * TEST_EPOCH_JOB_PERIOD_MS); - - // Install the test app1 again. It should not be assigned with new topics. - installTestSampleApp(); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Forces Maintenance job if broadcast for installation is missed or delayed. - // This is the second verification to justify the test even broadcast is missed. - forceMaintenanceJob(); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Invoke test app1. It should get empty returned topics because its derived data was wiped - // off from the uninstallation. - sContext.startActivity(testAppIntent); - Thread.sleep(EXECUTION_WAITING_TIME); - - // Unregistered the receiver and uninstall the test app1 - // Note aosp_x86 requires --user 0 to uninstall though arm doesn't. - sContext.unregisterReceiver(mTopicsResponseReceiver); - uninstallTestSampleApp(); - - // Finally, assert that the number of received broadcasts matches with expectation - assertThat(mExpectedTopicResponseBroadCastIndex) - .isEqualTo(EXPECTED_TOPIC_RESPONSE_BROADCASTS.length); - } - - // Broadcast Receiver to receive getTopicResponse broadcast from test apps - private void registerTopicResponseReceiver() { - final IntentFilter topicResponseIntentFilter = new IntentFilter(); - topicResponseIntentFilter.addAction(NON_EMPTY_TOPIC_RESPONSE_BROADCAST); - topicResponseIntentFilter.addAction(EMPTY_TOPIC_RESPONSE_BROADCAST); - - // Assert the result at each time the CTS test suite receives the broadcast. Specifically, - // First time: test app1 should get non-empty returned topics as it was assigned with topics - // when it gets installed. This is to test the app installation behaviors. - // Second time: test app1 should get non-empty returned topics as it calls Topics API via - // sdk. This is to test sdk topics assignment for newly installed apps. - // Third time: test app1 should get empty returned topics as it wasn't assigned with topics - // when it gets installed due to zero usage in last 3 epochs, as well as it was - // uninstalled. This is to test the uninstallation behaviors. - // Fourth time: test app1 should also get empty returned topics when it calls Topics API - // via sdk. - mTopicsResponseReceiver = - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - // Verify the broadcast type. - assertThat(mExpectedTopicResponseBroadCastIndex) - .isLessThan(EXPECTED_TOPIC_RESPONSE_BROADCASTS.length); - assertThat(action) - .isEqualTo( - EXPECTED_TOPIC_RESPONSE_BROADCASTS[ - mExpectedTopicResponseBroadCastIndex]); - - // Verify the returned topics if there is any - if (action.equals(NON_EMPTY_TOPIC_RESPONSE_BROADCAST)) { - int[] topics = - intent.getExtras().getIntArray(TOPIC_RESPONSE_BROADCAST_KEY); - - // topic is one of the 5 classification topics of the Test App. - assertThat(topics.length).isEqualTo(1); - assertThat(topics[0]) - .isIn(Arrays.asList(10147, 10253, 10175, 10254, 10333)); - } - - mExpectedTopicResponseBroadCastIndex++; - } - }; - - sContext.registerReceiver(mTopicsResponseReceiver, topicResponseIntentFilter); - } - - private void overridingBeforeTest() { - overridingAdservicesLoggingLevel("VERBOSE"); - - overrideDisableTopicsEnrollmentCheck("1"); - overrideEpochPeriod(TEST_EPOCH_JOB_PERIOD_MS); - - // We need to turn off random topic so that we can verify the returned topic. - overridePercentageForRandomTopic(TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); - - // We need to turn the Consent Manager into debug mode - overrideConsentManagerDebugMode(); - - // Turn off MDD to avoid model mismatching - disableMddBackgroundTasks(true); - } - - // Reset back the original values. - private void overridingAfterTest() { - overrideDisableTopicsEnrollmentCheck("0"); - overrideEpochPeriod(TOPICS_EPOCH_JOB_PERIOD_MS); - overridePercentageForRandomTopic(DEFAULT_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); - disableMddBackgroundTasks(false); - overridingAdservicesLoggingLevel("INFO"); - } - - // Switch on/off for MDD service. Default value is false, which means MDD is enabled. - private void disableMddBackgroundTasks(boolean isSwitchedOff) { - ShellUtils.runShellCommand( - "setprop debug.adservices.mdd_background_task_kill_switch " + isSwitchedOff); - } - - // Install test sample app 1 and verify the installation. - private void installTestSampleApp() { - String installMessage = ShellUtils.runShellCommand("pm install -r " + TEST_APK_PATH); - assertThat(installMessage).contains("Success"); - } - - // Note aosp_x86 requires --user 0 to uninstall though arm doesn't. - private void uninstallTestSampleApp() { - ShellUtils.runShellCommand("pm uninstall --user 0 " + TEST_PKG_NAME); - } - - // Override the flag to disable Topics enrollment check. - private void overrideDisableTopicsEnrollmentCheck(String val) { - // Setting it to 1 here disables the Topics enrollment check. - ShellUtils.runShellCommand( - "setprop debug.adservices.disable_topics_enrollment_check " + val); - } - - // Override the Epoch Period to shorten the Epoch Length in the test. - private void overrideEpochPeriod(long overrideEpochPeriod) { - ShellUtils.runShellCommand( - "setprop debug.adservices.topics_epoch_job_period_ms " + overrideEpochPeriod); - } - - // Override the Percentage For Random Topic in the test. - private void overridePercentageForRandomTopic(long overridePercentage) { - ShellUtils.runShellCommand( - "setprop debug.adservices.topics_percentage_for_random_topics " - + overridePercentage); - } - - // Override the Consent Manager behaviour - Consent Given - private void overrideConsentManagerDebugMode() { - ShellUtils.runShellCommand("setprop debug.adservices.consent_manager_debug_mode true"); - } - - // Forces JobScheduler to run the Epoch Computation job. - private void forceEpochComputationJob() { - ShellUtils.runShellCommand( - "cmd jobscheduler run -f" + " " + ADSERVICES_PACKAGE_NAME + " " + EPOCH_JOB_ID); - } - - // Forces JobScheduler to run the Maintenance job. - private void forceMaintenanceJob() { - ShellUtils.runShellCommand( - "cmd jobscheduler run -f" - + " " - + ADSERVICES_PACKAGE_NAME - + " " - + MAINTENANCE_JOB_ID); - } - - private void overridingAdservicesLoggingLevel(String loggingLevel) { - ShellUtils.runShellCommand("setprop log.tag.adservices %s", loggingLevel); - } - - // Used to get the package name. Copied over from com.android.adservices.AndroidServiceBinder - private static String getAdServicesPackageName() { - final Intent intent = new Intent(TOPICS_SERVICE_NAME); - final List<ResolveInfo> resolveInfos = - sContext.getPackageManager() - .queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); - - if (resolveInfos == null || resolveInfos.isEmpty()) { - Log.e( - TAG, - "Failed to find resolveInfo for adServices service. Intent action: " - + TOPICS_SERVICE_NAME); - return null; - } - - if (resolveInfos.size() > 1) { - Log.e( - TAG, - String.format( - "Found multiple services (%1$s) for the same intent action (%2$s)", - TOPICS_SERVICE_NAME, resolveInfos)); - return null; - } - - final ServiceInfo serviceInfo = resolveInfos.get(0).serviceInfo; - if (serviceInfo == null) { - Log.e(TAG, "Failed to find serviceInfo for adServices service"); - return null; - } - - return serviceInfo.packageName; - } -} diff --git a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/PreviewApiTest.java b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/PreviewApiTest.java new file mode 100644 index 0000000000..b3eff2dd81 --- /dev/null +++ b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/PreviewApiTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.adservices.tests.cts.topics; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.GetTopicsResponse; +import android.adservices.topics.Topic; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.compatibility.common.util.ShellUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +@RunWith(JUnit4.class) +public class PreviewApiTest { + private static final String TAG = "PreviewApiTest"; + // The JobId of the Epoch Computation. + private static final int EPOCH_JOB_ID = 2; + + // Override the Epoch Job Period to this value to speed up the epoch computation. + private static final long TEST_EPOCH_JOB_PERIOD_MS = 3_000; + + // Default Epoch Period. + private static final long TOPICS_EPOCH_JOB_PERIOD_MS = 7 * 86_400_000; // 7 days. + + // Use 0 percent for random topic in the test so that we can verify the returned topic. + private static final int TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 0; + private static final int TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 5; + + protected static final Context sContext = ApplicationProvider.getApplicationContext(); + private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); + + // Used to get the package name. Copied over from com.android.adservices.AdServicesCommon + private static final String TOPICS_SERVICE_NAME = "android.adservices.TOPICS_SERVICE"; + private static final String ADSERVICES_PACKAGE_NAME = getAdServicesPackageName(); + + @Before + public void setup() throws Exception { + // We need to skip 3 epochs so that if there is any usage from other test runs, it will + // not be used for epoch retrieval. + Thread.sleep(3 * TEST_EPOCH_JOB_PERIOD_MS); + + overrideEpochPeriod(TEST_EPOCH_JOB_PERIOD_MS); + // We need to turn off random topic so that we can verify the returned topic. + overridePercentageForRandomTopic(TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); + } + + @After + public void teardown() { + overrideEpochPeriod(TOPICS_EPOCH_JOB_PERIOD_MS); + overridePercentageForRandomTopic(TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); + } + + @Test + public void testRecordObservation() throws ExecutionException, InterruptedException { + // The Test app has 2 SDKs: sdk1 calls the Topics API. This will record the usage for Sdk1 + // by default, recordObservation is true. + AdvertisingTopicsClient advertisingTopicsClient1 = + new AdvertisingTopicsClient.Builder() + .setContext(sContext) + .setSdkName("sdk1") + .setExecutor(CALLBACK_EXECUTOR) + .build(); + + // Sdk2 calls the Topics API and set the Record Observation to false. This will not record + // the usage for sdk2. + AdvertisingTopicsClient advertisingTopicsClient2 = + new AdvertisingTopicsClient.Builder() + .setContext(sContext) + .setSdkName("sdk2") + .setShouldRecordObservation(false) + .setExecutor(CALLBACK_EXECUTOR) + .build(); + + // At beginning, Sdk1 receives no topic. + GetTopicsResponse sdk1Result = advertisingTopicsClient1.getTopics().get(); + assertThat(sdk1Result.getTopics()).isEmpty(); + + // At beginning, Sdk2 receives no topic. + GetTopicsResponse sdk2Result = advertisingTopicsClient2.getTopics().get(); + assertThat(sdk2Result.getTopics()).isEmpty(); + + // Now force the Epoch Computation Job. This should be done in the same epoch for + // callersCanLearnMap to have the entry for processing. + forceEpochComputationJob(); + + // Wait to the next epoch. We will not need to do this after we implement the fix in + // go/rb-topics-epoch-scheduling + Thread.sleep(TEST_EPOCH_JOB_PERIOD_MS); + + // Since the sdk1 called the Topics API in the previous Epoch, it should receive some topic. + sdk1Result = advertisingTopicsClient1.getTopics().get(); + assertThat(sdk1Result.getTopics()).isNotEmpty(); + + // We only have 1 test app which has 5 classification topics: 10147,10253,10175,10254,10333 + // in the precomputed list. + // These 5 classification topics will become top 5 topics of the epoch since there is + // no other apps calling Topics API. + // The app will be assigned one random topic from one of these 5 topics. + assertThat(sdk1Result.getTopics()).hasSize(1); + Topic topic = sdk1Result.getTopics().get(0); + + // topic is one of the 5 classification topics of the Test App. + assertThat(topic.getTopicId()).isIn(Arrays.asList(10147, 10253, 10175, 10254, 10333)); + assertThat(topic.getModelVersion()).isAtLeast(1L); + assertThat(topic.getTaxonomyVersion()).isAtLeast(1L); + + // Sdk2 can not get any topics in this epoch because sdk2 sets not to record + // observation in previous epoch. + sdk2Result = advertisingTopicsClient2.getTopics().get(); + assertThat(sdk2Result.getTopics()).isEmpty(); + } + + // Override the Epoch Period to shorten the Epoch Length in the test. + private void overrideEpochPeriod(long overrideEpochPeriod) { + ShellUtils.runShellCommand( + "setprop debug.adservices.topics_epoch_job_period_ms " + overrideEpochPeriod); + } + + // Override the Percentage For Random Topic in the test. + private void overridePercentageForRandomTopic(long overridePercentage) { + ShellUtils.runShellCommand( + "setprop debug.adservices.topics_percentage_for_random_topics " + + overridePercentage); + } + + /** Forces JobScheduler to run the Epoch Computation job */ + private void forceEpochComputationJob() { + ShellUtils.runShellCommand( + "cmd jobscheduler run -f" + " " + ADSERVICES_PACKAGE_NAME + " " + EPOCH_JOB_ID); + } + + // Used to get the package name. Copied over from com.android.adservices.AndroidServiceBinder + private static String getAdServicesPackageName() { + final Intent intent = new Intent(TOPICS_SERVICE_NAME); + final List<ResolveInfo> resolveInfos = + sContext.getPackageManager() + .queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); + + if (resolveInfos == null || resolveInfos.isEmpty()) { + Log.e( + TAG, + "Failed to find resolveInfo for adServices service. Intent action: " + + TOPICS_SERVICE_NAME); + return null; + } + + if (resolveInfos.size() > 1) { + Log.e( + TAG, + String.format( + "Found multiple services (%1$s) for the same intent action (%2$s)", + TOPICS_SERVICE_NAME, resolveInfos)); + return null; + } + + final ServiceInfo serviceInfo = resolveInfos.get(0).serviceInfo; + if (serviceInfo == null) { + Log.e(TAG, "Failed to find serviceInfo for adServices service"); + return null; + } + + return serviceInfo.packageName; + } +} diff --git a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java index 40dbe05a91..b0eaf7ddc6 100644 --- a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java +++ b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java @@ -55,6 +55,23 @@ public class TopicsManagerTest { // Default Epoch Period. private static final long TOPICS_EPOCH_JOB_PERIOD_MS = 7 * 86_400_000; // 7 days. + // Classifier test constants. + private static final int TEST_CLASSIFIER_NUMBER_OF_TOP_LABELS = 5; + // Each app is given topics with a confidence score between 0.0 to 1.0 float value. This + // denotes how confident are you that a particular topic t1 is related to the app x that is + // classified. + // Threshold value for classifier confidence set to 0 to allow all topics and avoid filtering. + private static final float TEST_CLASSIFIER_THRESHOLD = 0.0f; + // ON_DEVICE_CLASSIFIER + private static final int TEST_CLASSIFIER_TYPE = 1; + + // Classifier default constants. + private static final int DEFAULT_CLASSIFIER_NUMBER_OF_TOP_LABELS = 3; + // Threshold value for classifier confidence set back to the default. + private static final float DEFAULT_CLASSIFIER_THRESHOLD = 0.1f; + // PRECOMPUTED_THEN_ON_DEVICE_CLASSIFIER + private static final int DEFAULT_CLASSIFIER_TYPE = 3; + // Use 0 percent for random topic in the test so that we can verify the returned topic. private static final int TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 0; private static final int TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC = 5; @@ -72,16 +89,20 @@ public class TopicsManagerTest { // not be used for epoch retrieval. Thread.sleep(3 * TEST_EPOCH_JOB_PERIOD_MS); - overridingBeforeTest(); + overrideEpochPeriod(TEST_EPOCH_JOB_PERIOD_MS); + // We need to turn off random topic so that we can verify the returned topic. + overridePercentageForRandomTopic(TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); } @After public void teardown() { - overridingAfterTest(); + overrideEpochPeriod(TOPICS_EPOCH_JOB_PERIOD_MS); + overridePercentageForRandomTopic(TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); } @Test - public void testTopicsManager() throws Exception { + public void testTopicsManager_runDefaultClassifier() throws Exception { + // Default classifier uses the precomputed list first, then on-device classifier. // The Test App has 2 SDKs: sdk1 calls the Topics API and sdk2 does not. // Sdk1 calls the Topics API. AdvertisingTopicsClient advertisingTopicsClient1 = @@ -116,7 +137,7 @@ public class TopicsManagerTest { Topic topic = sdk1Result.getTopics().get(0); // topic is one of the 5 classification topics of the Test App. - assertThat(topic.getTopicId()).isIn(Arrays.asList(10147,10253,10175,10254,10333)); + assertThat(topic.getTopicId()).isIn(Arrays.asList(10147, 10253, 10175, 10254, 10333)); assertThat(topic.getModelVersion()).isAtLeast(1L); assertThat(topic.getTaxonomyVersion()).isAtLeast(1L); @@ -133,41 +154,78 @@ public class TopicsManagerTest { assertThat(sdk2Result2.getTopics()).isEmpty(); } - private void overridingBeforeTest() { - overridingAdservicesLoggingLevel("VERBOSE"); + @Test + public void testTopicsManager_runOnDeviceClassifier() throws Exception { + // Set classifier flag to use on-device classifier. + overrideClassifierType(TEST_CLASSIFIER_TYPE); + + // Set number of top labels returned by the on-device classifier to 5. + overrideClassifierNumberOfTopLabels(TEST_CLASSIFIER_NUMBER_OF_TOP_LABELS); + // Remove classifier threshold by setting it to 0. + overrideClassifierThreshold(TEST_CLASSIFIER_THRESHOLD); + + // The Test App has 1 SDK: sdk3 + // sdk3 calls the Topics API. + AdvertisingTopicsClient advertisingTopicsClient3 = + new AdvertisingTopicsClient.Builder() + .setContext(sContext) + .setSdkName("sdk3") + .setExecutor(CALLBACK_EXECUTOR) + .build(); - overrideDisableTopicsEnrollmentCheck("1"); - overrideEpochPeriod(TEST_EPOCH_JOB_PERIOD_MS); + // At beginning, Sdk3 receives no topic. + GetTopicsResponse sdk3Result = advertisingTopicsClient3.getTopics().get(); + assertThat(sdk3Result.getTopics()).isEmpty(); - // We need to turn off random topic so that we can verify the returned topic. - overridePercentageForRandomTopic(TEST_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); + // Now force the Epoch Computation Job. This should be done in the same epoch for + // callersCanLearnMap to have the entry for processing. + forceEpochComputationJob(); - // We need to turn the Consent Manager into debug mode - overrideConsentManagerDebugMode(); + // Wait to the next epoch. We will not need to do this after we implement the fix in + // go/rb-topics-epoch-scheduling + Thread.sleep(TEST_EPOCH_JOB_PERIOD_MS); + + // Since the sdk3 called the Topics API in the previous Epoch, it should receive some topic. + sdk3Result = advertisingTopicsClient3.getTopics().get(); + assertThat(sdk3Result.getTopics()).isNotEmpty(); + + // We only have 5 topics classified by the on-device classifier. + // The app will be assigned one random topic from one of these 5 topics. + assertThat(sdk3Result.getTopics()).hasSize(1); + Topic topic = sdk3Result.getTopics().get(0); + + // Top 5 classifications for empty string with v2 model are [10230, 10253, 10227, 10250, + // 10257]. This is computed by running the model on the device for empty string. + // topic is one of the 5 classification topics of the Test App. + List<Integer> expectedTopTopicIds = Arrays.asList(10230, 10253, 10227, 10250, 10257); + assertThat(topic.getTopicId()).isIn(expectedTopTopicIds); + + assertThat(topic.getModelVersion()).isAtLeast(2L); + assertThat(topic.getTaxonomyVersion()).isAtLeast(2L); - // Turn off MDD to avoid model mismatching - disableMddBackgroundTasks(true); + // Set classifier flag back to default. + overrideClassifierType(DEFAULT_CLASSIFIER_TYPE); + + // Set number of top labels returned by the on-device classifier back to default. + overrideClassifierNumberOfTopLabels(DEFAULT_CLASSIFIER_NUMBER_OF_TOP_LABELS); + // Set classifier threshold back to default. + overrideClassifierThreshold(DEFAULT_CLASSIFIER_THRESHOLD); } - private void overridingAfterTest() { - overrideDisableTopicsEnrollmentCheck("0"); - overrideEpochPeriod(TOPICS_EPOCH_JOB_PERIOD_MS); - overridePercentageForRandomTopic(TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC); - disableMddBackgroundTasks(false); - overridingAdservicesLoggingLevel("INFO"); + // Override the flag to select classifier type. + private void overrideClassifierType(int val) { + ShellUtils.runShellCommand("device_config put adservices classifier_type " + val); } - // Switch on/off for MDD service. Default value is false, which means MDD is enabled. - private void disableMddBackgroundTasks(boolean isSwitchedOff) { + // Override the flag to change the number of top labels returned by on-device classifier type. + private void overrideClassifierNumberOfTopLabels(int val) { ShellUtils.runShellCommand( - "setprop debug.adservices.mdd_background_task_kill_switch " + isSwitchedOff); + "device_config put adservices classifier_number_of_top_labels " + val); } - // Override the flag to disable Topics enrollment check. - private void overrideDisableTopicsEnrollmentCheck(String val) { - // Setting it to 1 here disables the Topics' enrollment check. - ShellUtils.runShellCommand( - "setprop debug.adservices.disable_topics_enrollment_check " + val); + // Override the flag to change the threshold for the classifier. + private void overrideClassifierThreshold(float val) { + ShellUtils.runShellCommand("device_config put adservices classifier_threshold " + val); } // Override the Epoch Period to shorten the Epoch Length in the test. @@ -183,21 +241,12 @@ public class TopicsManagerTest { + overridePercentage); } - // Override the Consent Manager behaviour - Consent Given - private void overrideConsentManagerDebugMode() { - ShellUtils.runShellCommand("setprop debug.adservices.consent_manager_debug_mode true"); - } - /** Forces JobScheduler to run the Epoch Computation job */ private void forceEpochComputationJob() { ShellUtils.runShellCommand( "cmd jobscheduler run -f" + " " + ADSERVICES_PACKAGE_NAME + " " + EPOCH_JOB_ID); } - private void overridingAdservicesLoggingLevel(String loggingLevel) { - ShellUtils.runShellCommand("setprop log.tag.adservices %s", loggingLevel); - } - // Used to get the package name. Copied over from com.android.adservices.AndroidServiceBinder private static String getAdServicesPackageName() { final Intent intent = new Intent(TOPICS_SERVICE_NAME); |