• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.settings.webview;
18 
19 import static android.provider.Settings.ACTION_WEBVIEW_SETTINGS;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.doNothing;
26 import static org.mockito.Mockito.doReturn;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.never;
29 import static org.mockito.Mockito.spy;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.PackageInfo;
38 import android.graphics.drawable.ColorDrawable;
39 import android.os.UserHandle;
40 import android.webkit.UserPackage;
41 
42 import androidx.fragment.app.FragmentActivity;
43 
44 import com.android.settings.testutils.shadow.ShadowUserManager;
45 import com.android.settingslib.applications.DefaultAppInfo;
46 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
47 import com.android.settingslib.widget.SelectorWithWidgetPreference;
48 
49 import org.junit.After;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 import org.robolectric.RobolectricTestRunner;
56 import org.robolectric.RuntimeEnvironment;
57 import org.robolectric.Shadows;
58 import org.robolectric.annotation.Config;
59 import org.robolectric.shadow.api.Shadow;
60 import org.robolectric.shadows.ShadowPackageManager;
61 import org.robolectric.util.ReflectionHelpers;
62 
63 import java.util.Arrays;
64 import java.util.Collections;
65 
66 @RunWith(RobolectricTestRunner.class)
67 @Config(shadows = {
68         ShadowUserManager.class,
69         com.android.settings.testutils.shadow.ShadowFragment.class,
70 })
71 public class WebViewAppPickerTest {
72 
73     private static final String PACKAGE_NAME = "com.example.test";
74     private static final String PACKAGE_VERSION = "1.0.0";
75     private static final String FIRST_USER_NAME = "FIRST_USER";
76     private static final String SECOND_USER_NAME = "SECOND_USER";
77 
78     @Mock
79     private FragmentActivity mActivity;
80 
81     private Context mContext;
82     private UserHandle mFirstUser;
83     private UserHandle mSecondUser;
84     private ShadowPackageManager mPackageManager;
85     private WebViewAppPicker mPicker;
86     private WebViewUpdateServiceWrapper mWvusWrapper;
87     private ShadowUserManager mUserManager;
88 
89     @Before
setUp()90     public void setUp() {
91         MockitoAnnotations.initMocks(this);
92         mContext = spy(RuntimeEnvironment.application);
93         mUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE));
94         mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
95 
96         final ApplicationInfo applicationInfo = new ApplicationInfo();
97         applicationInfo.name = PACKAGE_NAME;
98         applicationInfo.uid = 0;
99         applicationInfo.flags = 0;
100         applicationInfo.packageName = PACKAGE_NAME;
101 
102         final PackageInfo packageInfo = new PackageInfo();
103         packageInfo.packageName = PACKAGE_NAME;
104         packageInfo.applicationInfo = applicationInfo;
105         packageInfo.versionName = PACKAGE_VERSION;
106         mPackageManager.addPackage(packageInfo);
107         mPackageManager.setUnbadgedApplicationIcon(PACKAGE_NAME, new ColorDrawable());
108 
109         mFirstUser = mUserManager.addUser(0, FIRST_USER_NAME, 0);
110         mSecondUser = mUserManager.addUser(1, SECOND_USER_NAME, 0);
111         mPicker = new WebViewAppPicker();
112         mPicker = spy(mPicker);
113         doNothing().when(mPicker).updateCandidates();
114         doNothing().when(mPicker).updateCheckedState(any());
115         doReturn(mActivity).when(mPicker).getActivity();
116 
117         ReflectionHelpers.setField(mPicker, "mMetricsFeatureProvider",
118                 mock(MetricsFeatureProvider.class));
119         mWvusWrapper = mock(WebViewUpdateServiceWrapper.class);
120         mPicker.setWebViewUpdateServiceWrapper(mWvusWrapper);
121     }
122 
123     @After
tearDown()124     public void tearDown() {
125         mPackageManager.removePackage(PACKAGE_NAME);
126     }
127 
128     @Test
testClickingItemChangesProvider()129     public void testClickingItemChangesProvider() {
130         testSuccessfulClickChangesProvider();
131     }
132 
133     @Test
testFailingClick()134     public void testFailingClick() {
135         testFailingClickUpdatesSetting();
136     }
137 
138     @Test
testClickingItemInActivityModeChangesProviderAndFinishes()139     public void testClickingItemInActivityModeChangesProviderAndFinishes() {
140         useWebViewSettingIntent();
141         testSuccessfulClickChangesProvider();
142         verify(mActivity, times(1)).finish();
143     }
144 
145     @Test
testFailingClickInActivityMode()146     public void testFailingClickInActivityMode() {
147         useWebViewSettingIntent();
148         testFailingClick();
149     }
150 
151     @Test
testDisabledPackageShownAsDisabled()152     public void testDisabledPackageShownAsDisabled() {
153         DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(mContext,
154                 mContext.getPackageManager(),
155                 createApplicationInfo(PACKAGE_NAME), "disabled");
156 
157         SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
158         mPicker.bindPreference(preference, PACKAGE_NAME, webviewAppInfo, null);
159         mPicker.bindPreferenceExtra(preference, PACKAGE_NAME, webviewAppInfo, null, null);
160         verify(preference, times(1)).setEnabled(eq(false));
161         verify(preference, never()).setEnabled(eq(true));
162     }
163 
164     @Test
testEnabledPackageShownAsEnabled()165     public void testEnabledPackageShownAsEnabled() {
166         String disabledReason = "";
167         DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(mContext,
168                 mContext.getPackageManager(),
169                 createApplicationInfo(PACKAGE_NAME), disabledReason);
170 
171         SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
172         mPicker.bindPreference(preference, PACKAGE_NAME, webviewAppInfo, null);
173         mPicker.bindPreferenceExtra(preference, PACKAGE_NAME, webviewAppInfo, null, null);
174         verify(preference, times(1)).setEnabled(eq(true));
175         verify(preference, never()).setEnabled(eq(false));
176     }
177 
178     @Test
testDisabledPackageShowsDisabledReasonSummary()179     public void testDisabledPackageShowsDisabledReasonSummary() {
180         String disabledReason = "disabled";
181         DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(mContext,
182                 mContext.getPackageManager(),
183                 createApplicationInfo(PACKAGE_NAME), disabledReason);
184 
185         SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
186         mPicker.bindPreference(preference, PACKAGE_NAME, webviewAppInfo, null);
187         mPicker.bindPreferenceExtra(preference, PACKAGE_NAME, webviewAppInfo, null, null);
188         verify(preference, times(1)).setSummary(eq(disabledReason));
189         // Ensure we haven't called setSummary several times.
190         verify(preference, times(1)).setSummary(any());
191     }
192 
193     @Test
testEnabledPackageShowsEmptySummary()194     public void testEnabledPackageShowsEmptySummary() {
195         DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(mContext,
196                 mContext.getPackageManager(),
197                 createApplicationInfo(PACKAGE_NAME), null);
198 
199         SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class);
200         mPicker.bindPreference(preference, PACKAGE_NAME, webviewAppInfo, null);
201         mPicker.bindPreferenceExtra(preference, PACKAGE_NAME, webviewAppInfo, null, null);
202         verify(preference, never()).setSummary(any());
203     }
204 
205     @Test
testFinishIfNotAdmin()206     public void testFinishIfNotAdmin() {
207         mUserManager.setIsAdminUser(false);
208         mPicker.onAttach(mContext);
209         verify(mActivity, times(1)).finish();
210     }
211 
212     @Test
testNotFinishedIfAdmin()213     public void testNotFinishedIfAdmin() {
214         mUserManager.setIsAdminUser(true);
215         mPicker.onAttach(mContext);
216         verify(mActivity, never()).finish();
217     }
218 
219     @Test
testDisabledReasonNullIfPackagesOk()220     public void testDisabledReasonNullIfPackagesOk() {
221         UserPackage packageForFirstUser = mock(UserPackage.class);
222         when(packageForFirstUser.isEnabledPackage()).thenReturn(true);
223         when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
224 
225         UserPackage packageForSecondUser = mock(UserPackage.class);
226         when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
227         when(packageForSecondUser.isInstalledPackage()).thenReturn(true);
228 
229         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
230         when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
231                 .thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
232 
233         assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME)).isNull();
234     }
235 
236     @Test
testDisabledReasonForSingleUserDisabledPackage()237     public void testDisabledReasonForSingleUserDisabledPackage() {
238         UserPackage packageForFirstUser = mock(UserPackage.class);
239         when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
240         when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
241         when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
242 
243         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
244         when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
245                 .thenReturn(Collections.singletonList(packageForFirstUser));
246 
247         final String expectedReason = String.format("(disabled for user %s)", FIRST_USER_NAME);
248         assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
249                 .isEqualTo(expectedReason);
250     }
251 
252     @Test
testDisabledReasonForSingleUserUninstalledPackage()253     public void testDisabledReasonForSingleUserUninstalledPackage() {
254         UserPackage packageForFirstUser = mock(UserPackage.class);
255         when(packageForFirstUser.isEnabledPackage()).thenReturn(true);
256         when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
257         when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
258 
259         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
260         when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
261                 .thenReturn(Collections.singletonList(packageForFirstUser));
262 
263         final String expectedReason = String.format("(uninstalled for user %s)", FIRST_USER_NAME);
264         assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
265                 .isEqualTo(expectedReason);
266     }
267 
268     @Test
testDisabledReasonSeveralUsers()269     public void testDisabledReasonSeveralUsers() {
270         UserPackage packageForFirstUser = mock(UserPackage.class);
271         when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
272         when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
273         when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
274 
275         UserPackage packageForSecondUser = mock(UserPackage.class);
276         when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
277         when(packageForSecondUser.isInstalledPackage()).thenReturn(false);
278         when(packageForSecondUser.getUser()).thenReturn(mSecondUser);
279 
280         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
281         when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
282                 .thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
283 
284         final String expectedReason = String.format("(disabled for user %s)", FIRST_USER_NAME);
285         assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
286                 .isEqualTo(expectedReason);
287     }
288 
289     /**
290      * Ensure we only proclaim a package as uninstalled for a certain user if it's both uninstalled
291      * and disabled.
292      */
293     @Test
testDisabledReasonUninstalledAndDisabled()294     public void testDisabledReasonUninstalledAndDisabled() {
295         UserPackage packageForFirstUser = mock(UserPackage.class);
296         when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
297         when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
298         when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
299 
300         UserPackage packageForSecondUser = mock(UserPackage.class);
301         when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
302         when(packageForSecondUser.isInstalledPackage()).thenReturn(true);
303         when(packageForSecondUser.getUser()).thenReturn(mSecondUser);
304 
305         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
306         when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
307                 .thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
308 
309         final String expectedReason = String.format("(uninstalled for user %s)", FIRST_USER_NAME);
310         assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
311                 .isEqualTo(expectedReason);
312     }
313 
314     /**
315      * Ensure that the version name of a WebView package is displayed after its name in the
316      * preference title.
317      */
318     @Test
testWebViewVersionAddedAfterLabel()319     public void testWebViewVersionAddedAfterLabel() {
320         final DefaultAppInfo webviewAppInfo = mPicker.createDefaultAppInfo(mContext,
321                 mContext.getPackageManager(),
322                 createApplicationInfo(PACKAGE_NAME), "" /* disabledReason */);
323 
324         final SelectorWithWidgetPreference mockPreference =
325                 mock(SelectorWithWidgetPreference.class);
326         mPicker.bindPreference(mockPreference, PACKAGE_NAME, webviewAppInfo, null);
327         mPicker.bindPreferenceExtra(
328                 mockPreference, PACKAGE_NAME, webviewAppInfo, null, null);
329 
330         verify(mockPreference).setTitle(eq(PACKAGE_NAME + " " + PACKAGE_VERSION));
331         verify(mockPreference).setTitle(any());
332     }
333 
createApplicationInfo(String packageName)334     private static ApplicationInfo createApplicationInfo(String packageName) {
335         ApplicationInfo ai = new ApplicationInfo();
336         ai.packageName = packageName;
337         return ai;
338     }
339 
useWebViewSettingIntent()340     private void useWebViewSettingIntent() {
341         Intent intent = new Intent(ACTION_WEBVIEW_SETTINGS);
342         when(mActivity.getIntent()).thenReturn(intent);
343     }
344 
testSuccessfulClickChangesProvider()345     private void testSuccessfulClickChangesProvider() {
346         when(mWvusWrapper.getValidWebViewApplicationInfos(any()))
347                 .thenReturn(Collections.singletonList(createApplicationInfo(PACKAGE_NAME)));
348         when(mWvusWrapper.setWebViewProvider(eq(PACKAGE_NAME))).thenReturn(true);
349 
350         SelectorWithWidgetPreference defaultPackagePref = mock(SelectorWithWidgetPreference.class);
351         when(defaultPackagePref.getKey()).thenReturn(PACKAGE_NAME);
352         mPicker.onRadioButtonClicked(defaultPackagePref);
353 
354         verify(mWvusWrapper, times(1)).setWebViewProvider(eq(PACKAGE_NAME));
355         verify(mPicker, times(1)).updateCheckedState(PACKAGE_NAME);
356         verify(mWvusWrapper, never()).showInvalidChoiceToast(any());
357     }
358 
testFailingClickUpdatesSetting()359     private void testFailingClickUpdatesSetting() {
360         when(mWvusWrapper.getValidWebViewApplicationInfos(any()))
361                 .thenReturn(Collections.singletonList(createApplicationInfo(PACKAGE_NAME)));
362         when(mWvusWrapper.setWebViewProvider(eq(PACKAGE_NAME))).thenReturn(false);
363 
364         SelectorWithWidgetPreference defaultPackagePref = mock(SelectorWithWidgetPreference.class);
365         when(defaultPackagePref.getKey()).thenReturn(PACKAGE_NAME);
366         mPicker.onRadioButtonClicked(defaultPackagePref);
367 
368         verify(mWvusWrapper, times(1)).setWebViewProvider(eq(PACKAGE_NAME));
369         // Ensure we update the list of packages when we click a non-valid package - the list must
370         // have changed, otherwise this click wouldn't fail.
371         verify(mPicker, times(1)).updateCandidates();
372         verify(mWvusWrapper, times(1)).showInvalidChoiceToast(any());
373     }
374 }
375