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