diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-04 13:00:51 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-04 13:00:51 +0000 |
commit | 18aac3c1a5438c879e1f19bf395108f2e1a62366 (patch) | |
tree | 4773ffbfbbfe12a754da3f94dcee8518bad3eead | |
parent | 58f9ae0c1861f7deb2459e6b57719c04f4d6266a (diff) | |
parent | 377b4733b4a7316b25cc3d3272da002766578da3 (diff) | |
download | cts-android13-mainline-media-swcodec-release.tar.gz |
Snap for 9689921 from 377b4733b4a7316b25cc3d3272da002766578da3 to mainline-media-swcodec-releaseaml_swc_331712000android13-mainline-media-swcodec-release
Change-Id: I3a9e97fc3bb33c7e459b6a969882ab88996226fe
34 files changed, 1322 insertions, 70 deletions
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py index 1a30ac293f2..5c8c95dfcc6 100644 --- a/apps/CameraITS/tests/its_base_test.py +++ b/apps/CameraITS/tests/its_base_test.py @@ -33,6 +33,14 @@ WAIT_TIME_SEC = 5 SCROLLER_TIMEOUT_MS = 3000 VALID_NUM_DEVICES = (1, 2) NOT_YET_MANDATED_ALL = 100 +DEFAULT_TABLET_BRIGHTNESS = 192 +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 = ('Tablet brightness not set as per ' + f'{TABLET_REQUIREMENTS_URL} in the config file') # Not yet mandated tests ['test', first_api_level not yet mandatory] # ie. ['test_test_patterns', 30] is MANDATED for first_api_level > 30 @@ -56,6 +64,27 @@ NOT_YET_MANDATED = { logging.getLogger('matplotlib.font_manager').disabled = True +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) + else: + if brightness != DEFAULT_TABLET_BRIGHTNESS: + raise AssertionError(BRIGHTNESS_ERROR) + + class ItsBaseTest(base_test.BaseTestClass): """Base test for CameraITS tests. @@ -103,6 +132,12 @@ 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) + _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/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/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/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/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java new file mode 100644 index 00000000000..e56c7233335 --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBannersTest.java @@ -0,0 +1,153 @@ +/* + * 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.SHORT_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. + final String allowedCloudProviders = CloudProviderPrimary.AUTHORITY; + PhotoPickerCloudUtils.setAllowedProvidersDeviceConfig(allowedCloudProviders); + } + + @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 (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(SHORT_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/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/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java index 85b0e89fefd..02e0b0cac48 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java @@ -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/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/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml index 2e0a212502e..145e0cfcbd5 100755..100644 --- 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"/> 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/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/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/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/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/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/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(() -> { |