• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.cts.verifier;
18 
19 import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
20 
21 import android.annotation.SuppressLint;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.content.res.Resources;
29 import android.hardware.SensorPrivacyManager;
30 import android.os.BatteryManager;
31 import android.os.Bundle;
32 import android.os.UserManager;
33 import android.util.Log;
34 import android.widget.ListView;
35 
36 import com.android.cts.verifier.TestListActivity.DisplayMode;
37 import com.android.modules.utils.build.SdkLevel;
38 
39 import java.lang.reflect.InvocationTargetException;
40 import java.lang.reflect.Method;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.stream.Collectors;
51 
52 /**
53  * {@link TestListAdapter} that populates the {@link TestListActivity}'s {@link ListView} by reading
54  * data from the CTS Verifier's AndroidManifest.xml.
55  *
56  * <p>Making a new test activity to appear in the list requires the following steps:
57  *
58  * <ol>
59  *   <li>REQUIRED: Add an activity to the AndroidManifest.xml with an intent filter with a main
60  *       action and the MANUAL_TEST category.
61  *       <pre>
62  *             <intent-filter>
63  *                <action android:name="android.intent.action.MAIN" />
64  *                <category android:name="android.cts.intent.category.MANUAL_TEST" />
65  *             </intent-filter>
66  *         </pre>
67  *   <li>REQUIRED: Add a meta data attribute to indicate which display modes of tests the activity
68  *       should belong to. "single_display_mode" indicates a test is only needed to run on the main
69  *       display mode (i.e. unfolded), and "multi_display_mode" indicates a test is required to run
70  *       under both modes (i.e. both folded and unfolded).If you don't add this attribute, your test
71  *       will show up in both unfolded and folded modes.
72  *       <pre>
73  *             <meta-data android:name="display_mode" android:value="multi_display_mode" />
74  *         </pre>
75  *   <li>OPTIONAL: Add a meta data attribute to indicate what category of tests the activity should
76  *       belong to. If you don't add this attribute, your test will show up in the "Other" tests
77  *       category.
78  *       <pre>
79  *             <meta-data android:name="test_category" android:value="@string/test_category_security" />
80  *         </pre>
81  *   <li>OPTIONAL: Add a meta data attribute to indicate whether this test has a parent test.
82  *       <pre>
83  *             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
84  *         </pre>
85  *   <li>OPTIONAL: Add a meta data attribute to indicate what features are required to run the test.
86  *       If the device does not have all of the required features then it will not appear in the
87  *       test list. Use a colon (:) to specify multiple required features.
88  *       <pre>
89  *             <meta-data android:name="test_required_features" android:value="android.hardware.sensor.accelerometer" />
90  *         </pre>
91  *   <li>OPTIONAL: Add a meta data attribute to indicate features such that, if any present, the
92  *       test gets excluded from being shown. If the device has any of the excluded features then
93  *       the test will not appear in the test list. Use a colon (:) to specify multiple features to
94  *       exclude for the test. Note that the colon means "or" in this case.
95  *       <pre>
96  *             <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television" />
97  *         </pre>
98  *   <li>OPTIONAL: Add a meta data attribute to indicate features such that, if any present, the
99  *       test is applicable to run. If the device has any of the applicable features then the test
100  *       will appear in the test list. Use a colon (:) to specify multiple features
101  *       <pre>
102  *             <meta-data android:name="test_applicable_features" android:value="android.hardware.sensor.compass" />
103  *         </pre>
104  *   <li>OPTIONAL: Add a meta data attribute to indicate which intent actions are required to run
105  *       the test. If the device does not have activities that handle all those actions, then it
106  *       will not appear in the test list. Use a colon (:) to specify multiple required intent
107  *       actions.
108  *       <pre>
109  *             <meta-data android:name="test_required_actions" android:value="android.app.action.ADD_DEVICE_ADMIN" />
110  *         </pre>
111  *   <li>OPTIONAL: Add a meta data attribute to indicate which intent actions should not run when
112  *       the user running the test is of the given "type" (notice that the type here is not
113  *       necessarily the same as {@link UserManager#getUserType()}). Use a colon (:) to specify
114  *       multiple user types.
115  *       <pre>
116  *             <meta-data android:name="test_excluded_user_types" android:value="visible_background_non-profile_user" />
117  *         </pre>
118  * </ol>
119  */
120 public class ManifestTestListAdapter extends TestListAdapter {
121     private static final String LOG_TAG = "ManifestTestListAdapter";
122 
123     private static final String TEST_CATEGORY_META_DATA = "test_category";
124 
125     private static final String TEST_PARENT_META_DATA = "test_parent";
126 
127     private static final String TEST_REQUIRED_FEATURES_META_DATA = "test_required_features";
128 
129     private static final String TEST_EXCLUDED_FEATURES_META_DATA = "test_excluded_features";
130 
131     private static final String TEST_APPLICABLE_FEATURES_META_DATA = "test_applicable_features";
132 
133     private static final String TEST_REQUIRED_CONFIG_META_DATA = "test_required_configs";
134 
135     private static final String TEST_REQUIRED_ACTIONS_META_DATA = "test_required_actions";
136 
137     private static final String TEST_EXCLUDED_USER_TYPES_META_DATA = "test_excluded_user_types";
138 
139     private static final String TEST_DISPLAY_MODE_META_DATA = "display_mode";
140 
141     private static final String TEST_PASS_MODE = "test_pass_mode";
142 
143     private static final String CONFIG_BATTERY_SUPPORTED = "config_battery_supported";
144 
145     private static final String CONFIG_NO_EMULATOR = "config_no_emulator";
146 
147     private static final String CONFIG_HAS_RECENTS = "config_has_recents";
148 
149     private static final String CONFIG_HDMI_SOURCE = "config_hdmi_source";
150 
151     private static final String CONFIG_QUICK_SETTINGS_SUPPORTED = "config_quick_settings_supported";
152 
153     private static final String CONFIG_HAS_MIC_TOGGLE = "config_has_mic_toggle";
154 
155     private static final String CONFIG_HAS_CAMERA_TOGGLE = "config_has_camera_toggle";
156 
157     private static final String CONFIG_CHANGEABLE_VOLUME = "config_changeable_volume";
158 
159     private static final String CONFIG_SUPPORTS_BUBBLE = "config_supports_bubble";
160 
161     /**
162      * The config to represent that a test is only needed to run in the main display mode (i.e.
163      * unfolded).
164      */
165     private static final String SINGLE_DISPLAY_MODE = "single_display_mode";
166 
167     /**
168      * The config to represent that a test is needed to run in the multiple display modes (i.e. both
169      * unfolded and folded).
170      */
171     private static final String MULTIPLE_DISPLAY_MODE = "multi_display_mode";
172 
173     /** The config to represent that a test is only needed to run in the folded display mode. */
174     private static final String FOLDED_DISPLAY_MODE = "folded_display_mode";
175 
176     /**
177      * The config to represent that a test is marked as pass when it passes either in folded mode or
178      * in unfolded mode.
179      */
180     private static final String EITHER_MODE = "either_mode";
181 
182     /**
183      * The user is not a {@link UserManager#isProfile() profile} and is running in the background,
184      * but {@link UserManager#isUserVisible() visible} in a display.
185      */
186     private static final String USER_TYPE_VISIBLE_BG_USER = "visible_background_non-profile_user";
187 
188     /** The name of the camera ITS test of a {@link TestListItem}. */
189     private static final String CAMERA_ITS_TEST =
190             "com.android.cts.verifier.camera.its.ItsTestActivity";
191 
192     /** The name of the camera ITS test (folded mode) of a {@link TestListItem}. */
193     private static final String CAMERA_ITS_TEST_FOLDED = CAMERA_ITS_TEST + "[folded]";
194 
195     private final HashSet<String> mDisabledTests;
196 
197     private Context mContext;
198 
199     private String mTestParent;
200 
ManifestTestListAdapter(Context context, String testParent)201     public ManifestTestListAdapter(Context context, String testParent) {
202         this(context, testParent, context.getResources().getStringArray(R.array.disabled_tests));
203     }
204 
ManifestTestListAdapter(Context context, String testParent, String[] disabledTestArray)205     public ManifestTestListAdapter(Context context, String testParent, String[] disabledTestArray) {
206         super(context);
207         mContext = context;
208         mTestParent = testParent;
209         mDisabledTests = new HashSet<>(disabledTestArray.length);
210         for (int i = 0; i < disabledTestArray.length; i++) {
211             mDisabledTests.add(disabledTestArray[i]);
212         }
213     }
214 
215     @Override
getRows()216     protected List<TestListItem> getRows() {
217         // When launching at the first time or after killing the process, needs to fetch the
218         // test items of all display modes as the bases for switching.
219         if (mDisplayModesTests.isEmpty()) {
220             for (DisplayMode mode : DisplayMode.values()) {
221                 mDisplayModesTests.put(mode.toString(), getRowsWithDisplayMode(mode.toString()));
222                 PackageManager packageManager = mContext.getPackageManager();
223                 boolean isCustomLauncher =
224                         packageManager.hasSystemFeature("com.google.android.tv.custom_launcher");
225                 if (isCustomLauncher) {
226                     mDisabledTests.add(
227                             "com.android.cts.verifier.net.ConnectivityBackgroundTestActivity");
228                 }
229             }
230         }
231 
232         if (mTestFilter != null || TestListActivity.getIsSystemEnabled()) {
233             // Filter test rows dynamically when the filter is specified or verifier-system plan is
234             // enabled.
235             return getRowsWithDisplayMode(sCurrentDisplayMode.toString());
236         } else {
237             return mDisplayModesTests.getOrDefault(
238                     sCurrentDisplayMode.toString(), new ArrayList<>());
239         }
240     }
241 
242     /**
243      * Gets all rows based on the specific display mode.
244      *
245      * @param mode the given display mode
246      * @return a list containing all test itmes in the given display mode
247      */
getRowsWithDisplayMode(String mode)248     private List<TestListItem> getRowsWithDisplayMode(String mode) {
249         /*
250          * 1. Get all the tests belonging to the test parent.
251          * 2. Get all the tests keyed by their category.
252          * 3. Flatten the tests and categories into one giant list for the list view.
253          */
254         List<TestListItem> allRows = new ArrayList<TestListItem>();
255         List<ResolveInfo> infos = getResolveInfosForParent();
256         Map<String, List<TestListItem>> testsByCategory = getTestsByCategory(infos);
257 
258         List<String> testCategories = new ArrayList<String>(testsByCategory.keySet());
259         Collections.sort(testCategories);
260         for (String testCategory : testCategories) {
261             List<TestListItem> tests = filterTests(testsByCategory.get(testCategory), mode);
262             if (!tests.isEmpty()) {
263                 allRows.add(TestListItem.newCategory(testCategory));
264                 Collections.sort(tests, Comparator.comparing(item -> item.title));
265                 allRows.addAll(tests);
266             }
267         }
268         return allRows;
269     }
270 
getResolveInfosForParent()271     List<ResolveInfo> getResolveInfosForParent() {
272         Intent mainIntent = new Intent(Intent.ACTION_MAIN);
273         mainIntent.addCategory(CATEGORY_MANUAL_TEST);
274         mainIntent.setPackage(mContext.getPackageName());
275 
276         PackageManager packageManager = mContext.getPackageManager();
277         List<ResolveInfo> list =
278                 packageManager.queryIntentActivities(
279                         mainIntent, PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
280         int size = list.size();
281 
282         List<ResolveInfo> matchingList = new ArrayList<>();
283         for (int i = 0; i < size; i++) {
284             ResolveInfo info = list.get(i);
285             String parent = getTestParent(info.activityInfo.metaData);
286             if ((mTestParent == null && parent == null)
287                     || (mTestParent != null && mTestParent.equals(parent))) {
288                 matchingList.add(info);
289             }
290         }
291         return matchingList;
292     }
293 
getTestsByCategory(List<ResolveInfo> list)294     Map<String, List<TestListItem>> getTestsByCategory(List<ResolveInfo> list) {
295         Map<String, List<TestListItem>> testsByCategory = new HashMap<>();
296 
297         int size = list.size();
298         for (int i = 0; i < size; i++) {
299             ResolveInfo info = list.get(i);
300             if (info.activityInfo == null || mDisabledTests.contains(info.activityInfo.name)) {
301                 Log.w(LOG_TAG, "ignoring disabled test: " + info.activityInfo.name);
302                 continue;
303             }
304             String title = getTitle(mContext, info.activityInfo);
305             String testName = info.activityInfo.name;
306             Intent intent = getActivityIntent(info.activityInfo);
307             String[] requiredFeatures = getRequiredFeatures(info.activityInfo.metaData);
308             String[] requiredConfigs = getRequiredConfigs(info.activityInfo.metaData);
309             String[] requiredActions = getRequiredActions(info.activityInfo.metaData);
310             String[] excludedFeatures = getExcludedFeatures(info.activityInfo.metaData);
311             String[] excludedUserTypes = getExcludedUserTypes(info.activityInfo.metaData);
312             String[] applicableFeatures = getApplicableFeatures(info.activityInfo.metaData);
313             String displayMode = getDisplayMode(info.activityInfo.metaData);
314             boolean passInEitherMode = getTestPassMode(info.activityInfo.metaData, displayMode);
315 
316             TestListItem item =
317                     TestListItem.newTest(
318                             title,
319                             testName,
320                             intent,
321                             requiredFeatures,
322                             requiredConfigs,
323                             requiredActions,
324                             excludedFeatures,
325                             applicableFeatures,
326                             excludedUserTypes,
327                             displayMode,
328                             passInEitherMode);
329 
330             String testCategory = getTestCategory(mContext, info.activityInfo.metaData);
331             addTestToCategory(testsByCategory, testCategory, item);
332         }
333 
334         return testsByCategory;
335     }
336 
getTestCategory(Context context, Bundle metaData)337     static String getTestCategory(Context context, Bundle metaData) {
338         String testCategory = null;
339         if (metaData != null) {
340             testCategory = metaData.getString(TEST_CATEGORY_META_DATA);
341         }
342         if (testCategory != null) {
343             return testCategory;
344         } else {
345             return context.getString(R.string.test_category_other);
346         }
347     }
348 
getTestParent(Bundle metaData)349     static String getTestParent(Bundle metaData) {
350         return metaData != null ? metaData.getString(TEST_PARENT_META_DATA) : null;
351     }
352 
getRequiredFeatures(Bundle metaData)353     static String[] getRequiredFeatures(Bundle metaData) {
354         return getMetadataAttributes(metaData, TEST_REQUIRED_FEATURES_META_DATA);
355     }
356 
getRequiredActions(Bundle metaData)357     static String[] getRequiredActions(Bundle metaData) {
358         return getMetadataAttributes(metaData, TEST_REQUIRED_ACTIONS_META_DATA);
359     }
360 
getRequiredConfigs(Bundle metaData)361     static String[] getRequiredConfigs(Bundle metaData) {
362         return getMetadataAttributes(metaData, TEST_REQUIRED_CONFIG_META_DATA);
363     }
364 
getExcludedFeatures(Bundle metaData)365     static String[] getExcludedFeatures(Bundle metaData) {
366         return getMetadataAttributes(metaData, TEST_EXCLUDED_FEATURES_META_DATA);
367     }
368 
getApplicableFeatures(Bundle metaData)369     static String[] getApplicableFeatures(Bundle metaData) {
370         return getMetadataAttributes(metaData, TEST_APPLICABLE_FEATURES_META_DATA);
371     }
372 
getExcludedUserTypes(Bundle metaData)373     static String[] getExcludedUserTypes(Bundle metaData) {
374         return getMetadataAttributes(metaData, TEST_EXCLUDED_USER_TYPES_META_DATA);
375     }
376 
getMetadataAttributes(Bundle metaData, String attribute)377     private static String[] getMetadataAttributes(Bundle metaData, String attribute) {
378         if (metaData == null) {
379             return null;
380         } else {
381             String value = metaData.getString(attribute);
382             if (value == null) {
383                 return null;
384             } else {
385                 return value.split(":");
386             }
387         }
388     }
389 
390     /**
391      * Gets the configuration of the display mode per test. The default value is multi_display_mode.
392      *
393      * @param metaData the given metadata of the display mode
394      * @return a string representing the display mode of the test
395      */
getDisplayMode(Bundle metaData)396     static String getDisplayMode(Bundle metaData) {
397         if (metaData == null) {
398             return MULTIPLE_DISPLAY_MODE;
399         }
400         String displayMode = metaData.getString(TEST_DISPLAY_MODE_META_DATA);
401         return displayMode == null ? MULTIPLE_DISPLAY_MODE : displayMode;
402     }
403 
404     /**
405      * Gets the configuration of the test pass mode per test.
406      *
407      * @param metaData the given metadata of the test pass mode
408      * @return a boolean representing whether the test can be marked as pass when it passes either
409      *     in the folded mode or in the unfolded mode
410      */
getTestPassMode(Bundle metaData, String displayMode)411     static boolean getTestPassMode(Bundle metaData, String displayMode) {
412         if (metaData == null || !displayMode.equals(MULTIPLE_DISPLAY_MODE)) {
413             return false;
414         }
415         String testPassMode = metaData.getString(TEST_PASS_MODE);
416         return testPassMode != null && testPassMode.equals(EITHER_MODE);
417     }
418 
getTitle(Context context, ActivityInfo activityInfo)419     static String getTitle(Context context, ActivityInfo activityInfo) {
420         if (activityInfo.labelRes != 0) {
421             return context.getString(activityInfo.labelRes);
422         } else {
423             return activityInfo.name;
424         }
425     }
426 
getActivityIntent(ActivityInfo activityInfo)427     static Intent getActivityIntent(ActivityInfo activityInfo) {
428         Intent intent = new Intent();
429         intent.setClassName(activityInfo.packageName, activityInfo.name);
430         return intent;
431     }
432 
addTestToCategory( Map<String, List<TestListItem>> testsByCategory, String testCategory, TestListItem item)433     static void addTestToCategory(
434             Map<String, List<TestListItem>> testsByCategory,
435             String testCategory,
436             TestListItem item) {
437         List<TestListItem> tests;
438         if (testsByCategory.containsKey(testCategory)) {
439             tests = testsByCategory.get(testCategory);
440         } else {
441             tests = new ArrayList<TestListItem>();
442         }
443         testsByCategory.put(testCategory, tests);
444         tests.add(item);
445     }
446 
hasAnyFeature(String[] features)447     private boolean hasAnyFeature(String[] features) {
448         if (features != null) {
449             PackageManager packageManager = mContext.getPackageManager();
450             for (String feature : features) {
451                 if (packageManager.hasSystemFeature(feature)) {
452                     return true;
453                 }
454             }
455             Log.v(LOG_TAG, "Missing features " + Arrays.toString(features));
456         }
457         return false;
458     }
459 
hasAllFeatures(String[] features)460     private boolean hasAllFeatures(String[] features) {
461         if (features != null) {
462             PackageManager packageManager = mContext.getPackageManager();
463             for (String feature : features) {
464                 if (!packageManager.hasSystemFeature(feature)) {
465                     Log.v(LOG_TAG, "Missing feature " + feature);
466                     return false;
467                 }
468             }
469         }
470         return true;
471     }
472 
hasAllActions(String[] actions)473     private boolean hasAllActions(String[] actions) {
474         if (actions != null) {
475             PackageManager packageManager = mContext.getPackageManager();
476             for (String action : actions) {
477                 Intent intent = new Intent(action);
478                 if (packageManager.queryIntentActivities(intent, /* flags= */ 0).isEmpty()) {
479                     Log.v(LOG_TAG, "Missing action " + action);
480                     return false;
481                 }
482             }
483         }
484         return true;
485     }
486 
matchAllConfigs(Context context, String[] configs)487     public static boolean matchAllConfigs(Context context, String[] configs) {
488         if (configs != null) {
489             for (String config : configs) {
490                 switch (config) {
491                     case CONFIG_NO_EMULATOR:
492                         try {
493                             Method getStringMethod =
494                                     ClassLoader.getSystemClassLoader()
495                                             .loadClass("android.os.SystemProperties")
496                                             .getMethod("get", String.class);
497                             String emulatorKernel =
498                                     (String) getStringMethod.invoke("0", "ro.boot.qemu");
499                             if (emulatorKernel.equals("1")) {
500                                 return false;
501                             }
502                         } catch (Exception e) {
503                             Log.e(LOG_TAG, "Exception while checking for emulator support.", e);
504                         }
505                         break;
506                     case CONFIG_HAS_RECENTS:
507                         if (!getSystemResourceFlag(context, "config_hasRecents")) {
508                             return false;
509                         }
510                         break;
511                     case CONFIG_HDMI_SOURCE:
512                         final int DEVICE_TYPE_HDMI_SOURCE = 4;
513                         try {
514                             if (!getHdmiDeviceType().contains(DEVICE_TYPE_HDMI_SOURCE)) {
515                                 return false;
516                             }
517                         } catch (Exception exception) {
518                             Log.e(
519                                     LOG_TAG,
520                                     "Exception while looking up HDMI device type.",
521                                     exception);
522                         }
523                         break;
524                     case CONFIG_BATTERY_SUPPORTED:
525                         if (!hasBattery(context)) {
526                             return false;
527                         }
528                         break;
529                     case CONFIG_QUICK_SETTINGS_SUPPORTED:
530                         if (!getSystemResourceFlag(context, "config_quickSettingsSupported")) {
531                             return false;
532                         }
533                         break;
534                     case CONFIG_HAS_MIC_TOGGLE:
535                         return isHardwareToggleSupported(
536                                 context, SensorPrivacyManager.Sensors.MICROPHONE);
537                     case CONFIG_HAS_CAMERA_TOGGLE:
538                         return isHardwareToggleSupported(
539                                 context, SensorPrivacyManager.Sensors.CAMERA);
540                     case CONFIG_CHANGEABLE_VOLUME:
541                         return !getSystemResourceFlag(context, "config_useFixedVolume");
542                     case CONFIG_SUPPORTS_BUBBLE:
543                         return getSystemResourceFlag(context, "config_supportsBubble");
544                     default:
545                         break;
546                 }
547             }
548         }
549         return true;
550     }
551 
552     /**
553      * Checks if the test should be ran by the given display mode.
554      *
555      * @param mode the display mode config of the test
556      * @param currentMode the given display mode
557      * @return true if the given display mode matches the configs, otherwise, return false
558      */
matchDisplayMode(String mode, String currentMode)559     private boolean matchDisplayMode(String mode, String currentMode) {
560         if (mode == null) {
561             return false;
562         }
563         switch (mode) {
564             case SINGLE_DISPLAY_MODE:
565                 return currentMode.equals(DisplayMode.UNFOLDED.toString());
566             case MULTIPLE_DISPLAY_MODE:
567                 return true;
568             case FOLDED_DISPLAY_MODE:
569                 return currentMode.equals(DisplayMode.FOLDED.toString());
570             default:
571                 return false;
572         }
573     }
574 
575     /** Checks whether the test is being run by a user type that doesn't support it. */
matchAnyExcludedUserType(String[] userTypes)576     private boolean matchAnyExcludedUserType(String[] userTypes) {
577         if (userTypes == null) {
578             return false;
579         }
580 
581         for (String userType : userTypes) {
582             switch (userType) {
583                 case USER_TYPE_VISIBLE_BG_USER:
584                     if (isVisibleBackgroundNonProfileUser()) {
585                         Log.d(LOG_TAG, "Match for " + USER_TYPE_VISIBLE_BG_USER);
586                         return true;
587                     }
588                     return false;
589                 default:
590                     throw new IllegalArgumentException(
591                             "Invalid "
592                                     + TEST_EXCLUDED_USER_TYPES_META_DATA
593                                     + " value: "
594                                     + userType);
595             }
596         }
597 
598         return false;
599     }
600 
601     /** Checks whether the title of the test matches the test filter. */
macthTestFilter(String testTitle)602     private boolean macthTestFilter(String testTitle) {
603         if (mTestFilter == null) {
604             return true;
605         }
606         return testTitle != null
607                 && testTitle
608                         .toLowerCase(Locale.getDefault())
609                         .contains(mTestFilter.toLowerCase(Locale.getDefault()));
610     }
611 
612     /**
613      * Checks whether the test matches the current status of verifier-system plan.
614      *
615      * <p>When verifier-system plan is disabled, all CTS-V tests are shown.
616      *
617      * <p>When verifier-system plan is enabled, specific tests are filtered out, e.g., camera ITS.
618      */
matchSystemPlanStatus(String testName)619     private static boolean matchSystemPlanStatus(String testName) {
620         if (testName == null || !TestListActivity.getIsSystemEnabled()) {
621             return true;
622         }
623         return !testName.equals(CAMERA_ITS_TEST) && !testName.equals(CAMERA_ITS_TEST_FOLDED);
624     }
625 
isVisibleBackgroundNonProfileUser()626     private boolean isVisibleBackgroundNonProfileUser() {
627         if (!SdkLevel.isAtLeastU()) {
628             Log.d(LOG_TAG, "isVisibleBagroundNonProfileUser() returning false on pre-UDC device");
629             return false;
630         }
631         UserManager userMgr = mContext.getSystemService(UserManager.class);
632         return userMgr.isUserVisible() && !userMgr.isUserForeground() && !userMgr.isProfile();
633     }
634 
getSystemResourceFlag(Context context, String key)635     private static boolean getSystemResourceFlag(Context context, String key) {
636         final Resources systemRes = context.getResources().getSystem();
637         final int id = systemRes.getIdentifier(key, "bool", "android");
638         if (id == Resources.ID_NULL) {
639             // The flag being queried should exist in
640             // frameworks/base/core/res/res/values/config.xml.
641             throw new RuntimeException("System resource flag " + key + " not found");
642         }
643         return systemRes.getBoolean(id);
644     }
645 
getHdmiDeviceType()646     private static List<Integer> getHdmiDeviceType()
647             throws InvocationTargetException,
648                     IllegalAccessException,
649                     ClassNotFoundException,
650                     NoSuchMethodException {
651         Method getStringMethod =
652                 ClassLoader.getSystemClassLoader()
653                         .loadClass("android.os.SystemProperties")
654                         .getMethod("get", String.class);
655         String deviceTypesStr = (String) getStringMethod.invoke(null, "ro.hdmi.device_type");
656         if (deviceTypesStr.equals("")) {
657             return new ArrayList<>();
658         }
659         return Arrays.stream(deviceTypesStr.split(","))
660                 .map(Integer::parseInt)
661                 .collect(Collectors.toList());
662     }
663 
hasBattery(Context context)664     private static boolean hasBattery(Context context) {
665         final Intent batteryInfo =
666                 context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
667         return batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
668     }
669 
filterTests(List<TestListItem> tests, String mode)670     List<TestListItem> filterTests(List<TestListItem> tests, String mode) {
671         List<TestListItem> filteredTests = new ArrayList<>();
672         for (TestListItem test : tests) {
673             if (!hasAnyFeature(test.excludedFeatures)
674                     && hasAllFeatures(test.requiredFeatures)
675                     && hasAllActions(test.requiredActions)
676                     && matchAllConfigs(mContext, test.requiredConfigs)
677                     && matchDisplayMode(test.displayMode, mode)
678                     && !matchAnyExcludedUserType(test.excludedUserTypes)
679                     && macthTestFilter(test.title)
680                     && matchSystemPlanStatus(test.testName)) {
681                 if (test.applicableFeatures == null || hasAnyFeature(test.applicableFeatures)) {
682                     // Add suffix in test name if the test is in the folded mode.
683                     test.testName = setTestNameSuffix(mode, test.testName);
684                     // Remove suffix in test name if the test is in the unfolded mode.
685                     test.testName = removeTestNameSuffix(mode, test.testName);
686                     filteredTests.add(test);
687                 } else {
688                     Log.d(LOG_TAG, "Skipping " + test.testName + " due to metadata filtering");
689                 }
690             } else {
691                 Log.d(LOG_TAG, "Skipping " + test.testName + " due to metadata filtering");
692             }
693         }
694         return filteredTests;
695     }
696 
697     @SuppressLint("NewApi")
isHardwareToggleSupported(Context context, final int sensorType)698     private static boolean isHardwareToggleSupported(Context context, final int sensorType) {
699         boolean isToggleSupported = false;
700         SensorPrivacyManager sensorPrivacyManager =
701                 context.getSystemService(SensorPrivacyManager.class);
702         if (sensorPrivacyManager != null) {
703             isToggleSupported =
704                     sensorPrivacyManager.supportsSensorToggle(
705                             SensorPrivacyManager.TOGGLE_TYPE_HARDWARE, sensorType);
706         }
707         return isToggleSupported;
708     }
709 }
710