diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-30 05:27:45 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-30 05:27:45 +0000 |
commit | d5fdd2bac509340c67ca38185965d97eb8bb1c23 (patch) | |
tree | bdcb4a2faee8f6d636218f37db233d52f1260dff | |
parent | 73ed9155c17d245d03269125b75cc108f82e1ac9 (diff) | |
parent | 87dfd3179a0c5753b007e6daee16a4e9165a5408 (diff) | |
download | cts-android13-mainline-cellbroadcast-release.tar.gz |
Snap for 9847922 from 87dfd3179a0c5753b007e6daee16a4e9165a5408 to mainline-cellbroadcast-releaseaml_cbr_331810000android13-mainline-cellbroadcast-release
Change-Id: Ieac6d670c370cec2dccb351efa15cfbf05d6fbf5
157 files changed, 3961 insertions, 1185 deletions
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py index 1a30ac293f2..c0b8665cbc9 100644 --- a/apps/CameraITS/tests/its_base_test.py +++ b/apps/CameraITS/tests/its_base_test.py @@ -103,6 +103,13 @@ class ItsBaseTest(base_test.BaseTestClass): try: self.tablet = devices[1] self.tablet_screen_brightness = self.user_params['brightness'] + tablet_name_unencoded = self.tablet.adb.shell( + ['getprop', 'ro.build.product'] + ) + tablet_name = str(tablet_name_unencoded.decode('utf-8')).strip() + logging.debug('tablet name: %s', tablet_name) + its_session_utils.validate_tablet_brightness( + tablet_name, self.tablet_screen_brightness) except KeyError: logging.debug('Not all tablet arguments set.') else: # sensor_fusion or manual run diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py index fbc882e22a7..2b211babc27 100644 --- a/apps/CameraITS/tests/scene6/test_zoom.py +++ b/apps/CameraITS/tests/scene6/test_zoom.py @@ -38,7 +38,7 @@ MIN_CIRCLE_PTS = 25 MIN_FOCUS_DIST_TOL = 0.80 # allow charts a little closer than min NAME = os.path.splitext(os.path.basename(__file__))[0] NUM_STEPS = 10 -OFFSET_LOW_VAL = 10 # number of pixels +OFFSET_ATOL = 10 # number of pixels OFFSET_RTOL = 0.15 OFFSET_RTOL_LOW_OFFSET = 0.20 OFFSET_RTOL_MIN_FD = 0.30 @@ -207,6 +207,7 @@ class ZoomTest(its_base_test.ItsBaseTest): def test_zoom(self): test_data = {} + test_failed = False with its_session_utils.ItsSession( device_id=self.dut.serial, camera_id=self.camera_id, @@ -310,10 +311,10 @@ class ZoomTest(its_base_test.ItsBaseTest): for i, data in test_data.items(): logging.debug('Zoom: %.2f, fl: %.2f', data['z'], data['fl']) - offset_abs = [(data['circle'][0] - size[0] // 2), - (data['circle'][1] - size[1] // 2)] + offset_xy = [(data['circle'][0] - size[0] // 2), + (data['circle'][1] - size[1] // 2)] logging.debug('Circle r: %.1f, center offset x, y: %d, %d', - data['circle'][2], offset_abs[0], offset_abs[1]) + data['circle'][2], offset_xy[0], offset_xy[1]) z_ratio = data['z'] / z_0 # check relative size against zoom[0] @@ -326,20 +327,24 @@ class ZoomTest(its_base_test.ItsBaseTest): # check relative offset against init vals w/ no focal length change if i == 0 or test_data[i-1]['fl'] != data['fl']: # set init values z_init = float(data['z']) - offset_init = [(data['circle'][0] - size[0] // 2), - (data['circle'][1] - size[1] // 2)] + offset_hypot_init = math.hypot(offset_xy[0], offset_xy[1]) + logging.debug('offset_hypot_init: %.3f', offset_hypot_init) else: # check z_ratio = data['z'] / z_init - offset_rel = (distance(offset_abs[0], offset_abs[1]) / z_ratio / - distance(offset_init[0], offset_init[1])) - logging.debug('offset_rel: %.3f', offset_rel) + offset_hypot_rel = math.hypot(offset_xy[0], offset_xy[1]) / z_ratio + logging.debug('offset_hypot_rel: %.3f', offset_hypot_rel) rel_tol = data['o_tol'] - if (np.linalg.norm(offset_init) < OFFSET_LOW_VAL and - rel_tol == OFFSET_RTOL): - rel_tol = OFFSET_RTOL_LOW_OFFSET - if not math.isclose(offset_rel, 1.0, rel_tol=rel_tol): - raise AssertionError(f"zoom: {data['z']:.2f}, offset(rel to 1): " - f'{offset_rel:.4f}, RTOL: {rel_tol}') + if not math.isclose(offset_hypot_init, offset_hypot_rel, + rel_tol=rel_tol, abs_tol=OFFSET_ATOL): + test_failed = True + e_msg = (f"zoom: {data['z']:.2f}, " + f'offset init: {offset_hypot_init:.4f}, ' + f'offset rel: {offset_hypot_rel:.4f}, ' + f'RTOL: {rel_tol}, ATOL: {OFFSET_ATOL}') + logging.error(e_msg) + + if test_failed: + raise AssertionError(f'{NAME} failed! Check test_log.DEBUG for errors') if __name__ == '__main__': test_runner.main() diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py index a75ce5e8b17..ed3d50d2e0a 100755 --- a/apps/CameraITS/tools/run_all_tests.py +++ b/apps/CameraITS/tools/run_all_tests.py @@ -475,6 +475,13 @@ def main(): config_file_test_key = config_file_contents['TestBeds'][0]['Name'].lower() if TEST_KEY_TABLET in config_file_test_key: tablet_id = get_device_serial_number('tablet', config_file_contents) + tablet_name_cmd = f'adb -s {tablet_id} shell getprop ro.build.product' + raw_output = subprocess.check_output( + tablet_name_cmd, stderr=subprocess.STDOUT, shell=True) + tablet_name = str(raw_output.decode('utf-8')).strip() + logging.debug('Tablet name: %s', tablet_name) + brightness = test_params_content['brightness'] + its_session_utils.validate_tablet_brightness(tablet_name, brightness) else: tablet_id = None diff --git a/apps/CameraITS/utils/capture_request_utils.py b/apps/CameraITS/utils/capture_request_utils.py index 2887925e26b..521ec8442b5 100644 --- a/apps/CameraITS/utils/capture_request_utils.py +++ b/apps/CameraITS/utils/capture_request_utils.py @@ -173,7 +173,7 @@ def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None): out_sizes = [(cfg['width'], cfg['height']) for cfg in out_configs] if max_size: out_sizes = [ - s for s in out_sizes if s[0] <= max_size[0] and s[1] <= max_size[1] + s for s in out_sizes if s[0] <= int(max_size[0]) and s[1] <= int(max_size[1]) ] if match_ar_size: ar = match_ar_size[0] / float(match_ar_size[1]) diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py index b1fb77a72c3..3caad99137a 100644 --- a/apps/CameraITS/utils/its_session_utils.py +++ b/apps/CameraITS/utils/its_session_utils.py @@ -38,6 +38,15 @@ import opencv_processing_utils ANDROID13_API_LEVEL = 33 LOAD_SCENE_DELAY_SEC = 3 SUB_CAMERA_SEPARATOR = '.' +DEFAULT_TABLET_BRIGHTNESS = 192 # 8-bit tablet 75% brightness +ELEVEN_BIT_TABLET_BRIGHTNESS = 1536 +ELEVEN_BIT_TABLET_NAMES = ('nabu',) +LEGACY_TABLET_BRIGHTNESS = 96 +LEGACY_TABLET_NAME = 'dragon' +TABLET_REQUIREMENTS_URL = 'https://source.android.com/docs/compatibility/cts/camera-its-box#tablet-requirements' +BRIGHTNESS_ERROR_MSG = ('Tablet brightness not set as per ' + f'{TABLET_REQUIREMENTS_URL} in the config file') + _VALIDATE_LIGHTING_PATCH_H = 0.05 _VALIDATE_LIGHTING_PATCH_W = 0.05 _VALIDATE_LIGHTING_REGIONS = { @@ -50,6 +59,27 @@ _VALIDATE_LIGHTING_REGIONS = { _VALIDATE_LIGHTING_THRESH = 0.05 # Determined empirically from scene[1:6] tests +def validate_tablet_brightness(tablet_name, brightness): + """Ensures tablet brightness is set according to documentation. + + https://source.android.com/docs/compatibility/cts/camera-its-box#tablet-requirements + Args: + tablet_name: tablet product name specified by `ro.build.product`. + brightness: brightness specified by config file. + """ + name_to_brightness = { + LEGACY_TABLET_NAME: LEGACY_TABLET_BRIGHTNESS, + } + for name in ELEVEN_BIT_TABLET_NAMES: + name_to_brightness[name] = ELEVEN_BIT_TABLET_BRIGHTNESS + if tablet_name in name_to_brightness: + if brightness != name_to_brightness[tablet_name]: + raise AssertionError(BRIGHTNESS_ERROR_MSG) + else: + if brightness != DEFAULT_TABLET_BRIGHTNESS: + raise AssertionError(BRIGHTNESS_ERROR_MSG) + + class ItsSession(object): """Controls a device over adb to run ITS scripts. diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml index 6c464b1bad3..595fcc1b2d3 100644 --- a/apps/CtsVerifier/AndroidManifest.xml +++ b/apps/CtsVerifier/AndroidManifest.xml @@ -271,6 +271,8 @@ android:value="multi_display_mode" /> <meta-data android:name="ApiTest" android:value="android.companion.CompanionDeviceManager#associate" /> + <meta-data android:name="test_excluded_features" + android:value="android.hardware.type.watch" /> </activity> <activity android:name=".companion.CompanionDeviceServiceTestActivity" @@ -288,6 +290,8 @@ android:value="multi_display_mode" /> <meta-data android:name="ApiTest" android:value="android.companion.CompanionDeviceManager#startObservingDevicePresence|android.companion.CompanionDeviceManager#stopObservingDevicePresence" /> + <meta-data android:name="test_excluded_features" + android:value="android.hardware.type.watch" /> </activity> <service @@ -5357,6 +5361,7 @@ <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> <meta-data android:name="test_category" android:value="@string/test_category_audio" /> + <meta-data android:name="test_required_features" android:value="android.hardware.hdmi.cec" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> <meta-data android:name="ApiTest" android:value="android.media.AudioDescriptor#getStandard| diff --git a/apps/CtsVerifier/res/values-watch/strings.xml b/apps/CtsVerifier/res/values-watch/strings.xml index e594b9e37ea..4f412bfc6dc 100644 --- a/apps/CtsVerifier/res/values-watch/strings.xml +++ b/apps/CtsVerifier/res/values-watch/strings.xml @@ -61,4 +61,7 @@ 7) Repeat steps (1) through (6) for each screen lock type other than \"None\". </string> <string name="usb_tapjacking_usb_debugging_component">com.google.android.apps.wearable.settings/com.google.android.clockwork.settings.SecureAdbActivityAlias</string> + + <!-- Adjusted for watches to leave out second part, since not applicable to watches. --> + <string name="add_screen_lock">Add a secure screen lock of any type.</string> </resources> diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml index c4b65a54c6d..ad1bfd5b26c 100644 --- a/apps/CtsVerifier/res/values/strings.xml +++ b/apps/CtsVerifier/res/values/strings.xml @@ -5772,6 +5772,7 @@ Follow the instructions on the screen to measure the frequency response for the <string name="audio_tap2tone_too_few">Not enough edges. Use fingernail.</string> <string name="audio_tap2tone_too_many">Too many edges. Use fingernail. Ensure there is no background noise.</string> + <string name="audio_tap2tone_bad_streams">Can\'t open streams.</string> <!-- Strings for 6DoF test --> <string name="six_dof_test">6DoF Test</string> diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java index 8ecd14a0870..3b207b3de50 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java @@ -154,7 +154,9 @@ public class AnalogHeadsetAudioActivity mHeadsetVolUpText = (TextView)findViewById(R.id.headset_keycode_volume_up); mHeadsetVolDownText = (TextView)findViewById(R.id.headset_keycode_volume_down); - if (isTelevision()) { + mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); + + if (isTelevisionOrFixedVolume()) { mButtonsPromptTxt.setVisibility(View.GONE); mHeadsetHookText.setVisibility(View.GONE); mHeadsetVolUpText.setVisibility(View.GONE); @@ -164,8 +166,6 @@ public class AnalogHeadsetAudioActivity mResultsTxt = (TextView)findViewById(R.id.headset_results); - mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE); - setupPlayer(); mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler()); @@ -177,7 +177,7 @@ public class AnalogHeadsetAudioActivity showKeyMessagesState(); - setInfoResources(R.string.analog_headset_test, isTelevision() + setInfoResources(R.string.analog_headset_test, isTelevisionOrFixedVolume() ? R.string.analog_headset_test_info_tv : R.string.analog_headset_test_info, -1); setPassFailButtonClickListeners(); @@ -195,7 +195,7 @@ public class AnalogHeadsetAudioActivity boolean pass = mPlugIntentReceived && mHeadsetDeviceInfo != null && mPlaybackSuccess && - (isTelevision() + (isTelevisionOrFixedVolume() || ((mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown)); if (pass) { mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass)); @@ -499,7 +499,8 @@ public class AnalogHeadsetAudioActivity return super.onKeyDown(keyCode, event); } - private boolean isTelevision() { - return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); + private boolean isTelevisionOrFixedVolume() { + return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) + || mAudioManager.isVolumeFixed(); } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java index 3c1b8a1151e..793b6741bd3 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java @@ -316,11 +316,15 @@ public class AudioAEC extends AudioFrequencyActivity implements View.OnClickList setLevelForStream(playbackStreamType, desiredLevel); int currentLevel = getLevelForStream(playbackStreamType); - if (currentLevel != desiredLevel) { + if (am.isVolumeFixed()) { + sendMessage(AudioTestRunner.TEST_MESSAGE, + "configured for Fixed volume, bypassing volume level check"); + + } else if (currentLevel != desiredLevel) { am.setMode(originalMode); sendMessage(AudioTestRunner.TEST_ENDED_ERROR, - "Couldn't set level for STREAM_VOICE_CALL. Expected " + - desiredLevel +" got: " + currentLevel); + "Couldn't set level for STREAM_VOICE_CALL. Expected " + + desiredLevel +" got: " + currentLevel); return; } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java index cf09a74e062..c61689c9110 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java @@ -44,6 +44,7 @@ import com.android.cts.verifier.audio.audiolib.WaveformView; import com.android.cts.verifier.audio.sources.BlipAudioSourceProvider; import org.hyphonate.megaaudio.common.BuilderBase; +import org.hyphonate.megaaudio.common.StreamBase; import org.hyphonate.megaaudio.duplex.DuplexAudioManager; import org.hyphonate.megaaudio.player.AudioSource; import org.hyphonate.megaaudio.player.AudioSourceProvider; @@ -270,12 +271,18 @@ public class AudioTap2ToneActivity mDuplexAudioManager.setNumRecorderChannels(NUM_RECORD_CHANNELS); } - mDuplexAudioManager.setupStreams(mPlayerType, BuilderBase.TYPE_JAVA); - mDuplexAudioManager.start(); + if (mDuplexAudioManager.setupStreams(BuilderBase.TYPE_OBOE, BuilderBase.TYPE_JAVA) + == StreamBase.OK) { + mDuplexAudioManager.start(); - mBlipSource = (AudioSource) mDuplexAudioManager.getAudioSource(); + mBlipSource = (AudioSource) mDuplexAudioManager.getAudioSource(); - mIsRecording = true; + mIsRecording = true; + mResultsView.setText("Successfully opened streams"); + } else { + mIsRecording = false; + mResultsView.setText(getString(R.string.audio_tap2tone_bad_streams)); + } enableAudioButtons(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java index 793cbf7954d..c8cd8fd50b5 100755..100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/formats/CameraFormatsActivity.java @@ -653,7 +653,7 @@ public class CameraFormatsActivity extends PassFailButtons.Activity rotation = (360 - rotation) % 360; // de-compensate the mirror } - if (rotation != 0) { + if (rotation != 0 && rotation != 180) { Matrix transform = new Matrix(); mFormatView.setScaleType(ImageView.ScaleType.MATRIX); Rect viewRect = mFormatView.getDrawable().getBounds(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java index eddd930ac30..a7b58fa27cf 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java @@ -96,7 +96,7 @@ public final class FeatureUtil { /** * Checks whether the device is watch . */ - private static boolean isWatch(Context context) { + public static boolean isWatch(Context context) { PackageManager pm = context.getPackageManager(); return pm.hasSystemFeature(PackageManager.FEATURE_WATCH); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java index 760bd71bc77..6c6303773c3 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationPrivacyVerifierActivity.java @@ -29,7 +29,6 @@ import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Bundle; import android.provider.Settings; import android.util.Log; @@ -39,6 +38,7 @@ import android.view.ViewGroup; import androidx.annotation.StringRes; import com.android.cts.verifier.R; +import com.android.cts.verifier.features.FeatureUtil; import java.util.ArrayList; import java.util.List; @@ -105,12 +105,14 @@ public class NotificationPrivacyVerifierActivity extends InteractiveVerifierActi @Override protected List<InteractiveTestCase> createTestItems() { - boolean isAutomotive = getPackageManager().hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE); List<InteractiveTestCase> tests = new ArrayList<>(); - if (!isAutomotive) { - // FIRST: set redaction settings - tests.add(new SetScreenLockEnabledStep()); + + // FIRST: enable lock screen + tests.add(new SetScreenLockEnabledStep()); + + // for watches, no notifications should appear on the secure lock screen + if (!FeatureUtil.isWatch(this)) { + // THEN: set redaction settings tests.add(new SetGlobalVisibilityPublicStep()); tests.add(new SetChannelLockscreenVisibilityPrivateStep()); // NOW TESTING: redacted by channel @@ -132,13 +134,16 @@ public class NotificationPrivacyVerifierActivity extends InteractiveVerifierActi tests.add(new NotificationWhenOccludedShowsRedactedTest()); tests.add(new SetGlobalVisibilitySecretStep()); - // NOW TESTING: notifications do not appear - tests.add(new NotificationWhenLockedIsHiddenTest()); - tests.add(new NotificationWhenOccludedIsHiddenTest()); + } - // FINALLY: restore device state - tests.add(new SetScreenLockDisabledStep()); + // NOW TESTING: notifications do not appear + tests.add(new NotificationWhenLockedIsHiddenTest()); + if (!FeatureUtil.isWatch(this)) { + tests.add(new NotificationWhenOccludedIsHiddenTest()); } + + // FINALLY: restore device state + tests.add(new SetScreenLockDisabledStep()); return tests; } diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java index 46f1b478230..4ba312f2bab 100644 --- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java +++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java @@ -879,7 +879,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { */ @Test public void testBootclasspath_nonDuplicateClasses() throws Exception { - assumeTrue(mDeviceSdkLevel.isDeviceAtLeastR()); + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); assertThat(getDuplicateClasses(sBootclasspathJars)).isEmpty(); } @@ -888,7 +888,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { */ @Test public void testSystemServerClasspath_nonDuplicateClasses() throws Exception { - assumeTrue(mDeviceSdkLevel.isDeviceAtLeastR()); + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); ImmutableSet<String> overlapBurndownList; if (hasFeature(FEATURE_AUTOMOTIVE)) { overlapBurndownList = ImmutableSet.copyOf(AUTOMOTIVE_HIDL_OVERLAP_BURNDOWN_LIST); @@ -910,7 +910,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { */ @Test public void testBootClasspathAndSystemServerClasspath_nonDuplicateClasses() throws Exception { - assumeTrue(mDeviceSdkLevel.isDeviceAtLeastR()); + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); ImmutableList.Builder<String> jars = ImmutableList.builder(); jars.addAll(sBootclasspathJars); jars.addAll(sSystemserverclasspathJars); diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java index e4508a98625..f63f257ba48 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java @@ -98,6 +98,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_ManagedProfile_Success() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); if (!getDevice().hasFeature("android.software.managed_users")) { CLog.v(TAG, "Device doesn't support managed users; skipping test"); @@ -139,6 +140,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_TwoUsers_SingleUserUnlock_Success() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); if (!mSupportsMultiUser) { CLog.v(TAG, "Device doesn't support multi-user; skipping test"); @@ -191,6 +193,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_TwoUsers_BothUserUnlock_Success() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); if (!mSupportsMultiUser) { CLog.v(TAG, "Device doesn't support multi-user; skipping test"); @@ -245,6 +248,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_SingleUser_ServerBased_Success() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); int[] users = Utils.prepareSingleUser(getDevice()); int initialUser = users[0]; @@ -278,6 +282,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_SingleUser_MultiClient_ClientASuccess() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); int[] users = Utils.prepareSingleUser(getDevice()); int initialUser = users[0]; @@ -318,6 +323,7 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { @Test public void resumeOnReboot_SingleUser_MultiClient_ClientBSuccess() throws Exception { assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); + assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); int[] users = Utils.prepareSingleUser(getDevice()); int initialUser = users[0]; @@ -570,6 +576,10 @@ public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { return isAtleastS && getDevice().hasFeature(FEATURE_SECURE_LOCK_SCREEN); } + private boolean supportFileBasedEncryption() throws Exception { + return "file".equals(getDevice().getProperty("ro.crypto.type")); + } + private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> { public InstallMultiple() { super(getDevice(), getBuild(), getAbi()); diff --git a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java index 12fbb3c4eea..aab1d9e6617 100644 --- a/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java +++ b/hostsidetests/appsecurity/test-apps/ListeningPortsApp/src/android/appsecurity/cts/listeningports/ListeningPortsTest.java @@ -16,6 +16,7 @@ package android.appsecurity.cts.listeningports; +import android.app.UiAutomation; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Process; @@ -138,8 +139,15 @@ public class ListeningPortsTest extends AndroidTestCase { } private String uidToPackage(int uid) { + UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); PackageManager pm = this.getContext().getPackageManager(); - String[] packages = pm.getPackagesForUid(uid); + String[] packages; + try { + uiAutomation.adoptShellPermissionIdentity(); + packages = pm.getPackagesForUid(uid); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } if (packages == null) { return "[unknown]"; } diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java index b3d1d1c4970..56d2e0723df 100644 --- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java +++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java @@ -996,12 +996,7 @@ public class MediaStorageTest { // to keep rolling forward if we can't find our grant button final UiSelector grant = new UiSelector().textMatches("(?i)Allow"); if (isWatch()) { - UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true)); - try { - uiScrollable.scrollIntoView(grant); - } catch (UiObjectNotFoundException e) { - // Scrolling can fail if the UI is not scrollable - } + scrollIntoView(grant); } final boolean grantExists = new UiObject(grant).waitForExists(timeout); @@ -1016,8 +1011,11 @@ public class MediaStorageTest { // Verify that we now have access assertEquals(Activity.RESULT_OK, res.resultCode); } else { - // fine the Deny button + // find the Deny button final UiSelector deny = new UiSelector().textMatches("(?i)Deny"); + if (isWatch()) { + scrollIntoView(deny); + } final boolean denyExists = new UiObject(deny).waitForExists(timeout); assertThat(denyExists).isTrue(); @@ -1030,6 +1028,15 @@ public class MediaStorageTest { } } + private static void scrollIntoView(UiSelector selector) { + UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true)); + try { + uiScrollable.scrollIntoView(selector); + } catch (UiObjectNotFoundException e) { + // Scrolling can fail if the UI is not scrollable + } + } + private static Uri createDownload() throws IOException { final String content = "<html><body>Content</body></html>"; final String displayName = "cts" + System.nanoTime(); diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java index 94b83de6362..55e7dffc3fb 100644 --- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java +++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java @@ -36,9 +36,11 @@ import android.app.UiAutomation; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.provider.DeviceConfig; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; import android.support.test.uiautomator.UiDevice; @@ -46,7 +48,7 @@ import android.support.test.uiautomator.UiObject2; import android.util.Log; import com.android.compatibility.common.util.PollingCheck; - +import com.android.compatibility.common.util.SystemUtil; import com.android.cts.devicepolicy.PermissionBroadcastReceiver; import com.android.cts.devicepolicy.PermissionUtils; @@ -105,6 +107,11 @@ public class PermissionsTest extends BaseDeviceAdminTest { mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PERMISSION_RESULT)); mDevice = UiDevice.getInstance(getInstrumentation()); mUiAutomation = getInstrumentation().getUiAutomation(); + SystemUtil.runWithShellPermissionIdentity(() -> { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, + "safety_center_qs_tile_component_setting_flags", + Integer.toString(PackageManager.DONT_KILL_APP), false); + }); } @Override diff --git a/hostsidetests/incident/AndroidTest.xml b/hostsidetests/incident/AndroidTest.xml index 727277d3020..73be7b48824 100644 --- a/hostsidetests/incident/AndroidTest.xml +++ b/hostsidetests/incident/AndroidTest.xml @@ -20,9 +20,6 @@ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> - <target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer"> - <option name="user-type" value="system" /> - </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > <option name="jar" value="CtsIncidentHostTestCases.jar" /> </test> diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_261036568.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_261036568.java new file mode 100644 index 00000000000..223ea2229cf --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_261036568.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import static java.util.Collections.singletonMap; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class Bug_261036568 extends NonRootSecurityTestCase { + + private static final String TEST_PKG = "android.security.cts.BUG_261036568_test"; + + @Test + @AsbSecurityTest(cveBugId = 261036568) + public void testBug_261036568() { + ITestDevice device = null; + int newUser = -1; + try { + device = getDevice(); + assumeTrue("Test requires multiple users", device.isMultiUserSupported()); + + newUser = device.createUser("CtsUser", /* guest */ true, /* ephemeral */ false); + assumeTrue("Unable to create test user", device.startUser(newUser, /* wait */ true)); + + installPackage("Bug-261036568-provider.apk", "--user " + newUser); + installPackage("Bug-261036568-test.apk"); + + Map<String, String> args = singletonMap("target_user", Integer.toString(newUser)); + runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".DeviceTest", + "testShareUnownedUriAsPreview", args); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + if (newUser != -1) { + // Stop user 'CTSUser' + device.stopUser(newUser); + + // Remove user 'CTSUser' + device.removeUser(newUser); + } + } catch (Exception e) { + CLog.e("failed to clean up guest user %d: %e", newUser, e); + } + } + } + + private boolean runDeviceTestsWithArgs(String pkgName, String testClassName, + String testMethodName, Map<String, String> testArgs) + throws DeviceNotAvailableException { + final String testRunner = "androidx.test.runner.AndroidJUnitRunner"; + final long defaultTestTimeoutMs = 60 * 1000L; + final long defaultMaxTimeoutToOutputMs = 60 * 1000L; // 1min + return runDeviceTests(getDevice(), + testRunner, + pkgName, + testClassName, + testMethodName, + /* userId */ null, + defaultTestTimeoutMs, + defaultMaxTimeoutToOutputMs, + /* maxInstrumentationTimeoutMillis */ 0L, + /* checkResults */ true, + /* isHiddenApiCheckDisabled */ false, + testArgs); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java index 57b9a86c191..010cce50ea8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java @@ -16,6 +16,9 @@ package android.security.cts; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNoException; + import android.platform.test.annotations.AsbSecurityTest; import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; @@ -34,18 +37,25 @@ public class CVE_2021_0441 extends NonRootSecurityTestCase { /** * b/174495520 + * Vulnerable Module : com.google.android.mediaprovider + * Is Play managed : Yes */ @AsbSecurityTest(cveBugId = 174495520) @Test - public void testPocCVE_2021_0441() throws Exception { - ITestDevice device = getDevice(); - uninstallPackage(device, TEST_PKG); - - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); - - installPackage(TEST_APP); - runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_0441"); + public void testPocCVE_2021_0441() { + try { + assumeFalse(moduleIsPlayManaged("com.google.android.mediaprovider")); + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage(TEST_APP); + runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_0441"); + } catch (Exception e) { + assumeNoException(e); + } } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0963.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0963.java index 90d8196c3f6..645f909f2f5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0963.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0963.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,41 +28,39 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0963 extends StsExtraBusinessLogicHostTestBase { - static final String TEST_PKG = "android.security.cts.CVE_2021_0963"; +public class CVE_2021_0963 extends NonRootSecurityTestCase { - /** - * b/199754277 - * Vulnerable app : KeyChain.apk - * Vulnerable module : com.android.keychain - * Is Play managed : No - */ + // b/199754277 + // Vulnerable app : KeyChain.apk + // Vulnerable module : com.android.keychain + // Is Play managed : No @AsbSecurityTest(cveBugId = 199754277) @Test public void testPocCVE_2021_0963() { + int userId = 0; + String component = null; + ITestDevice device = null; try { - ITestDevice device = getDevice(); + // Install the application + installPackage("CVE-2021-0963.apk", "-t"); - /* Wake up the device */ - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + // Set test-app as device owner. + final String testPkg = "android.security.cts.CVE_2021_0963"; + component = testPkg + "/" + testPkg + ".PocDeviceAdminReceiver"; + device = getDevice(); + device.setDeviceOwner(component, userId); - /* Install the application */ - installPackage("CVE-2021-0963.apk"); - - /* - * Set device as owner. After the test is completed, this change is reverted in the - * DeviceTest.java's tearDown() method by calling clearDeviceOwnerApp() on an instance - * of DevicePolicyManager. - */ - AdbUtils.runCommandLine("dpm set-device-owner --user 0 '" + TEST_PKG + "/" + TEST_PKG - + ".PocDeviceAdminReceiver" + "'", device); - - /* Run the device test "testOverlayButtonPresence" */ - runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence"); + // Run the device test "testOverlayButtonPresence" + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence"); } catch (Exception e) { assumeNoException(e); + } finally { + try { + // Remove test-app as device owner. + device.removeAdmin(component, userId); + } catch (Exception e) { + // ignore + } } } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20360.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20360.java new file mode 100644 index 00000000000..0568740ce20 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20360.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_20360 extends NonRootSecurityTestCase { + + // b/228314987 + // Vulnerable apk : Settings.apk + // Vulnerable module : com.android.settings + // Is Play managed : No + @AsbSecurityTest(cveBugId = 228314987) + @Test + public void testPocCVE_2022_20360() { + final String testPkg = "android.security.cts.CVE_2022_20360"; + ITestDevice device = null; + int currentUser = -1; + int newUser = -1; + try { + device = getDevice(); + + // Check if device supports nfc + assumeTrue("Device does not support nfc", device.hasFeature("android.hardware.nfc")); + + // Get current user + currentUser = device.getCurrentUser(); + + // Create new guest user 'CTSUser' for test + newUser = device.createUser("CTSUser", true, false); + + // Start new guest user 'CTSUser' + assumeTrue("Unable to create new guest user", device.startUser(newUser, true)); + + // Switch to new user 'CTSUser' + assumeTrue("Unable to switch to guest user", device.switchUser(newUser)); + + // Install test-app + installPackage("CVE-2022-20360.apk", "--user " + newUser); + + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testSecureNfcPreferenceController"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + if (currentUser != -1) { + // Switch back to previous user + device.switchUser(currentUser); + } + if (newUser != -1) { + // Stop user 'CTSUser' + device.stopUser(newUser); + + // Remove user 'CTSUser' + device.removeUser(newUser); + } + } catch (Exception e) { + // Ignore exception here + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20415.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20415.java index 511cbd4ac45..e12c9253ab5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20415.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20415.java @@ -16,6 +16,7 @@ package android.security.cts; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; @@ -37,6 +38,11 @@ public class CVE_2022_20415 extends NonRootSecurityTestCase { @Test public void testPocCVE_2022_20415() { try { + // Test is not applicable for watch devices so skipping the test for watch devices + assumeFalse( + "Skipping the test for watch devices", + getDevice().hasFeature("android.hardware.type.watch")); + final String testPkg = "android.security.cts.CVE_2022_20415"; // Install the test-app diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_20955.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_20955.java new file mode 100644 index 00000000000..96f2257ed15 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_20955.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2023_20955 extends NonRootSecurityTestCase { + + // b/258653813 + // Vulnerable app : Settings.apk + // Vulnerable package : com.android.settings + // Is Play Managed : No + @AsbSecurityTest(cveBugId = 240663194) + @Test + public void testPocCVE_2023_20955() { + int userId = -1; + ITestDevice device = null; + final String testPkg = "android.security.cts.CVE_2023_20955_test"; + final String componentName = testPkg + "/.PocDeviceAdminReceiver"; + try { + device = getDevice(); + + // Install the test app + installPackage("CVE-2023-20955-test.apk", "-t"); + + // Set test app as device owner + assumeTrue("Failed to set test app as device owner", + device.setDeviceOwner(componentName, 0)); + + // Create a new user + userId = device.createUser("CTSUser"); + assumeTrue("Failed to create a user. ITestDevice.createUser() returned -1", + userId != -1); + + // Install test helper app for all users + installPackage("CVE-2023-20955-test-helper.apk", "--user all"); + + // Run device test to check if App Info window allows uninstall for all users if + // DevicePolicyManager has restricted it. + runDeviceTests(testPkg, testPkg + ".DeviceTest", + "testAppInfoUninstallForAllUsersDisabled"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // Remove user + device.removeUser(userId); + + // Remove test app as device owner + device.removeAdmin(componentName, 0); + } catch (Exception e) { + // ignore + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/Android.bp new file mode 100644 index 00000000000..d6d3022814e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/Android.bp @@ -0,0 +1,24 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test_helper_app { + name: "Bug-261036568-provider", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + asset_dirs: ["assets"], + test_suites: [ + "sts", + ], + sdk_version: "current", +}
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/AndroidManifest.xml new file mode 100644 index 00000000000..71e37a3431a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.BUG_261036568_provider"> + + <permission-tree android:name="com.android.cts"/> + + <application + android:label="BUG-261036568-provider">> + <provider + android:name=".ImageProvider" + android:authorities="android.security.cts.BUG_261036568_provider" + android:enabled="true" + android:exported="true" /> + </application> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/assets/x.png b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/assets/x.png Binary files differnew file mode 100644 index 00000000000..8d17dab8f6f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/assets/x.png diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/src/android/security/cts/BUG_261036568_provider/ImageProvider.java b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/src/android/security/cts/BUG_261036568_provider/ImageProvider.java new file mode 100644 index 00000000000..667a0c098e7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/content-provider/src/android/security/cts/BUG_261036568_provider/ImageProvider.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.BUG_261036568_provider; + +import static android.os.Binder.getCallingUid; +import static android.os.Binder.getCallingUserHandle; +import static android.os.Process.myUid; +import static android.os.Process.myUserHandle; +import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; +import android.provider.DocumentsContract; +import android.provider.OpenableColumns; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +public class ImageProvider extends ContentProvider { + + private final Set<String> accessedUris = new HashSet<>(); + + @Override + public boolean onCreate() { + return true; + } + + @Override + public AssetFileDescriptor openAssetFile(Uri uri, String mode) { + maybeRecordUriAccess(uri); + try { + return getContext().getAssets().openFd("x.png"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) { + AssetFileDescriptor fd = openAssetFile(uri, mode); + return fd == null ? null : fd.getParcelFileDescriptor(); + } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + Bundle result = new Bundle(); + if (method.equals("verify")) { + result.putBoolean("passed", accessedUris.isEmpty()); + result.putStringArrayList("accessed_uris", new ArrayList<>(accessedUris)); + accessedUris.clear(); + } + return result; + } + + + @Override + public String getType(Uri uri) { + return uri.getPath().endsWith(".png") ? "image/png" : "*/*"; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[] { + OpenableColumns.DISPLAY_NAME, + DocumentsContract.Root.COLUMN_TITLE, + DocumentsContract.Document.COLUMN_FLAGS + }); + cursor.addRow(new Object[] { + "DISPLAY_NAME", + "TITLE", + FLAG_SUPPORTS_THUMBNAIL + }); + return cursor; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException(); + } + + @Override + public int delete(Uri uri, String selection, + String[] selectionArgs) { + throw new UnsupportedOperationException(); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + throw new UnsupportedOperationException(); + } + + private void maybeRecordUriAccess(Uri uri) { + UserHandle caller = getCallingUserHandle(); + if (!myUserHandle().equals(caller)) { + accessedUris.add("uri=" + uri.toString() + + ", owner_uid=" + myUid() + + ", caller_uid=" + getCallingUid() + + " ('" + getCallingPackage() + "')"); + } + } +}
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/Android.bp new file mode 100644 index 00000000000..f7103aaf99c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "Bug-261036568-test", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", + platform_apis: true, +}
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/AndroidManifest.xml new file mode 100644 index 00000000000..c20ac2db0a1 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.BUG_261036568_test" + android:versionCode="1" + android:versionName="1.0"> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.BUG_261036568_test"/> + +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/src/android/security/cts/BUG_261036568_test/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/src/android/security/cts/BUG_261036568_test/DeviceTest.java new file mode 100644 index 00000000000..ab09be5f42e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-261036568/test-app/src/android/security/cts/BUG_261036568_test/DeviceTest.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.BUG_261036568_test; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; + +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.content.ClipData; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final long WAIT_AND_ASSERT_FOUND_TIMEOUT_MS = 5000; + private static final long WAIT_FOR_IDLE_TIMEOUT_MS = 5000; + private static final long WAIT_AND_ASSERT_NOT_FOUND_TIMEOUT_MS = 2500; + + private static final String PROVIDER_AUTHORITY = "android.security.cts.BUG_261036568_provider"; + private static final Uri PROVIDER_AUTHORITY_URI = Uri.parse("content://" + PROVIDER_AUTHORITY); + + private ContentProviderClient mClient; + private Uri mTargetImageUri; + private Uri mTargetAuthorityUri; + private Uri mTargetFileUri; + + @Before + public void setUp() { + Instrumentation instrumentation = getInstrumentation(); + Context context = instrumentation.getContext(); + + // Get the id of a test user created by host side test + Bundle args = InstrumentationRegistry.getArguments(); + int targetUser = Integer.parseInt(args.getString("target_user", "-1")); + assumeTrue("Could not find target user", targetUser != -1); + + mTargetAuthorityUri = withUserId(PROVIDER_AUTHORITY_URI, targetUser); + mTargetImageUri = withPath(mTargetAuthorityUri, "x.png"); + mTargetFileUri = withPath(mTargetAuthorityUri, "x.pdf"); + } + + @Test + public void testShareUnownedUriAsPreview() { + // SEND, single image + openAndCloseSharesheet(createSendImageIntent(mTargetImageUri)); + // SEND, text with thumbnail + openAndCloseSharesheet(createSendTextIntentWithPreview(mTargetImageUri)); + // SEND_MULTIPLE, two images + openAndCloseSharesheet(createSendFileIntentWithPreview(mTargetImageUri, mTargetImageUri)); + // SEND_MULTIPLE, mixed types + openAndCloseSharesheet(createSendFileIntentWithPreview(mTargetImageUri, mTargetFileUri)); + + verifyNoContentProviderAccess(); + } + + private void openAndCloseSharesheet(Intent target) { + Instrumentation instrumentation = getInstrumentation(); + UiDevice device = UiDevice.getInstance(instrumentation); + Context context = instrumentation.getTargetContext(); + Intent chooserIntent = Intent.createChooser(target, null); + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + String chooserPackage = resolveChooserPackage(context); + context.startActivity(chooserIntent); + device.waitForIdle(WAIT_FOR_IDLE_TIMEOUT_MS); + if (waitForPackageVisible(device, chooserPackage)) { + device.pressBack(); + assumeTrue(waitForPackageGone(device, chooserPackage)); + } + } + + private void verifyNoContentProviderAccess() { + Instrumentation instrumentation = getInstrumentation(); + Context context = instrumentation.getContext(); + UiAutomation automation = instrumentation.getUiAutomation(); + ContentResolver resolver = context.getContentResolver(); + + // only used for verification to access the provider directly + automation.adoptShellPermissionIdentity("android.permission.INTERACT_ACROSS_USERS"); + + try (ContentProviderClient client = + resolver.acquireContentProviderClient(mTargetAuthorityUri)) { + assumeNotNull("Could not access '" + mTargetAuthorityUri, client); + + Bundle result = client.call("verify", null, null); + assumeNotNull("Failed to fetch result from content provider", result); + + boolean passed = result.getBoolean("passed"); + ArrayList<String> accessedUris = result.getStringArrayList("accessed_uris"); + assertTrue("Failed. Cross user URI reads detected: " + accessedUris, passed); + } catch (RemoteException e) { + assumeNoException("Caught exception verifying result: " + e, e); + } finally { + automation.dropShellPermissionIdentity(); + } + } + + private Intent createSendImageIntent(Uri image) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_STREAM, image); + sendIntent.setType("image/png"); + return sendIntent; + } + + private Intent createSendTextIntentWithPreview(Uri image) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TITLE, "Preview Title"); + sendIntent.putExtra(Intent.EXTRA_TEXT, "Sharing Text"); + sendIntent.setType("text/plain"); + sendIntent.setClipData( + new ClipData( + "Clip Label", + new String[] {"image/png"}, + new ClipData.Item(image))); + return sendIntent; + } + + private Intent createSendFileIntentWithPreview(Uri... uris) { + Intent sendIntent = new Intent(); + if (uris.length > 1) { + sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE); + sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, + new ArrayList<>(Arrays.asList(uris))); + } else if (uris.length == 1) { + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_STREAM, uris[0]); + } + sendIntent.setType("application/pdf"); + return sendIntent; + } + + private String resolveChooserPackage(Context context) { + PackageManager pm = context.getPackageManager(); + Intent shareIntent = Intent.createChooser(new Intent(), null); + ResolveInfo chooser = pm.resolveActivity(shareIntent, PackageManager.MATCH_DEFAULT_ONLY); + assertNotNull(chooser); + assertNotNull(chooser.activityInfo); + return chooser.activityInfo.packageName; + } + + /** + * Same as waitAndAssertFound but searching the entire device UI. + */ + private boolean waitForPackageVisible(UiDevice device, String pkg) { + return device.wait( + Until.findObject(By.pkg(pkg).depth(0)), + WAIT_AND_ASSERT_FOUND_TIMEOUT_MS + ) != null; + } + + /** + * Same as waitAndAssertNotFound() but searching the entire device UI. + */ + private boolean waitForPackageGone(UiDevice device, String pkg) { + return device.wait(Until.gone(By.pkg(pkg)), WAIT_AND_ASSERT_NOT_FOUND_TIMEOUT_MS); + } + + private static Uri withUserId(Uri uri, int userId) { + Uri.Builder builder = uri.buildUpon(); + builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority()); + return builder.build(); + } + + private static Uri withPath(Uri uri, String path) { + return uri.buildUpon().appendPath(path).build(); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/Android.bp index ea39e68b2be..2a307910faa 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/Android.bp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,5 +35,5 @@ android_test_helper_app { "androidx.test.rules", "androidx.test.uiautomator_uiautomator", ], - platform_apis: true, + platform_apis: true, // required for using RemoteCallback } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/AndroidManifest.xml index ae0d4160fcb..dec0ae40059 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/AndroidManifest.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2022 The Android Open Source Project + Copyright 2023 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.security.cts.CVE_2021_0963"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> - <application> + <application android:testOnly="true"> <activity android:name=".PocActivity" android:exported="true"> <intent-filter> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_pkey b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_pkey Binary files differnew file mode 100644 index 00000000000..3a071135dfb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_pkey diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_usercert b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_usercert Binary files differnew file mode 100644 index 00000000000..4665a94043a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/raw/cve_2021_0963_usercert diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/integers.xml index 6a14b4a8240..18ccaba3381 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/integers.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/integers.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2022 The Android Open Source Project + Copyright 2023 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/strings.xml index 1da84fe3711..e6915d6df3e 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/strings.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/values/strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2022 The Android Open Source Project + Copyright 2023 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,25 +16,32 @@ --> <resources> - <string name="actionKeychainActivity">com.android.keychain.CHOOSER</string> - <string name="activityNotFoundMsg">The activity with intent was not found : </string> - <string name="activityNotStartedException">Unable to start the activity with intent : </string> + <string name="action">com.android.keychain.CHOOSER</string> <string name="alias">Client</string> - <string name="callbackKey">callback</string> - <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> - <string name="certType">X.509</string> - <string name="dumpsysActivity">dumpsys activity %1$s</string> - <string name="dumpsysActivityNotStartedException">Could not execute dumpsys activity command - </string> - <string name="errorMessage">Device is vulnerable to b/199754277 hence any app with - "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string> - <string name="keyType">RSA</string> - <string name="mResumedTrue">mResumed=true</string> - <string name="messageKey">message</string> - <string name="overlayButtonText">OverlayButton</string> - <string name="overlayServiceNotStartedException">Unable to start the overlay service</string> - <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> - <string name="statusKey">status</string> - <string name="vulActivityNotRunningError">The activity %1$s is not currently running - on the device</string> + <string name="callback">callback</string> + <string name="cmdAdbHome">input keyevent KEYCODE_HOME</string> + <string name="cmdDumpsysActivityByActivity">dumpsys activity %1$s</string> + <string name="cmdDumpsysActivityByPkg">dumpsys activity -p %1$s activities</string> + <string name="exceptionActivityNotFound">The activity with intent was not found : </string> + <string name="exceptionActivityNotStart">Unable to start the activity with intent : </string> + <string name="exceptionCanNotDrawOverlays">The application cannot draw overlays</string> + <string name="exceptionOverlayUiNotVisible">Overlay UI did not appear on the screen</string> + <string name="exceptionServiceNotStart">Unable to start the overlay service</string> + <string name="exceptionVulActivityNotResume">The activity %1$s is not currently resumed on the + device</string> + <string name="exceptionVulUiNotVisible">UI of vulnerable activity %1$s is not visible</string> + <string name="failMsg">Device is vulnerable to b/199754277 !! Any app with SYSTEM_ALERT_WINDOW + can overlay the %1$s screen</string> + <string name="flagActivityResumed">mResumed=true</string> + <string name="flagActivityVisible">mVisible=true</string> + <string name="message">message</string> + <string name="pKey">cve_2021_0963_pkey</string> + <string name="rawResOpenError">Could not open the raw resource %1$s</string> + <string name="status">status</string> + <string name="strSplitRegex"><![CDATA[((?<=mVisible=(true|false)))]]></string> + <string name="streamReadError">Could not read from stream of the raw resource %1$s</string> + <string name="txtOverlayBtn">CVE_2021_0963_button</string> + <string name="typeCert">X.509</string> + <string name="typeKey">RSA</string> + <string name="userCert">cve_2021_0963_usercert</string> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/xml/device_policies.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/xml/device_policies.xml index a826e80f531..ed5352d7c0f 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/xml/device_policies.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/res/xml/device_policies.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2022 The Android Open Source Project + Copyright 2023 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,5 @@ --> <device-admin> - <uses-policies> - </uses-policies> + <uses-policies /> </device-admin> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/DeviceTest.java index 3d1c0df2131..209bdf06306 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/DeviceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.cert.Certificate; @@ -56,256 +59,31 @@ import java.util.regex.Pattern; public class DeviceTest { private DevicePolicyManager mDevicePolicyManager; private ComponentName mComponentName; - Context mContext; - - /** - * Generated from above and converted with: - * - * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g' - */ - private static final byte[] PRIVATE_KEY = - new byte[] {(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x76, (byte) 0x02, - (byte) 0x01, (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, - (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, - (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, - (byte) 0x82, (byte) 0x02, (byte) 0x60, (byte) 0x30, (byte) 0x82, (byte) 0x02, - (byte) 0x5c, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, - (byte) 0x81, (byte) 0x00, (byte) 0xee, (byte) 0x6e, (byte) 0x51, (byte) 0xa8, - (byte) 0xc4, (byte) 0x44, (byte) 0xd9, (byte) 0xb7, (byte) 0x53, (byte) 0xf1, - (byte) 0xb9, (byte) 0x1b, (byte) 0x9d, (byte) 0x8d, (byte) 0x7c, (byte) 0x9f, - (byte) 0x06, (byte) 0xe7, (byte) 0xed, (byte) 0xa8, (byte) 0x05, (byte) 0xb8, - (byte) 0xaa, (byte) 0x0a, (byte) 0x2d, (byte) 0x74, (byte) 0x05, (byte) 0x8b, - (byte) 0xad, (byte) 0xfe, (byte) 0xd3, (byte) 0x3e, (byte) 0x08, (byte) 0x9d, - (byte) 0xc9, (byte) 0xf5, (byte) 0xf7, (byte) 0x81, (byte) 0x90, (byte) 0xf1, - (byte) 0xcc, (byte) 0x3f, (byte) 0x91, (byte) 0xda, (byte) 0xcb, (byte) 0x67, - (byte) 0x6a, (byte) 0xe8, (byte) 0x4a, (byte) 0xa0, (byte) 0xc3, (byte) 0x8a, - (byte) 0x53, (byte) 0xd9, (byte) 0xf0, (byte) 0x17, (byte) 0xbe, (byte) 0x90, - (byte) 0xbb, (byte) 0x95, (byte) 0x29, (byte) 0x01, (byte) 0xce, (byte) 0x32, - (byte) 0xce, (byte) 0xf8, (byte) 0x02, (byte) 0xfe, (byte) 0xe8, (byte) 0x19, - (byte) 0x91, (byte) 0x29, (byte) 0x46, (byte) 0xf7, (byte) 0x67, (byte) 0xd1, - (byte) 0xcb, (byte) 0xa7, (byte) 0x20, (byte) 0x8b, (byte) 0x85, (byte) 0x8a, - (byte) 0x0c, (byte) 0x07, (byte) 0xf8, (byte) 0xfe, (byte) 0xf4, (byte) 0x5d, - (byte) 0x08, (byte) 0xf4, (byte) 0x63, (byte) 0x4a, (byte) 0x69, (byte) 0x66, - (byte) 0x28, (byte) 0xcb, (byte) 0x0d, (byte) 0x1c, (byte) 0x7f, (byte) 0x7f, - (byte) 0x7e, (byte) 0x83, (byte) 0x49, (byte) 0x66, (byte) 0x6c, (byte) 0x83, - (byte) 0x2d, (byte) 0xa0, (byte) 0x51, (byte) 0xf6, (byte) 0x14, (byte) 0x68, - (byte) 0x47, (byte) 0x31, (byte) 0x72, (byte) 0x4d, (byte) 0xe9, (byte) 0x1e, - (byte) 0x12, (byte) 0x1b, (byte) 0xd0, (byte) 0xe6, (byte) 0x21, (byte) 0xd8, - (byte) 0x84, (byte) 0x5f, (byte) 0xe3, (byte) 0xef, (byte) 0x02, (byte) 0x03, - (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, - (byte) 0x24, (byte) 0x95, (byte) 0xb8, (byte) 0xe1, (byte) 0xf4, (byte) 0x7b, - (byte) 0xbc, (byte) 0x0c, (byte) 0x6d, (byte) 0x4d, (byte) 0x01, (byte) 0xe2, - (byte) 0x42, (byte) 0xe2, (byte) 0x9a, (byte) 0xe4, (byte) 0xab, (byte) 0xe2, - (byte) 0x9a, (byte) 0x8c, (byte) 0xd5, (byte) 0x93, (byte) 0xe8, (byte) 0x43, - (byte) 0x77, (byte) 0x85, (byte) 0xfd, (byte) 0xf3, (byte) 0xd8, (byte) 0xd6, - (byte) 0xe9, (byte) 0x02, (byte) 0xf3, (byte) 0xbf, (byte) 0x82, (byte) 0x65, - (byte) 0xc3, (byte) 0x7c, (byte) 0x96, (byte) 0x09, (byte) 0x04, (byte) 0x16, - (byte) 0x1d, (byte) 0x03, (byte) 0x3d, (byte) 0x82, (byte) 0xb8, (byte) 0xdc, - (byte) 0xbb, (byte) 0xd6, (byte) 0xbf, (byte) 0x2a, (byte) 0x52, (byte) 0x83, - (byte) 0x76, (byte) 0x5b, (byte) 0xae, (byte) 0x59, (byte) 0xf6, (byte) 0xee, - (byte) 0x84, (byte) 0x44, (byte) 0x4a, (byte) 0xa7, (byte) 0x25, (byte) 0x50, - (byte) 0x89, (byte) 0x63, (byte) 0x43, (byte) 0x0b, (byte) 0xc8, (byte) 0xd5, - (byte) 0x17, (byte) 0x9d, (byte) 0x8b, (byte) 0x62, (byte) 0xd5, (byte) 0xf1, - (byte) 0xde, (byte) 0x45, (byte) 0xe6, (byte) 0x35, (byte) 0x10, (byte) 0xba, - (byte) 0x58, (byte) 0x18, (byte) 0x44, (byte) 0xc1, (byte) 0x6d, (byte) 0xb6, - (byte) 0x1d, (byte) 0x2f, (byte) 0x53, (byte) 0xb6, (byte) 0x5a, (byte) 0xf1, - (byte) 0x66, (byte) 0xbc, (byte) 0x0e, (byte) 0x63, (byte) 0xa7, (byte) 0x0f, - (byte) 0x81, (byte) 0x4b, (byte) 0x07, (byte) 0x31, (byte) 0xa5, (byte) 0x70, - (byte) 0xec, (byte) 0x30, (byte) 0x57, (byte) 0xc4, (byte) 0x14, (byte) 0xb2, - (byte) 0x8b, (byte) 0x6f, (byte) 0x26, (byte) 0x7e, (byte) 0x55, (byte) 0x60, - (byte) 0x63, (byte) 0x7d, (byte) 0x90, (byte) 0xd7, (byte) 0x5f, (byte) 0xef, - (byte) 0x7d, (byte) 0xc1, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xfe, - (byte) 0x92, (byte) 0xa9, (byte) 0xf1, (byte) 0x29, (byte) 0x1e, (byte) 0xd4, - (byte) 0x72, (byte) 0xd3, (byte) 0x3f, (byte) 0x9d, (byte) 0xd6, (byte) 0x3d, - (byte) 0xe9, (byte) 0xcf, (byte) 0x3e, (byte) 0x06, (byte) 0xdc, (byte) 0x65, - (byte) 0x8f, (byte) 0xc0, (byte) 0x81, (byte) 0xc2, (byte) 0x66, (byte) 0xc1, - (byte) 0x5c, (byte) 0x2c, (byte) 0xfa, (byte) 0x08, (byte) 0x65, (byte) 0xb6, - (byte) 0x47, (byte) 0xc5, (byte) 0x14, (byte) 0x8d, (byte) 0x69, (byte) 0xe9, - (byte) 0xaf, (byte) 0x42, (byte) 0x02, (byte) 0x53, (byte) 0x04, (byte) 0x63, - (byte) 0x47, (byte) 0xaf, (byte) 0xcc, (byte) 0xae, (byte) 0x08, (byte) 0x31, - (byte) 0xba, (byte) 0xea, (byte) 0x85, (byte) 0xda, (byte) 0xd6, (byte) 0xb2, - (byte) 0xe7, (byte) 0x4c, (byte) 0xda, (byte) 0xad, (byte) 0x52, (byte) 0x76, - (byte) 0x48, (byte) 0x16, (byte) 0xeb, (byte) 0x02, (byte) 0x41, (byte) 0x00, - (byte) 0xef, (byte) 0xc4, (byte) 0x7d, (byte) 0x69, (byte) 0x7b, (byte) 0xcb, - (byte) 0xcb, (byte) 0xf7, (byte) 0x00, (byte) 0x2d, (byte) 0x05, (byte) 0x3c, - (byte) 0xe4, (byte) 0xfd, (byte) 0x5c, (byte) 0xea, (byte) 0xcf, (byte) 0x40, - (byte) 0x84, (byte) 0x10, (byte) 0xf1, (byte) 0xc0, (byte) 0xaf, (byte) 0xc7, - (byte) 0xc8, (byte) 0x51, (byte) 0xac, (byte) 0x18, (byte) 0x25, (byte) 0x63, - (byte) 0x75, (byte) 0xc7, (byte) 0x0e, (byte) 0xa9, (byte) 0xed, (byte) 0x9c, - (byte) 0x78, (byte) 0x08, (byte) 0x28, (byte) 0x1d, (byte) 0x9e, (byte) 0xfa, - (byte) 0x17, (byte) 0x0f, (byte) 0x7a, (byte) 0x6a, (byte) 0x78, (byte) 0x63, - (byte) 0x6e, (byte) 0xb3, (byte) 0x6b, (byte) 0xd6, (byte) 0x43, (byte) 0x4b, - (byte) 0x58, (byte) 0xb8, (byte) 0x77, (byte) 0x10, (byte) 0x07, (byte) 0x70, - (byte) 0xa6, (byte) 0xa9, (byte) 0xae, (byte) 0x0d, (byte) 0x02, (byte) 0x41, - (byte) 0x00, (byte) 0x92, (byte) 0x4c, (byte) 0x79, (byte) 0x0b, (byte) 0x95, - (byte) 0xc5, (byte) 0x18, (byte) 0xf4, (byte) 0x90, (byte) 0x40, (byte) 0x8c, - (byte) 0x15, (byte) 0x96, (byte) 0x69, (byte) 0x2a, (byte) 0xe7, (byte) 0x8b, - (byte) 0x8b, (byte) 0xd7, (byte) 0x76, (byte) 0x00, (byte) 0x7c, (byte) 0xd1, - (byte) 0xda, (byte) 0xb9, (byte) 0x9e, (byte) 0x9e, (byte) 0x5e, (byte) 0x66, - (byte) 0xbb, (byte) 0x05, (byte) 0x41, (byte) 0x43, (byte) 0x9a, (byte) 0x67, - (byte) 0x16, (byte) 0x89, (byte) 0xec, (byte) 0x65, (byte) 0x33, (byte) 0xee, - (byte) 0xbf, (byte) 0xa3, (byte) 0xca, (byte) 0x8b, (byte) 0xd6, (byte) 0x45, - (byte) 0xe1, (byte) 0x81, (byte) 0xaa, (byte) 0xd8, (byte) 0xa2, (byte) 0x6a, - (byte) 0x3c, (byte) 0x5e, (byte) 0x7e, (byte) 0x1c, (byte) 0xa5, (byte) 0xc3, - (byte) 0x5b, (byte) 0x93, (byte) 0x8c, (byte) 0x24, (byte) 0x57, (byte) 0x02, - (byte) 0x40, (byte) 0x0a, (byte) 0x6d, (byte) 0x3f, (byte) 0x0e, (byte) 0xf1, - (byte) 0x45, (byte) 0x41, (byte) 0x8f, (byte) 0x72, (byte) 0x40, (byte) 0x82, - (byte) 0xf3, (byte) 0xcc, (byte) 0xf9, (byte) 0x7f, (byte) 0xaa, (byte) 0xee, - (byte) 0x6c, (byte) 0x5d, (byte) 0xd1, (byte) 0xe6, (byte) 0xd1, (byte) 0x7c, - (byte) 0x53, (byte) 0x71, (byte) 0xd0, (byte) 0xab, (byte) 0x6d, (byte) 0x39, - (byte) 0x63, (byte) 0x03, (byte) 0xe2, (byte) 0x2e, (byte) 0x2f, (byte) 0x11, - (byte) 0x98, (byte) 0x36, (byte) 0x58, (byte) 0x14, (byte) 0x76, (byte) 0x85, - (byte) 0x4d, (byte) 0x56, (byte) 0xe7, (byte) 0x63, (byte) 0x69, (byte) 0x71, - (byte) 0xe6, (byte) 0xd1, (byte) 0x0f, (byte) 0x98, (byte) 0x66, (byte) 0xee, - (byte) 0xf2, (byte) 0x3d, (byte) 0xdf, (byte) 0x77, (byte) 0xbe, (byte) 0x08, - (byte) 0xb4, (byte) 0xcb, (byte) 0x6a, (byte) 0xa1, (byte) 0x99, (byte) 0x02, - (byte) 0x40, (byte) 0x52, (byte) 0x01, (byte) 0xde, (byte) 0x62, (byte) 0xc2, - (byte) 0x25, (byte) 0xbf, (byte) 0x5d, (byte) 0x77, (byte) 0xe4, (byte) 0x6b, - (byte) 0xb6, (byte) 0xd7, (byte) 0x8f, (byte) 0x89, (byte) 0x2c, (byte) 0xe6, - (byte) 0x8d, (byte) 0xe5, (byte) 0xad, (byte) 0x39, (byte) 0x17, (byte) 0x54, - (byte) 0x2b, (byte) 0x35, (byte) 0x53, (byte) 0xd1, (byte) 0xa1, (byte) 0xef, - (byte) 0x48, (byte) 0xbc, (byte) 0x95, (byte) 0x48, (byte) 0xcf, (byte) 0x62, - (byte) 0xf4, (byte) 0x33, (byte) 0xcf, (byte) 0x37, (byte) 0x78, (byte) 0xeb, - (byte) 0x17, (byte) 0xb4, (byte) 0x0b, (byte) 0x83, (byte) 0x4f, (byte) 0xb6, - (byte) 0xab, (byte) 0x7d, (byte) 0x67, (byte) 0x3e, (byte) 0x4e, (byte) 0x44, - (byte) 0x4a, (byte) 0x55, (byte) 0x2e, (byte) 0x34, (byte) 0x12, (byte) 0x0b, - (byte) 0x59, (byte) 0xb3, (byte) 0xb1, (byte) 0x1e, (byte) 0x3d}; - - - /** - * Generated from above and converted with: - * - * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g' - */ - private static final byte[] USER_CERT = - {(byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xd8, (byte) 0x30, (byte) 0x82, - (byte) 0x01, (byte) 0xc0, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01, - (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d, - (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x0b, (byte) 0x05, - (byte) 0x00, (byte) 0x30, (byte) 0x33, (byte) 0x31, (byte) 0x0b, (byte) 0x30, - (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, - (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, - (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, - (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, - (byte) 0x65, (byte) 0x31, (byte) 0x0f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x06, - (byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, - (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x32, - (byte) 0x30, (byte) 0x33, (byte) 0x32, (byte) 0x35, (byte) 0x30, (byte) 0x37, - (byte) 0x32, (byte) 0x30, (byte) 0x31, (byte) 0x32, (byte) 0x5a, (byte) 0x17, - (byte) 0x0d, (byte) 0x33, (byte) 0x32, (byte) 0x30, (byte) 0x33, (byte) 0x32, - (byte) 0x32, (byte) 0x30, (byte) 0x37, (byte) 0x32, (byte) 0x30, (byte) 0x31, - (byte) 0x32, (byte) 0x5a, (byte) 0x30, (byte) 0x33, (byte) 0x31, (byte) 0x0b, - (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, - (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, - (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, - (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, - (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x0f, (byte) 0x30, (byte) 0x0d, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, - (byte) 0x06, (byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, - (byte) 0x65, (byte) 0x30, (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, - (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, - (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, - (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, - (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, - (byte) 0xee, (byte) 0x6e, (byte) 0x51, (byte) 0xa8, (byte) 0xc4, (byte) 0x44, - (byte) 0xd9, (byte) 0xb7, (byte) 0x53, (byte) 0xf1, (byte) 0xb9, (byte) 0x1b, - (byte) 0x9d, (byte) 0x8d, (byte) 0x7c, (byte) 0x9f, (byte) 0x06, (byte) 0xe7, - (byte) 0xed, (byte) 0xa8, (byte) 0x05, (byte) 0xb8, (byte) 0xaa, (byte) 0x0a, - (byte) 0x2d, (byte) 0x74, (byte) 0x05, (byte) 0x8b, (byte) 0xad, (byte) 0xfe, - (byte) 0xd3, (byte) 0x3e, (byte) 0x08, (byte) 0x9d, (byte) 0xc9, (byte) 0xf5, - (byte) 0xf7, (byte) 0x81, (byte) 0x90, (byte) 0xf1, (byte) 0xcc, (byte) 0x3f, - (byte) 0x91, (byte) 0xda, (byte) 0xcb, (byte) 0x67, (byte) 0x6a, (byte) 0xe8, - (byte) 0x4a, (byte) 0xa0, (byte) 0xc3, (byte) 0x8a, (byte) 0x53, (byte) 0xd9, - (byte) 0xf0, (byte) 0x17, (byte) 0xbe, (byte) 0x90, (byte) 0xbb, (byte) 0x95, - (byte) 0x29, (byte) 0x01, (byte) 0xce, (byte) 0x32, (byte) 0xce, (byte) 0xf8, - (byte) 0x02, (byte) 0xfe, (byte) 0xe8, (byte) 0x19, (byte) 0x91, (byte) 0x29, - (byte) 0x46, (byte) 0xf7, (byte) 0x67, (byte) 0xd1, (byte) 0xcb, (byte) 0xa7, - (byte) 0x20, (byte) 0x8b, (byte) 0x85, (byte) 0x8a, (byte) 0x0c, (byte) 0x07, - (byte) 0xf8, (byte) 0xfe, (byte) 0xf4, (byte) 0x5d, (byte) 0x08, (byte) 0xf4, - (byte) 0x63, (byte) 0x4a, (byte) 0x69, (byte) 0x66, (byte) 0x28, (byte) 0xcb, - (byte) 0x0d, (byte) 0x1c, (byte) 0x7f, (byte) 0x7f, (byte) 0x7e, (byte) 0x83, - (byte) 0x49, (byte) 0x66, (byte) 0x6c, (byte) 0x83, (byte) 0x2d, (byte) 0xa0, - (byte) 0x51, (byte) 0xf6, (byte) 0x14, (byte) 0x68, (byte) 0x47, (byte) 0x31, - (byte) 0x72, (byte) 0x4d, (byte) 0xe9, (byte) 0x1e, (byte) 0x12, (byte) 0x1b, - (byte) 0xd0, (byte) 0xe6, (byte) 0x21, (byte) 0xd8, (byte) 0x84, (byte) 0x5f, - (byte) 0xe3, (byte) 0xef, (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, - (byte) 0x01, (byte) 0xa3, (byte) 0x7b, (byte) 0x30, (byte) 0x79, (byte) 0x30, - (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, - (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00, (byte) 0x30, (byte) 0x2c, - (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, - (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01, (byte) 0x0d, (byte) 0x04, - (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f, (byte) 0x70, (byte) 0x65, - (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c, (byte) 0x20, (byte) 0x47, - (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72, (byte) 0x61, (byte) 0x74, - (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72, - (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61, - (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, - (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, - (byte) 0x14, (byte) 0xee, (byte) 0xec, (byte) 0x08, (byte) 0xcc, (byte) 0xdd, - (byte) 0xa3, (byte) 0x29, (byte) 0x6e, (byte) 0x2b, (byte) 0x78, (byte) 0x23, - (byte) 0xb3, (byte) 0xf0, (byte) 0xb8, (byte) 0x9d, (byte) 0x53, (byte) 0x41, - (byte) 0x2e, (byte) 0x3c, (byte) 0x61, (byte) 0x30, (byte) 0x1f, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, - (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x86, (byte) 0xdb, - (byte) 0xa5, (byte) 0x5e, (byte) 0x0e, (byte) 0x03, (byte) 0xbc, (byte) 0xe4, - (byte) 0xc1, (byte) 0xc8, (byte) 0xf3, (byte) 0xed, (byte) 0x24, (byte) 0x48, - (byte) 0xb1, (byte) 0x37, (byte) 0x3a, (byte) 0x52, (byte) 0x10, (byte) 0x57, - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, - (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, - (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82, (byte) 0x01, - (byte) 0x01, (byte) 0x00, (byte) 0x15, (byte) 0x5a, (byte) 0x5c, (byte) 0x08, - (byte) 0xe4, (byte) 0x0e, (byte) 0x28, (byte) 0x4c, (byte) 0xa9, (byte) 0x0e, - (byte) 0x35, (byte) 0xbe, (byte) 0xe3, (byte) 0xd5, (byte) 0xd1, (byte) 0xb4, - (byte) 0x47, (byte) 0x87, (byte) 0x63, (byte) 0xd2, (byte) 0x5e, (byte) 0x7e, - (byte) 0xf6, (byte) 0xd8, (byte) 0xce, (byte) 0xdf, (byte) 0x10, (byte) 0x15, - (byte) 0x61, (byte) 0xc4, (byte) 0x9a, (byte) 0xf1, (byte) 0xba, (byte) 0x33, - (byte) 0xf2, (byte) 0xc2, (byte) 0x01, (byte) 0x95, (byte) 0xa7, (byte) 0x74, - (byte) 0x97, (byte) 0xc1, (byte) 0x43, (byte) 0x68, (byte) 0x92, (byte) 0xbe, - (byte) 0x9a, (byte) 0x6f, (byte) 0x38, (byte) 0xcb, (byte) 0xa0, (byte) 0xcf, - (byte) 0x1e, (byte) 0x5b, (byte) 0x03, (byte) 0xde, (byte) 0x45, (byte) 0x6d, - (byte) 0xea, (byte) 0xf0, (byte) 0x46, (byte) 0x4d, (byte) 0xb6, (byte) 0x4b, - (byte) 0x88, (byte) 0xc7, (byte) 0xb8, (byte) 0xe3, (byte) 0x9f, (byte) 0x58, - (byte) 0x8b, (byte) 0x2d, (byte) 0xbf, (byte) 0x4b, (byte) 0x3f, (byte) 0x54, - (byte) 0x2d, (byte) 0xa8, (byte) 0x27, (byte) 0x72, (byte) 0x5e, (byte) 0x36, - (byte) 0x67, (byte) 0x5c, (byte) 0x6e, (byte) 0x9a, (byte) 0x67, (byte) 0x73, - (byte) 0x44, (byte) 0xaf, (byte) 0x46, (byte) 0x7f, (byte) 0xd6, (byte) 0x2b, - (byte) 0x9d, (byte) 0x28, (byte) 0xb1, (byte) 0xc4, (byte) 0xc4, (byte) 0x72, - (byte) 0x3d, (byte) 0x6d, (byte) 0x7d, (byte) 0x28, (byte) 0x40, (byte) 0x62, - (byte) 0x40, (byte) 0x21, (byte) 0x52, (byte) 0xb5, (byte) 0x0b, (byte) 0xf3, - (byte) 0xcc, (byte) 0x36, (byte) 0x03, (byte) 0x10, (byte) 0x19, (byte) 0xe3, - (byte) 0xc2, (byte) 0xfe, (byte) 0xe9, (byte) 0x08, (byte) 0x0d, (byte) 0xd4, - (byte) 0x8b, (byte) 0x12, (byte) 0xd6, (byte) 0x3d, (byte) 0xc5, (byte) 0xb8, - (byte) 0x8c, (byte) 0xbd, (byte) 0xa5, (byte) 0xcd, (byte) 0xb3, (byte) 0xe4, - (byte) 0xd1, (byte) 0xd8, (byte) 0x4c, (byte) 0x32, (byte) 0x44, (byte) 0x3f, - (byte) 0x63, (byte) 0x32, (byte) 0x09, (byte) 0xdb, (byte) 0x8b, (byte) 0x7b, - (byte) 0x30, (byte) 0x58, (byte) 0xc7, (byte) 0xcf, (byte) 0xc3, (byte) 0x44, - (byte) 0xd9, (byte) 0xff, (byte) 0x63, (byte) 0x91, (byte) 0x74, (byte) 0xd8, - (byte) 0x62, (byte) 0x2b, (byte) 0x52, (byte) 0xc8, (byte) 0x82, (byte) 0x9f, - (byte) 0xeb, (byte) 0x22, (byte) 0x5c, (byte) 0xa2, (byte) 0x26, (byte) 0xfe, - (byte) 0x04, (byte) 0x31, (byte) 0x53, (byte) 0x09, (byte) 0xa7, (byte) 0x23, - (byte) 0xe3, (byte) 0x0f, (byte) 0xf8, (byte) 0xe9, (byte) 0x99, (byte) 0xad, - (byte) 0x4b, (byte) 0x23, (byte) 0x07, (byte) 0xfb, (byte) 0xfa, (byte) 0xc3, - (byte) 0x55, (byte) 0x59, (byte) 0xdb, (byte) 0x6b, (byte) 0x71, (byte) 0xdf, - (byte) 0x25, (byte) 0x0f, (byte) 0xaa, (byte) 0xa2, (byte) 0xfa, (byte) 0x28, - (byte) 0x49, (byte) 0x65, (byte) 0x7e, (byte) 0x0b, (byte) 0x74, (byte) 0x30, - (byte) 0xd9, (byte) 0x9a, (byte) 0xfe, (byte) 0x2c, (byte) 0x8c, (byte) 0x67, - (byte) 0x50, (byte) 0x0c, (byte) 0x6d, (byte) 0x4c, (byte) 0xba, (byte) 0x34, - (byte) 0x3b, (byte) 0x0d, (byte) 0x16, (byte) 0x45, (byte) 0x63, (byte) 0x73, - (byte) 0xc2, (byte) 0x9f, (byte) 0xb4, (byte) 0xdd, (byte) 0x6f, (byte) 0xde, - (byte) 0x9d, (byte) 0x71, (byte) 0xbf, (byte) 0x8d, (byte) 0x1b, (byte) 0x79, - (byte) 0xa0, (byte) 0x0a, (byte) 0x66, (byte) 0x7e, (byte) 0x56, (byte) 0x83, - (byte) 0x8f, (byte) 0x3f, (byte) 0x7d, (byte) 0x93, (byte) 0xf6, (byte) 0xc9, - (byte) 0x42, (byte) 0xfc, (byte) 0xc5, (byte) 0xf2, (byte) 0x49, (byte) 0xec}; + private Context mContext; + private UiDevice mDevice; + private Resources mResources; + + private byte[] getByteArrayFromRawRes(int resId, String resName) throws IOException { + byte[] byteArray = null; + try (InputStream inStream = mResources.openRawResource(resId); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); ) { + assumeTrue(mContext.getString(R.string.rawResOpenError, resName), inStream != null); + byteArray = new byte[1024]; + int nRead = inStream.read(byteArray, 0, byteArray.length); + assumeTrue(mContext.getString(R.string.streamReadError, resName), nRead > 0); + outStream.write(byteArray, 0, nRead); + } + return byteArray; + } @After public void tearDown() { try { + // Go to home screen + mDevice.executeShellCommand(mContext.getString(R.string.cmdAdbHome)); + + // Remove key pair added by the test as part of cleanup mDevicePolicyManager.removeKeyPair(mComponentName, mContext.getString(R.string.alias)); - mDevicePolicyManager.clearDeviceOwnerApp(mContext.getPackageName()); } catch (Exception e) { // ignore all exceptions as the test is already complete } @@ -314,85 +92,146 @@ public class DeviceTest { @Test public void testOverlayButtonPresence() { try { - /* Install key pair required to launch KeyChainActivity dialog */ + // Create the byte arrays from raw resources of private key and user certificate + // respectively. mContext = getInstrumentation().getContext(); - Resources resources = mContext.getResources(); - KeyFactory kf = KeyFactory.getInstance(mContext.getString(R.string.keyType)); - PrivateKey privKey = kf.generatePrivate(new PKCS8EncodedKeySpec(PRIVATE_KEY)); + mResources = mContext.getResources(); + byte[] privateKeyByteArray = + getByteArrayFromRawRes( + R.raw.cve_2021_0963_pkey, mContext.getString(R.string.pKey)); + byte[] userCertByteArray = + getByteArrayFromRawRes( + R.raw.cve_2021_0963_usercert, mContext.getString(R.string.userCert)); + + // Install key pair required to launch KeyChainActivity dialog + KeyFactory kf = KeyFactory.getInstance(mContext.getString(R.string.typeKey)); + PrivateKey privKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyByteArray)); CertificateFactory cf = - CertificateFactory.getInstance(mContext.getString(R.string.certType)); - Certificate cert = cf.generateCertificate(new ByteArrayInputStream(USER_CERT)); + CertificateFactory.getInstance(mContext.getString(R.string.typeCert)); + Certificate cert = cf.generateCertificate(new ByteArrayInputStream(userCertByteArray)); mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); - mComponentName = new ComponentName(PocDeviceAdminReceiver.class.getPackage().getName(), - PocDeviceAdminReceiver.class.getName()); - assumeTrue(mDevicePolicyManager.installKeyPair(mComponentName, privKey, cert, - mContext.getString(R.string.alias))); + mComponentName = + new ComponentName( + PocDeviceAdminReceiver.class.getPackage().getName(), + PocDeviceAdminReceiver.class.getName()); + assumeTrue( + mDevicePolicyManager.installKeyPair( + mComponentName, privKey, cert, mContext.getString(R.string.alias))); - /* Start the overlay service */ + // Start the overlay service Intent intent = new Intent(mContext, PocService.class); - assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg), + assumeTrue( + mContext.getString(R.string.exceptionCanNotDrawOverlays), Settings.canDrawOverlays(mContext)); CompletableFuture<PocStatus> callbackReturn = new CompletableFuture<>(); - RemoteCallback cb = new RemoteCallback((Bundle result) -> { - PocStatus pocStatus = - new PocStatus(result.getInt(mContext.getString(R.string.statusKey)), - result.getString(mContext.getString(R.string.messageKey))); - callbackReturn.complete(pocStatus); - }); - intent.putExtra(mContext.getString(R.string.callbackKey), cb); + RemoteCallback cb = + new RemoteCallback( + (Bundle result) -> { + PocStatus pocStatus = + new PocStatus( + result.getInt(mContext.getString(R.string.status)), + result.getString( + mContext.getString(R.string.message))); + callbackReturn.complete(pocStatus); + }); + intent.putExtra(mContext.getString(R.string.callback), cb); mContext.startService(intent); - PocStatus result = callbackReturn.get(resources.getInteger(R.integer.timeoutMs), - TimeUnit.MILLISECONDS); - assumeTrue(result.getErrorMessage(), - result.getStatusCode() != resources.getInteger(R.integer.assumptionFailure)); - - /* Wait for the overlay window */ - Pattern overlayTextPattern = Pattern.compile( - mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE); - UiDevice device = UiDevice.getInstance(getInstrumentation()); - assumeTrue(mContext.getString(R.string.overlayUiScreenError), - device.wait(Until.hasObject(By.text(overlayTextPattern)), - mContext.getResources().getInteger(R.integer.timeoutMs))); + PocStatus result = + callbackReturn.get( + mResources.getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS); + assumeTrue( + result.getErrorMessage(), + result.getStatusCode() != mResources.getInteger(R.integer.assumptionFailure)); + + // Wait for the overlay window + mDevice = UiDevice.getInstance(getInstrumentation()); + Pattern overlayTextPattern = + Pattern.compile( + mContext.getString(R.string.txtOverlayBtn), Pattern.CASE_INSENSITIVE); + assumeTrue( + mContext.getString(R.string.exceptionOverlayUiNotVisible), + mDevice.wait( + Until.hasObject(By.text(overlayTextPattern)), + mResources.getInteger(R.integer.timeoutMs))); - /* Start PocActivity which starts the vulnerable activity */ + // Start PocActivity which in turn starts the vulnerable activity intent = new Intent(mContext, PocActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); CompletableFuture<PocStatus> pocActivityReturn = new CompletableFuture<>(); - RemoteCallback pocActivityCb = new RemoteCallback((Bundle pocActivityResult) -> { - PocStatus pocStatus = new PocStatus( - pocActivityResult.getInt(mContext.getString(R.string.statusKey)), - pocActivityResult.getString(mContext.getString(R.string.messageKey))); - pocActivityReturn.complete(pocStatus); - }); - intent.putExtra(mContext.getString(R.string.callbackKey), pocActivityCb); + RemoteCallback pocActivityCb = + new RemoteCallback( + (Bundle pocActivityResult) -> { + PocStatus pocStatus = + new PocStatus( + pocActivityResult.getInt( + mContext.getString(R.string.status)), + pocActivityResult.getString( + mContext.getString(R.string.message))); + pocActivityReturn.complete(pocStatus); + }); + intent.putExtra(mContext.getString(R.string.callback), pocActivityCb); mContext.startActivity(intent); - result = pocActivityReturn.get(resources.getInteger(R.integer.timeoutMs), - TimeUnit.MILLISECONDS); - assumeTrue(result.getErrorMessage(), - result.getStatusCode() != resources.getInteger(R.integer.assumptionFailure)); - - /* Get the vulnerable activity name by using an alternative intent */ - Intent vulIntent = new Intent(mContext.getString(R.string.actionKeychainActivity)); - ResolveInfo ri = mContext.getPackageManager().resolveActivity(vulIntent, - PackageManager.MATCH_DEFAULT_ONLY); + result = + pocActivityReturn.get( + mResources.getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS); + assumeTrue( + result.getErrorMessage(), + result.getStatusCode() != mResources.getInteger(R.integer.assumptionFailure)); + + // Get the vulnerable activity name by using an alternative intent + Intent vulIntent = new Intent(mContext.getString(R.string.action)); + ResolveInfo ri = + mContext.getPackageManager() + .resolveActivity(vulIntent, PackageManager.MATCH_DEFAULT_ONLY); String vulnerableActivityName = ri.activityInfo.name; - - /* Wait until the object of launcher activity is gone */ - boolean overlayDisallowed = device.wait(Until.gone(By.pkg(mContext.getPackageName())), - mContext.getResources().getInteger(R.integer.timeoutMs)); - - /* Check if the currently running activity is the vulnerable activity */ - String activityDump = ""; - activityDump = device.executeShellCommand( - mContext.getString(R.string.dumpsysActivity, vulnerableActivityName)); - Pattern activityPattern = Pattern.compile(mContext.getString(R.string.mResumedTrue), - Pattern.CASE_INSENSITIVE); + String vulnerablePkgName = ri.activityInfo.packageName; + + // Wait until the object of launcher activity is gone + boolean overlayDisallowed = + mDevice.wait( + Until.gone(By.pkg(mContext.getPackageName())), + mResources.getInteger(R.integer.timeoutMs)); + + // Check if the currently resumed activity is the vulnerable activity + String activityDump = + mDevice.executeShellCommand( + mContext.getString( + R.string.cmdDumpsysActivityByActivity, vulnerableActivityName)); + Pattern activityPattern = + Pattern.compile( + mContext.getString(R.string.flagActivityResumed), + Pattern.CASE_INSENSITIVE); assumeTrue( - mContext.getString(R.string.vulActivityNotRunningError, vulnerableActivityName), + mContext.getString( + R.string.exceptionVulActivityNotResume, vulnerableActivityName), activityPattern.matcher(activityDump).find()); - /* Failing the test as fix is not present */ - assertTrue(mContext.getString(R.string.errorMessage, vulnerableActivityName), + // Check if vulnerable activity's UI is visible + String vulPkgDump = + mDevice.executeShellCommand( + mContext.getString( + R.string.cmdDumpsysActivityByPkg, vulnerablePkgName)); + boolean isVisible = false; + for (String vulPkgDumpElement : + vulPkgDump.split(mContext.getString(R.string.strSplitRegex))) { + if (vulPkgDumpElement + .toLowerCase() + .contains(vulnerableActivityName.toLowerCase())) { + if (vulPkgDumpElement.contains( + mContext.getString(R.string.flagActivityVisible))) { + isVisible = true; + break; + } + } + } + assumeTrue( + mContext.getString(R.string.exceptionVulUiNotVisible, vulnerableActivityName), + isVisible); + + // On vulnerable device, vulnerable activity will be overlaid so 'overlayDisallowed' + // will be set to true so the test fails, else it passes. + assertTrue( + mContext.getString(R.string.failMsg, vulnerableActivityName), overlayDisallowed); } catch (Exception e) { assumeNoException(e); diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocActivity.java index ac8ea15c6e0..bcf98e116fa 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,13 +27,19 @@ import androidx.annotation.Nullable; public class PocActivity extends Activity { @Override - protected void onCreate(Bundle savedInstanceState) { + public void onResume() { try { - super.onCreate(savedInstanceState); - KeyChainAliasCallback callback = new KeyChainAliasCallback() { - @Override - public void alias(@Nullable String alias) {} - }; + super.onResume(); + + // Waiting briefly for the PocActivity window transition animation to complete. + Thread.sleep(500); + + // Launching the vulnerable activity KeyChainActivity + KeyChainAliasCallback callback = + new KeyChainAliasCallback() { + @Override + public void alias(@Nullable String alias) {} + }; KeyChain.choosePrivateKeyAlias(this, callback, null, null, null, -1, null); sendTestResult(getResources().getInteger(R.integer.noException), ""); } catch (Exception e) { @@ -44,10 +50,10 @@ public class PocActivity extends Activity { void sendTestResult(int status, String message) { try { RemoteCallback cb = - (RemoteCallback) getIntent().getExtras().get(getString(R.string.callbackKey)); + (RemoteCallback) getIntent().getExtras().get(getString(R.string.callback)); Bundle res = new Bundle(); - res.putString(getString(R.string.messageKey), message); - res.putInt(getString(R.string.statusKey), status); + res.putString(getString(R.string.message), message); + res.putInt(getString(R.string.status), status); cb.sendResult(res); } catch (Exception e) { // ignore all exceptions diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocDeviceAdminReceiver.java index 5592323071b..b8ef104dca4 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocDeviceAdminReceiver.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocDeviceAdminReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,5 +18,4 @@ package android.security.cts.CVE_2021_0963; import android.app.admin.DeviceAdminReceiver; -public class PocDeviceAdminReceiver extends DeviceAdminReceiver { -} +public class PocDeviceAdminReceiver extends DeviceAdminReceiver {} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocService.java index b83e8247d54..c78d7d4a656 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocService.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,8 +55,8 @@ public class PocService extends Service { mWindowManager = getSystemService(WindowManager.class); mLayoutParams = new LayoutParams(); mLayoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY; - mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | - LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.flags = + LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; mLayoutParams.format = PixelFormat.OPAQUE; mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; mLayoutParams.width = getScreenWidth(); @@ -65,7 +65,7 @@ public class PocService extends Service { mLayoutParams.y = getScreenHeight() / 2; Context context = getApplicationContext(); mButton = new Button(context); - mButton.setText(context.getString(R.string.overlayButtonText)); + mButton.setText(context.getString(R.string.txtOverlayBtn)); mWindowManager.addView(mButton, mLayoutParams); sendTestResult(getResources().getInteger(R.integer.noException), ""); } catch (Exception e) { @@ -87,10 +87,10 @@ public class PocService extends Service { void sendTestResult(int status, String message) { try { RemoteCallback cb = - (RemoteCallback) mIntent.getExtras().get(getString(R.string.callbackKey)); + (RemoteCallback) mIntent.getExtras().get(getString(R.string.callback)); Bundle res = new Bundle(); - res.putString(getString(R.string.messageKey), message); - res.putInt(getString(R.string.statusKey), status); + res.putString(getString(R.string.message), message); + res.putInt(getString(R.string.status), status); cb.sendResult(res); } catch (Exception e) { // ignore exception here diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocStatus.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocStatus.java index de67f0ff104..7e6c63d7c73 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocStatus.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0963/src/android/security/cts/CVE_2021_0963/PocStatus.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/Android.bp new file mode 100644 index 00000000000..d9f8554c32d --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/Android.bp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20360", + defaults: [ + "cts_support_defaults" + ], + srcs: [ + "src/**/*.java" + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/AndroidManifest.xml new file mode 100644 index 00000000000..9e2361a9750 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/AndroidManifest.xml @@ -0,0 +1,23 @@ +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2022_20360"> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20360" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/res/values/strings.xml new file mode 100644 index 00000000000..9476f7af344 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="defaultSettingsPkg">com.android.settings</string> + <string name="disableSecureNfcFailed">Disabling secure NFC failed</string> + <string name="key">key</string> + <string name="msgDeviceLocked">Device is in sleep or locked mode</string> + <string name="msgTestFail"> Device is vulnerable to b/228314987!! Secure nfc can be disabled in + guest user via SettingsSlice</string> + <string name="secureNfcPreferenceControllerClassName">.nfc.SecureNfcPreferenceController + </string> + <string name="setCheckedMethod">setChecked</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/src/android/security/cts/CVE_2022_20360/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/src/android/security/cts/CVE_2022_20360/DeviceTest.java new file mode 100644 index 00000000000..9e73804e1b7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20360/src/android/security/cts/CVE_2022_20360/DeviceTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20360; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.KeyguardManager; +import android.app.UiAutomation; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.NfcManager; +import android.os.PowerManager; +import android.os.UserManager; +import android.provider.Settings; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testSecureNfcPreferenceController() { + boolean secureNfcEnabled = false; + NfcAdapter nfcAdapter = null; + UiAutomation uiAutomation = null; + try { + Context context = getApplicationContext(); + NfcManager nfcManager = context.getSystemService(NfcManager.class); + nfcAdapter = nfcManager.getDefaultAdapter(); + uiAutomation = getInstrumentation().getUiAutomation(); + + // Secure NFC APIs require device to be unlocked hence check if device is unlocked + PowerManager powerManager = context.getSystemService(PowerManager.class); + KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + assumeTrue(context.getString(R.string.msgDeviceLocked), + powerManager.isInteractive() && !keyguardManager.isKeyguardLocked()); + + + // Save secure NFC state(enabled/disabled) and disable secure NFC for test + secureNfcEnabled = nfcAdapter.isSecureNfcEnabled(); + if (secureNfcEnabled) { + nfcAdapter.enableSecureNfc(false); + } + assumeFalse(context.getString(R.string.disableSecureNfcFailed), + nfcAdapter.isSecureNfcEnabled()); + + // Retrieve settings package name dynamically + Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); + ComponentName settingsComponent = + settingsIntent.resolveActivity(context.getPackageManager()); + String settingsPkgName = settingsComponent != null ? settingsComponent.getPackageName() + : context.getString(R.string.defaultSettingsPkg); + + // Get vulnerable method 'setChecked' using reflection + Context settingsContext = context.createPackageContext(settingsPkgName, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + ClassLoader settingsClassLoader = settingsContext.getClassLoader(); + Class<?> targetClass = settingsClassLoader.loadClass(settingsPkgName + + context.getString(R.string.secureNfcPreferenceControllerClassName)); + Constructor<?> targetClassCstr = + targetClass.getConstructor(Context.class, String.class); + Object targetClassobject = + targetClassCstr.newInstance(context, context.getString(R.string.key)); + Method setCheckedMethod = targetClass + .getDeclaredMethod(context.getString(R.string.setCheckedMethod), boolean.class); + setCheckedMethod.setAccessible(true); + + // Check if current user is guest user + uiAutomation.adoptShellPermissionIdentity(android.Manifest.permission.CREATE_USERS); + UserManager userManager = context.getSystemService(UserManager.class); + assumeTrue(userManager.isGuestUser()); + + // Invoke vulnerable method 'setChecked' + boolean retVal = (boolean) setCheckedMethod.invoke(targetClassobject, true); + assertFalse(context.getString(R.string.msgTestFail), retVal); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // Disable secure NFC if it was disabled before the test + if (!secureNfcEnabled) { + nfcAdapter.enableSecureNfc(false); + } + } catch (Exception ignored) { + // Ignore any exception here + } finally { + uiAutomation.dropShellPermissionIdentity(); + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/Android.bp new file mode 100644 index 00000000000..e856574ef9c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/Android.bp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2023-20955-test", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "test-app/src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + resource_dirs: [ + "test-app/res", + ], + manifest: "test-app/AndroidManifest.xml", + sdk_version: "current", +} + +android_test_helper_app { + name: "CVE-2023-20955-test-helper", + defaults: [ + "cts_support_defaults", + ], + test_suites: [ + "sts", + ], + manifest: "test-helper-app/AndroidManifest.xml", + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/AndroidManifest.xml new file mode 100644 index 00000000000..2d74f5492fc --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2023_20955_test"> + <application android:testOnly="true"> + <receiver android:name=".PocDeviceAdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN" + android:exported="true"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_policies" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2023_20955_test" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/values/strings.xml new file mode 100644 index 00000000000..194436bd2b9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/values/strings.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="failMsg">Vulnerable to b/258653813 !!</string> + <string name="msgNotFoundTxt">Did not find an object with selector %1$s on the screen</string> + <string name="msgSetUserRestrictionFailed">Failed to set user restriction DISALLOW_APPS_CONTROL + </string> + <string name="patternMoreOptions">.*more options.*</string> + <string name="patternUninstall">.*uninstall.*</string> + <string name="patternUninstallAllUsers">.*uninstall for all users.*</string> + <string name="pkgNameHelper">android.security.cts.CVE_2023_20955_test_helper</string> + <string name="pkgNameInstaller">com.android.packageinstaller</string> + <string name="uriScheme">package</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/xml/device_policies.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/xml/device_policies.xml new file mode 100644 index 00000000000..ed5352d7c0f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/res/xml/device_policies.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<device-admin> + <uses-policies /> +</device-admin> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/DeviceTest.java new file mode 100644 index 00000000000..8b769040010 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/DeviceTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2023_20955_test; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.UserManager; +import android.provider.Settings; +import android.widget.ImageButton; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final int TIMEOUT_MS = 5000; + private Context mContext; + private UiDevice mDevice; + + private void waitAndClick(BySelector selector) { + assumeTrue(mContext.getString(R.string.msgNotFoundTxt, selector.toString()), + mDevice.wait(Until.hasObject(selector), TIMEOUT_MS)); + mDevice.findObject(selector).click(); + } + + @Test + public void testAppInfoUninstallForAllUsersDisabled() { + try { + mContext = getInstrumentation().getTargetContext(); + mDevice = UiDevice.getInstance(getInstrumentation()); + + // Restrict current user to disallow controlling apps + ComponentName deviceAdminComponent = + new ComponentName(mContext, PocDeviceAdminReceiver.class); + DevicePolicyManager policyManager = null; + policyManager = mContext.getSystemService(DevicePolicyManager.class); + policyManager.addUserRestriction(deviceAdminComponent, + UserManager.DISALLOW_APPS_CONTROL); + + // Waiting for user restriction to be added + boolean disallowAppsControlVal = + (boolean) policyManager.getUserRestrictions(deviceAdminComponent) + .getBoolean(UserManager.DISALLOW_APPS_CONTROL); + while (!disallowAppsControlVal && System.currentTimeMillis() < TIMEOUT_MS) { + disallowAppsControlVal = policyManager.getUserRestrictions(deviceAdminComponent) + .getBoolean(UserManager.DISALLOW_APPS_CONTROL); + Thread.sleep(100); + } + assumeTrue(mContext.getString(R.string.msgSetUserRestrictionFailed), + disallowAppsControlVal); + + // Launching "application info" window for test helper app + Uri pkgUri = Uri.fromParts(mContext.getString(R.string.uriScheme), + mContext.getString(R.string.pkgNameHelper), null); + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, pkgUri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); + + // Wait for the application info window and click on 3 dots with: + // content description = "More options" + Pattern descPattern = Pattern.compile(mContext.getString(R.string.patternMoreOptions), + Pattern.CASE_INSENSITIVE); + waitAndClick(By.clazz(ImageButton.class).desc(descPattern)); + + // Wait for a menu to appear. When it appears, click on the text "Uninstall for all + // users". + Pattern textPattern = + Pattern.compile(mContext.getString(R.string.patternUninstallAllUsers), + Pattern.CASE_INSENSITIVE); + waitAndClick(By.text(textPattern)); + + // Retrieve the package name of installer app + Intent packageInstallerIntent = new Intent(Intent.ACTION_DELETE, pkgUri); + ComponentName componentName = + packageInstallerIntent.resolveActivity(mContext.getPackageManager()); + String pkgNameInstaller = mContext.getString(R.string.pkgNameInstaller); + if (componentName != null && componentName.getPackageName() != null) { + pkgNameInstaller = componentName.getPackageName(); + } + + // Wait for UI with package = "com.android.packageinstaller" (AOSP), text containing + // "uninstall". If found, it indicates vulnerable behaviour and anyone can uninstall + // app for all users despite the user restrictions. + textPattern = Pattern.compile(mContext.getString(R.string.patternUninstall), + Pattern.CASE_INSENSITIVE); + assertFalse(mContext.getString(R.string.failMsg), mDevice + .wait(Until.hasObject(By.pkg(pkgNameInstaller).text(textPattern)), TIMEOUT_MS)); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // Go to the home screen + mDevice.pressHome(); + } catch (Exception e) { + // ignore + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/PocDeviceAdminReceiver.java new file mode 100644 index 00000000000..7141a402e65 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-app/src/android/security/cts/CVE_2023_20955_test/PocDeviceAdminReceiver.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2023_20955_test; + +import android.app.admin.DeviceAdminReceiver; + +public class PocDeviceAdminReceiver extends DeviceAdminReceiver { +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-helper-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-helper-app/AndroidManifest.xml new file mode 100644 index 00000000000..73420e4b366 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-20955/test-helper-app/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest package="android.security.cts.CVE_2023_20955_test_helper"> + <application /> +</manifest> diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java index 3b81acb26d8..86df388d725 100644 --- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java +++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java @@ -116,7 +116,7 @@ public class TestUsbTest extends DeviceTestCase implements IAbiReceiver, IBuildR /** * Check if adb serial number, USB serial number, ro.serialno, and android.os.Build.SERIAL - * all matches and meets the format requirement [a-zA-Z0-9]{6,20} + * all matches and meets the format requirement [a-zA-Z0-9\\._\\-,]+ */ @AppModeInstant(reason = "only instant apps fail when reading serial") public void testInstantAppsCannotReadSerial() throws Exception { @@ -127,7 +127,7 @@ public class TestUsbTest extends DeviceTestCase implements IAbiReceiver, IBuildR /** * Check if adb serial number, USB serial number, ro.serialno, and android.os.Build.SERIAL - * all matches and meets the format requirement [a-zA-Z0-9]{6,20} + * all matches and meets the format requirement [a-zA-Z0-9\\._\\-,]+ */ @AppModeFull(reason = "serial can not be read by instant apps") public void testUsbSerialReadOnDeviceMatches() throws Exception { @@ -148,7 +148,7 @@ public class TestUsbTest extends DeviceTestCase implements IAbiReceiver, IBuildR CommandResult result = RunUtil.getDefault().runTimedCmd(15000, "lsusb", "-v"); assertEquals("lsusb -v failed", result.getStatus(), CommandStatus.SUCCESS); String lsusbOutput = result.getStdout(); - Pattern pattern = Pattern.compile("^\\s+iSerial\\s+\\d+\\s+([a-zA-Z0-9]{6,20})", + Pattern pattern = Pattern.compile("^\\s+iSerial\\s+\\d+\\s+([a-zA-Z0-9\\._\\-,]+)", Pattern.MULTILINE); Matcher matcher = pattern.matcher(lsusbOutput); String usbSerial = ""; @@ -166,7 +166,7 @@ public class TestUsbTest extends DeviceTestCase implements IAbiReceiver, IBuildR runTestOnDevice("logSerial"); String logs = mDevice.executeAdbCommand( "logcat", "-v", "brief", "-d", "CtsUsbSerialTest:W", "*:S"); - pattern = Pattern.compile("^.*CtsUsbSerialTest\\(.*\\):\\s+([a-zA-Z0-9]{6,20})", + pattern = Pattern.compile("^.*CtsUsbSerialTest\\(.*\\):\\s+([a-zA-Z0-9\\._\\-,]+)", Pattern.MULTILINE); matcher = pattern.matcher(logs); String buildSerial = ""; diff --git a/libs/webkit-shared/src/android/webkit/cts/ExceptionWrapper.java b/libs/webkit-shared/src/android/webkit/cts/ExceptionWrapper.java new file mode 100644 index 00000000000..a4aca088280 --- /dev/null +++ b/libs/webkit-shared/src/android/webkit/cts/ExceptionWrapper.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit.cts; + +import android.os.RemoteException; + +/** + * Binder only handles a few exceptions. Runtime exceptions are silently ignored and any errors + * thrown will result in crashing the entire process and meaning no further tests will pass. We deal + * with this in a bit of a sneaky sneaky way and wrap any throwables in one of the supported + * exception types (IllegalStateException seems fairly representative of if the host app environment + * is broken), and then catch that and re-expose it in the SharedSdkWebServer where we strip that + * exception type out to avoid confusion. + * + * <p>The wrap/unwrap methods from this class should be used on either side of IPC calls by the + * IHostAppInvoker and IWebServer. + * + * <p>This allows JUnit to deal with any major broken program flows gracefully instead of moving on + * or crashing the rest of the tests. + * + * <p>It should be noted that binder parcel will take the cause and stringify the stack trace so the + * type information of these exceptions is lost in the journey. This means that the test code will + * not be able to react to these exception types. This is already a limitation of communicating + * through binder. + */ +class ExceptionWrapper { + public static <T> T wrap(WrappedTypedCall<T> c) { + try { + return c.wrap(); + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + public static void wrap(WrappedVoidCall r) { + wrap(() -> { + r.wrap(); + return null; + }); + } + + public static <T> T unwrap(UnwrappedTypedCall<T> c) { + try { + return c.unwrap(); + } catch (RemoteException e) { + // We are handling the remote exception separately from the IllegalStateException + // because this is happening binder proxy side so we would like to preserve the + // exception information. + // We still wrap this in a runtime exception so that the WebServer tests don't need to + // throw inside the Webkit utils run on main sync method as that would mean those + // functions would all have to return null (it would turn them into callables instead of + // runnables). + throw new RuntimeException(e); + } catch (IllegalStateException e) { + throw new RuntimeException(e.getMessage()); + } + } + + public static void unwrap(UnwrappedVoidCall c) { + unwrap(() -> { + c.unwrap(); + return null; + }); + } + + interface WrappedTypedCall<T> { + T wrap() throws Exception; + } + + interface WrappedVoidCall { + void wrap() throws Exception; + } + + interface UnwrappedTypedCall<T> { + T unwrap() throws RemoteException; + } + + interface UnwrappedVoidCall { + void unwrap() throws RemoteException; + } +} diff --git a/libs/webkit-shared/src/android/webkit/cts/SharedSdkWebServer.java b/libs/webkit-shared/src/android/webkit/cts/SharedSdkWebServer.java index c3ceaf8c84a..ac3fb6dcef8 100644 --- a/libs/webkit-shared/src/android/webkit/cts/SharedSdkWebServer.java +++ b/libs/webkit-shared/src/android/webkit/cts/SharedSdkWebServer.java @@ -16,7 +16,6 @@ package android.webkit.cts; -import android.os.RemoteException; import androidx.annotation.Nullable; @@ -38,31 +37,25 @@ public final class SharedSdkWebServer { } /** Starts the web server using the provided parameters}. */ - public void start(@SslMode int sslMode, @Nullable byte[] acceptedIssuerDer, - int keyResId, int certResId) { - try { + public void start( + @SslMode int sslMode, @Nullable byte[] acceptedIssuerDer, int keyResId, int certResId) { + ExceptionWrapper.unwrap(() -> { mWebServer.start(sslMode, acceptedIssuerDer, keyResId, certResId); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Shuts down the web server if it was started. */ public void shutdown() { - try { + ExceptionWrapper.unwrap(() -> { mWebServer.shutdown(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Resets all request state stored. */ public void resetRequestState() { - try { + ExceptionWrapper.unwrap(() -> { mWebServer.resetRequestState(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** @@ -71,162 +64,128 @@ public final class SharedSdkWebServer { */ public String setResponse( String path, String responseString, List<HttpHeader> responseHeaders) { - // We can't send a null value as a list - // so default to an empty list if null was provided. - if (responseHeaders == null) { - responseHeaders = Collections.emptyList(); - } - try { - return mWebServer.setResponse(path, responseString, responseHeaders); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + return ExceptionWrapper.unwrap(() -> { + // We can't send a null value as a list + // so default the responseHeaders to an empty list if null was provided. + return mWebServer.setResponse( + path, + responseString, + responseHeaders == null ? Collections.emptyList() : responseHeaders); + }); } /** Return the absolute URL that refers to a path. */ public String getAbsoluteUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getAbsoluteUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns a url that will contain the user agent in the header and in the body. */ public String getUserAgentUrl() { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getUserAgentUrl(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Get a delayed assert url for an asset path. */ public String getDelayedAssetUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getDelayedAssetUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Get a url that will redirect for a path. */ public String getRedirectingAssetUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getRedirectingAssetUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Get the full url for an asset. */ public String getAssetUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getAssetUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Get the full auth url for an asset. */ public String getAuthAssetUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getAuthAssetUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Get a binary url. */ public String getBinaryUrl(String mimeType, int contentLength) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getBinaryUrl(mimeType, contentLength); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns the url to the app cache. */ public String getAppCacheUrl() { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getAppCacheUrl(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns how many requests have been made. */ public int getRequestCount() { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getRequestCount(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns the request count for a particular path */ public int getRequestCount(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getRequestCountWithPath(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Verify if a resource was requested. */ public boolean wasResourceRequested(String url) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.wasResourceRequested(url); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Retrieve the last request to be made on a url. */ public HttpRequest getLastRequest(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getLastRequest(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Retrieve the last request for an asset path to be made on a url. */ public HttpRequest getLastAssetRequest(String url) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getLastAssetRequest(url); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns a url that will contain the path as a cookie. */ public String getCookieUrl(String path) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getCookieUrl(path); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** - * Returns a URL that attempts to set the cookie - * "key=value" with the given list of attributes when fetched. - */ + * Returns a URL that attempts to set the cookie "key=value" with the given list of attributes + * when fetched. + */ public String getSetCookieUrl(String path, String key, String value, String attributes) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getSetCookieUrl(path, key, value, attributes); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns a URL for a page with a script tag where src equals the URL passed in. */ public String getLinkedScriptUrl(String path, String url) { - try { + return ExceptionWrapper.unwrap(() -> { return mWebServer.getLinkedScriptUrl(path, url); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } } diff --git a/libs/webkit-shared/src/android/webkit/cts/SharedWebViewTestEnvironment.java b/libs/webkit-shared/src/android/webkit/cts/SharedWebViewTestEnvironment.java index eb43397fad6..d777c624678 100644 --- a/libs/webkit-shared/src/android/webkit/cts/SharedWebViewTestEnvironment.java +++ b/libs/webkit-shared/src/android/webkit/cts/SharedWebViewTestEnvironment.java @@ -21,7 +21,6 @@ import static org.junit.Assert.*; import android.app.Instrumentation; import android.app.UiAutomation; import android.content.Context; -import android.os.RemoteException; import android.os.StrictMode; import android.os.StrictMode.ThreadPolicy; import android.view.MotionEvent; @@ -37,7 +36,6 @@ import androidx.test.InstrumentationRegistry; import org.apache.http.util.EncodingUtils; import java.io.ByteArrayInputStream; -import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; @@ -90,66 +88,50 @@ public final class SharedWebViewTestEnvironment { * Use this method instead of EncodingUtils.getBytes. */ public byte[] getEncodingBytes(String data, String charset) { - try { + return ExceptionWrapper.unwrap(() -> { return mHostAppInvoker.getEncodingBytes(data, charset); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Invokes waitForIdleSync on the {@link Instrumentation} in the activity. */ public void waitForIdleSync() { - try { + ExceptionWrapper.unwrap(() -> { mHostAppInvoker.waitForIdleSync(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Invokes sendKeyDownUpSync on the {@link Instrumentation} in the activity. */ public void sendKeyDownUpSync(int keyCode) { - try { + ExceptionWrapper.unwrap(() -> { mHostAppInvoker.sendKeyDownUpSync(keyCode); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Invokes sendPointerSync on the {@link Instrumentation} in the activity. */ public void sendPointerSync(MotionEvent event) { - try { + ExceptionWrapper.unwrap(() -> { mHostAppInvoker.sendPointerSync(event); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } /** Returns a web server that can be used for web based testing. */ public SharedSdkWebServer getWebServer() { - try { + return ExceptionWrapper.unwrap(() -> { return new SharedSdkWebServer(mHostAppInvoker.getWebServer()); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + }); } - /** Returns a web server that has been started and can be used - * for web based testing. */ + /** Returns a web server that has been started and can be used for web based testing. */ public SharedSdkWebServer getSetupWebServer(@SslMode int sslMode) { return getSetupWebServer(sslMode, null, 0, 0); } - /** Returns a web server that has been started and can be used - * for web based testing. */ - public SharedSdkWebServer getSetupWebServer(@SslMode int sslMode, - @Nullable byte[] acceptedIssuerDer, int keyResId, int certResId) { - try { - SharedSdkWebServer webServer = getWebServer(); - webServer.start(sslMode, acceptedIssuerDer, keyResId, certResId); - return webServer; - } catch (Exception e) { - throw new RuntimeException(e); - } + /** Returns a web server that has been started and can be used for web based testing. */ + public SharedSdkWebServer getSetupWebServer( + @SslMode int sslMode, @Nullable byte[] acceptedIssuerDer, int keyResId, int certResId) { + SharedSdkWebServer webServer = getWebServer(); + webServer.start(sslMode, acceptedIssuerDer, keyResId, certResId); + return webServer; } /** @@ -227,171 +209,211 @@ public final class SharedWebViewTestEnvironment { private UiAutomation mUiAutomation; public void waitForIdleSync() { - mInstrumentation.waitForIdleSync(); + ExceptionWrapper.wrap(() -> { + mInstrumentation.waitForIdleSync(); + }); } public void sendKeyDownUpSync(int keyCode) { - mInstrumentation.sendKeyDownUpSync(keyCode); + ExceptionWrapper.wrap(() -> { + mInstrumentation.sendKeyDownUpSync(keyCode); + }); } public void sendPointerSync(MotionEvent event) { - if (allowUiAutomation) { - sendPointerSyncWithUiAutomation(event); - } else { - sendPointerSyncWithInstrumentation(event); - } + ExceptionWrapper.wrap(() -> { + if (allowUiAutomation) { + sendPointerSyncWithUiAutomation(event); + } else { + sendPointerSyncWithInstrumentation(event); + } + }); } public byte[] getEncodingBytes(String data, String charset) { - return EncodingUtils.getBytes(data, charset); + return ExceptionWrapper.wrap(() -> { + return EncodingUtils.getBytes(data, charset); + }); } public IWebServer getWebServer() { return new IWebServer.Stub() { private CtsTestServer mWebServer; - public void start(@SslMode int sslMode, @Nullable byte[] acceptedIssuerDer, - int keyResId, int certResId) { - assertNull(mWebServer); - final X509Certificate[] acceptedIssuerCerts; - if (acceptedIssuerDer != null) { - try { + public void start( + @SslMode int sslMode, + @Nullable byte[] acceptedIssuerDer, + int keyResId, + int certResId) { + ExceptionWrapper.wrap(() -> { + assertNull(mWebServer); + final X509Certificate[] acceptedIssuerCerts; + if (acceptedIssuerDer != null) { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - acceptedIssuerCerts = - new X509Certificate[] { - (X509Certificate) - certFactory.generateCertificate( - new ByteArrayInputStream( - acceptedIssuerDer)) - }; - } catch (CertificateException e) { - // Throw manually, because compiler does not understand that fail() - // does not return. - throw new AssertionError( - "Failed to create certificate chain: " + e.toString()); + acceptedIssuerCerts = new X509Certificate[] { + (X509Certificate) certFactory.generateCertificate( + new ByteArrayInputStream(acceptedIssuerDer)) + }; + } else { + acceptedIssuerCerts = null; } - } else { - acceptedIssuerCerts = null; - } - try { - X509TrustManager trustManager = new CtsTestServer.CtsTrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return acceptedIssuerCerts; - } - }; - mWebServer = new CtsTestServer(applicationContext, sslMode, - trustManager, keyResId, certResId); - } catch (Exception e) { - fail(" Failed to launch CtsTestServer: " + e); - } + X509TrustManager trustManager = + new CtsTestServer.CtsTrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return acceptedIssuerCerts; + } + }; + mWebServer = new CtsTestServer( + applicationContext, sslMode, trustManager, keyResId, certResId); + }); } public void shutdown() { if (mWebServer == null) { return; } - ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); - ThreadPolicy tmpPolicy = - new ThreadPolicy.Builder(oldPolicy).permitNetwork().build(); - StrictMode.setThreadPolicy(tmpPolicy); - mWebServer.shutdown(); - mWebServer = null; - StrictMode.setThreadPolicy(oldPolicy); + ExceptionWrapper.wrap(() -> { + ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + ThreadPolicy tmpPolicy = + new ThreadPolicy.Builder(oldPolicy) + .permitNetwork() + .build(); + StrictMode.setThreadPolicy(tmpPolicy); + mWebServer.shutdown(); + mWebServer = null; + StrictMode.setThreadPolicy(oldPolicy); + }); } public void resetRequestState() { - assertNotNull("The WebServer needs to be started", mWebServer); - mWebServer.resetRequestState(); + ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + mWebServer.resetRequestState(); + }); } public String setResponse( String path, String responseString, List<HttpHeader> responseHeaders) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.setResponse( - path, responseString, HttpHeader.asPairList(responseHeaders)); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.setResponse( + path, responseString, HttpHeader.asPairList(responseHeaders)); + }); } public String getAbsoluteUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getAbsoluteUrl(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getAbsoluteUrl(path); + }); } public String getUserAgentUrl() { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getUserAgentUrl(); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getUserAgentUrl(); + }); } public String getDelayedAssetUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getDelayedAssetUrl(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getDelayedAssetUrl(path); + }); } public String getRedirectingAssetUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getRedirectingAssetUrl(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getRedirectingAssetUrl(path); + }); } public String getAssetUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getAssetUrl(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getAssetUrl(path); + }); } public String getAuthAssetUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getAuthAssetUrl(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getAuthAssetUrl(path); + }); } public String getBinaryUrl(String mimeType, int contentLength) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getBinaryUrl(mimeType, contentLength); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getBinaryUrl(mimeType, contentLength); + }); } public String getAppCacheUrl() { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getAppCacheUrl(); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getAppCacheUrl(); + }); } public int getRequestCount() { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getRequestCount(); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getRequestCount(); + }); } public int getRequestCountWithPath(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getRequestCount(path); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getRequestCount(path); + }); } public boolean wasResourceRequested(String url) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.wasResourceRequested(url); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.wasResourceRequested(url); + }); } public HttpRequest getLastRequest(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return toHttpRequest(path, mWebServer.getLastRequest(path)); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return toHttpRequest(path, mWebServer.getLastRequest(path)); + }); } public HttpRequest getLastAssetRequest(String url) { - assertNotNull("The WebServer needs to be started", mWebServer); - return toHttpRequest(url, mWebServer.getLastAssetRequest(url)); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return toHttpRequest(url, mWebServer.getLastAssetRequest(url)); + }); } - public String getCookieUrl(String path) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getCookieUrl(path); + public String getCookieUrl(String path) { + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getCookieUrl(path); + }); } - public String getSetCookieUrl(String path, String key, String value, - String attributes) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getSetCookieUrl(path, key, value, attributes); + public String getSetCookieUrl( + String path, String key, String value, String attributes) { + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getSetCookieUrl(path, key, value, attributes); + }); } public String getLinkedScriptUrl(String path, String url) { - assertNotNull("The WebServer needs to be started", mWebServer); - return mWebServer.getLinkedScriptUrl(path, url); + return ExceptionWrapper.wrap(() -> { + assertNotNull("The WebServer needs to be started", mWebServer); + return mWebServer.getLinkedScriptUrl(path, url); + }); } private HttpRequest toHttpRequest( diff --git a/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTestUtils.java b/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTestUtils.java index cf105c2bf13..447c1a5bec1 100644 --- a/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTestUtils.java +++ b/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTestUtils.java @@ -16,17 +16,18 @@ package android.mediaprovidertranscode.cts; -import static androidx.test.InstrumentationRegistry.getContext; - +import static android.Manifest.permission.WRITE_DEVICE_CONFIG; import static android.mediaprovidertranscode.cts.TranscodeTestConstants.INTENT_EXTRA_CALLING_PKG; import static android.mediaprovidertranscode.cts.TranscodeTestConstants.INTENT_EXTRA_PATH; -import static android.mediaprovidertranscode.cts.TranscodeTestConstants.OPEN_FILE_QUERY; import static android.mediaprovidertranscode.cts.TranscodeTestConstants.INTENT_QUERY_TYPE; +import static android.provider.DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT; + +import static androidx.test.InstrumentationRegistry.getContext; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.Manifest; import android.app.ActivityManager; @@ -38,6 +39,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.media.MediaCodecInfo; +import android.media.MediaCodecInfo.CodecCapabilities; +import android.media.MediaCodecList; +import android.media.MediaFormat; import android.net.Uri; import android.os.Bundle; import android.os.Environment; @@ -46,18 +51,13 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemClock; import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.system.Os; import android.system.OsConstants; import android.util.Log; -import android.media.MediaCodecInfo; -import android.media.MediaCodecInfo.CodecCapabilities; -import android.media.MediaCodecInfo.VideoCapabilities; -import android.media.MediaCodecList; -import android.media.MediaFormat; - +import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import com.android.cts.install.lib.Install; @@ -85,6 +85,8 @@ public class TranscodeTestUtils { private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20); private static final long POLLING_SLEEP_MILLIS = 100; + private static final String TRANSCODE_COMPAT_MANIFEST_DEVICE_CONFIG_PROPERTY_NAME = + "transcode_compat_manifest"; public static Uri stageHEVCVideoFile(File videoFile) throws IOException { return stageVideoFile(videoFile, R.raw.testvideo_HEVC); @@ -165,9 +167,16 @@ public class TranscodeTestUtils { assertThat(numBytesWritten).isEqualTo(byteCount); } - public static void enableTranscodingForPackage(String packageName) throws Exception { - executeShellCommand("device_config put storage_native_boot transcode_compat_manifest " - + packageName + ",0"); + public static void enableTranscodingForPackage(String packageName) { + getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG); + try { + final String newPropertyValue = packageName + ",0"; + DeviceConfig.setProperty(NAMESPACE_STORAGE_NATIVE_BOOT, + TRANSCODE_COMPAT_MANIFEST_DEVICE_CONFIG_PROPERTY_NAME, newPropertyValue, + /* makeDefault */ false); + } finally { + getUiAutomation().dropShellPermissionIdentity(); + } SystemClock.sleep(1000); } @@ -186,8 +195,14 @@ public class TranscodeTestUtils { executeShellCommand(command); } - public static void disableTranscodingForAllPackages() throws IOException { - executeShellCommand("device_config delete storage_native_boot transcode_compat_manifest"); + public static void disableTranscodingForAllPackages() { + getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG); + try { + DeviceConfig.deleteProperty(NAMESPACE_STORAGE_NATIVE_BOOT, + TRANSCODE_COMPAT_MANIFEST_DEVICE_CONFIG_PROPERTY_NAME); + } finally { + getUiAutomation().dropShellPermissionIdentity(); + } SystemClock.sleep(1000); } @@ -486,4 +501,9 @@ public class TranscodeTestUtils { } return false; } + + @NonNull + private static UiAutomation getUiAutomation() { + return InstrumentationRegistry.getInstrumentation().getUiAutomation(); + } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java new file mode 100644 index 00000000000..da295d0074e --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.photopicker.cts; + +import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImage; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; +import static android.photopicker.cts.util.PhotoPickerUiUtils.TIMEOUT; +import static android.photopicker.cts.util.PhotoPickerUiUtils.findBannerActionButton; +import static android.photopicker.cts.util.PhotoPickerUiUtils.findBannerDismissButton; +import static android.photopicker.cts.util.PhotoPickerUiUtils.getBannerPrimaryText; +import static android.photopicker.cts.util.PhotoPickerUiUtils.isPhotoPickerVisible; +import static android.photopicker.cts.util.PhotoPickerUiUtils.verifySettingsActivityIsVisible; +import static android.provider.MediaStore.ACTION_PICK_IMAGES; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.photopicker.cts.cloudproviders.CloudProviderPrimary; + +import androidx.test.filters.SdkSuppress; +import androidx.test.uiautomator.UiObject; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Photo Picker Banner Tests for common flows. + */ +// TODO(b/195009187): Enabling the banners requires setting allowed_cloud_providers device config. +// We currently can't do this in R. +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) +public class PhotoPickerBannersTest extends PhotoPickerBaseTest { + + private static String sPreviouslyAllowedCloudProviders; + private Uri mLocalMediaFileUri; + + @BeforeClass + public static void setUpBeforeClass() { + // Store the current allowed cloud providers for reset at the end of tests. + sPreviouslyAllowedCloudProviders = PhotoPickerCloudUtils.getAllowedProvidersDeviceConfig(); + + // Override the allowed cloud providers config to enable the banners. + PhotoPickerCloudUtils.setAllowedProvidersDeviceConfig(sTargetPackageName); + } + + @AfterClass + public static void tearDownClass() { + // Reset the allowed cloud providers device config. + PhotoPickerCloudUtils.setAllowedProvidersDeviceConfig(sPreviouslyAllowedCloudProviders); + } + + @Before + public void setUp() throws Exception { + super.setUp(); + + setCloudProvider(mContext, /* authority */ null); + + // Create a local media file because if there's no media items for the picker grids, + // the recycler view gets hidden along with the banners. + mLocalMediaFileUri = createImage(mContext.getUserId(), /* isFavorite */ false).first; + } + + @After + public void tearDown() throws Exception { + if (!isHardwareSupported()) { + // No-op, skip tear down if hardware is not supported. + return; + } + + if (mActivity != null) { + mActivity.finish(); + } + + deleteMedia(mLocalMediaFileUri, mContext); + + setCloudProvider(mContext, /* authority */ null); + } + + @Test + public void testChooseAppBannerOnDismiss() throws Exception { + // 1. Setting up the 'Choose App' banner. + setCloudMediaInfoForChooseAppBanner(); + + // 2. Assert that the 'Choose App' banner is visible. + assertThat(getBannerPrimaryText()).isEqualTo("Choose cloud media app"); + + // 3. Click the banner 'Dismiss' button. + final UiObject dismissButton = findBannerDismissButton(); + dismissButton.click(); + + // 4. Assert that the Banner disappeared while the Picker is still visible. + assertWithMessage("Timed out waiting for the banner to disappear") + .that(dismissButton.waitUntilGone(TIMEOUT)) + .isTrue(); + assertThatPhotoPickerActivityIsVisible(); + } + + @Test + public void testChooseAppBannerOnActionButtonClick() throws Exception { + // 1. Setting up the 'Choose App' banner. + setCloudMediaInfoForChooseAppBanner(); + + // 2. Assert that the 'Choose App' banner is visible. + assertThat(getBannerPrimaryText()).isEqualTo("Choose cloud media app"); + + // 3. Click the banner 'Action' button. + findBannerActionButton().click(); + + // 4. Assert that Settings page is visible. + verifySettingsActivityIsVisible(); + sDevice.pressBack(); + } + + private void setCloudMediaInfoForChooseAppBanner() { + // 1. Set a non-null cloud provider and launch the photo picker. + setCloudProvider(mContext, CloudProviderPrimary.AUTHORITY); + launchPickerActivity(); + // 2. Close the photo picker. + mActivity.finish(); + // 3. Set the cloud provider as None and launch the photo picker. + setCloudProvider(mContext, /* authority */ null); + launchPickerActivity(); + } + + private void launchPickerActivity() { + final Intent intent = new Intent(ACTION_PICK_IMAGES); + mActivity.startActivity(intent); + assertThatPhotoPickerActivityIsVisible(); + } + + private void assertThatPhotoPickerActivityIsVisible() { + assertWithMessage("Timed out waiting for the photo picker activity to appear") + .that(isPhotoPickerVisible()) + .isTrue(); + } +} diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java index 12a10bb6f48..067cd7821d4 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java @@ -36,6 +36,8 @@ public class PhotoPickerBaseTest { public static int REQUEST_CODE = 42; private static final Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation(); + protected static final String sTargetPackageName = + sInstrumentation.getTargetContext().getPackageName(); protected static final UiDevice sDevice = UiDevice.getInstance(sInstrumentation); protected GetResultActivity mActivity; @@ -64,7 +66,7 @@ public class PhotoPickerBaseTest { sDevice.waitForIdle(); } - private static boolean isHardwareSupported() { + static boolean isHardwareSupported() { // These UI tests are not optimised for Watches, TVs, Auto; // IoT devices do not have a UI to run these UI tests PackageManager pm = sInstrumentation.getContext().getPackageManager(); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCloudUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCloudUtils.java index 96d3f933f03..59b552cdddf 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCloudUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCloudUtils.java @@ -16,18 +16,26 @@ package android.photopicker.cts; +import static android.Manifest.permission.READ_DEVICE_CONFIG; +import static android.Manifest.permission.WRITE_DEVICE_CONFIG; import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider; import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import android.app.UiAutomation; import android.content.ClipData; import android.content.Context; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.util.Pair; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject; @@ -36,6 +44,9 @@ import java.util.Collections; import java.util.List; public class PhotoPickerCloudUtils { + private static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; + private static final String ALLOWED_CLOUD_PROVIDERS_KEY = "allowed_cloud_providers"; + public static List<String> extractMediaIds(ClipData clipData, int minCount) { final int count = clipData.getItemCount(); assertThat(count).isAtLeast(minCount); @@ -97,4 +108,41 @@ public class PhotoPickerCloudUtils { assertThat(mediaIds).contains(contained); assertThat(mediaIds).containsNoneIn(Collections.singletonList(notContained)); } + + @Nullable + static String getAllowedProvidersDeviceConfig() { + getUiAutomation().adoptShellPermissionIdentity(READ_DEVICE_CONFIG); + try { + return DeviceConfig.getProperty(NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY); + } finally { + getUiAutomation().dropShellPermissionIdentity(); + } + } + + static void setAllowedProvidersDeviceConfig(@Nullable String allowedCloudProviders) { + getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG); + try { + if (allowedCloudProviders == null) { + DeviceConfig.deleteProperty(NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY); + assertWithMessage("Failed to delete the allowed cloud providers device config") + .that(getAllowedProvidersDeviceConfig()) + .isNull(); + } else { + DeviceConfig.setProperty(NAMESPACE_STORAGE_NATIVE_BOOT, ALLOWED_CLOUD_PROVIDERS_KEY, + allowedCloudProviders, /* makeDefault */ false); + assertWithMessage("Failed to update the allowed cloud providers device config") + .that(getAllowedProvidersDeviceConfig()) + .isEqualTo(allowedCloudProviders); + } + } finally { + getUiAutomation().dropShellPermissionIdentity(); + } + } + + @NonNull + private static UiAutomation getUiAutomation() { + return InstrumentationRegistry.getInstrumentation().getUiAutomation(); + } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java index f818c10baaf..d8cc456fe32 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java @@ -36,8 +36,6 @@ import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; -import java.io.IOException; - /** * Photo Picker tests for settings page launched from the overflow menu in PhotoPickerActivity or * the Settings app. @@ -47,37 +45,22 @@ import java.io.IOException; @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) public class PhotoPickerSettingsTest extends PhotoPickerBaseTest { - private static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; - private static final String ALLOWED_CLOUD_PROVIDERS_KEY = "allowed_cloud_providers"; - private static String sPreviouslyAllowedCloudProviders; @BeforeClass - public static void setUpBeforeClass() throws Exception { + public static void setUpBeforeClass() { // Store current allowed cloud providers for reset at the end of tests. - sPreviouslyAllowedCloudProviders = getAllowedProvidersDeviceConfig(); + sPreviouslyAllowedCloudProviders = PhotoPickerCloudUtils.getAllowedProvidersDeviceConfig(); // Enable Settings menu item in PhotoPickerActivity's overflow menu. - sDevice.executeShellCommand( - String.format("device_config put %s %s not_empty", NAMESPACE_STORAGE_NATIVE_BOOT, - ALLOWED_CLOUD_PROVIDERS_KEY)); - Assume.assumeTrue(!getAllowedProvidersDeviceConfig().isBlank()); + PhotoPickerCloudUtils.setAllowedProvidersDeviceConfig( + /* allowedCloudProviders */ "not_empty"); } @AfterClass - public static void tearDownClass() throws Exception { + public static void tearDownClass() { // Reset allowed cloud providers device config. - if (sPreviouslyAllowedCloudProviders == null - || sPreviouslyAllowedCloudProviders.isBlank()) { - // Delete the device config since `device_config put` does not support empty values. - sDevice.executeShellCommand( - String.format("device_config delete %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, - ALLOWED_CLOUD_PROVIDERS_KEY)); - } else { - sDevice.executeShellCommand( - String.format("device_config put %s %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, - ALLOWED_CLOUD_PROVIDERS_KEY, sPreviouslyAllowedCloudProviders)); - } + PhotoPickerCloudUtils.setAllowedProvidersDeviceConfig(sPreviouslyAllowedCloudProviders); } @Test @@ -93,16 +76,10 @@ public class PhotoPickerSettingsTest extends PhotoPickerBaseTest { PhotoPickerUiUtils.clickAndWait(sDevice, settingsMenuItem); // Verify PhotoPickerSettingsActivity is launched and visible. - verifySettingsActivityIsVisible(sDevice); + verifySettingsActivityIsVisible(); verifySettingsActionBarIsVisible(); verifySettingsTitleIsVisible(); verifySettingsDescriptionIsVisible(); verifySettingsFragmentContainerExists(); } - - private static String getAllowedProvidersDeviceConfig() throws IOException { - return sDevice.executeShellCommand( - String.format("device_config get %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, - ALLOWED_CLOUD_PROVIDERS_KEY)); - } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java b/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java index 5110781914f..216b5de1d1f 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java @@ -432,8 +432,7 @@ public class PickerProviderMediaGenerator { } albumId = bundle.getString(CloudMediaProviderContract.EXTRA_ALBUM_ID, null); - mimeType = bundle.getString(CloudMediaProviderContract.EXTRA_MIME_TYPE, - null); + mimeType = bundle.getString(Intent.EXTRA_MIME_TYPES, null); sizeBytes = bundle.getLong(CloudMediaProviderContract.EXTRA_SIZE_LIMIT_BYTES, 0); generation = bundle.getLong(CloudMediaProviderContract.EXTRA_SYNC_GENERATION, 0); } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java index 85b0e89fefd..855a0b93982 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java @@ -34,7 +34,7 @@ import java.util.List; public class PhotoPickerUiUtils { public static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS; - private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS; + public static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS; public static final String REGEX_PACKAGE_NAME = "com(.google)?.android.providers.media(.module)?"; @@ -158,7 +158,7 @@ public class PhotoPickerUiUtils { .isTrue(); } - public static void verifySettingsActivityIsVisible(UiDevice uiDevice) { + public static void verifySettingsActivityIsVisible() { // id/settings_activity_root is the root layout in activity_photo_picker_settings.xml assertWithMessage("Timed out waiting for settings activity to appear") .that(new UiObject(new UiSelector() @@ -171,4 +171,23 @@ public class PhotoPickerUiUtils { uiObject.click(); uiDevice.waitForIdle(); } + + public static String getBannerPrimaryText() throws Exception { + final UiObject bannerPrimaryText = new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/banner_primary_text")); + assertWithMessage("Timed out waiting for the banner to appear") + .that(bannerPrimaryText.waitForExists(TIMEOUT)) + .isTrue(); + return bannerPrimaryText.getText(); + } + + public static UiObject findBannerDismissButton() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/dismiss_button")); + } + + public static UiObject findBannerActionButton() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/action_button")); + } } diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java index e36d1ee0c20..64cc0f126bc 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java @@ -18,6 +18,7 @@ import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -90,9 +91,10 @@ public class AccessibilityFingerprintGestureTest { @Test public void testGestureDetectionListener_whenAuthenticationStartsAndStops_calledBack() { - if (!mFingerprintGestureController.isGestureDetectionAvailable()) { - return; - } + assumeTrue("Fingerprint gesture detection is not available", + mFingerprintGestureController.isGestureDetectionAvailable()); + assumeTrue("No enrolled fingerprints; cannot open fingerprint prompt", + mFingerprintManager.hasEnrolledFingerprints()); // Launch an activity to make sure we're in the foreground mActivityRule.launchActivity(null); mFingerprintGestureController.registerFingerprintGestureCallback( diff --git a/tests/app/Android.bp b/tests/app/Android.bp index 11bc2047c3f..a17e5104ff0 100644 --- a/tests/app/Android.bp +++ b/tests/app/Android.bp @@ -41,6 +41,7 @@ android_test { srcs: [ "src/**/*.java", "src/**/*.kt", + "app/src/android/app/stubs/RemoteActivity.java", "NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl", ], // Tag this module as a cts test artifact diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml index b78bb087c3f..7367ba98a2d 100644 --- a/tests/app/app/AndroidManifest.xml +++ b/tests/app/app/AndroidManifest.xml @@ -66,6 +66,7 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <application android:label="Android TestCase" android:icon="@drawable/size_48x48" @@ -591,6 +592,10 @@ </intent-filter> </receiver> + <activity android:name="android.app.stubs.RemoteActivity" + android:process=":remote" + android:excludeFromRecents="true" + android:exported="true" /> </application> </manifest> diff --git a/tests/app/app/src/android/app/stubs/RemoteActivity.java b/tests/app/app/src/android/app/stubs/RemoteActivity.java new file mode 100644 index 00000000000..0af1cbaf7cf --- /dev/null +++ b/tests/app/app/src/android/app/stubs/RemoteActivity.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.stubs; + +import android.app.Activity; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; + +/** + * An empty helper activity. + */ +public final class RemoteActivity extends Activity { + + /** Extras to the launching intent */ + public static final String EXTRA_CALLBACK = "callback"; + + private final IBinder mStub = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + switch (code) { + case IBinder.FIRST_CALL_TRANSACTION: + finish(); + return true; + default: + return false; + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent intent = getIntent(); + final IBinder callback = intent.getExtras().getBinder(EXTRA_CALLBACK); + final Parcel data = Parcel.obtain(); + try { + data.writeStrongBinder(mStub); + callback.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); + } catch (RemoteException e) { + } finally { + data.recycle(); + } + } +} diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java index e837f9a5f6f..3c69b092340 100644 --- a/tests/app/src/android/app/cts/ActivityManagerTest.java +++ b/tests/app/src/android/app/cts/ActivityManagerTest.java @@ -61,6 +61,7 @@ import android.app.stubs.CommandReceiver; import android.app.stubs.LocalForegroundService; import android.app.stubs.MockApplicationActivity; import android.app.stubs.MockService; +import android.app.stubs.RemoteActivity; import android.app.stubs.ScreenOnActivity; import android.app.stubs.TestHomeActivity; import android.app.stubs.TrimMemService; @@ -184,7 +185,7 @@ public class ActivityManagerTest { public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); mTargetContext = mInstrumentation.getTargetContext(); - mActivityManager = (ActivityManager) mInstrumentation.getContext() + mActivityManager = (ActivityManager) mTargetContext .getSystemService(Context.ACTIVITY_SERVICE); mPackageManager = mInstrumentation.getContext().getPackageManager(); mStartedActivityList = new ArrayList<Activity>(); @@ -192,6 +193,7 @@ public class ActivityManagerTest { mAppStandbyEnabled = AppStandbyUtils.isAppStandbyEnabled(); mAutomotiveDevice = mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); mLeanbackOnly = mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY); + toggleScreenOn(true); startSubActivity(ScreenOnActivity.class); drainOrderedBroadcastQueue(2); } @@ -2310,4 +2312,123 @@ public class ActivityManagerTest { return context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_TELEVISION); } + + @Test + public void testKillBackgroundProcess() throws Exception { + final String otherPackage = "com.android.app1"; + final ApplicationInfo ai1 = mTargetContext.getPackageManager() + .getApplicationInfo(otherPackage, 0); + final WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, Process.myUid(), + WAITFOR_MSEC); + final WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, ai1.uid, + WAITFOR_MSEC); + try { + launchHome(); + + // Since we're running instrumentation, our proc state will stay above FGS. + uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, + WatchUidRunner.STATE_FG_SERVICE); + + // Start an activity in another process in our package, our proc state will goto TOP. + final CountDownLatch remoteBinderDeathLatch1 = startRemoteActivityAndLinkToDeath( + new ComponentName(mTargetContext, RemoteActivity.class), + uid1Watcher); + + final CountDownLatch remoteBinderDeathLatch2 = startRemoteActivityAndLinkToDeath( + new ComponentName(otherPackage, STUB_PACKAGE_NAME + ".RemoteActivity"), + uid2Watcher); + + // Launch home again so our activity will be backgrounded. + launchHome(); + + // The uid goes back to FGS state, + // but the process with the remote activity should have been in the background. + uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, + WatchUidRunner.STATE_FG_SERVICE); + + // And the test package should be in background too. + uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, + WatchUidRunner.STATE_LAST); + + // Now, try to kill the background process of our own, it should succeed. + mActivityManager.killBackgroundProcesses(mTargetContext.getPackageName()); + + assertTrue("We should be able to kill our own process", + remoteBinderDeathLatch1.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS)); + + // Try to kill the background process of other app, it should fail. + mActivityManager.killBackgroundProcesses(otherPackage); + + assertFalse("We should be able to kill the processes of other package", + remoteBinderDeathLatch2.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS)); + + // Adopt the permission, we should be able to kill it now. + mInstrumentation.getUiAutomation().adoptShellPermissionIdentity( + android.Manifest.permission.FORCE_STOP_PACKAGES); + + mActivityManager.killBackgroundProcesses(otherPackage); + + assertTrue("We should be able to kill the processes of other package", + remoteBinderDeathLatch2.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS)); + } finally { + uid1Watcher.finish(); + uid2Watcher.finish(); + mInstrumentation.getUiAutomation().dropShellPermissionIdentity(); + finishAndRemoveTask(new ComponentName(mTargetContext, RemoteActivity.class)); + } + } + + private void finishAndRemoveTask(ComponentName activity) { + for (ActivityManager.AppTask task : mActivityManager.getAppTasks()) { + final ActivityManager.RecentTaskInfo info = task.getTaskInfo(); + if (info != null && activity.equals(info.topActivity)) { + task.finishAndRemoveTask(); + break; + } + } + } + + private CountDownLatch startRemoteActivityAndLinkToDeath(ComponentName activity, + WatchUidRunner uidWatcher) throws Exception { + final IBinder[] remoteBinderHolder = new IBinder[1]; + final CountDownLatch remoteBinderLatch = new CountDownLatch(1); + final IBinder binder = new Binder() { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + switch (code) { + case IBinder.FIRST_CALL_TRANSACTION: + remoteBinderHolder[0] = data.readStrongBinder(); + remoteBinderLatch.countDown(); + return true; + default: + return false; + } + } + }; + final CountDownLatch remoteBinderDeathLatch = new CountDownLatch(1); + final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + remoteBinderDeathLatch.countDown(); + } + }; + final Intent intent = new Intent(); + intent.setComponent(activity); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final Bundle extras = new Bundle(); + extras.putBinder(RemoteActivity.EXTRA_CALLBACK, binder); + intent.putExtras(extras); + mTargetContext.startActivity(intent); + + uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP); + assertTrue("Failed to receive the callback from remote activity", + remoteBinderLatch.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS)); + assertNotNull(remoteBinderHolder[0]); + remoteBinderHolder[0].linkToDeath(recipient, 0); + + // Sleep a while to let things go through. + Thread.sleep(WAIT_TIME); + return remoteBinderDeathLatch; + } } diff --git a/tests/app/src/android/app/cts/NotificationTemplateTest.kt b/tests/app/src/android/app/cts/NotificationTemplateTest.kt index cd1de26dd66..f654724b78f 100644 --- a/tests/app/src/android/app/cts/NotificationTemplateTest.kt +++ b/tests/app/src/android/app/cts/NotificationTemplateTest.kt @@ -300,9 +300,6 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { checkViews(builder.createBigContentView()) { val pictureView = requireViewByIdName<ImageView>("big_picture") assertThat(pictureView.visibility).isEqualTo(View.VISIBLE) - assertThat(pictureView.width.toFloat()) - .isWithin(1f) - .of((pictureView.height * 4 / 3).toFloat()) assertThat(pictureView.scaleType).isEqualTo(ImageView.ScaleType.CENTER_CROP) } } diff --git a/tests/autofillservice/res/layout/scrollable_login_activity.xml b/tests/autofillservice/res/layout/scrollable_login_activity.xml new file mode 100644 index 00000000000..dfa5227a458 --- /dev/null +++ b/tests/autofillservice/res/layout/scrollable_login_activity.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:focusable="true" + android:focusableInTouchMode="true" + android:orientation="vertical" > + + <ScrollView android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <include layout="@layout/login_activity" /> + + </LinearLayout> + + </ScrollView> + +</LinearLayout> diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java index 6dd4d1ae033..381eefb0a6c 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java +++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java @@ -543,10 +543,9 @@ public class SessionLifecycleTest extends AutoFillServiceTestCase.ManualActivity // It works fine for portrait but for the platforms that the default orientation // is landscape, e.g. automotive. Depending on the height of the IME, the ID_LOGIN // button may not be visible. - // In order to avoid that, - // generate back key event to hide IME before pressing ID_LOGIN button. - mUiBot.pressBack(); + // In order to avoid that, scroll until the ID_LOGIN button appears. + mUiBot.scrollToTextObject(ID_LOGIN); mUiBot.selectByRelativeId(ID_LOGIN); mUiBot.assertSaveShowing(SAVE_DATA_TYPE_USERNAME); diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/OutOfProcessLoginActivity.java index 4cab12cf18e..ca910901bee 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/activities/OutOfProcessLoginActivity.java +++ b/tests/autofillservice/src/android/autofillservice/cts/activities/OutOfProcessLoginActivity.java @@ -41,7 +41,7 @@ public class OutOfProcessLoginActivity extends Activity { Log.i(TAG, "onCreate(" + savedInstanceState + ")"); super.onCreate(savedInstanceState); - setContentView(R.layout.login_activity); + setContentView(R.layout.scrollable_login_activity); findViewById(R.id.login).setOnClickListener((v) -> finish()); diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java index 4ee066f268f..70f0c062e51 100644 --- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java @@ -40,8 +40,8 @@ import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.RECORD; import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.VGA; import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.YUV; -import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; @@ -78,7 +78,6 @@ import android.media.CamcorderProfile; import android.media.Image; import android.media.ImageReader; import android.media.ImageWriter; -import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.util.Size; @@ -95,12 +94,11 @@ import org.junit.runners.Parameterized; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; -import java.util.Iterator; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; /** * Tests exercising edge cases in camera setup, configuration, and usage. @@ -217,28 +215,6 @@ public class RobustnessTest extends Camera2AndroidTestCase { * Test for making sure the mandatory stream combinations work as expected. */ private void testMandatoryOutputCombinations(boolean maxResolution) throws Exception { - final int AVAILABILITY_TIMEOUT_MS = 10; - final LinkedBlockingQueue<Pair<String, String>> unavailablePhysicalCamEventQueue = - new LinkedBlockingQueue<>(); - CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() { - @Override - public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) { - unavailablePhysicalCamEventQueue.offer(new Pair<>(cameraId, physicalCameraId)); - } - }; - - mCameraManager.registerAvailabilityCallback(ac, mHandler); - Set<Pair<String, String>> unavailablePhysicalCameras = new HashSet<Pair<String, String>>(); - Pair<String, String> candidatePhysicalIds = - unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, - java.util.concurrent.TimeUnit.MILLISECONDS); - while (candidatePhysicalIds != null) { - unavailablePhysicalCameras.add(candidatePhysicalIds); - candidatePhysicalIds = - unavailablePhysicalCamEventQueue.poll(AVAILABILITY_TIMEOUT_MS, - java.util.concurrent.TimeUnit.MILLISECONDS); - } - mCameraManager.unregisterAvailabilityCallback(ac); CameraCharacteristics.Key<MandatoryStreamCombination []> ck = CameraCharacteristics.SCALER_MANDATORY_STREAM_COMBINATIONS; @@ -276,22 +252,10 @@ public class RobustnessTest extends Camera2AndroidTestCase { if (mStaticInfo.isLogicalMultiCamera()) { Set<String> physicalCameraIds = mStaticInfo.getCharacteristics().getPhysicalCameraIds(); - boolean skipTest = false; for (String physicalId : physicalCameraIds) { if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) { // If physicalId is advertised in camera ID list, do not need to test // its stream combination through logical camera. - skipTest = true; - } - for (Pair<String, String> unavailPhysicalCam : unavailablePhysicalCameras) { - if (unavailPhysicalCam.first.equals(id) || - unavailPhysicalCam.second.equals(physicalId)) { - // This particular physical camera isn't available. Skip. - skipTest = true; - break; - } - } - if (skipTest) { continue; } StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId); diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml index 2e0a212502e..de97acc76b7 100755 --- a/tests/framework/base/windowmanager/app/AndroidManifest.xml +++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml @@ -301,7 +301,7 @@ android:exported="true"/> <activity android:name=".ShowWhenLockedDialogActivity" android:exported="true" - android:theme="@android:style/Theme.Material.Dialog"/> + android:theme="@style/ShowWhenLockedDialogTheme"/> <activity android:name=".ShowWhenLockedTranslucentActivity" android:exported="true" android:theme="@android:style/Theme.Translucent"/> @@ -332,10 +332,11 @@ android:exported="true" android:configChanges="uiMode"/> <activity android:name=".FontScaleActivity" + android:configChanges="screenSize|screenLayout" android:exported="true"/> <activity android:name=".FontScaleNoRelaunchActivity" android:exported="true" - android:configChanges="fontScale"/> + android:configChanges="fontScale|screenSize|screenLayout"/> <activity android:name=".DisplayAccessCheckEmbeddingActivity" android:allowEmbedded="true" android:exported="true"/> diff --git a/tests/framework/base/windowmanager/app/res/values-watch/styles.xml b/tests/framework/base/windowmanager/app/res/values-watch/styles.xml new file mode 100644 index 00000000000..14da3216ffc --- /dev/null +++ b/tests/framework/base/windowmanager/app/res/values-watch/styles.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<resources> + <style name="ShowWhenLockedDialogTheme" parent="@android:style/Theme.Material.Dialog"> + <!-- Wear dialog theme has been converted to non-floating, but this breaks occluding --> + <item name="@android:windowIsFloating">true</item> + <!-- Likewise, for the purpose of CTS, make this non-swipeable, as there are visibility + implications there as well --> + <item name="@android:windowSwipeToDismiss">false</item> + </style> +</resources> diff --git a/tests/framework/base/windowmanager/app/res/values/styles.xml b/tests/framework/base/windowmanager/app/res/values/styles.xml index 43f60ebe9a7..d0e35c0c053 100644 --- a/tests/framework/base/windowmanager/app/res/values/styles.xml +++ b/tests/framework/base/windowmanager/app/res/values/styles.xml @@ -101,4 +101,7 @@ <item name="android:windowLayoutInDisplayCutoutMode">always</item> <item name="android:windowSoftInputMode">stateHidden</item> </style> + <style name="ShowWhenLockedDialogTheme" parent="@android:style/Theme.Material.Dialog"> + <!-- no-op except on Wear, where dialog theme has been converted to non-floating --> + </style> </resources> diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java index c2752e6241e..4326f7d7ccc 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java @@ -39,6 +39,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; import android.graphics.Rect; +import android.server.wm.IgnoreOrientationRequestSession; import android.server.wm.jetpack.utils.TestActivity; import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity; import android.server.wm.jetpack.utils.TestValueCountJavaConsumer; @@ -167,56 +168,66 @@ public class ExtensionWindowLayoutComponentTest extends WindowManagerJetpackTest @Test public void testGetWindowLayoutInfo_configChanged_windowLayoutUpdates() - throws ExecutionException, InterruptedException, TimeoutException { + throws InterruptedException { mWindowLayoutInfo = getExtensionWindowLayoutInfo(mActivity); assumeHasDisplayFeatures(mWindowLayoutInfo); - TestConfigChangeHandlingActivity configHandlingActivity + final TestConfigChangeHandlingActivity configHandlingActivity = (TestConfigChangeHandlingActivity) startActivityNewTask( TestConfigChangeHandlingActivity.class); - setActivityOrientationActivityHandlesOrientationChanges(configHandlingActivity, - ORIENTATION_PORTRAIT); - final WindowLayoutInfo portraitWindowLayoutInfo = getExtensionWindowLayoutInfo( - configHandlingActivity); - final Rect portraitBounds = getActivityBounds(configHandlingActivity); - final Rect portraitMaximumBounds = getMaximumActivityBounds(configHandlingActivity); - - setActivityOrientationActivityHandlesOrientationChanges(configHandlingActivity, - ORIENTATION_LANDSCAPE); - final WindowLayoutInfo landscapeWindowLayoutInfo = getExtensionWindowLayoutInfo( - configHandlingActivity); - final Rect landscapeBounds = getActivityBounds(configHandlingActivity); - final Rect landscapeMaximumBounds = getMaximumActivityBounds(configHandlingActivity); - - final boolean doesDisplayRotateForOrientation = doesDisplayRotateForOrientation( - portraitMaximumBounds, landscapeMaximumBounds); - assertEqualWindowLayoutInfo(portraitWindowLayoutInfo, landscapeWindowLayoutInfo, - portraitBounds, landscapeBounds, doesDisplayRotateForOrientation); + try (IgnoreOrientationRequestSession session = + new IgnoreOrientationRequestSession(false /* enable */)) { + setActivityOrientationActivityHandlesOrientationChanges(configHandlingActivity, + ORIENTATION_PORTRAIT); + final WindowLayoutInfo portraitWindowLayoutInfo = getExtensionWindowLayoutInfo( + configHandlingActivity); + final Rect portraitBounds = getActivityBounds(configHandlingActivity); + final Rect portraitMaximumBounds = getMaximumActivityBounds(configHandlingActivity); + + setActivityOrientationActivityHandlesOrientationChanges(configHandlingActivity, + ORIENTATION_LANDSCAPE); + final WindowLayoutInfo landscapeWindowLayoutInfo = getExtensionWindowLayoutInfo( + configHandlingActivity); + final Rect landscapeBounds = getActivityBounds(configHandlingActivity); + final Rect landscapeMaximumBounds = getMaximumActivityBounds(configHandlingActivity); + + final boolean doesDisplayRotateForOrientation = doesDisplayRotateForOrientation( + portraitMaximumBounds, landscapeMaximumBounds); + assertTrue(doesDisplayRotateForOrientation); + assertEqualWindowLayoutInfo(portraitWindowLayoutInfo, landscapeWindowLayoutInfo, + portraitBounds, landscapeBounds, doesDisplayRotateForOrientation); + } } @Test public void testGetWindowLayoutInfo_windowRecreated_windowLayoutUpdates() - throws ExecutionException, InterruptedException, TimeoutException { - mWindowLayoutInfo = getExtensionWindowLayoutInfo(mActivity); - assumeHasDisplayFeatures(mWindowLayoutInfo); - - setActivityOrientationActivityDoesNotHandleOrientationChanges(mActivity, - ORIENTATION_PORTRAIT); - final WindowLayoutInfo portraitWindowLayoutInfo = getExtensionWindowLayoutInfo(mActivity); - final Rect portraitBounds = getActivityBounds(mActivity); - final Rect portraitMaximumBounds = getMaximumActivityBounds(mActivity); - - setActivityOrientationActivityDoesNotHandleOrientationChanges(mActivity, - ORIENTATION_LANDSCAPE); - final WindowLayoutInfo landscapeWindowLayoutInfo = getExtensionWindowLayoutInfo(mActivity); - final Rect landscapeBounds = getActivityBounds(mActivity); - final Rect landscapeMaximumBounds = getMaximumActivityBounds(mActivity); - - final boolean doesDisplayRotateForOrientation = doesDisplayRotateForOrientation( - portraitMaximumBounds, landscapeMaximumBounds); - assertEqualWindowLayoutInfo(portraitWindowLayoutInfo, landscapeWindowLayoutInfo, - portraitBounds, landscapeBounds, doesDisplayRotateForOrientation); + throws InterruptedException { + try (IgnoreOrientationRequestSession session = + new IgnoreOrientationRequestSession(false /* enable */)) { + mWindowLayoutInfo = getExtensionWindowLayoutInfo(mActivity); + assumeHasDisplayFeatures(mWindowLayoutInfo); + + setActivityOrientationActivityDoesNotHandleOrientationChanges(mActivity, + ORIENTATION_PORTRAIT); + final WindowLayoutInfo portraitWindowLayoutInfo = + getExtensionWindowLayoutInfo(mActivity); + final Rect portraitBounds = getActivityBounds(mActivity); + final Rect portraitMaximumBounds = getMaximumActivityBounds(mActivity); + + setActivityOrientationActivityDoesNotHandleOrientationChanges(mActivity, + ORIENTATION_LANDSCAPE); + final WindowLayoutInfo landscapeWindowLayoutInfo = + getExtensionWindowLayoutInfo(mActivity); + final Rect landscapeBounds = getActivityBounds(mActivity); + final Rect landscapeMaximumBounds = getMaximumActivityBounds(mActivity); + + final boolean doesDisplayRotateForOrientation = doesDisplayRotateForOrientation( + portraitMaximumBounds, landscapeMaximumBounds); + assertTrue(doesDisplayRotateForOrientation); + assertEqualWindowLayoutInfo(portraitWindowLayoutInfo, landscapeWindowLayoutInfo, + portraitBounds, landscapeBounds, doesDisplayRotateForOrientation); + } } /** diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java index f7b352f8e99..143f7c00e36 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java @@ -16,9 +16,6 @@ package android.server.wm.jetpack.utils; -import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds; -import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getMaximumActivityBounds; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -38,8 +35,6 @@ import androidx.window.extensions.layout.FoldingFeature; import androidx.window.extensions.layout.WindowLayoutComponent; import androidx.window.extensions.layout.WindowLayoutInfo; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -109,7 +104,7 @@ public class ExtensionUtil { @Nullable public static WindowLayoutInfo getExtensionWindowLayoutInfo(Activity activity) - throws ExecutionException, InterruptedException, TimeoutException { + throws InterruptedException { WindowLayoutComponent windowLayoutComponent = getExtensionWindowLayoutComponent(); if (windowLayoutComponent == null) { return null; @@ -117,7 +112,14 @@ public class ExtensionUtil { TestValueCountJavaConsumer<WindowLayoutInfo> windowLayoutInfoConsumer = new TestValueCountJavaConsumer<>(); windowLayoutComponent.addWindowLayoutInfoListener(activity, windowLayoutInfoConsumer); - return windowLayoutInfoConsumer.waitAndGet(); + WindowLayoutInfo info = windowLayoutInfoConsumer.waitAndGet(); + + // The default implementation only allows a single listener per activity. Since we are using + // a local windowLayoutInfoConsumer within this function, we must remember to clean up. + // Otherwise, subsequent calls to addWindowLayoutInfoListener with the same activity will + // fail to have its callback registered. + windowLayoutComponent.removeWindowLayoutInfoListener(windowLayoutInfoConsumer); + return info; } @NonNull diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java index 2d4260ccdd5..d47cc6d76db 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java @@ -16,6 +16,7 @@ package android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.server.wm.ActivityTransitionTests.EdgeExtensionActivity.BOTTOM; import static android.server.wm.ActivityTransitionTests.EdgeExtensionActivity.DIRECTION_KEY; @@ -114,7 +115,9 @@ public class ActivityTransitionTests extends ActivityManagerTestBase { final Intent intent = new Intent(mContext, LauncherActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - return (LauncherActivity) instrumentation.startActivitySync(intent); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + return (LauncherActivity) instrumentation.startActivitySync(intent, options.toBundle()); } @Test diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java index 79bbe9c5ffa..a975e5a8680 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java @@ -65,7 +65,6 @@ import org.junit.Test; public class AssistantStackTests extends ActivityManagerTestBase { private int mAssistantDisplayId = DEFAULT_DISPLAY; - private int mDefaultWindowingMode; public void setUp() throws Exception { super.setUp(); @@ -76,7 +75,6 @@ public class AssistantStackTests extends ActivityManagerTestBase { WindowManagerState.Task assistantStack = mWmState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT); mAssistantDisplayId = assistantStack.mDisplayId; - mDefaultWindowingMode = getDefaultDisplayWindowingMode(); } } @@ -92,7 +90,7 @@ public class AssistantStackTests extends ActivityManagerTestBase { // Ensure that the activity launched in the fullscreen assistant stack assertAssistantStackExists(); // In a multi-window environment the assistant might not be fullscreen - assumeTrue(mDefaultWindowingMode == WINDOWING_MODE_FULLSCREEN); + assumeTrue(getDefaultDisplayWindowingMode() == WINDOWING_MODE_FULLSCREEN); assertTrue("Expected assistant stack to be fullscreen", mWmState.getRootTaskByActivityType( ACTIVITY_TYPE_ASSISTANT).isFullscreen()); @@ -134,7 +132,7 @@ public class AssistantStackTests extends ActivityManagerTestBase { @Test public void testAssistantStackLaunchNewTask() throws Exception { - assertAssistantStackCanLaunchAndReturnFromNewTask(mDefaultWindowingMode); + assertAssistantStackCanLaunchAndReturnFromNewTask(); } @Test @@ -151,7 +149,7 @@ public class AssistantStackTests extends ActivityManagerTestBase { //assertAssistantStackCanLaunchAndReturnFromNewTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); } - private void assertAssistantStackCanLaunchAndReturnFromNewTask(int expectedWindowingMode) + private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception { // Enable the assistant and launch an assistant activity which will launch a new task try (final AssistantSession assistantSession = new AssistantSession()) { @@ -161,8 +159,7 @@ public class AssistantStackTests extends ActivityManagerTestBase { extraString(EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY)), extraString(EXTRA_ASSISTANT_DISPLAY_ID, Integer.toString(mAssistantDisplayId))); // Ensure that the fullscreen stack is on top and the test activity is now visible - waitForValidStateWithActivityTypeAndWindowingMode( - TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, expectedWindowingMode); + waitForValidStateWithActivityType(TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD); } if (isAssistantOnTopOfDream()) { @@ -175,11 +172,13 @@ public class AssistantStackTests extends ActivityManagerTestBase { mWmState.assertFocusedRootTask("Assistant stack should be focused.", WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT); } else { + final int testActivityWindowingMode = + mWmState.getTaskDisplayArea(TEST_ACTIVITY).getWindowingMode(); mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY); mWmState.assertFrontStack("TestActivity stack should be on top.", - expectedWindowingMode, ACTIVITY_TYPE_STANDARD); + testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); mWmState.assertFocusedRootTask("TestActivity stack should be focused.", - expectedWindowingMode, ACTIVITY_TYPE_STANDARD); + testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); } // Now, tell it to finish itself and ensure that the assistant stack is brought back forward @@ -211,15 +210,17 @@ public class AssistantStackTests extends ActivityManagerTestBase { mWmState.waitFor((amState) -> !amState.containsActivity(ASSISTANT_ACTIVITY), getActivityName(ASSISTANT_ACTIVITY) + " finished"); } - waitForValidStateWithActivityTypeAndWindowingMode( - TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode); + waitForValidStateWithActivityType(TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD); waitAndAssertTopResumedActivity(TEST_ACTIVITY, mAssistantDisplayId, "TestActivity should be resumed"); mWmState.assertFocusedActivity("TestActivity should be focused", TEST_ACTIVITY); - mWmState.assertFrontStack("Fullscreen stack should be on top.", - mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD); - mWmState.assertFocusedRootTask("Fullscreen stack should be focused.", - mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD); + + final int testActivityWindowingMode = + mWmState.getTaskDisplayArea(TEST_ACTIVITY).getWindowingMode(); + mWmState.assertFrontStack("TestActivity stack should be on top.", + testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); + mWmState.assertFocusedRootTask("TestActivity stack should be focused.", + testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); } @Test @@ -273,8 +274,7 @@ public class AssistantStackTests extends ActivityManagerTestBase { launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK, extraString(EXTRA_ASSISTANT_IS_TRANSLUCENT, "true"), extraString(EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY))); - waitForValidStateWithActivityTypeAndWindowingMode( - TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode); + waitForValidStateWithActivityType(TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD); final ComponentName homeActivity = mWmState.getHomeActivityName(); int windowingMode = mWmState.getFocusedRootTaskWindowingMode(); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java index c1396d6e5a0..6109decbffe 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java @@ -253,17 +253,23 @@ public class ConfigChangeTests extends ActivityManagerTestBase { separateTestJournal(); fontScaleSession.set(fontScale); mWmState.computeState(activityName); - assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1); + // The number of config changes could be greater than expected as there may have + // other configuration change events triggered after font scale changed, such as + // NavigationBar recreated. + new ActivityLifecycleCounts(activityName).assertCountWithRetry( + "relaunch or config changed", + countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, relaunch ? 1 : 0), + countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, relaunch ? 1 : 0), + countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, + CountSpec.GREATER_THAN_OR_EQUALS, relaunch ? 0 : 1)); // Verify that the display metrics are updated, and therefore the text size is also // updated accordingly. final Bundle changedExtras = TestJournalContainer.get(activityName).extras; + final float scale = fontScale; waitForOrFail("reported fontPixelSize from " + activityName, - () -> changedExtras.containsKey(EXTRA_FONT_PIXEL_SIZE)); - final int expectedFontPixelSize = - scaledPixelsToPixels(EXPECTED_FONT_SIZE_SP, fontScale, densityDpi); - assertEquals("Expected font pixel size should match", expectedFontPixelSize, - changedExtras.getInt(EXTRA_FONT_PIXEL_SIZE)); + () -> scaledPixelsToPixels(EXPECTED_FONT_SIZE_SP, scale, densityDpi) + == changedExtras.getInt(EXTRA_FONT_PIXEL_SIZE)); } } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java index ac06678d80b..713958d94a4 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java @@ -28,6 +28,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import android.app.Activity; +import android.content.res.Resources; +import android.content.pm.PackageManager; import android.graphics.Insets; import android.os.Bundle; import android.view.View; @@ -55,6 +57,7 @@ public class ForceRelayoutTestBase { assertNotNull("test setup failed", activity.mLastContentInsets); assumeFalse(Insets.NONE.equals(activity.mLastContentInsets.getInsetsIgnoringVisibility( statusBars() | navigationBars()))); + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { activity.mLayoutHappened = false; @@ -113,4 +116,15 @@ public class ForceRelayoutTestBase { setContentView(view); } } + + private static boolean remoteInsetsControllerControlsSystemBars() { + return InstrumentationRegistry.getInstrumentation().getTargetContext().getResources() + .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars); + } + + private boolean isCar() { + PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext() + .getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeepClearRectsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeepClearRectsTests.java index 55eae8c3585..81173bbe767 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/KeepClearRectsTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/KeepClearRectsTests.java @@ -361,6 +361,7 @@ public class KeepClearRectsTests extends WindowManagerTestBase { final Rect keepClearRect = new Rect(0, 0, 25, 25); final View v = createTestViewInActivity(activity, keepClearRect); + final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true)); assertSameElementsEventually(Arrays.asList(keepClearRect), () -> getKeepClearRectsForActivity(activity)); @@ -372,15 +373,16 @@ public class KeepClearRectsTests extends WindowManagerTestBase { assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> getKeepClearRectsForActivity(activity)); - final List<Rect> expectedRectsInScreenSpace = - getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, activity.getComponentName()); - assertSameElementsEventually(expectedRectsInScreenSpace, + final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); + expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); + expectedRectsOnDisplay.addAll( + getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, activity.getComponentName())); + assertSameElementsEventually(expectedRectsOnDisplay, () -> getKeepClearRectsOnDefaultDisplay()); activity.finishAndRemoveTask(); - assertTrue(Collections.disjoint( - expectedRectsInScreenSpace, - getKeepClearRectsOnDefaultDisplay())); + assertSameElementsEventually(prevKeepClearRectsOnDisplay, + () -> getKeepClearRectsOnDefaultDisplay()); } @Test @@ -419,6 +421,7 @@ public class KeepClearRectsTests extends WindowManagerTestBase { final Rect viewBounds = new Rect(0, 0, 25, 25); final View v1 = createTestViewInActivity(activity1, viewBounds); + final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true)); assertSameElementsEventually(Arrays.asList(viewBounds), () -> getKeepClearRectsForActivity(activity1)); @@ -438,8 +441,12 @@ public class KeepClearRectsTests extends WindowManagerTestBase { mWmState.assertVisibility(activity2.getComponentName(), true); // Since both activities are fullscreen, WM only takes the keep clear areas from the top one - assertSameElementsEventually(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, - activity2.getComponentName()), () -> getKeepClearRectsOnDefaultDisplay()); + final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); + expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); + expectedRectsOnDisplay.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, + activity2.getComponentName())); + assertSameElementsEventually(expectedRectsOnDisplay, + () -> getKeepClearRectsOnDefaultDisplay()); } @Test @@ -449,15 +456,24 @@ public class KeepClearRectsTests extends WindowManagerTestBase { translucentTestSession.launchTestActivityOnDisplaySync( TranslucentTestActivity.class, DEFAULT_DISPLAY); final TestActivity activity1 = translucentTestSession.getActivity(); - final Rect viewBounds = new Rect(0, 0, 25, 25); final View v1 = createTestViewInActivity(activity1, viewBounds); + final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); translucentTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true)); - assertSameElementsEventually(getRectsInScreenSpace(Arrays.asList(viewBounds), - activity1.getComponentName()), () -> getKeepClearRectsOnDefaultDisplay()); + // Add keep-clear rects in the activity + final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>(); + expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); + expectedRectsOnDisplay.addAll(getRectsInScreenSpace(Arrays.asList(viewBounds), + activity1.getComponentName())); + assertSameElementsEventually(expectedRectsOnDisplay, + () -> getKeepClearRectsOnDefaultDisplay()); + + // Start an opaque activity on top mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); final TestActivity activity2 = mTestSession.getActivity(); + + // Add keep-clear rects in the opaque activity final View v2 = createTestViewInActivity(activity2); mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS)); assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, @@ -466,8 +482,13 @@ public class KeepClearRectsTests extends WindowManagerTestBase { mWmState.waitAndAssertVisibilityGone(activity1.getComponentName()); mWmState.assertVisibility(activity2.getComponentName(), true); - assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, - () -> getKeepClearRectsForActivity(activity2)); + // Only the opaque activity's keep-clear areas should be reported on the display + expectedRectsOnDisplay.clear(); + expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay); + expectedRectsOnDisplay.addAll(getRectsInScreenSpace( + TEST_KEEP_CLEAR_RECTS, activity2.getComponentName())); + assertSameElementsEventually(expectedRectsOnDisplay, + () -> getKeepClearRectsOnDefaultDisplay()); } @Test @@ -475,30 +496,15 @@ public class KeepClearRectsTests extends WindowManagerTestBase { assumeTrue("Skipping test: no split multi-window support", supportsSplitScreenMultiWindow()); - final LaunchActivityBuilder activityBuilder1 = getLaunchActivityBuilder() - .setUseInstrumentation() - .setIntentExtra(extra -> { - extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, - new ArrayList(TEST_KEEP_CLEAR_RECTS)); - }) - .setTargetActivity(KEEP_CLEAR_RECTS_ACTIVITY); + startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY, + KEEP_CLEAR_RECTS_ACTIVITY2, Collections.emptyList(), Collections.emptyList()); + final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay(); - final LaunchActivityBuilder activityBuilder2 = getLaunchActivityBuilder() - .setUseInstrumentation() - .setIntentExtra(extra -> { - extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, - new ArrayList(TEST_KEEP_CLEAR_RECTS_2)); - }) - .setTargetActivity(KEEP_CLEAR_RECTS_ACTIVITY2); + removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY).mTaskId); + removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY2).mTaskId); - launchActivitiesInSplitScreen(activityBuilder1, activityBuilder2); - - waitAndAssertResumedActivity(KEEP_CLEAR_RECTS_ACTIVITY, KEEP_CLEAR_RECTS_ACTIVITY - + " must be resumed"); - waitAndAssertResumedActivity(KEEP_CLEAR_RECTS_ACTIVITY2, KEEP_CLEAR_RECTS_ACTIVITY2 - + " must be resumed"); - mWmState.assertVisibility(KEEP_CLEAR_RECTS_ACTIVITY, true); - mWmState.assertVisibility(KEEP_CLEAR_RECTS_ACTIVITY2, true); + startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY, + KEEP_CLEAR_RECTS_ACTIVITY2, TEST_KEEP_CLEAR_RECTS, TEST_KEEP_CLEAR_RECTS_2); assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY)); @@ -506,11 +512,38 @@ public class KeepClearRectsTests extends WindowManagerTestBase { () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY2)); final List<Rect> expected = new ArrayList(); + expected.addAll(prevKeepClearRectsOnDisplay); expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, KEEP_CLEAR_RECTS_ACTIVITY)); expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS_2, KEEP_CLEAR_RECTS_ACTIVITY2)); assertSameElementsEventually(expected, () -> getKeepClearRectsOnDefaultDisplay()); } + private void startKeepClearActivitiesInSplitscreen(ComponentName activity1, + ComponentName activity2, List<Rect> keepClearRects1, List<Rect> keepClearRects2) { + final LaunchActivityBuilder activityBuilder1 = getLaunchActivityBuilder() + .setUseInstrumentation() + .setTargetActivity(activity1) + .setIntentExtra(extra -> { + extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, + new ArrayList(keepClearRects1)); + }); + + final LaunchActivityBuilder activityBuilder2 = getLaunchActivityBuilder() + .setUseInstrumentation() + .setTargetActivity(activity2) + .setIntentExtra(extra -> { + extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS, + new ArrayList(keepClearRects2)); + }); + + launchActivitiesInSplitScreen(activityBuilder1, activityBuilder2); + + waitAndAssertResumedActivity(activity1, activity1 + " must be resumed"); + waitAndAssertResumedActivity(activity2, activity2 + " must be resumed"); + mWmState.assertVisibility(activity1, true); + mWmState.assertVisibility(activity2, true); + } + @Test public void testUnrestrictedKeepClearRects() throws Exception { mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY); @@ -547,6 +580,7 @@ public class KeepClearRectsTests extends WindowManagerTestBase { mTestSession.runOnMainSyncAndWait(() -> { activity.addView(newView, params); }); + waitForIdle(); return newView; } @@ -607,7 +641,8 @@ public class KeepClearRectsTests extends WindowManagerTestBase { private static <T> void assertSameElementsEventually(List<T> expected, Callable<List<T>> actual) throws Exception { - PollingCheck.check("Lists do not have the same elements.", + PollingCheck.check("Lists do not have the same elements." + + "Expected=" + expected + ", actual=" + actual.call(), SAME_ELEMENT_ASSERTION_TIMEOUT, () -> hasSameElements(expected, actual.call())); } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java index c39aaa2ced6..cbc40261c66 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/PrivacyIndicatorBoundsTests.java @@ -89,6 +89,7 @@ public class PrivacyIndicatorBoundsTests extends ActivityManagerTestBase { // TODO(b/187757919): Allow Automotive to skip this test until privacy chip is implemented // in immersive mode assumeFalse(isCar()); + assumeFalse(isWatch()); final PrivacyIndicatorBoundsTests.TestActivity activity = mTestActivity.launchActivity( new Intent().putExtra(EXTRA_ORIENTATION, orientation)); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SnapshotTaskTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SnapshotTaskTests.java index 59a1a2bcccd..198e4b344c5 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/SnapshotTaskTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/SnapshotTaskTests.java @@ -17,6 +17,7 @@ package android.server.wm; import static android.server.wm.WindowManagerTestBase.startActivity; +import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -85,6 +86,9 @@ public class SnapshotTaskTests extends ActivityManagerTestBase { @Test public void testSetDisablePreviewScreenshots() throws Exception { + final View decor = mActivity.getWindow().getDecorView(); + final int captionBarHeight = decor.getRootWindowInsets().getInsets(captionBar()).top; + BitmapPixelChecker pixelChecker = new BitmapPixelChecker(PixelColor.RED); int retries = 0; @@ -93,7 +97,8 @@ public class SnapshotTaskTests extends ActivityManagerTestBase { Bitmap bitmap = mWindowManager.snapshotTaskForRecents(mActivity.getTaskId()); if (bitmap != null) { int expectedMatching = - bitmap.getWidth() * bitmap.getHeight() - MATCHING_PIXEL_MISMATCH_ALLOWED; + bitmap.getWidth() * bitmap.getHeight() - MATCHING_PIXEL_MISMATCH_ALLOWED + - (captionBarHeight * decor.getWidth()); Rect boundToCheck = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); int matchingPixels = pixelChecker.getNumMatchingPixels(bitmap, boundToCheck); matchesPixels = matchingPixels >= expectedMatching; diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java index cfbf89edbc8..87c683e930c 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java @@ -16,6 +16,7 @@ package android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.server.wm.MockImeHelper.createManagedMockImeSession; import static android.view.SurfaceControlViewHost.SurfacePackage; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -32,6 +33,7 @@ import static org.junit.Assume.assumeTrue; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.Instrumentation; import android.app.UiAutomation; import android.content.ComponentName; @@ -66,6 +68,7 @@ import android.widget.FrameLayout; import android.widget.PopupWindow; import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ActivityScenario; import androidx.test.filters.FlakyTest; import androidx.test.rule.ActivityTestRule; @@ -96,6 +99,8 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme private final ActivityTestRule<ConfigChangeHandlingActivity> mActivityRule = new ActivityTestRule<>(ConfigChangeHandlingActivity.class); + private ActivityScenario<ConfigChangeHandlingActivity> mScenario; + private Instrumentation mInstrumentation; private Activity mActivity; private SurfaceView mSurfaceView; @@ -919,6 +924,19 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme @Test public void testEmbeddedViewReceivesInputOnBottom() throws Throwable { + // Close the activity that was launched by setup + mActivityRule.finishActivity(); + + // Launch activity in fullscreen windowing mode + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mInstrumentation.getTargetContext(), ConfigChangeHandlingActivity.class); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + mScenario = ActivityScenario.launch(intent, options.toBundle()); + mScenario.onActivity(activity -> { + mActivity = activity; + }); + mEmbeddedView = new Button(mActivity); mEmbeddedView.setOnClickListener((View v) -> { mClicked = true; @@ -929,7 +947,7 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme waitUntilEmbeddedViewDrawn(); // We should receive no input until we punch a hole - CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView); + CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, null, mSurfaceView); mInstrumentation.waitForIdleSync(); assertFalse(mClicked); @@ -949,7 +967,7 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme // operations waitForTouchableRegionChanged(originalRegion); - CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView); + CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, null, mSurfaceView); mInstrumentation.waitForIdleSync(); assertTrue(mClicked); } @@ -977,6 +995,19 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme @Test public void testHostInputTokenAllowsObscuredTouches() throws Throwable { + // Close the activity that was launched by setup + mActivityRule.finishActivity(); + + // Launch activity in fullscreen windowing mode + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mInstrumentation.getTargetContext(), ConfigChangeHandlingActivity.class); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + mScenario = ActivityScenario.launch(intent, options.toBundle()); + mScenario.onActivity(activity -> { + mActivity = activity; + }); + SurfaceControlViewHost.SurfacePackage p = null; mTestService = getService(); @@ -987,7 +1018,7 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme mSurfaceView.getRootSurfaceControl().setTouchableRegion(new Region()); }); mInstrumentation.waitForIdleSync(); - CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView); + CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, null, mSurfaceView); mInstrumentation.waitForIdleSync(); assertTrue(mTestService.getViewIsTouchedAndObscured()); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java b/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java index 6b8be2d2f12..7a4c54345a9 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/UnsupportedErrorDialogTests.java @@ -60,7 +60,7 @@ public class UnsupportedErrorDialogTests extends ActivityManagerTestBase { resetAppErrors(); } - /** Make sure the developer options apply correctly leading to the dialog being shown. */ + /** Make sure the developer option applies correctly leading to the dialog being shown. */ @Test public void testDevSettingOverride() { try (SettingsSession<Integer> devDialogShow = @@ -71,16 +71,6 @@ public class UnsupportedErrorDialogTests extends ActivityManagerTestBase { secureIntSession(Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION)) { // set developer setting to show dialogs anyway devDialogShow.set(1); - - // enable only the regular option for showing the crash dialog after the first crash - showOnFirstCrash.set(1); - showOnFirstCrashDev.set(0); - launchActivityNoWait(Components.CRASHING_ACTIVITY); - findCrashDialogAndCloseApp(); - ensureActivityNotFocused(Components.CRASHING_ACTIVITY); - - resetAppErrors(); - // enable only the dev option for showing the crash dialog after the first crash showOnFirstCrash.set(0); showOnFirstCrashDev.set(1); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java index e73ae81999d..7bfc7cd506a 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java @@ -307,6 +307,7 @@ public class WindowInputTests { TAPPING_TARGET_WINDOW_SIZE); // Any opacity higher than this would make InputDispatcher block the touch params.alpha = mInputManager.getMaximumObscuringOpacityForTouch(); + params.setFitInsetsTypes(0); intent.putExtra(EXTRA_LAYOUT_PARAMS, params); mActivity.startForegroundService(intent); }); @@ -361,6 +362,7 @@ public class WindowInputTests { placeWindowAtLayoutCenter(params, TAPPING_TARGET_WINDOW_SIZE, viewOnScreenLocation[0], viewOnScreenLocation[1], TAPPING_TARGET_WINDOW_SIZE); + params.setFitInsetsTypes(0); intent.putExtra(EXTRA_LAYOUT_PARAMS, params); mActivity.startForegroundService(intent); }); @@ -462,6 +464,7 @@ public class WindowInputTests { placeWindowAtLayoutCenter(params, TAPPING_TARGET_WINDOW_SIZE, viewOnScreenLocation[0], viewOnScreenLocation[1], TAPPING_TARGET_WINDOW_SIZE); + params.setFitInsetsTypes(0); intent.putExtra(EXTRA_LAYOUT_PARAMS, params); mActivity.startForegroundService(intent); }); @@ -517,6 +520,7 @@ public class WindowInputTests { viewOnScreenLocation[0], viewOnScreenLocation[1], TAPPING_TARGET_WINDOW_SIZE); // Move it off the touch path (center) but still overlap with window above params.y += PARTIAL_OBSCURING_WINDOW_SIZE; + params.setFitInsetsTypes(0); intent.putExtra(EXTRA_LAYOUT_PARAMS, params); mActivity.startForegroundService(intent); }); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java index 1d9164d78b2..0281f55a71d 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java @@ -246,6 +246,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_andCancel() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { runOnUiThread(() -> { setupAnimationListener(); @@ -266,6 +268,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Test public void testControl_andImmediatelyCancel() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { runOnUiThread(() -> { setupAnimationListener(); @@ -283,6 +287,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_immediately_show() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { setVisibilityAndWait(mType, false); @@ -306,6 +312,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_immediately_hide() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { setVisibilityAndWait(mType, true); @@ -329,6 +337,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_transition_show() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { setVisibilityAndWait(mType, false); @@ -350,6 +360,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_transition_hide() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { setVisibilityAndWait(mType, true); @@ -371,6 +383,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_transition_show_interpolator() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { mInterpolator = new DecelerateInterpolator(); setVisibilityAndWait(mType, false); @@ -393,6 +407,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Presubmit @Test public void testControl_transition_hide_interpolator() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { mInterpolator = new AccelerateInterpolator(); setVisibilityAndWait(mType, true); @@ -414,6 +430,8 @@ public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase @Test public void testControl_andLoseControl() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars() && mType != ime()); + retryIfCancelled(() -> { mInterpolator = new AccelerateInterpolator(); setVisibilityAndWait(mType, true); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java index 86ec2fa236c..87c0fc8bb27 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java @@ -29,6 +29,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; @@ -70,6 +71,7 @@ public class WindowInsetsAnimationTests extends WindowInsetsAnimationTestBase { WINDOWING_MODE_FULLSCREEN); mRootView = mActivity.getWindow().getDecorView(); assumeTrue(hasWindowInsets(mRootView, systemBars())); + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); } @Test diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java index 91916b169b3..4a76d928c9d 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java @@ -116,6 +116,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testHide() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -134,6 +136,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testShow() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -181,6 +185,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testTopAppHidesStatusBarByMethod() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -190,6 +196,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testTopAppHidesStatusBarByWindowFlag() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivity(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -199,6 +207,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testTopAppHidesStatusBarBySystemUiFlag() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivity(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -291,6 +301,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testSetSystemBarsBehavior_default() throws InterruptedException { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -317,6 +329,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testSetSystemBarsBehavior_showTransientBarsBySwipe() throws InterruptedException { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivity(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -398,6 +412,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testSystemUiVisibilityCallbackCausedByInsets() { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivity(TestActivity.class); final View controlTarget = activity.getWindow().getDecorView(); final int[] targetSysUiVis = new int[1]; @@ -425,6 +441,7 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { private void testSysUiVisCallbackCausedByInsets(int insetsType, int sysUiFlag, View target, int[] targetSysUiVis, int[] nonTargetSysUiVis) { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); if (target.getRootWindowInsets().isVisible(insetsType)) { // Controlled by methods @@ -453,6 +470,11 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { public void testSystemUiVisibilityCallbackCausedByAppearance() { final TestActivity activity = startActivity(TestActivity.class); final View controlTarget = activity.getWindow().getDecorView(); + + // Assume we have at least one visible system bar. + assumeTrue(controlTarget.getRootWindowInsets().isVisible(statusBars()) || + controlTarget.getRootWindowInsets().isVisible(navigationBars())); + final int[] targetSysUiVis = new int[1]; getInstrumentation().runOnMainSync(() -> { controlTarget.setOnSystemUiVisibilityChangeListener( @@ -468,6 +490,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testSetSystemUiVisibilityAfterCleared_showBarsBySwipe() throws Exception { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -527,6 +551,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testSetSystemUiVisibilityAfterCleared_showBarsByApp() throws Exception { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -567,6 +593,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testHideOnCreate() throws Exception { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestHideOnCreateActivity activity = startActivityInWindowingModeFullScreen(TestHideOnCreateActivity.class); final View rootView = activity.getWindow().getDecorView(); @@ -654,6 +682,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testInsetsDispatch() throws Exception { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + // Start an activity which hides system bars in fullscreen mode, // otherwise, it might not be able to hide system bars in other windowing modes. final TestHideOnCreateActivity activity = startActivityInWindowingModeFullScreen( @@ -688,6 +718,8 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { @Test public void testWindowInsetsController_availableAfterAddView() throws Exception { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + final TestHideOnCreateActivity activity = startActivityInWindowingModeFullScreen(TestHideOnCreateActivity.class); final View rootView = activity.getWindow().getDecorView(); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java index 806cee588e7..b0c2a12cff1 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java @@ -199,6 +199,8 @@ public class WindowInsetsPolicyTest extends ActivityManagerTestBase { @Test public void testImmersiveFullscreenHidesSystemBars() throws Throwable { + assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars()); + // Run the test twice, because the issue that shows system bars even in the immersive mode, // happens at the 2nd try. for (int i = 1; i <= 2; ++i) { diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java index f895e4f8a1a..f9211904bf4 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java @@ -240,6 +240,9 @@ public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase { assertEmptySequence(PipActivity.class, getTransitionLog(), "launchBelow"); + // Set secondary split as launch root + mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); + // Launch second activity to side getTransitionLog().clear(); new Launcher(SecondActivity.class) @@ -264,6 +267,9 @@ public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase { // Enter split screen moveTaskToPrimarySplitScreenAndVerify(firstActivity, sideActivity); + // Set secondary split as launch root + mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId()); + // Launch second activity to side final Activity secondActivity = new Launcher(SecondActivity.class) .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java index 46081611198..bfb3e88dce2 100644 --- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java +++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java @@ -1999,6 +1999,7 @@ public abstract class ActivityManagerTestBase { static final int EQUALS = 1; static final int GREATER_THAN = 2; static final int LESS_THAN = 3; + static final int GREATER_THAN_OR_EQUALS = 4; final T mEvent; final int mRule; @@ -2022,6 +2023,9 @@ public abstract class ActivityManagerTestBase { case LESS_THAN: mMessage = event + " must be less than " + count; break; + case GREATER_THAN_OR_EQUALS: + mMessage = event + " must be greater than (or equals to) " + count; + break; default: mMessage = "Don't care"; } @@ -2039,6 +2043,8 @@ public abstract class ActivityManagerTestBase { return value > mCount; case LESS_THAN: return value < mCount; + case GREATER_THAN_OR_EQUALS: + return value >= mCount; default: } throw new RuntimeException("Unknown CountSpec rule"); diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java index e42835e2c9f..9ced5527653 100644 --- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java +++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java @@ -28,6 +28,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.ApiTest; +import com.android.compatibility.common.util.MediaUtils; import org.junit.Assert; import org.junit.Assume; @@ -168,18 +169,37 @@ public class CodecInfoTest { IntStream.of(caps.colorFormats) .noneMatch(x -> x == COLOR_FormatSurface)); } + } - // For devices launching with Android T, if a codec supports an HDR profile and device - // supports HDR display, it must advertise P010 support + /** For devices launching with Android T or higher, if a codec supports an HDR profile and + * device supports HDR display, it must support COLOR_FormatYUVP010 as a video decoder output + * format. For TVs, this requirement is optional. + */ + @Test + public void testP010SupportForHDRDisplay() { + Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); + MediaCodecInfo.CodecCapabilities caps = mCodecInfo.getCapabilitiesForType(mMediaType); int[] HdrProfileArray = mProfileHdrMap.get(mMediaType); if (FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) { for (CodecProfileLevel pl : caps.profileLevels) { if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) { - assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," + - " but does not support COLOR_FormatYUVP010", - IntStream.of(caps.colorFormats) - .noneMatch(x -> x == COLOR_FormatYUVP010)); + if (MediaUtils.isTv()) { + // Some TV devices support HDR10 display with VO instead of GPU. In this + // case, skip checking P010 on TV devices. + Assume.assumeFalse(mCodecInfo.getName() + " supports HDR profile " + + pl.profile + "," + + " but does not support COLOR_FormatYUVP010." + + " Skip checking on TV device", + IntStream.of(caps.colorFormats) + .noneMatch(x -> x == COLOR_FormatYUVP010)); + } else { + assertFalse(mCodecInfo.getName() + " supports HDR profile " + + pl.profile + "," + + " but does not support COLOR_FormatYUVP010", + IntStream.of(caps.colorFormats) + .noneMatch(x -> x == COLOR_FormatYUVP010)); + } } } } diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java index c196d6e492b..3867f04e71e 100644 --- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java +++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java @@ -128,12 +128,11 @@ public class EncoderProfileLevelTest extends CodecEncoderTestBase { {MediaFormat.MIMETYPE_VIDEO_MPEG4, 3000000, 704, 576, 30}, {MediaFormat.MIMETYPE_VIDEO_MPEG4, 8000000, 720, 576, 30}, - // TODO (b/151430764) - /*{MediaFormat.MIMETYPE_VIDEO_VP9, 200000, 256, 144, 15}, + {MediaFormat.MIMETYPE_VIDEO_VP9, 200000, 256, 144, 15}, {MediaFormat.MIMETYPE_VIDEO_VP9, 8000000, 384, 192, 30}, {MediaFormat.MIMETYPE_VIDEO_VP9, 1800000, 480, 256, 30}, {MediaFormat.MIMETYPE_VIDEO_VP9, 3600000, 640, 384, 30}, - {MediaFormat.MIMETYPE_VIDEO_VP9, 7200000, 1080, 512, 30},*/ + {MediaFormat.MIMETYPE_VIDEO_VP9, 7200000, 1080, 512, 30}, {MediaFormat.MIMETYPE_VIDEO_VP9, 12000000, 1280, 768, 30}, {MediaFormat.MIMETYPE_VIDEO_VP9, 18000000, 2048, 1088, 30}, {MediaFormat.MIMETYPE_VIDEO_VP9, 30000000, 2048, 1088, 60}, diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java index e9067208b94..ed80e302d4e 100644 --- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java +++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java @@ -1980,11 +1980,13 @@ public class UsageStatsTest { final long startTime = System.currentTimeMillis(); launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP); - SystemClock.sleep(500); + final long sleepTime1 = 500; + SystemClock.sleep(sleepTime1); // TEST_APP_PKG should take focus, pausing the TEST_APP2_CLASS_PIP activity. launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); - SystemClock.sleep(500); + final long sleepTime2 = 500; + SystemClock.sleep(sleepTime2); mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, WindowManagerState.STATE_PAUSED); @@ -2027,6 +2029,13 @@ public class UsageStatsTest { assertEquals("Unexpected number of activity resumes", 1, resumes); assertEquals("Unexpected number of activity pauses", 1, pauses); assertEquals("Unexpected number of activity stops", 0, stops); + + final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( + startTime, endTime); + final UsageStats stats = map.get(TEST_APP2_PKG); + assertNotNull(stats); + final long totalTimeVisible = stats.getTotalTimeVisible(); + assertLessThan(sleepTime1 + sleepTime2, totalTimeVisible); } @AppModeFull(reason = "No usage events access in instant apps") diff --git a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSelfManagedTest.kt b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSelfManagedTest.kt index ca63dd220ea..a37ced0564f 100644 --- a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSelfManagedTest.kt +++ b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSelfManagedTest.kt @@ -8,6 +8,7 @@ import android.companion.cts.common.CompanionActivity import android.companion.cts.common.RecordingCallback.OnAssociationCreated import android.content.Intent import android.platform.test.annotations.AppModeFull +import com.android.compatibility.common.util.FeatureUtil import org.junit.Assume.assumeFalse import org.junit.Test import org.junit.runner.RunWith @@ -34,6 +35,7 @@ class AssociationEndToEndSelfManagedTest( override fun setUp() { super.setUp() + assumeFalse(FeatureUtil.isWatch()) // TODO(b/211602270): Add support for WATCH and "null" profiles in the // confirmation UI (the "self-managed" flow variant). assumeFalse(profile == null) diff --git a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSingleDeviceTest.kt b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSingleDeviceTest.kt index 92e1b49fd17..d1cee82224c 100644 --- a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSingleDeviceTest.kt +++ b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndSingleDeviceTest.kt @@ -5,6 +5,7 @@ import android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION import android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER import android.platform.test.annotations.AppModeFull +import com.android.compatibility.common.util.FeatureUtil import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -28,6 +29,7 @@ class AssociationEndToEndSingleDeviceTest( override fun setUp() { super.setUp() + assumeFalse(FeatureUtil.isWatch()) // TODO(b/211722613): Add support for DEVICE_PROFILE_APP_STREAMING // DEVICE_PROFILE_COMPUTER and DEVICE_PROFILE_AUTOMOTIVE_PROJECTION // profiles in the confirmation UI (the "single_device" flow variant). diff --git a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndTest.kt b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndTest.kt index ffc52eb7708..8da92e64199 100644 --- a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndTest.kt +++ b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/AssociationEndToEndTest.kt @@ -20,6 +20,7 @@ import android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING import android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION import android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER import android.platform.test.annotations.AppModeFull +import com.android.compatibility.common.util.FeatureUtil import org.junit.Assume.assumeFalse import org.junit.Test import org.junit.runner.RunWith @@ -41,6 +42,7 @@ class AssociationEndToEndTest( override fun setUp() { super.setUp() + assumeFalse(FeatureUtil.isWatch()) // TODO(b/211590680): Add support for APP_STREAMING, AUTOMOTIVE_PROJECTION and COMPUTER // in the confirmation UI (the "multiple devices" flow variant). assumeFalse(profile == DEVICE_PROFILE_COMPUTER) diff --git a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java index 68edc76c597..58a40117df6 100644 --- a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java +++ b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java @@ -72,7 +72,7 @@ public class FeatureTest extends AndroidTestCase { } // Managed profiles only required for handheld devices - if (!isHandheldDevice()) { + if (!isHandheldOrTabletDevice()) { return; } @@ -91,12 +91,20 @@ public class FeatureTest extends AndroidTestCase { } /** - * The CDD defines a handheld device as one that has a battery and a screen size between - * 2.5 and 8 inches. + * The CDD defines: + * - A handheld device as one that has a battery and a screen size between 2.5 and 8 inches. + * - A tablet device as one that has a battery and a screen size between 7 and 18 inches. */ - private boolean isHandheldDevice() throws Exception { + private boolean isHandheldOrTabletDevice() throws Exception { + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) { + return false; + } + double screenInches = getScreenSizeInInches(); - return deviceHasBattery() && screenInches >= 2.5 && screenInches <= 8.0; + return deviceHasBattery() && screenInches >= 2.5 && screenInches <= 18.0; } private boolean deviceHasBattery() { diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java index ef44528cbff..852223c212c 100644 --- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java @@ -1010,7 +1010,7 @@ public class BitmapFactoryTest { @Test @RequiresDevice - public void testDecode10BitHEIFTo10BitBitmap() { + public void testDecode10BitHEIF() { assumeTrue( "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU)); assumeTrue( @@ -1018,13 +1018,23 @@ public class BitmapFactoryTest { SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + Config preferredConfig = Config.RGBA_1010102; + + // For TVs, even if the device advertises that 10 bits profile is supported, the output + // format might not be CPU readable, but can still be displayed. When the TV device is + // not capable to decode to YUVP010 format, it should be able to fall back to decode to + // RGB_565 format. + if (MediaUtils.isTv() && !hasHEVCDecoderSupportsYUVP010()) { + preferredConfig = Config.RGB_565; + } + BitmapFactory.Options opt = new BitmapFactory.Options(); - opt.inPreferredConfig = Config.RGBA_1010102; + opt.inPreferredConfig = preferredConfig; Bitmap bm = BitmapFactory.decodeStream(obtainInputStream(R.raw.heifimage_10bit), null, opt); assertNotNull(bm); assertEquals(4096, bm.getWidth()); assertEquals(3072, bm.getHeight()); - assertEquals(Config.RGBA_1010102, bm.getConfig()); + assertEquals(preferredConfig, bm.getConfig()); } @Test @@ -1037,6 +1047,16 @@ public class BitmapFactoryTest { SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + // For TVs, even if the device advertises that 10 bits profile is supported, the output + // format might not be CPU readable, but can still be displayed, and only when the TV + // is capable to decode to YUVP010 format, the image can be converted into RGBA_1010102, + // and this test can continue. + if (MediaUtils.isTv()) { + assumeTrue( + "The TV is unable to decode to RGBA_1010102 format, skip the test", + hasHEVCDecoderSupportsYUVP010()); + } + BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Config.ARGB_8888; Bitmap bm1 = @@ -1113,4 +1133,26 @@ public class BitmapFactoryTest { } return true; } + + private static boolean hasHEVCDecoderSupportsYUVP010() { + MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) { + if (mediaCodecInfo.isEncoder()) { + continue; + } + for (String mediaType : mediaCodecInfo.getSupportedTypes()) { + if (mediaType.equalsIgnoreCase("video/hevc")) { + MediaCodecInfo.CodecCapabilities codecCapabilities = + mediaCodecInfo.getCapabilitiesForType(mediaType); + for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) { + if (codecCapabilities.colorFormats[i] + == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) { + return true; + } + } + } + } + } + return false; + } } diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java index b6689d87f54..8bb2a3c72c7 100644 --- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java @@ -252,6 +252,16 @@ public class ImageDecoderTest { SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + // For TVs, even if the device advertises that 10 bits profile is supported, the output + // format might not be CPU readable, but can still be displayed, and only when the TV + // is capable to decode to YUVP010 format, the image can be converted into RGBA_1010102, + // and this test can continue. + if (MediaUtils.isTv()) { + assumeTrue( + "The TV is unable to decode to YUVP010 format, skip the test", + hasHEVCDecoderSupportsYUVP010()); + } + try { ImageDecoder.Source src = ImageDecoder .createSource(getResources(), R.raw.heifimage_10bit); @@ -2789,4 +2799,26 @@ public class ImageDecoderTest { } return true; } + + private static boolean hasHEVCDecoderSupportsYUVP010() { + MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) { + if (mediaCodecInfo.isEncoder()) { + continue; + } + for (String mediaType : mediaCodecInfo.getSupportedTypes()) { + if (mediaType.equalsIgnoreCase("video/hevc")) { + MediaCodecInfo.CodecCapabilities codecCapabilities = + mediaCodecInfo.getCapabilitiesForType(mediaType); + for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) { + if (codecCapabilities.colorFormats[i] + == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) { + return true; + } + } + } + } + } + return false; + } } diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java index 56dd1b0f561..bf2e941bfa5 100644 --- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java @@ -16,11 +16,13 @@ package android.keystore.cts; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -37,6 +39,13 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.bedstead.nene.annotations.Nullable; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; @@ -63,6 +72,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; @@ -78,11 +88,6 @@ import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.security.auth.x500.X500Principal; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - @RunWith(AndroidJUnit4.class) public class AndroidKeyStoreTest { private static final String TAG = AndroidKeyStoreTest.class.getSimpleName(); @@ -823,6 +828,12 @@ public class AndroidKeyStoreTest { expectedAliases.length, count); } + private void deleteEntryIfNotNull(@Nullable String alias) throws Exception { + if (alias != null) { + mKeyStore.deleteEntry(alias); + } + } + @Test public void testKeyStore_Aliases_Unencrypted_Success() throws Exception { mKeyStore.load(null, null); @@ -2161,8 +2172,9 @@ public class AndroidKeyStoreTest { PrivateKey privateKey3 = generatePrivateKey("RSA", FAKE_RSA_KEY_1); final int MAX_NUMBER_OF_KEYS = 2500; - final String ALIAS_PREFIX = "test_large_number_of_rsa_keys_"; + final StringBuilder aliasPrefix = new StringBuilder("test_large_number_of_rsa_keys_"); int keyCount = 0; + String entryName2 = null; mKeyStore.load(null); try { @@ -2175,11 +2187,12 @@ public class AndroidKeyStoreTest { new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}), protectionParams); - keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX, - new PrivateKeyEntry(privateKey3, new Certificate[] {cert3}), protectionParams); + keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, aliasPrefix, + new PrivateKeyEntry(privateKey3, new Certificate[] {cert3}), + protectionParams); keyCount++; - String entryName2 = "test" + keyCount; + entryName2 = "test" + keyCount; mKeyStore.setEntry(entryName2, new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}), protectionParams); @@ -2206,7 +2219,9 @@ public class AndroidKeyStoreTest { sig.update(message); assertTrue(sig.verify(signature)); } finally { - deleteManyTestKeys(keyCount, ALIAS_PREFIX); + mKeyStore.deleteEntry(entryName1); + deleteEntryIfNotNull(entryName2); + deleteManyTestKeys(keyCount, aliasPrefix); } } @@ -2235,8 +2250,9 @@ public class AndroidKeyStoreTest { PrivateKey privateKey3 = generatePrivateKey("EC", FAKE_EC_KEY_1); final int MAX_NUMBER_OF_KEYS = 2500; - final String ALIAS_PREFIX = "test_large_number_of_ec_keys_"; + final StringBuilder aliasPrefix = new StringBuilder("test_large_number_of_ec_keys_"); int keyCount = 0; + String entryName2 = null; mKeyStore.load(null); try { @@ -2248,12 +2264,12 @@ public class AndroidKeyStoreTest { new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}), protectionParams); - keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX, + keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, aliasPrefix, new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}), protectionParams); keyCount++; - String entryName2 = "test" + keyCount; + entryName2 = "test" + keyCount; mKeyStore.setEntry(entryName2, new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}), protectionParams); @@ -2280,7 +2296,9 @@ public class AndroidKeyStoreTest { sig.update(message); assertTrue(sig.verify(signature)); } finally { - deleteManyTestKeys(keyCount, ALIAS_PREFIX); + mKeyStore.deleteEntry(entryName1); + deleteEntryIfNotNull(entryName2); + deleteManyTestKeys(keyCount, aliasPrefix); } } @@ -2307,8 +2325,9 @@ public class AndroidKeyStoreTest { HexEncoding.decode("33333333333333333333777777777777"), "AES"); final int MAX_NUMBER_OF_KEYS = 10000; - final String ALIAS_PREFIX = "test_large_number_of_aes_keys_"; + final StringBuilder aliasPrefix = new StringBuilder("test_large_number_of_aes_keys_"); int keyCount = 0; + String entryName2 = null; mKeyStore.load(null); try { @@ -2319,11 +2338,11 @@ public class AndroidKeyStoreTest { .build(); mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams); - keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX, + keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, aliasPrefix, new KeyStore.SecretKeyEntry(key3), protectionParams); ++keyCount; - String entryName2 = "test" + keyCount; + entryName2 = "test" + keyCount; mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams); SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null); SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null); @@ -2345,7 +2364,9 @@ public class AndroidKeyStoreTest { cipher.init(Cipher.DECRYPT_MODE, key2, cipherParams); assertArrayEquals(plaintext, cipher.doFinal(ciphertext)); } finally { - deleteManyTestKeys(keyCount, ALIAS_PREFIX); + mKeyStore.deleteEntry(entryName1); + deleteEntryIfNotNull(entryName2); + deleteManyTestKeys(keyCount, aliasPrefix); } } @@ -2374,8 +2395,9 @@ public class AndroidKeyStoreTest { HexEncoding.decode("33333333333333333333777777777777"), "HmacSHA256"); final int MAX_NUMBER_OF_KEYS = 10000; - final String ALIAS_PREFIX = "test_large_number_of_hmac_keys_"; + final StringBuilder aliasPrefix = new StringBuilder("test_large_number_of_hmac_keys_"); int keyCount = 0; + String entryName2 = null; mKeyStore.load(null); try { @@ -2384,11 +2406,11 @@ public class AndroidKeyStoreTest { .build(); mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams); - keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX, + keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, aliasPrefix, new KeyStore.SecretKeyEntry(key3), protectionParams); keyCount++; - String entryName2 = "test" + keyCount; + entryName2 = "test" + keyCount; mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams); SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null); SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null); @@ -2408,7 +2430,9 @@ public class AndroidKeyStoreTest { "59b57e77e4e2cb36b5c7b84af198ac004327bc549de6931a1b5505372dd8c957"), mac.doFinal(message)); } finally { - deleteManyTestKeys(keyCount, ALIAS_PREFIX); + mKeyStore.deleteEntry(entryName1); + deleteEntryIfNotNull(entryName2); + deleteManyTestKeys(keyCount, aliasPrefix); } } @@ -2606,8 +2630,8 @@ public class AndroidKeyStoreTest { * * This method is time-bounded */ - private int importKeyManyTimes(int numberOfKeys, String aliasPrefix, Entry keyEntry, - KeyProtection protectionParams) + private int importKeyManyTimes(int numberOfKeys, StringBuilder aliasPrefix, Entry keyEntry, + KeyProtection protectionParams, boolean isTimeBound) throws InterruptedException { TimeBox timeBox = new TimeBox(mMaxImportDuration); AtomicInteger keyCounter = new AtomicInteger(0); @@ -2616,7 +2640,7 @@ public class AndroidKeyStoreTest { threads.add(new Thread(() -> { // Import key lots of times, under different aliases. Do this until we either run // out of time or we import the key numberOfKeys times. - while (!timeBox.isOutOfTime()) { + while (!isTimeBound || !timeBox.isOutOfTime()) { int count = keyCounter.incrementAndGet(); if (count > numberOfKeys) { // The loop is inherently racy, as multiple threads are simultaneously @@ -2629,7 +2653,7 @@ public class AndroidKeyStoreTest { if ((count % 1000) == 0) { Log.i(TAG, "Imported " + count + " keys"); } - String entryAlias = aliasPrefix + count; + String entryAlias = aliasPrefix.toString() + count; try { mKeyStore.setEntry(entryAlias, keyEntry, protectionParams); } catch (Throwable e) { @@ -2646,7 +2670,7 @@ public class AndroidKeyStoreTest { threads.get(i).join(); } Log.i(TAG, "Imported " + keyCounter.get() + " keys in " + timeBox.elapsed()); - if (keyCounter.get() < MIN_SUPPORTED_KEY_COUNT) { + if (keyCounter.get() != numberOfKeys && keyCounter.get() < MIN_SUPPORTED_KEY_COUNT) { fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in " + timeBox.elapsed() + ". Imported: " + keyCounter.get() + " keys"); } @@ -2654,11 +2678,22 @@ public class AndroidKeyStoreTest { return keyCounter.get(); } + private int importKeyManyTimes(int numberOfKeys, StringBuilder aliasPrefix, Entry keyEntry, + KeyProtection protectionParams) throws InterruptedException { + return importKeyManyTimes(numberOfKeys, aliasPrefix, keyEntry, protectionParams, true); + } + + private int importKeyManyTimesWithoutTimeLimit(int numberOfKeys, StringBuilder aliasPrefix, + Entry keyEntry, + KeyProtection protectionParams) throws InterruptedException { + return importKeyManyTimes(numberOfKeys, aliasPrefix, keyEntry, protectionParams, false); + } + /** * Delete <code>numberOfKeys</code> keys that follow the pattern "[aliasPrefix][keyCounter]". * This is done across multiple threads to both increase throughput as well as stress keystore. */ - private void deleteManyTestKeys(int numberOfKeys, String aliasPrefix) + private void deleteManyTestKeys(int numberOfKeys, StringBuilder aliasPrefix) throws InterruptedException { // Clean up Keystore without using KeyStore.aliases() which can't handle this many // entries. @@ -2673,8 +2708,9 @@ public class AndroidKeyStoreTest { if ((key > 0) && ((key % 1000) == 0)) { Log.i(TAG, "Deleted " + key + " keys"); } + String entryAlias = aliasPrefix.toString() + key; try { - mKeyStore.deleteEntry("test" + key); + mKeyStore.deleteEntry(entryAlias); } catch (Exception e) { fail("Unexpected exception in key cleanup: " + e); } @@ -2690,4 +2726,114 @@ public class AndroidKeyStoreTest { } Log.i(TAG, "Deleted " + numberOfKeys + " keys"); } + + private Set<String> createLargeNumberOfKeyStoreEntryAliases(int numberOfKeys, + StringBuilder aliasPrefix) + throws Exception { + Certificate cert = generateCertificate(FAKE_RSA_USER_1); + PrivateKey privateKey = generatePrivateKey("RSA", FAKE_RSA_KEY_1); + + mKeyStore.load(null); + KeyProtection protectionParams = new KeyProtection.Builder( + KeyProperties.PURPOSE_SIGN) + .setDigests(KeyProperties.DIGEST_SHA256) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .build(); + + int keyCount = importKeyManyTimesWithoutTimeLimit(numberOfKeys, aliasPrefix, + new PrivateKeyEntry(privateKey, new Certificate[]{cert}), protectionParams); + + // Construct expected aliases list. + final Set<String> expectedAliases = new HashSet<>(keyCount); + for (int count = 1; count <= keyCount; count++) { + String entryAlias = aliasPrefix.toString() + count; + expectedAliases.add(entryAlias); + } + + return expectedAliases; + } + + private void importLargeNumberOfKeysValidateAliases(int numberOfKeys, StringBuilder aliasPrefix) + throws Exception { + Set<String> importedKeyAliases = createLargeNumberOfKeyStoreEntryAliases(numberOfKeys, + aliasPrefix); + assertThat(importedKeyAliases.size()).isEqualTo(numberOfKeys); + + try { + // b/222287335 Currently, limiting Keystore `listEntries` API to return subset of the + // Keystore entries to avoid running out of binder buffer space. + // To verify that all the imported key aliases are present in Keystore, get the list of + // aliases from Keystore, delete the matched aliases from Keystore and imported list of + // key aliases, continue this till all the imported key aliases are matched. + while (!importedKeyAliases.isEmpty()) { + // List the keystore entries aliases until all the imported key aliases are matched. + Set<String> aliases = new HashSet<String>(Collections.list(mKeyStore.aliases())); + + // Try to match the aliases with imported key aliases. + // Cleanup matching aliases from Keystore and imported key aliases list. + for (String alias: aliases) { + if (importedKeyAliases.contains(alias)) { + mKeyStore.deleteEntry(alias); + importedKeyAliases.remove(alias); + } + } + } + assertTrue("Failed to match imported keystore entries.", + importedKeyAliases.isEmpty()); + } finally { + if (!importedKeyAliases.isEmpty()) { + Log.i(TAG, "Final cleanup of imported keys"); + for (String alias: importedKeyAliases) { + mKeyStore.deleteEntry(alias); + } + } + } + assertTrue(importedKeyAliases.isEmpty()); + } + + /** + * Create long alias prefix of length 6000 characters. + */ + private StringBuilder createLongAliasPrefix() { + char[] prefixChar = new char[6000]; + Arrays.fill(prefixChar, 'T'); + StringBuilder prefixAlias = new StringBuilder(); + prefixAlias.append(prefixChar); + + return prefixAlias; + } + + /** + * Create large number of Keystore entries with long aliases and try to list aliases of all the + * entries in the keystore. + */ + @Test + public void testKeyStore_LargeNumberOfLongAliases() throws Exception { + final int maxNumberOfKeys = 100; + + importLargeNumberOfKeysValidateAliases(maxNumberOfKeys, createLongAliasPrefix()); + } + + /** + * Create limited number of Keystore entries with long aliases and try to list aliases of all + * the entries in the keystore. Test should successfully list all the Keystore entries aliases. + */ + @Test + public void testKeyStore_LimitedNumberOfLongAliasesSuccess() throws Exception { + final int maxNumberOfKeys = 10; + importLargeNumberOfKeysValidateAliases(maxNumberOfKeys, createLongAliasPrefix()); + } + + /** + * Create large number of Keystore entries with short length aliases and try to list aliases of + * all the entries in the keystore. Test should successfully list all the Keystore entries + * aliases. + */ + @Test + public void testKeyStore_LargeNumberShortAliasesSuccess() throws Exception { + final int maxNumberOfKeys = 2500; + final StringBuilder aliasPrefix = new StringBuilder("test_short_key_alias_"); + + importLargeNumberOfKeysValidateAliases(maxNumberOfKeys, aliasPrefix); + } } diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java index e6de05385de..26b3658d974 100644 --- a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java @@ -616,16 +616,11 @@ public class CipherTest { return (pm != null && pm.hasSystemFeature("android.software.leanback_only")); } - private boolean hasSecureLockScreen() { - PackageManager pm = getContext().getPackageManager(); - return (pm != null && pm.hasSystemFeature("android.software.secure_lock_screen")); - } - @Presubmit @Test public void testKeyguardLockAndUnlock() throws Exception { - if (!hasSecureLockScreen()) { + if (!TestUtils.hasSecureLockScreen(getContext())) { return; } @@ -647,7 +642,7 @@ public class CipherTest { final boolean isUnlockedDeviceRequired = true; final boolean isUserAuthRequired = false; - if (!hasSecureLockScreen()) { + if (!TestUtils.hasSecureLockScreen(getContext())) { return; } @@ -1165,7 +1160,7 @@ public class CipherTest { final boolean isUnlockedDeviceRequired = false; final boolean isUserAuthRequired = true; - if (!hasSecureLockScreen()) { + if (!TestUtils.hasSecureLockScreen(getContext())) { return; } diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java index 043b5de11cd..99e3d756ff4 100644 --- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java @@ -60,6 +60,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.content.Context; import android.content.pm.PackageManager; @@ -494,6 +495,9 @@ public class KeyAttestationTest { @Test public void testEcAttestation_UniqueIdWorksWithCorrectPermission() throws Exception { + assumeTrue("Device doesn't have secure lock screen", + TestUtils.hasSecureLockScreen(getContext())); + String keystoreAlias = "test_key"; KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN) .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) diff --git a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java index 84e8969007f..b965204fa76 100644 --- a/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java +++ b/tests/tests/keystore/src/android/keystore/cts/util/TestUtils.java @@ -24,8 +24,8 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import android.content.Context; -import android.content.pm.PackageManager; import android.content.pm.FeatureInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.SystemProperties; import android.security.keystore.KeyGenParameterSpec; @@ -55,6 +55,7 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.UnrecoverableEntryException; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; @@ -76,7 +77,6 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.security.SecureRandom; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; @@ -1140,4 +1140,9 @@ public class TestUtils { public static boolean isAttestationSupported() { return Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.O; } + + public static boolean hasSecureLockScreen(Context context) { + PackageManager pm = context.getPackageManager(); + return (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)); + } } diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java index 5ba8f7e64fa..c9588f45344 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java +++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java @@ -107,7 +107,7 @@ public class AudioManagerTest extends InstrumentationTestCase { private final static String TAG = "AudioManagerTest"; private final static long ASYNC_TIMING_TOLERANCE_MS = 50; - private final static long POLL_TIME_VOLUME_ADJUST = 200; + private final static long POLL_TIME_VOLUME_ADJUST = 400; private final static long POLL_TIME_UPDATE_INTERRUPTION_FILTER = 5000; private final static int MP3_TO_PLAY = R.raw.testmp3; // ~ 5 second mp3 private final static long POLL_TIME_PLAY_MUSIC = 2000; diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java index ac09cef061c..3de299c8265 100644 --- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java +++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java @@ -115,6 +115,8 @@ public class MediaPlayerTest extends MediaPlayerTestBase { private static final int RECORDED_VIDEO_HEIGHT = 144; private static final long RECORDED_DURATION_MS = 3000; private static final float FLOAT_TOLERANCE = .0001f; + private static final int PLAYBACK_DURATION_MS = 10000; + private static final int ANR_DETECTION_TIME_MS = 20000; private final Vector<Integer> mTimedTextTrackIndex = new Vector<>(); private final Monitor mOnTimedTextCalled = new Monitor(); @@ -713,9 +715,9 @@ public class MediaPlayerTest extends MediaPlayerTestBase { player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); player.prepare(); // Test needs the mediaplayer to playback at least about 5 seconds of content. - // Clip used here has a duration of 61 seconds, so seek to 50 seconds in the media file. + // Clip used here has a duration of 61 seconds, given PLAYBACK_DURATION_MS for play. // This leaves enough remaining time, with gapless enabled or disabled, - player.seekTo(50000); + player.seekTo(player.getDuration() - PLAYBACK_DURATION_MS); } } @@ -757,6 +759,8 @@ public class MediaPlayerTest extends MediaPlayerTestBase { @Test public void testSetNextMediaPlayer() throws Exception { + final int TOTAL_TIMEOUT_MS = PLAYBACK_DURATION_MS * 4 + ANR_DETECTION_TIME_MS + + 5000 /* listener latency(ms) */; initMediaPlayer(mMediaPlayer); final Monitor mTestCompleted = new Monitor(); @@ -770,9 +774,9 @@ public class MediaPlayerTest extends MediaPlayerTestBase { return; } long now = SystemClock.elapsedRealtime(); - if ((now - startTime) > 45000) { - // We've been running for 45 seconds and still aren't done, so we're stuck - // somewhere. Signal ourselves to dump the thread stacks. + if ((now - startTime) > TOTAL_TIMEOUT_MS) { + // We've been running beyond TOTAL_TIMEOUT and still aren't done, + // so we're stuck somewhere. Signal ourselves to dump the thread stacks. android.os.Process.sendSignal(android.os.Process.myPid(), 3); SystemClock.sleep(2000); fail("Test is stuck, see ANR stack trace for more info. You may need to" + diff --git a/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java b/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java index f5b0140667c..b0cbdcbc9fe 100644 --- a/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java +++ b/tests/tests/networksecurityconfig/src/android/security/net/config/cts/BaseTestCase.java @@ -17,6 +17,7 @@ package android.security.net.config.cts; import android.content.Context; +import android.content.pm.PackageManager; import android.net.wifi.WifiManager; import android.test.AndroidTestCase; @@ -26,16 +27,28 @@ import com.android.compatibility.common.util.SystemUtil; * Base test case for all tests under {@link android.security.net.config.cts}. */ public class BaseTestCase extends AndroidTestCase { + + private boolean isWifiSupported(Context context) { + final PackageManager packageManager = context.getPackageManager(); + return packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI); + } + @Override public void setUp() throws Exception { // Instant Apps cannot access WifiManager, skip wifi check. if (getContext().getPackageManager().isInstantApp()) { return; } + + // Skip accessing wifi manager if Wifi feature is not supported. + if (!isWifiSupported(getContext())) { + return; + } + WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); if (!wifiManager.isWifiEnabled()) { SystemUtil.runShellCommand("svc wifi enable"); // toggle wifi on. - Thread.sleep(5000); // sleep 5 second for wifi to connect to a network. + Thread.sleep(6000); // sleep 6 second for wifi to connect to a network. } } } diff --git a/tests/tests/permission/src/android/permission/cts/NoWakeLockPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoWakeLockPermissionTest.java index fd7e977b118..030f341aa74 100644 --- a/tests/tests/permission/src/android/permission/cts/NoWakeLockPermissionTest.java +++ b/tests/tests/permission/src/android/permission/cts/NoWakeLockPermissionTest.java @@ -17,6 +17,8 @@ package android.permission.cts; +import static android.content.pm.PackageManager.FEATURE_WIFI; + import android.content.Context; import android.media.MediaPlayer; import android.net.wifi.WifiManager; @@ -49,6 +51,10 @@ public class NoWakeLockPermissionTest extends AndroidTestCase { @AppModeFull(reason = "Instant apps cannot access the WifiManager") @SmallTest public void testWifiLockAcquire() { + if (!mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI)) { + return; + } + final WifiManager wifiManager = (WifiManager) mContext.getSystemService( Context.WIFI_SERVICE); final WifiLock wifiLock = wifiManager.createWifiLock("WakeLockPermissionTest"); diff --git a/tests/tests/permission/src/android/permission/cts/NoWifiStatePermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoWifiStatePermissionTest.java index d8acd507d26..6be8d7d4690 100644 --- a/tests/tests/permission/src/android/permission/cts/NoWifiStatePermissionTest.java +++ b/tests/tests/permission/src/android/permission/cts/NoWifiStatePermissionTest.java @@ -16,6 +16,8 @@ package android.permission.cts; +import static android.content.pm.PackageManager.FEATURE_WIFI; + import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -32,10 +34,17 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { private static final int TEST_NET_ID = 1; private static final WifiConfiguration TEST_WIFI_CONFIGURATION = new WifiConfiguration(); private WifiManager mWifiManager; + private boolean mHasWifi; @Override protected void setUp() throws Exception { super.setUp(); + + mHasWifi = mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI); + if (!mHasWifi) { + return; + } + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); assertNotNull(mWifiManager); } @@ -46,6 +55,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. */ public void testGetWifiState() { + if (!mHasWifi) { + return; + } + try { mWifiManager.getWifiState(); fail("WifiManager.getWifiState didn't throw SecurityException as expected"); @@ -60,6 +73,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. */ public void testGetConfiguredNetworks() { + if (!mHasWifi) { + return; + } + try { mWifiManager.getConfiguredNetworks(); fail("WifiManager.getConfiguredNetworks didn't throw SecurityException as expected"); @@ -74,6 +91,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. */ public void testGetConnectionInfo() { + if (!mHasWifi) { + return; + } + try { mWifiManager.getConnectionInfo(); fail("WifiManager.getConnectionInfo didn't throw SecurityException as expected"); @@ -88,6 +109,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. */ public void testGetScanResults() { + if (!mHasWifi) { + return; + } + try { mWifiManager.getScanResults(); fail("WifiManager.getScanResults didn't throw SecurityException as expected"); @@ -102,6 +127,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#ACCESS_WIFI_STATE}. */ public void testGetDhcpInfo() { + if (!mHasWifi) { + return; + } + try { mWifiManager.getDhcpInfo(); fail("WifiManager.getDhcpInfo didn't throw SecurityException as expected"); @@ -116,6 +145,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testDisconnect() { + if (!mHasWifi) { + return; + } + try { mWifiManager.disconnect(); fail("WifiManager.disconnect didn't throw SecurityException as expected"); @@ -130,6 +163,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testReconnect() { + if (!mHasWifi) { + return; + } + try { mWifiManager.reconnect(); fail("WifiManager.reconnect didn't throw SecurityException as expected"); @@ -144,6 +181,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testReassociate() { + if (!mHasWifi) { + return; + } + try { mWifiManager.reassociate(); fail("WifiManager.reassociate didn't throw SecurityException as expected"); @@ -158,6 +199,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testAddNetwork() { + if (!mHasWifi) { + return; + } + try { mWifiManager.addNetwork(TEST_WIFI_CONFIGURATION); fail("WifiManager.addNetwork didn't throw SecurityException as expected"); @@ -172,6 +217,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testUpdateNetwork() { + if (!mHasWifi) { + return; + } + TEST_WIFI_CONFIGURATION.networkId = 2; try { @@ -188,6 +237,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testRemoveNetwork() { + if (!mHasWifi) { + return; + } + try { mWifiManager.removeNetwork(TEST_NET_ID); fail("WifiManager.removeNetwork didn't throw SecurityException as expected"); @@ -202,6 +255,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testEnableNetwork() { + if (!mHasWifi) { + return; + } + try { mWifiManager.enableNetwork(TEST_NET_ID, false); fail("WifiManager.enableNetwork didn't throw SecurityException as expected"); @@ -216,6 +273,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testDisableNetwork() { + if (!mHasWifi) { + return; + } + try { mWifiManager.disableNetwork(TEST_NET_ID); fail("WifiManager.disableNetwork didn't throw SecurityException as expected"); @@ -230,6 +291,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testPingSupplicant() { + if (!mHasWifi) { + return; + } + try { mWifiManager.pingSupplicant(); fail("WifiManager.pingSupplicant didn't throw SecurityException as expected"); @@ -244,6 +309,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testStartScan() { + if (!mHasWifi) { + return; + } + try { mWifiManager.startScan(); fail("WifiManager.startScan didn't throw SecurityException as expected"); @@ -258,6 +327,10 @@ public class NoWifiStatePermissionTest extends AndroidTestCase { * {@link android.Manifest.permission#CHANGE_WIFI_STATE}. */ public void testSetWifiEnabled() { + if (!mHasWifi) { + return; + } + try { mWifiManager.setWifiEnabled(true); fail("WifiManager.setWifiEnabled didn't throw SecurityException as expected"); diff --git a/tests/tests/permission2/res/raw/wear_android_manifest.xml b/tests/tests/permission2/res/raw/wear_android_manifest.xml deleted file mode 100644 index 08488bc3d72..00000000000 --- a/tests/tests/permission2/res/raw/wear_android_manifest.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<!-- This file contains permissions which were back ported. - These permissions are already present on future platform releases. - --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android" coreApp="true" android:sharedUserId="android.uid.system" - android:sharedUserLabel="@string/android_system_label"> - - <!-- @hide Allows an application to access wrist temperature data from the watch sensors. - <p class="note"><strong>Note: </strong> This permission is for Wear OS only. - <p>Protection level: dangerous --> - <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_bodySensorsWristTemperature" - android:description="@string/permdesc_bodySensorsWristTemperature" - android:backgroundPermission="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND" - android:protectionLevel="dangerous" /> - - <!-- @hide Allows an application to access wrist temperature data from the watch sensors. - If you're requesting this permission, you must also request - {@link #BODY_SENSORS_WRIST_TEMPERATURE}. Requesting this permission by itself doesn't - give you wrist temperature body sensors access. - <p class="note"><strong>Note: </strong> This permission is for Wear OS only. - <p>Protection level: dangerous - - <p> This is a hard restricted permission which cannot be held by an app until - the installer on record allowlists the permission. For more details see - {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. - --> - <permission android:name="android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND" - android:permissionGroup="android.permission-group.UNDEFINED" - android:label="@string/permlab_bodySensors_wristTemperature_background" - android:description="@string/permdesc_bodySensors_wristTemperature_background" - android:protectionLevel="dangerous" - android:permissionFlags="hardRestricted" /> - -</manifest>
\ No newline at end of file diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java index 637380313a2..576df286de4 100644 --- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java +++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java @@ -176,9 +176,6 @@ public class PermissionPolicyTest { } declaredPermissionsMap.putAll(carServiceBuiltInPermissionsMap); } - if (sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { - expectedPermissions.addAll(loadExpectedPermissions(R.raw.wear_android_manifest)); - } for (ExpectedPermissionInfo expectedPermission : expectedPermissions) { String expectedPermissionName = expectedPermission.name; diff --git a/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt index 2a9f815aec5..e10131c3f66 100644 --- a/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt +++ b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt @@ -55,7 +55,6 @@ import android.Manifest.permission.WRITE_CONTACTS import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.Manifest.permission_group.UNDEFINED import android.app.AppOpsManager.permissionToOp -import android.content.pm.PackageManager import android.content.pm.PackageManager.GET_PERMISSIONS import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP @@ -166,24 +165,6 @@ class RuntimePermissionProperties { // Add runtime permissions added in U which were _not_ split from a previously existing // runtime permission expectedPerms.add(READ_MEDIA_VISUAL_USER_SELECTED) - - // Add runtime permissions added in V (back ported from U) which were _not_ split from a - // previously existing runtime permission - if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { - expectedPerms.add(BODY_SENSORS_WRIST_TEMPERATURE) - expectedPerms.add(BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND) - } - assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name }) } - - companion object { - // These permissions are back ported from Android U to tm-wear, hidden in the - // "core/res/AndroidManifest.xml" file of /framework/base repo. Added these 2 constants here - // because hidden permissions can't be imported like other imported permissions in this file - private const val BODY_SENSORS_WRIST_TEMPERATURE = - "android.permission.BODY_SENSORS_WRIST_TEMPERATURE" - private const val BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND = - "android.permission.BODY_SENSORS_WRIST_TEMPERATURE_BACKGROUND" - } } diff --git a/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt b/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt index 104655f3bde..3c8a35c058d 100644 --- a/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt +++ b/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt @@ -22,6 +22,7 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.content.Intent import android.content.pm.PackageManager +import android.os.Bundle import android.os.Handler import android.os.Looper @@ -43,9 +44,13 @@ class CreateNotificationChannelsActivity : Activity() { lateinit var notificationManager: NotificationManager var launchActivityOnSecondResume = false var isFirstResume = true + var windowHasFocus = false + var pendingCreateChannel = false val handler = Handler(Looper.getMainLooper()) - override fun onStart() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val launchSecondActivity = intent.getBooleanExtra(EXTRA_START_SECOND_ACTIVITY, false) notificationManager = baseContext.getSystemService(NotificationManager::class.java)!! if (intent.getBooleanExtra(EXTRA_START_SECOND_APP, false)) { @@ -73,7 +78,6 @@ class CreateNotificationChannelsActivity : Activity() { } } - if (intent.getBooleanExtra(EXTRA_REQUEST_OTHER_PERMISSIONS, false)) { requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 0) } else if (intent.getBooleanExtra(EXTRA_REQUEST_OTHER_PERMISSIONS_DELAYED, false)) { @@ -85,8 +89,6 @@ class CreateNotificationChannelsActivity : Activity() { if (intent.getBooleanExtra(EXTRA_REQUEST_NOTIF_PERMISSION, false)) { requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0) } - - super.onStart() } private fun launchSecondActivity() { @@ -99,7 +101,21 @@ class CreateNotificationChannelsActivity : Activity() { }, LONG_DELAY_MS) } + override fun onWindowFocusChanged(hasFocus: Boolean) { + windowHasFocus = hasFocus + if (windowHasFocus && pendingCreateChannel) { + pendingCreateChannel = false + createChannel() + } + } + private fun createChannel() { + // Wait until window has focus so the permission prompt can be displayed + if (!windowHasFocus) { + pendingCreateChannel = true + return + } + if (notificationManager.getNotificationChannel(CHANNEL_ID_31) == null) { notificationManager.createNotificationChannel(NotificationChannel(CHANNEL_ID_31, "Foreground Services", NotificationManager.IMPORTANCE_HIGH)) diff --git a/tests/tests/permission3/CreateNotificationChannelsApp33/Android.bp b/tests/tests/permission3/CreateNotificationChannelsApp33/Android.bp index 876b68f350a..293897c2c52 100644 --- a/tests/tests/permission3/CreateNotificationChannelsApp33/Android.bp +++ b/tests/tests/permission3/CreateNotificationChannelsApp33/Android.bp @@ -23,7 +23,7 @@ android_test_helper_app { defaults: ["mts-target-sdk-version-current"], // TODO ntmyren: change to "33" when it is a valid target sdk_version: "test_current", - min_sdk_version: "test_current", + min_sdk_version: "current", static_libs: [ "kotlin-stdlib", diff --git a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt index d55e3a400b1..dee76c2fc9d 100644 --- a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt @@ -194,6 +194,7 @@ class NotificationPermissionTest : BaseUsePermissionTest() { fun notificationPromptShownForSubsequentStartsIfTaskStartWasLauncher() { installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true) launchApp(startSecondActivity = true) + waitFindObject(By.res(ALLOW_BUTTON)) pressBack() clickPermissionRequestAllowButton() } diff --git a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt index 0b88e01f772..7499e0f2c04 100644 --- a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt @@ -65,6 +65,7 @@ class SensorBlockedBannerTest : BaseUsePermissionTest() { @Before fun setup() { Assume.assumeFalse(isTv) + Assume.assumeFalse(isWatch) // TODO(b/203784852) Auto will eventually support the blocked sensor banner, but there won't // be support in T or below Assume.assumeFalse(isAutomotive) diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java index 5ad3cf53e0c..48f55befa1f 100644 --- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java +++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java @@ -279,7 +279,8 @@ public class ProviderTestUtils { // file creation fails, we ignore and let `dd` command create it instead. } - executeShellCommand(String.format("dd bs=1 if=%s skip=%d count=%d of=%s", + executeShellCommand(String.format( + "dd bs=4K if=%s iflag=skip_bytes,count_bytes skip=%d count=%d of=%s", source.getAbsolutePath(), skip, count, file.getAbsolutePath())); // Force sync to try updating other views diff --git a/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkMimeTypeMapTest.java b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkMimeTypeMapTest.java new file mode 100644 index 00000000000..4834cb3d621 --- /dev/null +++ b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkMimeTypeMapTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.sdksandbox.webkit.cts; + +import android.app.sdksandbox.testutils.testscenario.KeepSdkSandboxAliveRule; +import android.platform.test.annotations.AppModeFull; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@MediumTest +@AppModeFull +@RunWith(AndroidJUnit4.class) +public class SdkMimeTypeMapTest { + @ClassRule + public static final KeepSdkSandboxAliveRule sSdkTestSuiteSetup = + new KeepSdkSandboxAliveRule("com.android.emptysdkprovider"); + + @Rule + public final WebViewSandboxTestRule sdkTester = + new WebViewSandboxTestRule("android.webkit.cts.MimeTypeMapTest"); + + @Test + public void testGetFileExtensionFromUrl() throws Exception { + sdkTester.assertSdkTestRunPasses("testGetFileExtensionFromUrl"); + } + + @Test + public void testHasMimeType() throws Exception { + sdkTester.assertSdkTestRunPasses("testHasMimeType"); + } + + @Test + public void testGetMimeTypeFromExtension() throws Exception { + sdkTester.assertSdkTestRunPasses("testGetMimeTypeFromExtension"); + } + + @Test + public void testHasExtension() throws Exception { + sdkTester.assertSdkTestRunPasses("testHasExtension"); + } + + @Test + public void testGetExtensionFromMimeType() throws Exception { + sdkTester.assertSdkTestRunPasses("testGetExtensionFromMimeType"); + } + + @Test + public void testGetSingleton() throws Exception { + sdkTester.assertSdkTestRunPasses("testGetSingleton"); + } +} diff --git a/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkServiceWorkerWebSettingsTest.java b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkServiceWorkerWebSettingsTest.java new file mode 100644 index 00000000000..e83a333463e --- /dev/null +++ b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkServiceWorkerWebSettingsTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.sdksandbox.webkit.cts; + +import android.app.sdksandbox.testutils.testscenario.KeepSdkSandboxAliveRule; +import android.platform.test.annotations.AppModeFull; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@MediumTest +@AppModeFull +@RunWith(AndroidJUnit4.class) +public class SdkServiceWorkerWebSettingsTest { + @ClassRule + public static final KeepSdkSandboxAliveRule sSdkTestSuiteSetup = + new KeepSdkSandboxAliveRule("com.android.emptysdkprovider"); + + @Rule + public final WebViewSandboxTestRule sdkTester = + new WebViewSandboxTestRule("android.webkit.cts.ServiceWorkerWebSettingsTest"); + + @Test + public void testCacheMode() throws Exception { + sdkTester.assertSdkTestRunPasses("testCacheMode"); + } + + @Test + public void testAllowContentAccess() throws Exception { + sdkTester.assertSdkTestRunPasses("testAllowContentAccess"); + } + + @Test + public void testAllowFileAccess() throws Exception { + sdkTester.assertSdkTestRunPasses("testAllowFileAccess"); + } + + @Test + public void testBlockNetworkLoads() throws Exception { + sdkTester.assertSdkTestRunPasses("testBlockNetworkLoads"); + } +} diff --git a/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkWebViewRenderProcessTest.java b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkWebViewRenderProcessTest.java new file mode 100644 index 00000000000..91f3fbedaec --- /dev/null +++ b/tests/tests/sdksandbox/webkit/src/android/sdksandbox/webkit/cts/SdkWebViewRenderProcessTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.sdksandbox.webkit.cts; + +import android.app.sdksandbox.testutils.testscenario.KeepSdkSandboxAliveRule; +import android.platform.test.annotations.AppModeFull; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@MediumTest +@AppModeFull +@RunWith(AndroidJUnit4.class) +public class SdkWebViewRenderProcessTest { + @ClassRule + public static final KeepSdkSandboxAliveRule sSdkTestSuiteSetup = + new KeepSdkSandboxAliveRule("com.android.emptysdkprovider"); + + @Rule + public final WebViewSandboxTestRule sdkTester = + new WebViewSandboxTestRule("android.webkit.cts.WebViewRenderProcessTest"); + + @Test + public void testGetWebViewRenderProcess() throws Exception { + sdkTester.assertSdkTestRunPasses("testGetWebViewRenderProcess"); + } +} diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp index 56d02d21ee3..989edcac454 100644 --- a/tests/tests/security/Android.bp +++ b/tests/tests/security/Android.bp @@ -104,6 +104,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert1", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-1", manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", } @@ -111,6 +113,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert1Dup", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-1", manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", } @@ -118,6 +122,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert2", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-2", manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", } @@ -125,6 +131,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert3", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-3", manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", } @@ -132,6 +140,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert4", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-4", manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", } @@ -139,6 +149,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert12", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-1", additional_certificates: [ ":permission-test-cert-2", @@ -149,6 +161,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert12Dup", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-1", additional_certificates: [ ":permission-test-cert-2", @@ -159,6 +173,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert34", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-3", additional_certificates: [ ":permission-test-cert-4", @@ -169,6 +185,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert123", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-1", additional_certificates: [ ":permission-test-cert-2", @@ -180,6 +198,8 @@ android_test_helper_app { android_test_helper_app { name: "CtsPermissionBackupAppCert4History124", min_sdk_version: "30", + resource_dirs: [], + asset_dirs: [], certificate: ":permission-test-cert-4", additional_certificates: [ ":permission-test-cert-1", @@ -197,6 +217,8 @@ android_app_certificate { android_test_helper_app { name: "RolePermissionOverrideTestApp", + resource_dirs: [], + asset_dirs: [], manifest: "testdata/rolepermissionoverridetestapp.xml", } diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl index 9c1a33985f3..4ed5e452d2b 100644 --- a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl +++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl @@ -20,4 +20,5 @@ interface IIsolatedService { String[] getCachedSystemServices(); IBinder getSystemService(String serviceName); boolean getProcessIsIsolated(); + void registerBroadcastReceiver(); } diff --git a/tests/tests/security/res/values/strings.xml b/tests/tests/security/res/values/strings.xml index 73dd5b93d2c..ee1f73ee241 100644 --- a/tests/tests/security/res/values/strings.xml +++ b/tests/tests/security/res/values/strings.xml @@ -25,4 +25,11 @@ VoicemailSettingsActivity</string> <string name="cve_2021_0642_msgResolveErrorPocAction">The intent with action CVE_2021_0642_ACTION should not be resolved to test package</string> + + <!-- CVE-2023-20338 --> + <string name="cve_2022_20338_authority">google.com</string> + <string name="cve_2022_20338_failMsg">Device is vulnerable to b/171966843 !!</string> + <string name="cve_2022_20338_invalidURL">https://google.com@evil.com</string> + <string name="cve_2022_20338_path">@evil.com</string> + <string name="cve_2022_20338_scheme">https</string> </resources> diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20338.java b/tests/tests/security/src/android/security/cts/CVE_2022_20338.java new file mode 100644 index 00000000000..15de30af076 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20338.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; + +import android.content.Context; +import android.net.Uri; +import android.os.Parcel; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20338 extends StsExtraBusinessLogicTestCase { + + // b/171966843 + // Vulnerable package : framework.jar + // Vulnerable app : Not applicable + @AsbSecurityTest(cveBugId = 171966843) + @Test + public void testPocCVE_2022_20338() { + try { + Context context = getInstrumentation().getContext(); + + final int representation = 1 /* REPRESENTATION_ENCODED */; + Parcel parcel = Parcel.obtain(); + parcel.writeInt(3 /* HierarchicalUri.TYPE_ID */); + parcel.writeByteArray((context.getString(R.string.cve_2022_20338_scheme)).getBytes()); + parcel.writeInt(representation); + parcel.writeByteArray( + (context.getString(R.string.cve_2022_20338_authority)).getBytes()); + parcel.writeInt(representation); + parcel.writeByteArray((context.getString(R.string.cve_2022_20338_path)).getBytes()); + parcel.writeInt(representation); + parcel.writeByteArray(null /* query */); + parcel.writeInt(representation); + parcel.writeByteArray(null /* fragment */); + parcel.setDataPosition(0); + Uri uri = Uri.CREATOR.createFromParcel(parcel); + + // on vulnerable device, the uri format will be incorrect due to improper input + // validation. The test fails if the uri string matches the invalidURL. + assertFalse( + context.getString(R.string.cve_2022_20338_failMsg), + uri.toString().equals((context.getString(R.string.cve_2022_20338_invalidURL)))); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2023_21087.java b/tests/tests/security/src/android/security/cts/CVE_2023_21087.java new file mode 100644 index 00000000000..ddb55034830 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2023_21087.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; + +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2023_21087 extends StsExtraBusinessLogicTestCase { + + @AsbSecurityTest(cveBugId = 261723753) + @Test + public void testPocCVE_2023_21087() { + try { + // Taking reference from the constant of the same name located at + // path:services\core\java\com\android\server\notification\PreferencesHelper.java + // and using twice it's original value as notificationChannelGroupCountLimit. + final int notificationChannelGroupCountLimit = 12000; + + // Adding notification channel groups more than the set limit. + NotificationManager notificationManager = + getApplicationContext().getSystemService(NotificationManager.class); + try { + for (int i = 0; i < notificationChannelGroupCountLimit; ++i) { + NotificationChannelGroup group = + new NotificationChannelGroup(String.valueOf(i), String.valueOf(i)); + notificationManager.createNotificationChannelGroup(group); + } + fail( + "Device is vulnerable to b/261723753 !! Allowed to create notification" + + " channel groups more than the current count limit of 6000"); + } catch (IllegalStateException e) { + + // In the presence of fix adding notification channel group above the limit + // will cause an IllegalStateException to occur so ignore. + } + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java index 91e39e8a5fe..6bf5733b6db 100644 --- a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java +++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java @@ -15,6 +15,8 @@ */ package android.security.cts; +import static org.junit.Assert.assertThrows; + import android.app.Instrumentation; import android.content.ComponentName; import android.content.Context; @@ -27,17 +29,23 @@ import android.platform.test.annotations.AsbSecurityTest; import android.security.cts.IIsolatedService; import android.security.cts.IsolatedService; import android.util.Log; + import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + import com.android.internal.util.ArrayUtils; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + import junit.framework.Assert; -import org.junit.Before; -import org.junit.After; -import androidx.test.runner.AndroidJUnit4; -import org.junit.runner.RunWith; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + + @RunWith(AndroidJUnit4.class) public class IsolatedProcessTest { @@ -112,6 +120,11 @@ public class IsolatedProcessTest { Assert.assertTrue(mService.getProcessIsIsolated()); } + @Test + public void testRegisterBroadcastListener() throws RemoteException { + assertThrows(SecurityException.class, () -> mService.registerBroadcastReceiver()); + } + @After public void tearDown() { getInstrumentation().getContext().unbindService(mServiceConnection); diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java index 094f689a669..d77ef65ef31 100644 --- a/tests/tests/security/src/android/security/cts/IsolatedService.java +++ b/tests/tests/security/src/android/security/cts/IsolatedService.java @@ -18,9 +18,13 @@ package android.security.cts; import android.app.Service; import android.content.Intent; +import android.content.IntentFilter; import android.os.IBinder; import android.os.Process; import android.util.Log; + +import com.android.compatibility.common.util.BlockingBroadcastReceiver; + import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; @@ -79,6 +83,11 @@ public class IsolatedService extends Service { return Process.isIsolated(); } + public void registerBroadcastReceiver() throws SecurityException { + BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver( + getApplicationContext()); + registerReceiver(receiver, new IntentFilter("testAction")); + } }; @Override diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml index 15933b6e967..3bcb78ef8c1 100644 --- a/tests/tests/systemui/AndroidManifest.xml +++ b/tests/tests/systemui/AndroidManifest.xml @@ -29,13 +29,11 @@ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application android:requestLegacyExternalStorage="true"> - <!-- Have LightBarActivity always take up the full screen, regardless of orientation, - so it's never letterboxed. If the activity is letterboxed, then the status icons will - be outside of the activity and the tests will fail. See b/246515090. --> + <!-- Have LightBarActivity always be in portrait orientation, since the test's screenshots + rely on portrait orientation. See b/260069585. --> <activity android:name=".LightBarActivity" android:theme="@android:style/Theme.Material.NoActionBar" - android:screenOrientation="unspecified" - android:resizeableActivity="true"/> + android:screenOrientation="portrait"/> <activity android:name=".LightBarThemeActivity" android:theme="@style/LightBarTheme" android:screenOrientation="portrait"/> diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java index 50792176d28..c09cb536d3c 100644 --- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java +++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java @@ -41,6 +41,7 @@ import android.os.SystemClock; import android.permission.PermissionManager; import android.permission.cts.PermissionUtils; import android.platform.test.annotations.AppModeFull; +import android.server.wm.IgnoreOrientationRequestSession; import android.view.Gravity; import android.view.InputDevice; import android.view.MotionEvent; @@ -54,6 +55,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.SystemUtil; import com.android.compatibility.common.util.ThrowingRunnable; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -86,6 +89,7 @@ public class LightBarTests extends LightBarTestBase { private final String NOTIFICATION_CHANNEL_ID = "test_channel"; private final String NOTIFICATION_GROUP_KEY = "test_group"; private NotificationManager mNm; + private IgnoreOrientationRequestSession mOrientationRequestSession; @Rule public ActivityTestRule<LightBarActivity> mActivityRule = new ActivityTestRule<>( @@ -93,6 +97,28 @@ public class LightBarTests extends LightBarTestBase { @Rule public TestName mTestName = new TestName(); + + + @Before + public void setUp() { + // We need to prevent letterboxing because when an activity is letterboxed, then the status + // bar icons are outside the activity space so our verification will fail. See b/246515090. + // + // When ignore_orientation_request is set to true and the device is in landscape but the + // activity is in portrait, then the device remains in landscape but letterboxes the + // activity (so the activity is *not* full screen). Setting ignore_orientation_request to + // false will cause the device to instead rotate to portrait to match the activity, thus + // preventing letterboxing. + mOrientationRequestSession = new IgnoreOrientationRequestSession(false /* enable */); + } + + @After + public void tearDown() { + if (mOrientationRequestSession != null) { + mOrientationRequestSession.close(); + } + } + @Test @AppModeFull // Instant apps cannot create notifications public void testLightStatusBarIcons() throws Throwable { diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml index 57d09e7bec8..da8f52c7b87 100644 --- a/tests/tests/telecom/AndroidManifest.xml +++ b/tests/tests/telecom/AndroidManifest.xml @@ -96,6 +96,13 @@ </intent-filter> </service> + <service android:name=".NullBindingCallRedirectionServiceController" + android:exported="true"> + <intent-filter> + <action android:name="android.telecom.cts.ACTION_CONTROL_CALL_REDIRECTION_SERVICE"/> + </intent-filter> + </service> + <service android:name="android.telecom.cts.MockInCallService" android:permission="android.permission.BIND_INCALL_SERVICE" android:exported="true"> diff --git a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java index 53a36c93013..060afe9e305 100644 --- a/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/CallRedirectionServiceTest.java @@ -25,7 +25,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; - import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -33,13 +32,14 @@ import android.os.IBinder; import android.os.Looper; import android.os.Process; import android.os.UserHandle; - import android.telecom.Call; import android.telecom.PhoneAccount; +import android.telecom.TelecomManager; import android.telecom.cts.redirectiontestapp.CtsCallRedirectionService; import android.telecom.cts.redirectiontestapp.CtsCallRedirectionServiceController; import android.telecom.cts.redirectiontestapp.ICtsCallRedirectionServiceController; import android.text.TextUtils; +import android.util.Log; import com.android.compatibility.common.util.CddTest; @@ -91,7 +91,7 @@ public class CallRedirectionServiceTest extends BaseTelecomTestWithMockServices NewOutgoingCallBroadcastReceiver.reset(); mHandler = new Handler(Looper.getMainLooper()); mRoleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE); - setupControlBinder(); + setupControlBinder(null, null); setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE); rememberPreviousCallRedirectionApp(); // Ensure CTS app holds the call redirection role. @@ -230,16 +230,46 @@ public class CallRedirectionServiceTest extends BaseTelecomTestWithMockServices assertFalse(mCallRedirectionServiceController.waitForOnPlaceCallInvoked()); } + public void testServiceUnbindOnNullBinding() + throws Exception { + if (!shouldTestTelecom(mContext)) { + return; + } + // Set up control binder using NullBindingCallRedirectionServiceController. + // This will invoke onNullBinding. + setupControlBinder(NullBindingCallRedirectionServiceController.CONTROL_INTERFACE_ACTION, + NullBindingCallRedirectionServiceController.CONTROL_INTERFACE_COMPONENT); + Bundle extras = new Bundle(); + extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, + TestUtils.TEST_PHONE_ACCOUNT_HANDLE); + mTelecomManager.placeCall(createTestNumber(), extras); + TestUtils.waitOnLocalMainLooper(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); + TestUtils.waitOnAllHandlers(getInstrumentation()); + // Assert bind and unbind latch countdown + assertTrue(TestUtils.waitForLatchCountDown( + NullBindingCallRedirectionServiceController.sBindLatch)); + assertTrue(TestUtils.waitForLatchCountDown( + NullBindingCallRedirectionServiceController.sUnbindLatch)); + } + /** * Sets up a binder used to control the CallRedirectionServiceCtsTestApp. * This app is a standalone APK so that it can reside in a package name outside of the one the * CTS test itself runs in. * @throws InterruptedException */ - private void setupControlBinder() throws InterruptedException { - Intent bindIntent = new Intent( - CtsCallRedirectionServiceController.CONTROL_INTERFACE_ACTION); - bindIntent.setComponent(CtsCallRedirectionServiceController.CONTROL_INTERFACE_COMPONENT); + private void setupControlBinder(String interfaceAction, + ComponentName interfaceComponentName) throws InterruptedException { + if (interfaceAction == null) { + interfaceAction = CtsCallRedirectionServiceController.CONTROL_INTERFACE_ACTION; + } + if (interfaceComponentName == null) { + interfaceComponentName = + CtsCallRedirectionServiceController.CONTROL_INTERFACE_COMPONENT; + } + + Intent bindIntent = new Intent(interfaceAction); + bindIntent.setComponent(interfaceComponentName); final CountDownLatch bindLatch = new CountDownLatch(1); boolean success = mContext.bindService(bindIntent, new ServiceConnection() { @@ -253,6 +283,15 @@ public class CallRedirectionServiceTest extends BaseTelecomTestWithMockServices public void onServiceDisconnected(ComponentName name) { mCallRedirectionServiceController = null; } + + @Override + public void onNullBinding(ComponentName name) { + // This path will only be hit if NullBindingCallRedirectionServiceController is set + // as the controller. + Log.i(TAG, "onNullBinding: null binding received from onBind"); + bindLatch.countDown(); + mContext.unbindService(this); + } }, Context.BIND_AUTO_CREATE); if (!success) { fail("Failed to get control interface -- bind error"); diff --git a/tests/tests/telecom/src/android/telecom/cts/NullBindingCallRedirectionServiceController.java b/tests/tests/telecom/src/android/telecom/cts/NullBindingCallRedirectionServiceController.java new file mode 100644 index 00000000000..2ffc6d65ee9 --- /dev/null +++ b/tests/tests/telecom/src/android/telecom/cts/NullBindingCallRedirectionServiceController.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecom.cts; + +import android.app.Service; +import android.content.ComponentName; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +import java.util.concurrent.CountDownLatch; + +public class NullBindingCallRedirectionServiceController extends Service { + private static final String TAG = NullBindingCallRedirectionServiceController.class + .getSimpleName(); + public static final String CONTROL_INTERFACE_ACTION = + "android.telecom.cts.ACTION_CONTROL_CALL_REDIRECTION_SERVICE"; + public static final ComponentName CONTROL_INTERFACE_COMPONENT = + ComponentName.unflattenFromString( + "android.telecom.cts/.NullBindingCallRedirectionServiceController"); + + public static CountDownLatch sBindLatch = new CountDownLatch(1); + public static CountDownLatch sUnbindLatch = new CountDownLatch(1); + + private static NullBindingCallRedirectionServiceController + sCallRedirectionServiceController = null; + + + public static NullBindingCallRedirectionServiceController getInstance() { + return sCallRedirectionServiceController; + } + + public static void resetBindLatches() { + sBindLatch = new CountDownLatch(1); + sUnbindLatch = new CountDownLatch(1); + } + + @Override + public IBinder onBind(Intent intent) { + Log.i(TAG, "onBind: returning null binding"); + sCallRedirectionServiceController = this; + // Treat case as null binding from onBind. This should hit onNullBinding. + sUnbindLatch = new CountDownLatch(1); + sBindLatch.countDown(); + return null; + } + + @Override + public boolean onUnbind(Intent intent) { + Log.i(TAG, "onUnbind: unbinding service"); + sCallRedirectionServiceController = null; + sUnbindLatch.countDown(); + return false; + } +} + diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java index cf46203dbcb..291ce7c2bab 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java @@ -1210,10 +1210,8 @@ public class TelephonyManagerTest { Build::getSerial); assertNotNull("Non-telephony devices must have a Build.getSerial() number.", serial); - assertTrue("Hardware id must be no longer than 20 characters.", - serial.length() <= 20); assertTrue("Hardware id must be alphanumeric.", - Pattern.matches("[0-9A-Za-z]+", serial)); + Pattern.matches("[0-9A-Za-z.,_-]+", serial)); } private void assertMacAddress(String macAddress) { diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt index 7a91ab9ed79..08666e20809 100644 --- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt +++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.kt @@ -81,7 +81,9 @@ class DrawActivity : Activity() { } window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or - View.SYSTEM_UI_FLAG_FULLSCREEN + View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY window.decorView.keepScreenOn = true mHandler = RenderSpecHandler() diff --git a/tests/tests/view/src/android/view/cts/OWNERS b/tests/tests/view/src/android/view/cts/OWNERS index b7cb83689dc..3ecbdbc6e14 100644 --- a/tests/tests/view/src/android/view/cts/OWNERS +++ b/tests/tests/view/src/android/view/cts/OWNERS @@ -2,6 +2,7 @@ per-file ASurfaceControlBackPressureTest.java = file:platform/frameworks/base:/s per-file ASurfaceControlTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS per-file AttachedSurfaceControlSyncTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS per-file AttachedSurfaceControlTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS +per-file SetTouchableRegionTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS per-file SurfaceViewSyncTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS per-file *ScrollCapture*.java = file:platform/frameworks/base:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS diff --git a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java index c89cdb858d6..9cff62b816c 100644 --- a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java +++ b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java @@ -72,6 +72,9 @@ public class PixelCopyViewProducerActivity extends Activity implements OnDrawLis view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); mContent.getViewTreeObserver().addOnDrawListener(this); mContent.setOnApplyWindowInsetsListener(this); + + //setDecorFitsSystemWindows to false will ignore the cutout + getWindow().setDecorFitsSystemWindows(false); } @Override diff --git a/tests/tests/view/src/android/view/cts/SetTouchableRegionTest.java b/tests/tests/view/src/android/view/cts/SetTouchableRegionTest.java index ad73c21fb3a..2cf7b5c41f2 100644 --- a/tests/tests/view/src/android/view/cts/SetTouchableRegionTest.java +++ b/tests/tests/view/src/android/view/cts/SetTouchableRegionTest.java @@ -16,14 +16,17 @@ package android.view.cts; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import android.app.Activity; +import android.app.ActivityOptions; import android.app.Instrumentation; import android.content.Context; +import android.content.Intent; import android.graphics.Region; -import android.view.AttachedSurfaceControl; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -33,22 +36,18 @@ import android.widget.PopupWindow; import com.android.compatibility.common.util.CtsTouchUtils; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; -import androidx.test.rule.ActivityTestRule; +import androidx.test.core.app.ActivityScenario; import androidx.test.runner.AndroidJUnit4; @RunWith(AndroidJUnit4.class) public class SetTouchableRegionTest { private Instrumentation mInstrumentation; private Activity mActivity; - @Rule - public ActivityTestRule<CtsActivity> mActivityRule = - new ActivityTestRule<>(CtsActivity.class); + private ActivityScenario<CtsActivity> mScenario; class MotionRecordingView extends View { public MotionRecordingView(Context context) { @@ -80,20 +79,28 @@ public class SetTouchableRegionTest { @Before public void setup() { mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mActivity = mActivityRule.getActivity(); + // Launch activity in fullscreen windowing mode + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mInstrumentation.getTargetContext(), CtsActivity.class); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + mScenario = ActivityScenario.launch(intent, options.toBundle()); + mScenario.onActivity(activity -> { + mActivity = activity; + }); } void tapSync() { mInstrumentation.waitForIdleSync(); assertFalse(mMotionRecordingView.gotEvent()); - CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mMotionRecordingView); + CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, null, mMotionRecordingView); mInstrumentation.waitForIdleSync(); } @Test public void testClickthroughRegion() throws Throwable { - mActivityRule.runOnUiThread(() -> { + mScenario.onActivity(activity -> { mMotionRecordingView = new MotionRecordingView(mActivity); mActivity.setContentView(mMotionRecordingView); }); @@ -101,7 +108,7 @@ public class SetTouchableRegionTest { // We have a view filling our entire hierarchy and so a tap should reach it assertTrue(mMotionRecordingView.gotEvent()); - mActivityRule.runOnUiThread(() -> { + mScenario.onActivity(activity -> { mPopupView = new View(mActivity); PopupWindow popup = new PopupWindow(mPopupView, ViewGroup.LayoutParams.FILL_PARENT, @@ -114,7 +121,7 @@ public class SetTouchableRegionTest { // and so the tap should not reach us assertFalse(mMotionRecordingView.gotEvent()); - mActivityRule.runOnUiThread(() -> { + mScenario.onActivity(activity -> { mPopupView.getRootSurfaceControl().setTouchableRegion(new Region()); }); tapSync(); diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java index c4f36b7c8e4..08c2231673a 100644 --- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java +++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java @@ -97,6 +97,7 @@ public class ASurfaceControlTestActivity extends Activity { decorView.setPointerIcon( PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + getWindow().setDecorFitsSystemWindows(false); mLayoutParams = new FrameLayout.LayoutParams(DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT, Gravity.LEFT | Gravity.TOP); @@ -156,6 +157,12 @@ public class ASurfaceControlTestActivity extends Activity { } public void verifyScreenshot(PixelChecker pixelChecker, TestName name) { + int retries = 0; + int maxRetries = 2; + int numMatchingPixels = 0; + Rect bounds = null; + boolean success = false; + // Wait for the stable insets update. The position of the surface view is in correct before // the update. Sometimes this callback isn't called, so we don't want to fail the test // because it times out. @@ -163,33 +170,44 @@ public class ASurfaceControlTestActivity extends Activity { Log.w(TAG, "Insets animation wait timed out."); } - final CountDownLatch countDownLatch = new CountDownLatch(1); - UiAutomation uiAutomation = mInstrumentation.getUiAutomation(); - mHandler.post(() -> { - mScreenshot = uiAutomation.takeScreenshot(getWindow()); - mParent.removeAllViews(); - countDownLatch.countDown(); - }); - - try { - countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS); - } catch (Exception e) { - } - - assertNotNull(mScreenshot); + while (retries < maxRetries) { + final CountDownLatch countDownLatch = new CountDownLatch(1); + UiAutomation uiAutomation = mInstrumentation.getUiAutomation(); + mHandler.post(() -> { + mScreenshot = uiAutomation.takeScreenshot(getWindow()); + countDownLatch.countDown(); + }); - Bitmap swBitmap = mScreenshot.copy(Bitmap.Config.ARGB_8888, false); - mScreenshot.recycle(); + try { + countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS); + } catch (Exception e) { + } - int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap); - Rect bounds = pixelChecker.getBoundsToCheck(swBitmap); - boolean success = pixelChecker.checkPixels(numMatchingPixels, swBitmap.getWidth(), - swBitmap.getHeight()); - if (!success) { - saveFailureCapture(swBitmap, name); + assertNotNull(mScreenshot); + + Bitmap swBitmap = mScreenshot.copy(Bitmap.Config.ARGB_8888, false); + mScreenshot.recycle(); + + numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap); + bounds = pixelChecker.getBoundsToCheck(swBitmap); + success = pixelChecker.checkPixels(numMatchingPixels, swBitmap.getWidth(), + swBitmap.getHeight()); + if (!success) { + saveFailureCapture(swBitmap, name); + swBitmap.recycle(); + retries++; + try { + Thread.sleep(300); + } catch (InterruptedException e) { + } + } else { + swBitmap.recycle(); + break; + } } - swBitmap.recycle(); - + mHandler.post(() -> { + mParent.removeAllViews(); + }); assertTrue("Actual matched pixels:" + numMatchingPixels + " Bitmap size:" + bounds.width() + "x" + bounds.height(), success); } diff --git a/tests/tests/webkit/src/android/webkit/cts/MimeTypeMapTest.java b/tests/tests/webkit/src/android/webkit/cts/MimeTypeMapTest.java index d112353ce19..b59d19c7878 100644 --- a/tests/tests/webkit/src/android/webkit/cts/MimeTypeMapTest.java +++ b/tests/tests/webkit/src/android/webkit/cts/MimeTypeMapTest.java @@ -33,7 +33,7 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) -public class MimeTypeMapTest { +public class MimeTypeMapTest extends SharedWebViewTest{ private MimeTypeMap mMimeTypeMap; @@ -42,6 +42,11 @@ public class MimeTypeMapTest { mMimeTypeMap = MimeTypeMap.getSingleton(); } + @Override + protected SharedWebViewTestEnvironment createTestEnvironment() { + return new SharedWebViewTestEnvironment.Builder().build(); + } + @Test public void testGetFileExtensionFromUrl() { assertEquals("html", MimeTypeMap.getFileExtensionFromUrl("http://localhost/index.html")); diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java index 8b0c7228cf2..33ee18733a0 100644 --- a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java +++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java @@ -38,8 +38,7 @@ import org.junit.runner.RunWith; @MediumTest @RunWith(AndroidJUnit4.class) -public class ServiceWorkerWebSettingsTest { - +public class ServiceWorkerWebSettingsTest extends SharedWebViewTest { private ServiceWorkerWebSettings mSettings; private WebViewOnUiThread mOnUiThread; @@ -49,14 +48,10 @@ public class ServiceWorkerWebSettingsTest { @Before public void setUp() throws Exception { - Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable()); - mActivityScenarioRule.getScenario().onActivity(activity -> { - WebViewCtsActivity webViewCtsActivity = (WebViewCtsActivity) activity; - WebView webview = webViewCtsActivity.getWebView(); - if (webview != null) { - mOnUiThread = new WebViewOnUiThread(webview); - } - }); + WebView webview = getTestEnvironment().getWebView(); + if (webview != null) { + mOnUiThread = new WebViewOnUiThread(webview); + } mSettings = ServiceWorkerController.getInstance().getServiceWorkerWebSettings(); } @@ -67,6 +62,27 @@ public class ServiceWorkerWebSettingsTest { } } + @Override + protected SharedWebViewTestEnvironment createTestEnvironment() { + Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable()); + + SharedWebViewTestEnvironment.Builder builder = new SharedWebViewTestEnvironment.Builder(); + + mActivityScenarioRule + .getScenario() + .onActivity( + activity -> { + WebView webView = ((WebViewCtsActivity) activity).getWebView(); + builder.setHostAppInvoker( + SharedWebViewTestEnvironment.createHostAppInvoker( + activity)) + .setContext(activity) + .setWebView(webView); + }); + + return builder.build(); + } + /** * This should remain functionally equivalent to * androidx.webkit.ServiceWorkerWebSettingsCompatTest#testCacheMode. Modifications to this test diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java index 288932421ea..5f5e1183ed4 100644 --- a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java +++ b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java @@ -48,7 +48,7 @@ import java.util.concurrent.Future; @AppModeFull @MediumTest @RunWith(AndroidJUnit4.class) -public class WebViewRenderProcessTest { +public class WebViewRenderProcessTest extends SharedWebViewTest { private WebViewOnUiThread mOnUiThread; @Rule @@ -57,14 +57,10 @@ public class WebViewRenderProcessTest { @Before public void setUp() throws Exception { - Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable()); - mActivityScenarioRule.getScenario().onActivity(activity -> { - WebViewCtsActivity webViewCtsActivity = (WebViewCtsActivity) activity; - WebView webview = webViewCtsActivity.getWebView(); - if (webview != null) { - mOnUiThread = new WebViewOnUiThread(webview); - } - }); + WebView webview = getTestEnvironment().getWebView(); + if (webview != null) { + mOnUiThread = new WebViewOnUiThread(webview); + } } @After @@ -74,6 +70,26 @@ public class WebViewRenderProcessTest { } } + @Override + protected SharedWebViewTestEnvironment createTestEnvironment() { + Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable()); + + SharedWebViewTestEnvironment.Builder builder = new SharedWebViewTestEnvironment.Builder(); + + mActivityScenarioRule + .getScenario() + .onActivity( + activity -> { + WebView webView = ((WebViewCtsActivity) activity).getWebView(); + builder.setHostAppInvoker( + SharedWebViewTestEnvironment.createHostAppInvoker( + activity)) + .setWebView(webView); + }); + + return builder.build(); + } + private boolean terminateRenderProcessOnUiThread( final WebViewRenderProcess renderer) { return WebkitUtils.onMainThreadSync(() -> { diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml index f75cabf52df..3d1a7a7d9ec 100644 --- a/tests/tests/widget/res/values/styles.xml +++ b/tests/tests/widget/res/values/styles.xml @@ -400,6 +400,8 @@ </style> <style name="Theme.PopupWindowCtsActivity" parent="@android:style/Theme.Holo"> + <item name="android:windowSwipeToDismiss">false</item> + <item name="android:windowLayoutInDisplayCutoutMode">never</item> </style> <style name="TextView_FontResource"> diff --git a/tests/tests/widget/res/values/themes.xml b/tests/tests/widget/res/values/themes.xml index 938452e669c..374834a8d9d 100644 --- a/tests/tests/widget/res/values/themes.xml +++ b/tests/tests/widget/res/values/themes.xml @@ -25,7 +25,7 @@ <item name="themeString">@string/remoteviews_theme_string</item> </style> - <style name="HorizontalScrollViewCtsActivityTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="HorizontalScrollViewCtsActivityTheme" parent="@android:style/Theme.Material.Light.DarkActionBar"> <item name="android:windowSwipeToDismiss">false</item> </style> </resources> diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java index c74f21b9eb7..d94667cb58b 100644 --- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java +++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java @@ -16,6 +16,8 @@ package android.widget.cts; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -34,8 +36,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.app.Activity; +import android.app.ActivityOptions; import android.app.Instrumentation; import android.content.Context; +import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -56,6 +60,7 @@ import android.widget.PopupWindow; import android.widget.TextView; import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ActivityScenario; import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @@ -89,6 +94,8 @@ public class ListPopupWindowTest { private AdapterView.OnItemClickListener mItemClickListener; + private ActivityScenario<ListPopupWindowCtsActivity> mScenario; + /** * Item click listener that dismisses our <code>ListPopupWindow</code> when any item * is clicked. Note that this needs to be a separate class that is also protected (not @@ -500,7 +507,7 @@ public class ListPopupWindowTest { final ListView popupListView = mPopupWindow.getListView(); final Rect rect = new Rect(); mPopupWindow.getBackground().getPadding(rect); - CtsTouchUtils.emulateTapOnView(instrumentation, mActivityRule, popupListView, + CtsTouchUtils.emulateTapOnView(instrumentation, null, popupListView, -rect.left - 20, popupListView.getHeight() / 2); // At this point our popup should not be showing and should have notified its @@ -515,6 +522,21 @@ public class ListPopupWindowTest { @Test public void testDismissalOutsideNonModal() { + // Close the activity that was launched by setup + mActivityRule.finishActivity(); + + // Launch activity in fullscreen windowing mode + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mInstrumentation.getTargetContext(), ListPopupWindowCtsActivity.class); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + + mScenario = ActivityScenario.launch(intent, options.toBundle()); + mScenario.onActivity(activity -> { + mActivity = activity; + mActivity.getApplicationInfo().setEnableOnBackInvokedCallback(false); + }); + verifyDismissalViaTouch(false); } diff --git a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java index bca2bc72cfa..50ee3b1f4b8 100755 --- a/tests/tests/widget/src/android/widget/cts/SpinnerTest.java +++ b/tests/tests/widget/src/android/widget/cts/SpinnerTest.java @@ -403,34 +403,6 @@ public class SpinnerTest { TestUtils.assertAllPixelsOfColor("Drop down should be blue", dropDownBackground, dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(), false, Color.BLUE, 1, true); - waitForHasFocusMS(SPINNER_HAS_FOCUS_DELAY_MS); - // Dismiss the popup with the emulated back key - mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); - // Verify that we're not showing the popup - PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing()); - - // Set yellow background on the popup - mActivityRule.runOnUiThread(() -> - mSpinnerDropdownMode.setPopupBackgroundDrawable( - mActivity.getDrawable(R.drawable.yellow_fill))); - - // Use instrumentation to emulate a tap on the spinner to bring down its popup - WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSpinnerDropdownMode, () -> { - mSpinnerDropdownMode.performClick(); - }); - // Verify that we're showing the popup - PollingCheck.waitFor(() -> mSpinnerDropdownMode.isPopupShowing()); - // And test its fill - dropDownBackground = mSpinnerDropdownMode.getPopupBackground(); - TestUtils.assertAllPixelsOfColor("Drop down should be yellow", dropDownBackground, - dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(), - false, Color.YELLOW, 1, true); - - waitForHasFocusMS(SPINNER_HAS_FOCUS_DELAY_MS); - // Dismiss the popup with the emulated escape key - mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ESCAPE); - // Verify that we're not showing the popup - PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing()); } @Test @@ -450,38 +422,5 @@ public class SpinnerTest { PollingCheck.waitFor(() -> mSpinnerDialogMode.isPopupShowing()); // And test that getPopupBackground returns null assertNull(mSpinnerDialogMode.getPopupBackground()); - - // Dismiss the popup with the emulated back key - mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); - // Verify that we're not showing the popup - PollingCheck.waitFor(() -> !mSpinnerDialogMode.isPopupShowing()); - - // Set yellow background on the popup - mActivityRule.runOnUiThread(() -> - mSpinnerDialogMode.setPopupBackgroundDrawable( - mActivity.getDrawable(R.drawable.yellow_fill))); - - // Use instrumentation to emulate a tap on the spinner to bring down its popup - WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSpinnerDialogMode, () -> { - mSpinnerDialogMode.performClick(); - }); - // Verify that we're showing the popup - PollingCheck.waitFor(() -> mSpinnerDialogMode.isPopupShowing()); - // And test that getPopupBackground returns null - assertNull(mSpinnerDialogMode.getPopupBackground()); - - // Use emulated escape key to close popup - mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ESCAPE); - // Verify that we're not showing the popup - PollingCheck.waitFor(() -> !mSpinnerDropdownMode.isPopupShowing()); - } - - private void waitForHasFocusMS(int milliseconds) { - try { - Thread.sleep(milliseconds); - } catch (InterruptedException e) { - fail("unexpected InterruptedException : "+ e); - } - } } diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java index 38dedf66091..ca8dfb043cd 100644 --- a/tests/tests/widget/src/android/widget/cts/ToastTest.java +++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java @@ -64,6 +64,7 @@ import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.PollingCheck; import com.android.compatibility.common.util.SystemUtil; import com.android.compatibility.common.util.TestUtils; @@ -989,7 +990,9 @@ public class ToastTest { * us to wait a given amount of time to test that the limit has been enforced/lifted. */ @Test + @ApiTest(apis = {"android.widget.Toast#show"}) public void testRateLimitingToastsWhenInBackground() throws Throwable { + assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch()); // enable rate limiting to test it SystemUtil.runWithShellPermissionIdentity(() -> mNotificationManager .setToastRateLimitingEnabled(true)); @@ -1051,7 +1054,9 @@ public class ToastTest { } @Test + @ApiTest(apis = {"android.widget.Toast#show"}) public void testAppWithUnlimitedToastsPermissionCanPostUnlimitedToasts() throws Throwable { + assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch()); // enable rate limiting to test it SystemUtil.runWithShellPermissionIdentity(() -> mNotificationManager .setToastRateLimitingEnabled(true)); diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java index 864b31c6bf2..90941ab8788 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java @@ -1884,6 +1884,7 @@ public class WifiManagerTest extends WifiJUnit3TestBase { TestSoftApCallback lohsSoftApCallback = new TestSoftApCallback(mLock); UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); setWifiEnabled(false); + Thread.sleep(TEST_WAIT_DURATION_MS); boolean wifiEnabled = mWifiManager.isWifiEnabled(); try { uiAutomation.adoptShellPermissionIdentity(); diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed index 3d57a11d0f7..39292355dbc 100755 --- a/tools/cts-tradefed/etc/cts-tradefed +++ b/tools/cts-tradefed/etc/cts-tradefed @@ -99,6 +99,9 @@ JAR_DIR=${CTS_ROOT}/android-cts/tools for JAR in ${JAR_DIR}/*.jar; do JAR_PATH=${JAR_PATH}:${JAR} done +# Move 'tradefed.jar' to the front of the jar path +TRADEFED_JAR=":${JAR_DIR}/tradefed.jar" +JAR_PATH="${TRADEFED_JAR}${JAR_PATH/$TRADEFED_JAR}" JAR_PATH=${JAR_PATH:1} # Strip off leading ':' # load any shared libraries for host-side executables diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml index 4f6360f27ca..39e5c1f7bcd 100644 --- a/tools/cts-tradefed/res/config/cts-known-failures.xml +++ b/tools/cts-tradefed/res/config/cts-known-failures.xml @@ -314,4 +314,9 @@ <!-- b/266101051--> <option name="compatibility:exclude-filter" value="CtsGameServiceTestCases" /> + + <!--- b/260389884 --> + <option name="compatibility:exclude-filter" value="CtsGameManagerTestCases android.gamemanager.cts.GameManagerTest#testSetGameStatePerformanceMode" /> + <option name="compatibility:exclude-filter" value="CtsGameManagerTestCases android.gamemanager.cts.GameManagerTest#testSetGameStatePerformanceMode_withParams" /> + <option name="compatibility:exclude-filter" value="CtsGameManagerTestCases android.gamemanager.cts.GameManagerTest#testSetGameStateStandardMode" /> </configuration> diff --git a/tools/cts-tradefed/res/config/cts-system.xml b/tools/cts-tradefed/res/config/cts-system.xml new file mode 100644 index 00000000000..530fe79beb6 --- /dev/null +++ b/tools/cts-tradefed/res/config/cts-system.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<configuration description="Runs a subset of CTS tests when vendor image is not changed"> + <option name="plan" value="cts-system" /> + <option name="result-attribute" key="system" value="1" /> + + <include name="cts" /> + + <!-- dEQP tests to be included in this plan--> + <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-EGL.*" /> + <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-VK.wsi.*" /> + <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-GLES2.functional.prerequisite#*" /> + <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-GLES3.functional.prerequisite#*" /> + <option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-VK.api.smoke#*" /> + +</configuration>
\ No newline at end of file |