1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.managedprofile;
18 
19 import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
20 
21 import android.app.admin.DevicePolicyManager;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.os.UserManager;
29 import android.provider.MediaStore;
30 import android.test.InstrumentationTestCase;
31 
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * Verify that certain cross profile intent filters are disallowed when the device admin sets
40  * DISALLOW_SHARE_INTO_MANAGED_PROFILE restriction.
41  * <p>
42  * The way we check if a particular cross profile intent filter is disallowed is by trying to
43  * resolve an example intent that matches the intent filter. The cross profile filter functions
44  * correctly if and only if the resolution result contains a system intent forwarder activity
45  * (com.android.internal.app.IntentForwarderActivity), which is the framework's mechanism to
46  * trampoline intents across profiles. Instead of hardcoding the system's intent forwarder activity,
47  * we retrieve it programmatically by resolving known cross profile intents specifically set up for
48  * this purpose: {@link #KNOWN_ACTION_TO_PROFILE} and {@link #KNOWN_ACTION_TO_PERSONAL}
49  */
50 public class DisallowSharingIntoProfileTest extends InstrumentationTestCase {
51 
52     // These are the data sharing intents which can be forwarded to the managed profile.
53     private final List<Intent> sharingIntentsToProfile = Arrays.asList(
54             new Intent(Intent.ACTION_SEND).setType("*/*"),
55             new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*"));
56 
57     // These are the data sharing intents which can be forwarded to the primary profile.
58     private final List<Intent> sharingIntentsToPersonal = new ArrayList<>(Arrays.asList(
59             new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
60                     Intent.CATEGORY_OPENABLE),
61             new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
62                     Intent.CATEGORY_OPENABLE),
63             new Intent(Intent.ACTION_PICK).setType("*/*").addCategory(
64                     Intent.CATEGORY_DEFAULT),
65             new Intent(Intent.ACTION_PICK).addCategory(Intent.CATEGORY_DEFAULT)));
66 
67     // These are the data sharing intents which can be forwarded to the primary profile,
68     // if the device supports FEATURE_CAMERA
69     private final List<Intent> sharingIntentsToPersonalIfCameraExists = Arrays.asList(
70             new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
71             new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
72             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
73             new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
74             new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
75             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE));
76 
77     private static final String KNOWN_ACTION_TO_PROFILE = ManagedProfileActivity.ACTION;
78     private static final String KNOWN_ACTION_TO_PERSONAL = PrimaryUserActivity.ACTION;
79 
80     protected Context mContext;
81     protected DevicePolicyManager mDevicePolicyManager;
82 
83     @Override
setUp()84     public void setUp() throws Exception {
85         super.setUp();
86 
87         mContext = getInstrumentation().getContext();
88         mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
89         assertNotNull(mDevicePolicyManager);
90     }
91 
testSetUp()92     public void testSetUp() throws Exception {
93         // toggle the restriction to reset system's built-in cross profile intent filters,
94         // simulating the default state of a work profile created by ManagedProvisioning
95         testDisableSharingIntoProfile();
96         testEnableSharingIntoProfile();
97 
98         PackageManager pm = mContext.getPackageManager();
99         if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
100             sharingIntentsToPersonal.addAll(sharingIntentsToPersonalIfCameraExists);
101         }
102 
103         mDevicePolicyManager.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
104         // Set up cross profile intent filters so we can resolve these to find out framework's
105         // intent forwarder activity as ground truth
106         mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
107                 new IntentFilter(KNOWN_ACTION_TO_PERSONAL),
108                 DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
109         mDevicePolicyManager.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT,
110                 new IntentFilter(KNOWN_ACTION_TO_PROFILE),
111                 DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
112     }
113 
114     /**
115      * Test sharing initiated from the personal side are mainly driven from the host side, see
116      * ManagedProfileTest.testDisallowSharingIntoProfileFromPersonal() See javadoc of
117      * {@link #DisallowSharingIntoProfileTest} class for the mechanism behind this test.
118      */
testSharingFromPersonalFails()119     public void testSharingFromPersonalFails() {
120         ResolveInfo toProfileForwarderInfo = getIntentForwarder(
121                 new Intent(KNOWN_ACTION_TO_PROFILE));
122         assertCrossProfileIntentsResolvability(sharingIntentsToProfile, toProfileForwarderInfo,
123                 /* expectForwardable */ false);
124     }
125 
testSharingFromPersonalSucceeds()126     public void testSharingFromPersonalSucceeds() {
127         ResolveInfo toProfileForwarderInfo = getIntentForwarder(
128                 new Intent(KNOWN_ACTION_TO_PROFILE));
129         assertCrossProfileIntentsResolvability(sharingIntentsToProfile, toProfileForwarderInfo,
130                 /* expectForwardable */ true);
131     }
132 
133     /**
134      * Test sharing initiated from the profile side i.e. user tries to pick up personal data within
135      * a work app. See javadoc of {@link #DisallowSharingIntoProfileTest} class for the mechanism
136      * behind this test.
137      */
testSharingFromProfile()138     public void testSharingFromProfile() throws Exception {
139         testSetUp();
140         ResolveInfo toPersonalForwarderInfo = getIntentForwarder(
141                 new Intent(KNOWN_ACTION_TO_PERSONAL));
142 
143         testDisableSharingIntoProfile();
144         assertCrossProfileIntentsResolvability(sharingIntentsToPersonal, toPersonalForwarderInfo,
145                 /* expectForwardable */ false);
146         testEnableSharingIntoProfile();
147         assertCrossProfileIntentsResolvability(sharingIntentsToPersonal, toPersonalForwarderInfo,
148                 /* expectForwardable */ true);
149     }
150 
testEnableSharingIntoProfile()151     public void testEnableSharingIntoProfile() throws Exception {
152         setSharingEnabled(true);
153     }
154 
testDisableSharingIntoProfile()155     public void testDisableSharingIntoProfile() throws Exception {
156         setSharingEnabled(false);
157     }
158 
setSharingEnabled(boolean enabled)159     private void setSharingEnabled(boolean enabled) throws InterruptedException {
160         final CountDownLatch latch = new CountDownLatch(1);
161         BroadcastReceiver receiver = new BroadcastReceiver() {
162             @Override
163             public void onReceive(Context context, Intent intent) {
164                 latch.countDown();
165             }
166         };
167         IntentFilter filter = new IntentFilter();
168         filter.addAction(DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
169         mContext.registerReceiver(receiver, filter);
170         try {
171             if (enabled) {
172                 mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
173                         UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
174             } else {
175                 mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
176                         UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
177             }
178             // Wait for the restriction to apply
179             assertTrue("Restriction not applied after 5 seconds", latch.await(5, TimeUnit.SECONDS));
180         } finally {
181             mContext.unregisterReceiver(receiver);
182         }
183     }
184 
assertCrossProfileIntentsResolvability(List<Intent> intents, ResolveInfo expectedForwarder, boolean expectForwardable)185     private void assertCrossProfileIntentsResolvability(List<Intent> intents,
186             ResolveInfo expectedForwarder, boolean expectForwardable) {
187         for (Intent intent : intents) {
188             List<ResolveInfo> resolveInfoList = mContext.getPackageManager().queryIntentActivities(
189                     intent,
190                     PackageManager.MATCH_DEFAULT_ONLY);
191             if (expectForwardable) {
192                 assertTrue("Expect " + intent + " to be forwardable, but resolve list"
193                         + " does not contain expected intent forwarder " + expectedForwarder,
194                         containsResolveInfo(resolveInfoList, expectedForwarder));
195             } else {
196                 assertFalse("Expect " + intent + " not to be forwardable, but resolve list "
197                         + "contains intent forwarder " + expectedForwarder,
198                         containsResolveInfo(resolveInfoList, expectedForwarder));
199             }
200         }
201     }
202 
getIntentForwarder(Intent intent)203     private ResolveInfo getIntentForwarder(Intent intent) {
204         List<ResolveInfo> result = mContext.getPackageManager().queryIntentActivities(intent,
205                 PackageManager.MATCH_DEFAULT_ONLY);
206         assertEquals("Expect only one resolve result for " + intent, 1, result.size());
207         return result.get(0);
208     }
209 
containsResolveInfo(List<ResolveInfo> list, ResolveInfo info)210     private boolean containsResolveInfo(List<ResolveInfo> list, ResolveInfo info) {
211         for (ResolveInfo entry : list) {
212             if (entry.activityInfo.packageName.equals(info.activityInfo.packageName)
213                     && entry.activityInfo.name.equals(info.activityInfo.name)) {
214                 return true;
215             }
216         }
217         return false;
218     }
219 }
220