summaryrefslogtreecommitdiff
path: root/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests
diff options
context:
space:
mode:
Diffstat (limited to 'adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests')
-rw-r--r--adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/AppUpdateTest.java399
-rw-r--r--adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/PreviewApiTest.java198
-rw-r--r--adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java123
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);