1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.browserservices.permissiondelegation;
6 
7 import static org.mockito.ArgumentMatchers.any;
8 import static org.mockito.ArgumentMatchers.anyBoolean;
9 import static org.mockito.ArgumentMatchers.anyInt;
10 import static org.mockito.ArgumentMatchers.anyString;
11 import static org.mockito.ArgumentMatchers.eq;
12 import static org.mockito.Mockito.doAnswer;
13 import static org.mockito.Mockito.never;
14 import static org.mockito.Mockito.verify;
15 import static org.robolectric.Shadows.shadowOf;
16 
17 import android.content.ComponentName;
18 import android.content.Intent;
19 import android.content.pm.PackageManager;
20 import android.content.pm.ResolveInfo;
21 
22 import org.junit.Before;
23 import org.junit.Rule;
24 import org.junit.Test;
25 import org.junit.rules.TestRule;
26 import org.junit.runner.RunWith;
27 import org.mockito.Mock;
28 import org.mockito.MockitoAnnotations;
29 import org.robolectric.RuntimeEnvironment;
30 import org.robolectric.annotation.Config;
31 import org.robolectric.shadows.ShadowPackageManager;
32 
33 import org.chromium.base.test.BaseRobolectricTestRunner;
34 import org.chromium.base.test.util.Feature;
35 import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
36 import org.chromium.chrome.test.util.browser.Features;
37 import org.chromium.components.content_settings.ContentSettingsType;
38 import org.chromium.components.embedder_support.util.Origin;
39 
40 /**
41  * Tests for {@link NotificationPermissionUpdater}.
42  */
43 @RunWith(BaseRobolectricTestRunner.class)
44 @Config(manifest = Config.NONE)
45 public class NotificationPermissionUpdaterTest {
46     private static final Origin ORIGIN = Origin.create("https://www.website.com");
47     private static final String PACKAGE_NAME = "com.package.name";
48     private static final String OTHER_PACKAGE_NAME = "com.other.package.name";
49 
50     @Rule
51     public TestRule mProcessor = new Features.JUnitProcessor();
52 
53     @Mock public TrustedWebActivityPermissionManager mPermissionManager;
54     @Mock public TrustedWebActivityClient mTrustedWebActivityClient;
55 
56     private NotificationPermissionUpdater mNotificationPermissionUpdater;
57     private ShadowPackageManager mShadowPackageManager;
58 
59     private boolean mNotificationsEnabled;
60 
61     @Before
setUp()62     public void setUp() {
63         MockitoAnnotations.initMocks(this);
64 
65         PackageManager pm = RuntimeEnvironment.application.getPackageManager();
66         mShadowPackageManager = shadowOf(pm);
67         mNotificationPermissionUpdater =
68                 new NotificationPermissionUpdater(mPermissionManager, mTrustedWebActivityClient);
69 
70         installBrowsableIntentHandler(ORIGIN, PACKAGE_NAME);
71     }
72 
73     @Test
74     @Feature("TrustedWebActivities")
doesntRegister_whenClientDoesntHaveService()75     public void doesntRegister_whenClientDoesntHaveService() {
76         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
77 
78         verifyPermissionNotUpdated();
79     }
80 
81     @Test
82     @Feature("TrustedWebActivities")
disablesNotifications_whenClientNotificationsAreDisabled()83     public void disablesNotifications_whenClientNotificationsAreDisabled() {
84         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
85         setNotificationsEnabledForClient(false);
86 
87         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
88 
89         verifyPermissionUpdated(false);
90     }
91 
92     @Test
93     @Feature("TrustedWebActivities")
enablesNotifications_whenClientNotificationsAreEnabled()94     public void enablesNotifications_whenClientNotificationsAreEnabled() {
95         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
96         setNotificationsEnabledForClient(true);
97 
98         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
99 
100         verifyPermissionUpdated(true);
101     }
102 
103     @Test
104     @Feature("TrustedWebActivities")
updatesPermission_onSubsequentCalls()105     public void updatesPermission_onSubsequentCalls() {
106 
107         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
108         setNotificationsEnabledForClient(true);
109         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
110         verifyPermissionUpdated(true);
111 
112         setNotificationsEnabledForClient(false);
113         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
114         verifyPermissionUpdated(false);
115     }
116 
117     @Test
118     @Feature("TrustedWebActivities")
updatesPermission_onNewClient()119     public void updatesPermission_onNewClient() {
120         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
121         setNotificationsEnabledForClient(true);
122         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
123         verifyPermissionUpdated(true);
124 
125         installTrustedWebActivityService(ORIGIN, OTHER_PACKAGE_NAME);
126         setNotificationsEnabledForClient(false);
127         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, OTHER_PACKAGE_NAME);
128         verifyPermissionUpdated(OTHER_PACKAGE_NAME, false);
129     }
130 
131     @Test
132     @Feature("TrustedWebActivities")
unregisters_onClientUninstall()133     public void unregisters_onClientUninstall() {
134         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
135         setNotificationsEnabledForClient(true);
136 
137         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
138 
139         uninstallTrustedWebActivityService(ORIGIN);
140         mNotificationPermissionUpdater.onClientAppUninstalled(ORIGIN);
141 
142         verifyPermissionUnregistered();
143     }
144 
145     @Test
146     @Feature("TrustedWebActivities")
doesntUnregister_whenOtherClientsRemain()147     public void doesntUnregister_whenOtherClientsRemain() {
148 
149         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
150         setNotificationsEnabledForClient(true);
151 
152         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
153         verifyPermissionUpdated(true);
154 
155         // Since we haven't called uninstallTrustedWebActivityService, the Updater sees that
156         // notifications can still be handled by other apps. We don't unregister, but we do update
157         // to the permission to that of the other app.
158         setNotificationsEnabledForClient(false);
159         mNotificationPermissionUpdater.onClientAppUninstalled(ORIGIN);
160         verifyPermissionNotUnregistered();
161         verifyPermissionUpdated(false);
162 
163         uninstallTrustedWebActivityService(ORIGIN);
164         mNotificationPermissionUpdater.onClientAppUninstalled(ORIGIN);
165         verifyPermissionUnregistered();
166     }
167 
168     /** "Installs" the given package to handle intents for that origin. */
installBrowsableIntentHandler(Origin origin, String packageName)169     private void installBrowsableIntentHandler(Origin origin, String packageName) {
170         Intent intent = new Intent();
171         intent.setPackage(packageName);
172         intent.setData(origin.uri());
173         intent.setAction(Intent.ACTION_VIEW);
174         intent.addCategory(Intent.CATEGORY_BROWSABLE);
175 
176         mShadowPackageManager.addResolveInfoForIntent(intent, new ResolveInfo());
177     }
178 
179     /** "Installs" a Trusted Web Activity Service for the origin. */
180     @SuppressWarnings("unchecked")
installTrustedWebActivityService(Origin origin, String packageName)181     private void installTrustedWebActivityService(Origin origin, String packageName) {
182         doAnswer(invocation -> {
183             TrustedWebActivityClient.PermissionCheckCallback callback =
184                     invocation.getArgument(1);
185             callback.onPermissionCheck(
186                     new ComponentName(packageName, "FakeClass"),
187                     mNotificationsEnabled);
188             return true;
189         }).when(mTrustedWebActivityClient).checkNotificationPermission(eq(origin), any());
190     }
191 
setNotificationsEnabledForClient(boolean enabled)192     private void setNotificationsEnabledForClient(boolean enabled) {
193         mNotificationsEnabled = enabled;
194     }
195 
uninstallTrustedWebActivityService(Origin origin)196     private void uninstallTrustedWebActivityService(Origin origin) {
197         doAnswer(invocation -> {
198             TrustedWebActivityClient.PermissionCheckCallback callback =
199                     invocation.getArgument(1);
200             callback.onNoTwaFound();
201             return true;
202         }).when(mTrustedWebActivityClient).checkNotificationPermission(eq(origin), any());
203     }
204 
verifyPermissionNotUpdated()205     private void verifyPermissionNotUpdated() {
206         verify(mPermissionManager, never())
207                 .updatePermission(any(), anyString(), anyInt(), anyBoolean());
208     }
209 
verifyPermissionUpdated(boolean enabled)210     private void verifyPermissionUpdated(boolean enabled) {
211         verifyPermissionUpdated(PACKAGE_NAME, enabled);
212     }
213 
verifyPermissionUpdated(String packageName, boolean enabled)214     private void verifyPermissionUpdated(String packageName, boolean enabled) {
215         verify(mPermissionManager)
216                 .updatePermission(eq(ORIGIN), eq(packageName),
217                         eq(ContentSettingsType.NOTIFICATIONS), eq(enabled));
218     }
219 
verifyPermissionUnregistered()220     private void verifyPermissionUnregistered() {
221         verify(mPermissionManager).unregister(eq(ORIGIN));
222     }
223 
verifyPermissionNotUnregistered()224     private void verifyPermissionNotUnregistered() {
225         verify(mPermissionManager, never()).unregister(eq(ORIGIN));
226     }
227 }
228