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