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