1 /*
2  * Copyright (C) 2023 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 package com.android.adservices.common;
17 
18 import static com.android.adservices.common.AbstractAdServicesSystemPropertiesDumperRule.SYSTEM_PROPERTY_FOR_DEBUGGING_PREFIX;
19 import static com.android.adservices.service.FlagsConstants.ARRAY_SPLITTER_COMMA;
20 import static com.android.adservices.service.FlagsConstants.KEY_ADID_KILL_SWITCH;
21 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_SERVICE_KILL_SWITCH;
22 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED;
23 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_SELECT_ADS_KILL_SWITCH;
24 import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_KILL_SWITCH;
25 import static com.android.adservices.service.FlagsConstants.NAMESPACE_ADSERVICES;
26 
27 import com.android.adservices.common.annotations.DisableGlobalKillSwitch;
28 import com.android.adservices.common.annotations.EnableAllApis;
29 import com.android.adservices.common.annotations.SetAllLogcatTags;
30 import com.android.adservices.common.annotations.SetCompatModeFlags;
31 import com.android.adservices.common.annotations.SetDefaultLogcatTags;
32 import com.android.adservices.common.annotations.SetMsmtApiAppAllowList;
33 import com.android.adservices.common.annotations.SetMsmtWebContextClientAppAllowList;
34 import com.android.adservices.common.annotations.SetPpapiAppAllowList;
35 import com.android.adservices.service.FlagsConstants;
36 import com.android.adservices.shared.testing.AbstractFlagsSetterRule;
37 import com.android.adservices.shared.testing.DeviceConfigHelper;
38 import com.android.adservices.shared.testing.Logger.LogLevel;
39 import com.android.adservices.shared.testing.Logger.RealLogger;
40 import com.android.adservices.shared.testing.NameValuePair.Matcher;
41 import com.android.adservices.shared.testing.SystemPropertiesHelper;
42 
43 import org.junit.runner.Description;
44 
45 import java.lang.annotation.Annotation;
46 import java.util.Arrays;
47 
48 // TODO(b/294423183): add unit tests for the most relevant / less repetitive stuff (don't need to
49 // test all setters / getters, for example)
50 /**
51  * Rule used to properly set AdService flags - it will take care of permissions, restoring values at
52  * the end, setting {@link android.provider.DeviceConfig} or {@link android.os.SystemProperties},
53  * etc...
54  */
55 ////////////////////////////////////////////////////////////////////////////////////////////////////
56 // NOTE: DO NOT add new setXyz() methods, unless they need non-trivial logic. Instead, let your   //
57 // test call setFlags(flagName) (statically import FlagsConstant.flagName), which will make it    //
58 // easier to transition the test to an annotated-base approach.                                   //
59 ////////////////////////////////////////////////////////////////////////////////////////////////////
60 public abstract class AbstractAdServicesFlagsSetterRule<
61                 T extends AbstractAdServicesFlagsSetterRule<T>>
62         extends AbstractFlagsSetterRule<T> {
63 
64     // TODO(b/295321663): move these constants (and those from LogFactory) to AdServicesCommon
65     protected static final String LOGCAT_TAG_ADSERVICES = "adservices";
66     protected static final String LOGCAT_TAG_ADSERVICES_SERVICE = LOGCAT_TAG_ADSERVICES + "-system";
67     protected static final String LOGCAT_TAG_TOPICS = LOGCAT_TAG_ADSERVICES + ".topics";
68     protected static final String LOGCAT_TAG_FLEDGE = LOGCAT_TAG_ADSERVICES + ".fledge";
69     protected static final String LOGCAT_TAG_KANON = LOGCAT_TAG_ADSERVICES + ".kanon";
70     protected static final String LOGCAT_TAG_MEASUREMENT = LOGCAT_TAG_ADSERVICES + ".measurement";
71     protected static final String LOGCAT_TAG_UI = LOGCAT_TAG_ADSERVICES + ".ui";
72     protected static final String LOGCAT_TAG_ADID = LOGCAT_TAG_ADSERVICES + ".adid";
73     protected static final String LOGCAT_TAG_APPSETID = LOGCAT_TAG_ADSERVICES + ".appsetid";
74     protected static final String LOGCAT_TAG_SHARED = "adservices-shared";
75 
76     // TODO(b/294423183): instead of hardcoding the SYSTEM_PROPERTY_FOR_LOGCAT_TAGS_PREFIX, we
77     // should dynamically calculate it based on setLogcatTag() calls
78     private static final Matcher PROPERTIES_PREFIX_MATCHER =
79             (prop) ->
80                     prop.name.startsWith(SYSTEM_PROPERTY_FOR_DEBUGGING_PREFIX)
81                             || prop.name.startsWith(
82                                     SYSTEM_PROPERTY_FOR_LOGCAT_TAGS_PREFIX + "adservices");
83 
84     private static final boolean USE_TEST_PACKAGE_AS_DEFAULT = true;
85     private static final boolean DONT_USE_TEST_PACKAGE_AS_DEFAULT = false;
86 
AbstractAdServicesFlagsSetterRule( RealLogger logger, DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory, SystemPropertiesHelper.Interface systemPropertiesInterface)87     protected AbstractAdServicesFlagsSetterRule(
88             RealLogger logger,
89             DeviceConfigHelper.InterfaceFactory deviceConfigInterfaceFactory,
90             SystemPropertiesHelper.Interface systemPropertiesInterface) {
91         super(
92                 logger,
93                 NAMESPACE_ADSERVICES,
94                 SYSTEM_PROPERTY_FOR_DEBUGGING_PREFIX,
95                 PROPERTIES_PREFIX_MATCHER,
96                 deviceConfigInterfaceFactory,
97                 systemPropertiesInterface);
98     }
99 
100     @Override
isAnnotationSupported(Annotation annotation)101     protected boolean isAnnotationSupported(Annotation annotation) {
102         // NOTE: add annotations sorted by "most likely usage"
103         return annotation instanceof DisableGlobalKillSwitch
104                 || annotation instanceof EnableAllApis
105                 || annotation instanceof SetCompatModeFlags
106                 || annotation instanceof SetPpapiAppAllowList
107                 || annotation instanceof SetDefaultLogcatTags
108                 || annotation instanceof SetAllLogcatTags
109                 || annotation instanceof SetMsmtApiAppAllowList
110                 || annotation instanceof SetMsmtWebContextClientAppAllowList;
111     }
112 
113     @Override
processAnnotation(Description description, Annotation annotation)114     protected void processAnnotation(Description description, Annotation annotation) {
115         // NOTE: add annotations sorted by "most likely usage"
116         if (annotation instanceof DisableGlobalKillSwitch) {
117             setGlobalKillSwitch(false);
118         } else if (annotation instanceof EnableAllApis) {
119             enableAllApis();
120         } else if (annotation instanceof SetCompatModeFlags) {
121             setCompatModeFlags();
122         } else if (annotation instanceof SetPpapiAppAllowList) {
123             setPpapiAppAllowList(
124                     ((SetPpapiAppAllowList) annotation).value(), USE_TEST_PACKAGE_AS_DEFAULT);
125         } else if (annotation instanceof SetDefaultLogcatTags) {
126             setDefaultLogcatTags();
127         } else if (annotation instanceof SetAllLogcatTags) {
128             setAllLogcatTags();
129         } else if (annotation instanceof SetMsmtApiAppAllowList) {
130             setMsmtApiAppAllowList(
131                     ((SetMsmtApiAppAllowList) annotation).value(), USE_TEST_PACKAGE_AS_DEFAULT);
132         } else if (annotation instanceof SetMsmtWebContextClientAppAllowList) {
133             setMsmtWebContextClientAllowList(
134                     ((SetMsmtWebContextClientAppAllowList) annotation).value(),
135                     USE_TEST_PACKAGE_AS_DEFAULT);
136         } else {
137             // should not happen
138             throw new IllegalStateException(
139                     "INTERNAL ERROR: processAnnotation() called with unsupported annotation: "
140                             + annotation);
141         }
142     }
143 
144     /**
145      * Gets the package name of the app running this test.
146      *
147      * <p>Used on annotations that applies to the test app by default (for example, for allowlist).
148      */
getTestPackageName()149     protected String getTestPackageName() {
150         //
151         throw new UnsupportedOperationException(
152                 "Concrete rule ("
153                         + getClass().getSimpleName()
154                         + ") cannot infer the name of the test package (typically happens on"
155                         + " host-side tests)");
156     }
157 
158     // Helper methods to set more commonly used flags such as kill switches.
159     // Less common flags can be set directly using setFlags methods.
160 
161     // TODO(b/303901926): add unit test
162     /**
163      * Sets a flag that takes an array of strings with just the given value, using the default
164      * separator.
165      */
setSimpleArrayFlag(String name, String value)166     public final T setSimpleArrayFlag(String name, String value) {
167         return setFlag(name, new String[] {value}, ARRAY_SPLITTER_COMMA);
168     }
169 
170     /**
171      * Overrides the flag that sets the global AdServices kill switch.
172      *
173      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link DisableGlobalKillSwitch}
174      * in this case), unless the test need to dynamically change the flags after it started.
175      */
setGlobalKillSwitch(boolean value)176     public final T setGlobalKillSwitch(boolean value) {
177         return setFlag(FlagsConstants.KEY_GLOBAL_KILL_SWITCH, value);
178     }
179 
enableAllApis()180     final T enableAllApis() {
181         return setAllLogcatTags()
182                 .setGlobalKillSwitch(false)
183                 .setTopicsKillSwitch(false)
184                 .setFlag(KEY_ADID_KILL_SWITCH, false)
185                 .setFlag(KEY_MEASUREMENT_KILL_SWITCH, false)
186                 .setFlag(KEY_FLEDGE_CUSTOM_AUDIENCE_SERVICE_KILL_SWITCH, false)
187                 .setFlag(KEY_FLEDGE_SELECT_ADS_KILL_SWITCH, false)
188                 .setFlag(KEY_FLEDGE_SCHEDULE_CUSTOM_AUDIENCE_UPDATE_ENABLED, true);
189     }
190 
191     /**
192      * Overrides flag used by {@link com.android.adservices.service.PhFlags#getAdServicesEnabled}.
193      */
setAdServicesEnabled(boolean value)194     public final T setAdServicesEnabled(boolean value) {
195         return setFlag(FlagsConstants.KEY_ADSERVICES_ENABLED, value);
196     }
197 
198     /** Overrides the flag that sets the Topics kill switch. */
setTopicsKillSwitch(boolean value)199     public final T setTopicsKillSwitch(boolean value) {
200         return setFlag(FlagsConstants.KEY_TOPICS_KILL_SWITCH, value);
201     }
202 
203     /** Overrides the flag that sets the Topics Device Classifier kill switch. */
setTopicsOnDeviceClassifierKillSwitch(boolean value)204     public final T setTopicsOnDeviceClassifierKillSwitch(boolean value) {
205         return setFlag(FlagsConstants.KEY_TOPICS_ON_DEVICE_CLASSIFIER_KILL_SWITCH, value);
206     }
207 
208     /**
209      * Overrides flag used by {@link com.android.adservices.service.PhFlags#getEnableBackCompat()}.
210      */
setEnableBackCompat(boolean value)211     public final T setEnableBackCompat(boolean value) {
212         return setFlag(FlagsConstants.KEY_ENABLE_BACK_COMPAT, value);
213     }
214 
215     /**
216      * Overrides flag used by {@link
217      * com.android.adservices.service.PhFlags#getMeasurementRollbackDeletionAppSearchKillSwitch()}.
218      */
setMeasurementRollbackDeletionAppSearchKillSwitch(boolean value)219     public final T setMeasurementRollbackDeletionAppSearchKillSwitch(boolean value) {
220         return setFlag(
221                 FlagsConstants.KEY_MEASUREMENT_ROLLBACK_DELETION_APP_SEARCH_KILL_SWITCH, value);
222     }
223 
224     /**
225      * Overrides flag used by {@link com.android.adservices.service.PhFlags#getPpapiAppAllowList()}.
226      *
227      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link SetPpapiAppAllowList} in
228      * this case), unless the test need to dynamically change the flags after it started.
229      */
setPpapiAppAllowList(String... value)230     public final T setPpapiAppAllowList(String... value) {
231         return setPpapiAppAllowList(value, DONT_USE_TEST_PACKAGE_AS_DEFAULT);
232     }
233 
setPpapiAppAllowList(String[] value, boolean useTestPackageAsDefault)234     private T setPpapiAppAllowList(String[] value, boolean useTestPackageAsDefault) {
235         mLog.d(
236                 "setPpapiAppAllowList(useTestPackageAsDefault=%b): %s",
237                 useTestPackageAsDefault, Arrays.toString(value));
238         return setAllowListFlag(
239                 FlagsConstants.KEY_PPAPI_APP_ALLOW_LIST, value, useTestPackageAsDefault);
240     }
241 
242     /**
243      * Overrides flag used by {@link
244      * com.android.adservices.service.PhFlags#getMsmtApiAppAllowList()}.
245      *
246      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link SetMsmtApiAppAllowList} in
247      * this case), unless the test need to dynamically change the flags after it started.
248      */
setMsmtApiAppAllowList(String... value)249     public final T setMsmtApiAppAllowList(String... value) {
250         return setMsmtApiAppAllowList(value, DONT_USE_TEST_PACKAGE_AS_DEFAULT);
251     }
252 
setMsmtApiAppAllowList(String[] value, boolean useTestPackageAsDefault)253     private T setMsmtApiAppAllowList(String[] value, boolean useTestPackageAsDefault) {
254         mLog.d(
255                 "setMsmtApiAppAllowList(useTestPackageAsDefault=%b): %s",
256                 useTestPackageAsDefault, Arrays.toString(value));
257         return setAllowListFlag(
258                 FlagsConstants.KEY_MSMT_API_APP_ALLOW_LIST, value, useTestPackageAsDefault);
259     }
260 
261     /**
262      * Overrides flag used by {@link
263      * com.android.adservices.service.PhFlags#getWebContextClientAppAllowList()}.
264      *
265      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link
266      * SetMsmtWebContextClientAppAllowList} in this case), unless the test need to dynamically
267      * change the flags after it started.
268      */
setMsmtWebContextClientAllowList(String... value)269     public final T setMsmtWebContextClientAllowList(String... value) {
270         return setMsmtWebContextClientAllowList(value, DONT_USE_TEST_PACKAGE_AS_DEFAULT);
271     }
272 
setMsmtWebContextClientAllowList(String[] value, boolean useTestPackageAsDefault)273     private T setMsmtWebContextClientAllowList(String[] value, boolean useTestPackageAsDefault) {
274         mLog.d(
275                 "setMsmtWebContextClientAllowList(useTestPackageAsDefault=%b): %s",
276                 useTestPackageAsDefault, Arrays.toString(value));
277         return setAllowListFlag(
278                 FlagsConstants.KEY_WEB_CONTEXT_CLIENT_ALLOW_LIST, value, useTestPackageAsDefault);
279     }
280 
281     /**
282      * Overrides flag used by {@link
283      * com.android.adservices.service.PhFlags#getMddBackgroundTaskKillSwitch()}.
284      */
setMddBackgroundTaskKillSwitch(boolean value)285     public T setMddBackgroundTaskKillSwitch(boolean value) {
286         return setFlag(FlagsConstants.KEY_MDD_BACKGROUND_TASK_KILL_SWITCH, value);
287     }
288 
289     ////////////////////////////////////////////////////////////////////////////////////////////////
290     // NOTE: DO NOT add new setXyz() methods, unless they need non-trivial logic. Instead, let    //
291     // your test call setFlags(flagName) (statically import FlagsConstant.flagName), which will   //
292     // make it easier to transition the test to an annotated-base approach.                       //
293     ////////////////////////////////////////////////////////////////////////////////////////////////
294 
295     /**
296      * Sets all flags needed to enable compatibility mode, according to the Android version of the
297      * device running the test.
298      *
299      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link SetCompatModeFlags} in
300      * this case), unless the test need to dynamically change the flags after it started.
301      */
setCompatModeFlags()302     public T setCompatModeFlags() {
303         return runOrCache(
304                 "setCompatModeFlags()",
305                 () -> {
306                     if (isAtLeastT()) {
307                         mLog.d("setCompatModeFlags(): ignored on SDK %d", getDeviceSdk());
308                         // Do nothing; this method is intended to set flags for Android S- only.
309                         return;
310                     }
311 
312                     if (isAtLeastS()) {
313                         mLog.d("setCompatModeFlags(): setting flags for S+");
314                         setFlag(FlagsConstants.KEY_ENABLE_BACK_COMPAT, true);
315                         setFlag(
316                                 FlagsConstants.KEY_BLOCKED_TOPICS_SOURCE_OF_TRUTH,
317                                 FlagsConstants.APPSEARCH_ONLY);
318                         setFlag(
319                                 FlagsConstants.KEY_CONSENT_SOURCE_OF_TRUTH,
320                                 FlagsConstants.APPSEARCH_ONLY);
321                         setFlag(FlagsConstants.KEY_ENABLE_APPSEARCH_CONSENT_DATA, true);
322                         setFlag(
323                                 FlagsConstants
324                                         .KEY_MEASUREMENT_ROLLBACK_DELETION_APP_SEARCH_KILL_SWITCH,
325                                 false);
326                         return;
327                     }
328                     mLog.d("setCompatModeFlags(): setting flags for R+");
329                     setFlag(FlagsConstants.KEY_ENABLE_BACK_COMPAT, true);
330                     setFlag(FlagsConstants.KEY_ENABLE_ADEXT_DATA_SERVICE_DEBUG_PROXY, true);
331                 });
332     }
333 
334     ////////////////////////////////////////////////////////////////////////////////////////////////
335     // NOTE: DO NOT add new setXyz() methods, unless they need non-trivial logic. Instead, let    //
336     // your test call setFlags(flagName) (statically import FlagsConstant.flagName), which will   //
337     // make it easier to transition the test to an annotated-base approach.                       //
338     ////////////////////////////////////////////////////////////////////////////////////////////////
339 
340     /**
341      * Sets the common AdServices {@code logcat} tags.
342      *
343      * <p>This method is usually set automatically by the factory methods, but should be set again
344      * (on host-side tests) after reboot.
345      *
346      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link SetDefaultLogcatTags} in
347      * this case), unless the test need to dynamically change the flags after it started.
348      */
349     public T setDefaultLogcatTags() {
350         setInfraLogcatTags();
351         setLogcatTag(LOGCAT_TAG_ADSERVICES, LogLevel.VERBOSE);
352         setLogcatTag(LOGCAT_TAG_SHARED, LogLevel.VERBOSE);
353         setLogcatTag(LOGCAT_TAG_ADSERVICES_SERVICE, LogLevel.VERBOSE);
354         return getThis();
355     }
356 
357     /**
358      * Sets all AdServices {@code logcat} tags.
359      *
360      * <p>This method is usually set automatically by the factory methods, but should be set again
361      * (on host-side tests) after reboot.
362      *
363      * <p>NOTE: it's usually cleaner to use an annotation instead ({@link SetAllLogcatTags} in this
364      * case), unless the test need to dynamically change the flags after it started.
365      */
366     public T setAllLogcatTags() {
367         setDefaultLogcatTags();
368         setLogcatTag(LOGCAT_TAG_TOPICS, LogLevel.VERBOSE);
369         setLogcatTag(LOGCAT_TAG_FLEDGE, LogLevel.VERBOSE);
370         setLogcatTag(LOGCAT_TAG_MEASUREMENT, LogLevel.VERBOSE);
371         setLogcatTag(LOGCAT_TAG_ADID, LogLevel.VERBOSE);
372         setLogcatTag(LOGCAT_TAG_APPSETID, LogLevel.VERBOSE);
373         setLogcatTag(LOGCAT_TAG_KANON, LogLevel.VERBOSE);
374         return getThis();
375     }
376 
377     /**
378      * Sets Measurement {@code logcat} tags.
379      *
380      * <p>This method is usually set automatically by the factory methods, but should be set again
381      * (on host-side tests) after reboot.
382      */
383     public T setMeasurementTags() {
384         setLogcatTag(LOGCAT_TAG_MEASUREMENT, LogLevel.VERBOSE);
385         return getThis();
386     }
387 
388     ////////////////////////////////////////////////////////////////////////////////////////////////
389     // NOTE: DO NOT add new setXyz() methods, unless they need non-trivial logic. Instead, let    //
390     // your test call setFlags(flagName) (statically import FlagsConstant.flagName), which will   //
391     // make it easier to transition the test to an annotated-base approach.                       //
392     ////////////////////////////////////////////////////////////////////////////////////////////////
393 
394     private T setAllowListFlag(String name, String[] values, boolean useTestPackageAsDefault) {
395         if (values.length == 0 && useTestPackageAsDefault) {
396             String testPkg = getTestPackageName();
397             mLog.d(
398                     "setAllowListUsingTestAppAsDefault(%s): package not set by annotation, using"
399                             + " test package name %s",
400                     name, testPkg);
401             values = new String[] {testPkg};
402         }
403         return setFlag(name, values, ARRAY_SPLITTER_COMMA);
404     }
405 }
406