Migrate data sharing into profile test.
The test checks how Android handles user requests to apen a
file or a document across profile and ensures that
DISALLOW_SHARE_INTO_MANAGED_PROFILE restriction is respected.
Bug: 197767033
Test: atest android.devicepolicy.cts.CrossProfileSharingTest
Change-Id: I04eeeb6b504f80e0f4fc927624601e9ae51aa090
diff --git a/common/device-side/bedstead/testapp/manifests/EmptyTestApp2Manifest.xml b/common/device-side/bedstead/testapp/manifests/EmptyTestApp2Manifest.xml
index 5e48d51..40b1af0 100644
--- a/common/device-side/bedstead/testapp/manifests/EmptyTestApp2Manifest.xml
+++ b/common/device-side/bedstead/testapp/manifests/EmptyTestApp2Manifest.xml
@@ -22,6 +22,19 @@
android:appComponentFactory="com.android.bedstead.testapp.TestAppAppComponentFactory">
<activity android:name="android.testapp.activity" android:exported="true" />
+
+ <activity android:name="android.testapp.CrossProfileSharingActivity"
+ android:exported="true">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="com.android.testapp.SOME_ACTION"/>
+ </intent-filter>
+ <!-- Catch ACTION_PICK in case there is no other app handing it-->
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <action android:name="android.intent.action.PICK"/>
+ </intent-filter>
+ </activity>
</application>
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
index 348348c..9a37709 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DisallowSharingIntoProfileTest.java
@@ -17,6 +17,7 @@
package com.android.cts.managedprofile;
import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
import static java.util.concurrent.TimeUnit.SECONDS;
import android.app.admin.DevicePolicyManager;
@@ -130,24 +131,6 @@
/* expectForwardable */ true);
}
- /**
- * Test sharing initiated from the profile side i.e. user tries to pick up personal data within
- * a work app. See javadoc of {@link #DisallowSharingIntoProfileTest} class for the mechanism
- * behind this test.
- */
- public void testSharingFromProfile() throws Exception {
- testSetUp();
- ResolveInfo toPersonalForwarderInfo = getIntentForwarder(
- new Intent(KNOWN_ACTION_TO_PERSONAL));
-
- testDisableSharingIntoProfile();
- assertCrossProfileIntentsResolvability(sharingIntentsToPersonal, toPersonalForwarderInfo,
- /* expectForwardable */ false);
- testEnableSharingIntoProfile();
- assertCrossProfileIntentsResolvability(sharingIntentsToPersonal, toPersonalForwarderInfo,
- /* expectForwardable */ true);
- }
-
public void testEnableSharingIntoProfile() throws Exception {
setSharingEnabled(true);
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 041491a..82529ed 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -381,19 +381,6 @@
.build());
}
- @FlakyTest
- @Test
- public void testDisallowSharingIntoPersonalFromProfile() throws Exception {
- // Set up activities: PrimaryUserActivity will only be enabled in the personal user
- // This activity is used to find out the ground truth about the system's cross profile
- // intent forwarding activity.
- disableActivityForUser("PrimaryUserActivity", mProfileUserId);
-
- // Tests from the profile side
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".DisallowSharingIntoProfileTest", "testSharingFromProfile", mProfileUserId);
- }
-
@Test
public void testDisallowSharingIntoProfileFromPersonal() throws Exception {
// Set up activities: ManagedProfileActivity will only be enabled in the managed profile
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
new file mode 100644
index 0000000..e274e43
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.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.devicepolicy.cts;
+
+import static android.app.admin.DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static android.os.UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE;
+
+import static com.android.bedstead.remotedpc.RemoteDpc.DPC_COMPONENT_NAME;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile;
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.testapp.TestApp;
+import com.android.bedstead.testapp.TestAppInstanceReference;
+import com.android.bedstead.testapp.TestAppProvider;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(BedsteadJUnit4.class)
+public class CrossProfileSharingTest {
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final TestApis sTestApis = new TestApis();
+ private static final Context sContext = sTestApis.context().instrumentedContext();
+ private static final TestAppProvider sTestAppProvider = new TestAppProvider();
+
+ // TODO(b/198420874): rather than querying by package name, query apps by intents they need to
+ // handle: "com.android.testapp.SOME_ACTION" and "android.intent.action.PICK"
+ private static final TestApp sTestApp = sTestAppProvider.query().wherePackageName()
+ .isEqualTo("com.android.bedstead.testapp.EmptyTestApp2").get();
+
+ // Known action that is handled in the opposite profile, used to query forwarder activity.
+ private static final String CROSS_PROFILE_ACTION = "com.android.testapp.SOME_ACTION";
+
+ // TODO(b/191640667): use parametrization instead of looping once available.
+ // These are the data sharing intents which can be forwarded to the primary profile.
+ private static final Intent[] OPENING_INTENTS = {
+ new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Intent.ACTION_PICK).setType("*/*").addCategory(
+ Intent.CATEGORY_DEFAULT),
+ new Intent(Intent.ACTION_PICK).addCategory(Intent.CATEGORY_DEFAULT)
+ };
+
+ /**
+ * Test sharing initiated from the profile side i.e. user tries to pick up personal data within
+ * a work app when DISALLOW_SHARE_INTO_MANAGED_PROFILE is enforced.
+ */
+ @Test
+ @Postsubmit(reason = "new test")
+ @RequireRunOnWorkProfile
+ public void openingPersonalFromProfile_disallowShareIntoProfile_restrictionApplied()
+ throws Exception {
+ ResolveInfo toPersonalForwarderInfo = getToPersonalForwarderInfo();
+
+ // Enforce the restriction and wait for it to be applied.
+ setSharingIntoProfileEnabled(false);
+ // Test app handles android.intent.action.PICK just in case no other app does.
+ try (TestAppInstanceReference testAppParent =
+ sTestApp.install(sDeviceState.primaryUser())) {
+ // Verify that the intents don't resolve into cross-profile forwarder.
+ assertCrossProfileIntentsResolvability(OPENING_INTENTS,
+ toPersonalForwarderInfo, /* expectForwardable */ false);
+ } finally {
+ // Restore default state.
+ setSharingIntoProfileEnabled(true);
+ }
+ }
+
+ /**
+ * Test sharing initiated from the profile side i.e. user tries to pick up personal data within
+ * a work app when DISALLOW_SHARE_INTO_MANAGED_PROFILE is NOT enforced.
+ */
+ @Test
+ @Postsubmit(reason = "new test")
+ @RequireRunOnWorkProfile
+ public void openingPersonalFromProfile_disallowShareIntoProfile_restrictionRemoved()
+ throws Exception {
+ ResolveInfo toPersonalForwarderInfo = getToPersonalForwarderInfo();
+
+ // Enforce the restriction and wait for it to be applied, then remove it and wait again.
+ setSharingIntoProfileEnabled(false);
+ setSharingIntoProfileEnabled(true);
+
+ // Test app handles android.intent.action.PICK just in case no other app does.
+ try (TestAppInstanceReference testAppParent =
+ sTestApp.install(sDeviceState.primaryUser())) {
+ // Verify that the intents resolve into cross-profile forwarder.
+ assertCrossProfileIntentsResolvability(OPENING_INTENTS,
+ toPersonalForwarderInfo, /* expectForwardable */ true);
+ }
+ }
+
+ /**
+ * Finds ResolveInfo for a system activity that forwards cross-profile intent by resolving an
+ * intent that it handled in the primary user from the profile user.
+ * TODO(b/198759180): replace it with a @TestApi
+ */
+ private ResolveInfo getToPersonalForwarderInfo() {
+ ResolveInfo forwarderInfo;
+ try (TestAppInstanceReference testAppParent =
+ sTestApp.install(sDeviceState.primaryUser())) {
+ // Set up cross profile intent filters so we can resolve these to find out framework's
+ // intent forwarder activity as ground truth
+ sDeviceState.dpc().devicePolicyManager()
+ .addCrossProfileIntentFilter(DPC_COMPONENT_NAME,
+ new IntentFilter(CROSS_PROFILE_ACTION),
+ DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+ try {
+ forwarderInfo = getCrossProfileIntentForwarder(new Intent(CROSS_PROFILE_ACTION));
+ } finally {
+ sDeviceState.dpc().devicePolicyManager()
+ .clearCrossProfileIntentFilters(DPC_COMPONENT_NAME);
+ }
+ }
+ return forwarderInfo;
+ }
+
+ //
+ private ResolveInfo getCrossProfileIntentForwarder(Intent intent) {
+ List<ResolveInfo> result = sTestApis.context().instrumentedContext().getPackageManager()
+ .queryIntentActivities(intent, MATCH_DEFAULT_ONLY);
+ assertWithMessage("Failed to get intent forwarder component")
+ .that(result.size()).isEqualTo(1);
+ ResolveInfo forwarder = result.get(0);
+ assertWithMessage("Forwarder doesn't consider itself as such")
+ .that(forwarder.isCrossProfileIntentForwarderActivity()).isTrue();
+ return forwarder;
+ }
+
+ private void setSharingIntoProfileEnabled(boolean enabled) {
+ IntentFilter filter = new IntentFilter(ACTION_DATA_SHARING_RESTRICTION_APPLIED);
+ try (BlockingBroadcastReceiver receiver =
+ BlockingBroadcastReceiver.create(sContext, filter).register()) {
+ if (enabled) {
+ sDeviceState.dpc().devicePolicyManager().clearUserRestriction(
+ DPC_COMPONENT_NAME, DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+ } else {
+ sDeviceState.dpc().devicePolicyManager().addUserRestriction(
+ DPC_COMPONENT_NAME, DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+ }
+ }
+ }
+
+ private void assertCrossProfileIntentsResolvability(
+ Intent[] intents, ResolveInfo expectedForwarder, boolean expectForwardable) {
+ for (Intent intent : intents) {
+ List<ResolveInfo> resolveInfoList = sTestApis.context().instrumentedContext()
+ .getPackageManager().queryIntentActivities(intent, MATCH_DEFAULT_ONLY);
+ if (expectForwardable) {
+ assertWithMessage(String.format(
+ "Expect %s to be forwardable, but resolve list does not contain expected "
+ + "intent forwarder %s", intent, expectedForwarder))
+ .that(containsResolveInfo(resolveInfoList, expectedForwarder)).isTrue();
+ } else {
+ assertWithMessage(String.format(
+ "Expect %s not to be forwardable, but resolve list contains intent "
+ + "forwarder %s", intent, expectedForwarder))
+ .that(containsResolveInfo(resolveInfoList, expectedForwarder)).isFalse();
+ }
+ }
+ }
+
+ private boolean containsResolveInfo(List<ResolveInfo> list, ResolveInfo info) {
+ for (ResolveInfo entry : list) {
+ if (entry.activityInfo.packageName.equals(info.activityInfo.packageName)
+ && entry.activityInfo.name.equals(info.activityInfo.name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}