• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.intentresolver;
18 
19 import static android.testing.PollingCheck.waitFor;
20 
21 import static androidx.test.espresso.Espresso.onView;
22 import static androidx.test.espresso.action.ViewActions.click;
23 import static androidx.test.espresso.action.ViewActions.swipeUp;
24 import static androidx.test.espresso.assertion.ViewAssertions.matches;
25 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
26 import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
27 import static androidx.test.espresso.matcher.ViewMatchers.withId;
28 import static androidx.test.espresso.matcher.ViewMatchers.withText;
29 
30 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.NO_BLOCKER;
31 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_ACCESS_BLOCKER;
32 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.PERSONAL_PROFILE_SHARE_BLOCKER;
33 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_ACCESS_BLOCKER;
34 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.ExpectedBlocker.WORK_PROFILE_SHARE_BLOCKER;
35 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.Tab.PERSONAL;
36 import static com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.Tab.WORK;
37 import static com.android.intentresolver.ChooserWrapperActivity.sOverrides;
38 
39 import static org.hamcrest.CoreMatchers.not;
40 import static org.mockito.ArgumentMatchers.eq;
41 import static org.mockito.Mockito.when;
42 
43 import android.companion.DeviceFilter;
44 import android.content.Intent;
45 import android.os.UserHandle;
46 
47 import androidx.test.espresso.NoMatchingViewException;
48 import androidx.test.platform.app.InstrumentationRegistry;
49 import androidx.test.rule.ActivityTestRule;
50 
51 import com.android.intentresolver.ChooserActivityWorkProfileTest.TestCase.Tab;
52 import com.android.intentresolver.data.repository.FakeUserRepository;
53 import com.android.intentresolver.data.repository.UserRepository;
54 import com.android.intentresolver.data.repository.UserRepositoryModule;
55 import com.android.intentresolver.inject.ApplicationUser;
56 import com.android.intentresolver.inject.ProfileParent;
57 import com.android.intentresolver.shared.model.User;
58 
59 import dagger.hilt.android.testing.BindValue;
60 import dagger.hilt.android.testing.HiltAndroidRule;
61 import dagger.hilt.android.testing.HiltAndroidTest;
62 import dagger.hilt.android.testing.UninstallModules;
63 
64 import junit.framework.AssertionFailedError;
65 
66 import org.junit.Before;
67 import org.junit.Rule;
68 import org.junit.Test;
69 import org.junit.runner.RunWith;
70 import org.junit.runners.Parameterized;
71 import org.mockito.Mockito;
72 
73 import java.util.ArrayList;
74 import java.util.Arrays;
75 import java.util.Collection;
76 import java.util.List;
77 
78 @DeviceFilter.MediumType
79 @RunWith(Parameterized.class)
80 @HiltAndroidTest
81 @UninstallModules(UserRepositoryModule.class)
82 public class ChooserActivityWorkProfileTest {
83 
84     private static final UserHandle PERSONAL_USER_HANDLE = InstrumentationRegistry
85             .getInstrumentation().getTargetContext().getUser();
86     private static final UserHandle WORK_USER_HANDLE = UserHandle.of(10);
87 
88     @Rule(order = 0)
89     public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
90 
91     @Rule(order = 1)
92     public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
93             new ActivityTestRule<>(ChooserWrapperActivity.class, false,
94                     false);
95 
96     @BindValue
97     @ApplicationUser
98     public final UserHandle mApplicationUser;
99 
100     @BindValue
101     @ProfileParent
102     public final UserHandle mProfileParent;
103 
104     /** For setup of test state, a mutable reference of mUserRepository  */
105     private final FakeUserRepository mFakeUserRepo = new FakeUserRepository(
106             List.of(new User(PERSONAL_USER_HANDLE.getIdentifier(), User.Role.PERSONAL)));
107 
108     @BindValue
109     public final UserRepository mUserRepository;
110 
111     private final TestCase mTestCase;
112 
ChooserActivityWorkProfileTest(TestCase testCase)113     public ChooserActivityWorkProfileTest(TestCase testCase) {
114         mTestCase = testCase;
115         mApplicationUser = mTestCase.getMyUserHandle();
116         mProfileParent = PERSONAL_USER_HANDLE;
117         mUserRepository = new FakeUserRepository(List.of(
118                 new User(PERSONAL_USER_HANDLE.getIdentifier(), User.Role.PERSONAL),
119                 new User(WORK_USER_HANDLE.getIdentifier(), User.Role.WORK)));
120     }
121 
122     @Before
cleanOverrideData()123     public void cleanOverrideData() {
124         // TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
125         // permissions we require (which we'll read from the manifest at runtime).
126         InstrumentationRegistry
127                 .getInstrumentation()
128                 .getUiAutomation()
129                 .adoptShellPermissionIdentity();
130 
131         sOverrides.reset();
132     }
133 
134     @Test
testBlocker()135     public void testBlocker() {
136         setUpPersonalAndWorkComponentInfos();
137         sOverrides.hasCrossProfileIntents = mTestCase.hasCrossProfileIntents();
138 
139         launchActivity(mTestCase.getIsSendAction());
140         switchToTab(mTestCase.getTab());
141 
142         switch (mTestCase.getExpectedBlocker()) {
143             case NO_BLOCKER:
144                 assertNoBlockerDisplayed();
145                 break;
146             case PERSONAL_PROFILE_SHARE_BLOCKER:
147                 assertCantSharePersonalAppsBlockerDisplayed();
148                 break;
149             case WORK_PROFILE_SHARE_BLOCKER:
150                 assertCantShareWorkAppsBlockerDisplayed();
151                 break;
152             case PERSONAL_PROFILE_ACCESS_BLOCKER:
153                 assertCantAccessPersonalAppsBlockerDisplayed();
154                 break;
155             case WORK_PROFILE_ACCESS_BLOCKER:
156                 assertCantAccessWorkAppsBlockerDisplayed();
157                 break;
158         }
159     }
160 
161     @Parameterized.Parameters(name = "{0}")
tests()162     public static Collection tests() {
163         return Arrays.asList(
164                 new TestCase(
165                         /* isSendAction= */ true,
166                         /* hasCrossProfileIntents= */ true,
167                         /* myUserHandle= */ WORK_USER_HANDLE,
168                         /* tab= */ WORK,
169                         /* expectedBlocker= */ NO_BLOCKER
170                 ),
171                 new TestCase(
172                         /* isSendAction= */ true,
173                         /* hasCrossProfileIntents= */ false,
174                         /* myUserHandle= */ WORK_USER_HANDLE,
175                         /* tab= */ WORK,
176                         /* expectedBlocker= */ NO_BLOCKER
177                 ),
178                 new TestCase(
179                         /* isSendAction= */ true,
180                         /* hasCrossProfileIntents= */ true,
181                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
182                         /* tab= */ WORK,
183                         /* expectedBlocker= */ NO_BLOCKER
184                 ),
185                 new TestCase(
186                         /* isSendAction= */ true,
187                         /* hasCrossProfileIntents= */ false,
188                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
189                         /* tab= */ WORK,
190                         /* expectedBlocker= */ WORK_PROFILE_SHARE_BLOCKER
191                 ),
192                 new TestCase(
193                         /* isSendAction= */ true,
194                         /* hasCrossProfileIntents= */ true,
195                         /* myUserHandle= */ WORK_USER_HANDLE,
196                         /* tab= */ PERSONAL,
197                         /* expectedBlocker= */ NO_BLOCKER
198                 ),
199                 new TestCase(
200                         /* isSendAction= */ true,
201                         /* hasCrossProfileIntents= */ false,
202                         /* myUserHandle= */ WORK_USER_HANDLE,
203                         /* tab= */ PERSONAL,
204                         /* expectedBlocker= */ PERSONAL_PROFILE_SHARE_BLOCKER
205                 ),
206                 new TestCase(
207                         /* isSendAction= */ true,
208                         /* hasCrossProfileIntents= */ true,
209                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
210                         /* tab= */ PERSONAL,
211                         /* expectedBlocker= */ NO_BLOCKER
212                 ),
213                 new TestCase(
214                         /* isSendAction= */ true,
215                         /* hasCrossProfileIntents= */ false,
216                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
217                         /* tab= */ PERSONAL,
218                         /* expectedBlocker= */ NO_BLOCKER
219                 ),
220                 new TestCase(
221                         /* isSendAction= */ false,
222                         /* hasCrossProfileIntents= */ true,
223                         /* myUserHandle= */ WORK_USER_HANDLE,
224                         /* tab= */ WORK,
225                         /* expectedBlocker= */ NO_BLOCKER
226                 ),
227                 new TestCase(
228                         /* isSendAction= */ false,
229                         /* hasCrossProfileIntents= */ false,
230                         /* myUserHandle= */ WORK_USER_HANDLE,
231                         /* tab= */ WORK,
232                         /* expectedBlocker= */ NO_BLOCKER
233                 ),
234                 new TestCase(
235                         /* isSendAction= */ false,
236                         /* hasCrossProfileIntents= */ true,
237                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
238                         /* tab= */ WORK,
239                         /* expectedBlocker= */ NO_BLOCKER
240                 ),
241                 new TestCase(
242                         /* isSendAction= */ false,
243                         /* hasCrossProfileIntents= */ false,
244                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
245                         /* tab= */ WORK,
246                         /* expectedBlocker= */ WORK_PROFILE_ACCESS_BLOCKER
247                 ),
248                 new TestCase(
249                         /* isSendAction= */ false,
250                         /* hasCrossProfileIntents= */ true,
251                         /* myUserHandle= */ WORK_USER_HANDLE,
252                         /* tab= */ PERSONAL,
253                         /* expectedBlocker= */ NO_BLOCKER
254                 ),
255                 new TestCase(
256                         /* isSendAction= */ false,
257                         /* hasCrossProfileIntents= */ false,
258                         /* myUserHandle= */ WORK_USER_HANDLE,
259                         /* tab= */ PERSONAL,
260                         /* expectedBlocker= */ PERSONAL_PROFILE_ACCESS_BLOCKER
261                 ),
262                 new TestCase(
263                         /* isSendAction= */ false,
264                         /* hasCrossProfileIntents= */ true,
265                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
266                         /* tab= */ PERSONAL,
267                         /* expectedBlocker= */ NO_BLOCKER
268                 ),
269                 new TestCase(
270                         /* isSendAction= */ false,
271                         /* hasCrossProfileIntents= */ false,
272                         /* myUserHandle= */ PERSONAL_USER_HANDLE,
273                         /* tab= */ PERSONAL,
274                         /* expectedBlocker= */ NO_BLOCKER
275                 )
276         );
277     }
278 
createResolvedComponentsForTestWithOtherProfile( int numberOfResults, int userId, UserHandle resolvedForUser)279     private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile(
280             int numberOfResults, int userId, UserHandle resolvedForUser) {
281         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
282         for (int i = 0; i < numberOfResults; i++) {
283             infoList.add(
284                     ResolverDataProvider
285                             .createResolvedComponentInfoWithOtherId(i, userId, resolvedForUser));
286         }
287         return infoList;
288     }
289 
createResolvedComponentsForTest(int numberOfResults, UserHandle resolvedForUser)290     private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults,
291             UserHandle resolvedForUser) {
292         List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
293         for (int i = 0; i < numberOfResults; i++) {
294             infoList.add(ResolverDataProvider.createResolvedComponentInfo(i, resolvedForUser));
295         }
296         return infoList;
297     }
298 
setUpPersonalAndWorkComponentInfos()299     private void setUpPersonalAndWorkComponentInfos() {
300         int workProfileTargets = 4;
301         List<ResolvedComponentInfo> personalResolvedComponentInfos =
302                 createResolvedComponentsForTestWithOtherProfile(3,
303                         /* userId */ WORK_USER_HANDLE.getIdentifier(), PERSONAL_USER_HANDLE);
304         List<ResolvedComponentInfo> workResolvedComponentInfos =
305                 createResolvedComponentsForTest(workProfileTargets, WORK_USER_HANDLE);
306         setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
307     }
308 
setupResolverControllers( List<ResolvedComponentInfo> personalResolvedComponentInfos, List<ResolvedComponentInfo> workResolvedComponentInfos)309     private void setupResolverControllers(
310             List<ResolvedComponentInfo> personalResolvedComponentInfos,
311             List<ResolvedComponentInfo> workResolvedComponentInfos) {
312         when(sOverrides.resolverListController.getResolversForIntentAsUser(
313                 Mockito.anyBoolean(),
314                 Mockito.anyBoolean(),
315                 Mockito.anyBoolean(),
316                 Mockito.isA(List.class),
317                 eq(UserHandle.SYSTEM)))
318                         .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
319         when(sOverrides.workResolverListController.getResolversForIntentAsUser(
320                 Mockito.anyBoolean(),
321                 Mockito.anyBoolean(),
322                 Mockito.anyBoolean(),
323                 Mockito.isA(List.class),
324                 eq(UserHandle.SYSTEM)))
325                         .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
326         when(sOverrides.workResolverListController.getResolversForIntentAsUser(
327                 Mockito.anyBoolean(),
328                 Mockito.anyBoolean(),
329                 Mockito.anyBoolean(),
330                 Mockito.isA(List.class),
331                 eq(WORK_USER_HANDLE)))
332                         .thenReturn(new ArrayList<>(workResolvedComponentInfos));
333     }
334 
waitForIdle()335     private void waitForIdle() {
336         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
337     }
338 
assertCantAccessWorkAppsBlockerDisplayed()339     private void assertCantAccessWorkAppsBlockerDisplayed() {
340         onView(withText(R.string.resolver_cross_profile_blocked))
341                 .check(matches(isDisplayed()));
342         onView(withText(R.string.resolver_cant_access_work_apps_explanation))
343                 .check(matches(isDisplayed()));
344     }
345 
assertCantAccessPersonalAppsBlockerDisplayed()346     private void assertCantAccessPersonalAppsBlockerDisplayed() {
347         onView(withText(R.string.resolver_cross_profile_blocked))
348                 .check(matches(isDisplayed()));
349         onView(withText(R.string.resolver_cant_access_personal_apps_explanation))
350                 .check(matches(isDisplayed()));
351     }
352 
assertCantShareWorkAppsBlockerDisplayed()353     private void assertCantShareWorkAppsBlockerDisplayed() {
354         onView(withText(R.string.resolver_cross_profile_blocked))
355                 .check(matches(isDisplayed()));
356         onView(withText(R.string.resolver_cant_share_with_work_apps_explanation))
357                 .check(matches(isDisplayed()));
358     }
359 
assertCantSharePersonalAppsBlockerDisplayed()360     private void assertCantSharePersonalAppsBlockerDisplayed() {
361         onView(withText(R.string.resolver_cross_profile_blocked))
362                 .check(matches(isDisplayed()));
363         onView(withText(R.string.resolver_cant_share_with_personal_apps_explanation))
364                 .check(matches(isDisplayed()));
365     }
366 
assertNoBlockerDisplayed()367     private void assertNoBlockerDisplayed() {
368         try {
369             onView(withText(R.string.resolver_cross_profile_blocked))
370                     .check(matches(not(isDisplayed())));
371         } catch (NoMatchingViewException ignored) {
372         }
373     }
374 
switchToTab(Tab tab)375     private void switchToTab(Tab tab) {
376         final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
377                 : R.string.resolver_personal_tab;
378 
379         waitFor(() -> {
380             onView(withText(stringId)).perform(click());
381             waitForIdle();
382 
383             try {
384                 onView(withText(stringId)).check(matches(isSelected()));
385                 return true;
386             } catch (AssertionFailedError e) {
387                 return false;
388             }
389         });
390 
391         onView(withId(com.android.internal.R.id.contentPanel))
392                 .perform(swipeUp());
393         waitForIdle();
394     }
395 
createTextIntent(boolean isSendAction)396     private Intent createTextIntent(boolean isSendAction) {
397         Intent sendIntent = new Intent();
398         if (isSendAction) {
399             sendIntent.setAction(Intent.ACTION_SEND);
400         }
401         sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
402         sendIntent.setType("text/plain");
403         return sendIntent;
404     }
405 
launchActivity(boolean isSendAction)406     private void launchActivity(boolean isSendAction) {
407         Intent sendIntent = createTextIntent(isSendAction);
408         mActivityRule.launchActivity(Intent.createChooser(sendIntent, "Test"));
409         waitForIdle();
410     }
411 
412     public static class TestCase {
413         private final boolean mIsSendAction;
414         private final boolean mHasCrossProfileIntents;
415         private final UserHandle mMyUserHandle;
416         private final Tab mTab;
417         private final ExpectedBlocker mExpectedBlocker;
418 
419         public enum ExpectedBlocker {
420             NO_BLOCKER,
421             PERSONAL_PROFILE_SHARE_BLOCKER,
422             WORK_PROFILE_SHARE_BLOCKER,
423             PERSONAL_PROFILE_ACCESS_BLOCKER,
424             WORK_PROFILE_ACCESS_BLOCKER
425         }
426 
427         public enum Tab {
428             WORK,
429             PERSONAL
430         }
431 
TestCase(boolean isSendAction, boolean hasCrossProfileIntents, UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker)432         public TestCase(boolean isSendAction, boolean hasCrossProfileIntents,
433                 UserHandle myUserHandle, Tab tab, ExpectedBlocker expectedBlocker) {
434             mIsSendAction = isSendAction;
435             mHasCrossProfileIntents = hasCrossProfileIntents;
436             mMyUserHandle = myUserHandle;
437             mTab = tab;
438             mExpectedBlocker = expectedBlocker;
439         }
440 
getIsSendAction()441         public boolean getIsSendAction() {
442             return mIsSendAction;
443         }
444 
hasCrossProfileIntents()445         public boolean hasCrossProfileIntents() {
446             return mHasCrossProfileIntents;
447         }
448 
getMyUserHandle()449         public UserHandle getMyUserHandle() {
450             return mMyUserHandle;
451         }
452 
getTab()453         public Tab getTab() {
454             return mTab;
455         }
456 
getExpectedBlocker()457         public ExpectedBlocker getExpectedBlocker() {
458             return mExpectedBlocker;
459         }
460 
461         @Override
toString()462         public String toString() {
463             StringBuilder result = new StringBuilder("test");
464 
465             if (mTab == WORK) {
466                 result.append("WorkTab_");
467             } else {
468                 result.append("PersonalTab_");
469             }
470 
471             if (mIsSendAction) {
472                 result.append("sendAction_");
473             } else {
474                 result.append("notSendAction_");
475             }
476 
477             if (mHasCrossProfileIntents) {
478                 result.append("hasCrossProfileIntents_");
479             } else {
480                 result.append("doesNotHaveCrossProfileIntents_");
481             }
482 
483             if (mMyUserHandle.equals(PERSONAL_USER_HANDLE)) {
484                 result.append("myUserIsPersonal_");
485             } else {
486                 result.append("myUserIsWork_");
487             }
488 
489             if (mExpectedBlocker == ExpectedBlocker.NO_BLOCKER) {
490                 result.append("thenNoBlocker");
491             } else if (mExpectedBlocker == PERSONAL_PROFILE_ACCESS_BLOCKER) {
492                 result.append("thenAccessBlockerOnPersonalProfile");
493             } else if (mExpectedBlocker == PERSONAL_PROFILE_SHARE_BLOCKER) {
494                 result.append("thenShareBlockerOnPersonalProfile");
495             } else if (mExpectedBlocker == WORK_PROFILE_ACCESS_BLOCKER) {
496                 result.append("thenAccessBlockerOnWorkProfile");
497             } else if (mExpectedBlocker == WORK_PROFILE_SHARE_BLOCKER) {
498                 result.append("thenShareBlockerOnWorkProfile");
499             }
500 
501             return result.toString();
502         }
503     }
504 }
505