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.fuelgauge;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Matchers.any;
22 import static org.mockito.Matchers.anyInt;
23 import static org.mockito.Matchers.anyString;
24 import static org.mockito.Matchers.eq;
25 import static org.mockito.Mockito.doAnswer;
26 import static org.mockito.Mockito.doNothing;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.doThrow;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.app.ActivityManager;
35 import android.app.Application;
36 import android.app.Fragment;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.pm.ApplicationInfo;
40 import android.content.pm.PackageInfo;
41 import android.content.pm.PackageManager;
42 import android.os.UserManager;
43 import android.widget.Button;
44 
45 import com.android.settings.R;
46 import com.android.settings.SettingsActivity;
47 import com.android.settings.TestConfig;
48 import com.android.settings.core.lifecycle.Lifecycle;
49 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
50 import com.android.settings.testutils.FakeFeatureFactory;
51 import com.android.settingslib.applications.AppUtils;
52 import com.android.settingslib.applications.ApplicationsState;
53 import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
54 
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 import org.mockito.Answers;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.Mock;
61 import org.mockito.MockitoAnnotations;
62 import org.mockito.invocation.InvocationOnMock;
63 import org.mockito.stubbing.Answer;
64 import org.robolectric.RobolectricTestRunner;
65 import org.robolectric.annotation.Config;
66 import org.robolectric.util.ReflectionHelpers;
67 
68 @RunWith(RobolectricTestRunner.class)
69 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
70 public class AppButtonsPreferenceControllerTest {
71     private static final String PACKAGE_NAME = "com.android.settings";
72     private static final String RESOURCE_STRING = "string";
73     private static final boolean ALL_USERS = false;
74     private static final boolean DISABLE_AFTER_INSTALL = true;
75     private static final int REQUEST_UNINSTALL = 0;
76     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
77     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
78     private SettingsActivity mSettingsActivity;
79     @Mock
80     private TestFragment mFragment;
81     @Mock
82     private Lifecycle mLifecycle;
83     @Mock
84     private ApplicationsState mState;
85     @Mock
86     private ApplicationsState.AppEntry mAppEntry;
87     @Mock
88     private ApplicationInfo mAppInfo;
89     @Mock
90     private PackageManager mPackageManger;
91     @Mock
92     private DevicePolicyManagerWrapper mDpm;
93     @Mock
94     private ActivityManager mAm;
95     @Mock
96     private UserManager mUserManager;
97     @Mock
98     private Application mApplication;
99     @Mock
100     private PackageInfo mPackageInfo;
101     @Mock
102     private Button mUninstallButton;
103     @Mock
104     private Button mForceStopButton;
105 
106     private Intent mUninstallIntent;
107     private AppButtonsPreferenceController mController;
108 
109     @Before
setUp()110     public void setUp() throws Exception {
111         MockitoAnnotations.initMocks(this);
112 
113         FakeFeatureFactory.setupForTest(mSettingsActivity);
114         doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE);
115         doReturn(mPackageManger).when(mSettingsActivity).getPackageManager();
116         doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE);
117         doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
118         doReturn(mApplication).when(mSettingsActivity).getApplication();
119         when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(RESOURCE_STRING);
120 
121         mController = spy(new AppButtonsPreferenceController(mSettingsActivity, mFragment,
122                 mLifecycle, PACKAGE_NAME, mState, mDpm, mUserManager, mPackageManger,
123                 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN));
124         doReturn(false).when(mController).isFallbackPackage(anyString());
125 
126         mAppEntry.info = mAppInfo;
127         mAppInfo.packageName = PACKAGE_NAME;
128         mAppInfo.flags = 0;
129         mPackageInfo.packageName = PACKAGE_NAME;
130         mPackageInfo.applicationInfo = mAppInfo;
131 
132         mController.mUninstallButton = mUninstallButton;
133         mController.mForceStopButton = mForceStopButton;
134         mController.mPackageInfo = mPackageInfo;
135 
136         final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
137         Answer<Void> callable = new Answer<Void>() {
138             @Override
139             public Void answer(InvocationOnMock invocation) throws Exception {
140                 mUninstallIntent = captor.getValue();
141                 return null;
142             }
143         };
144         doAnswer(callable).when(mFragment).startActivityForResult(captor.capture(), anyInt());
145     }
146 
147     @Test
testRetrieveAppEntry_hasAppEntry_notNull()148     public void testRetrieveAppEntry_hasAppEntry_notNull()
149             throws PackageManager.NameNotFoundException {
150         doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
151 
152         mController.retrieveAppEntry();
153 
154         assertThat(mController.mAppEntry).isNotNull();
155         assertThat(mController.mPackageInfo).isNotNull();
156     }
157 
158 
159     @Test
testRetrieveAppEntry_noAppEntry_null()160     public void testRetrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
161         doReturn(null).when(mState).getEntry(eq(PACKAGE_NAME), anyInt());
162         doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
163 
164         mController.retrieveAppEntry();
165 
166         assertThat(mController.mAppEntry).isNull();
167         assertThat(mController.mPackageInfo).isNull();
168     }
169 
170     @Test
testRetrieveAppEntry_throwException_null()171     public void testRetrieveAppEntry_throwException_null() throws
172             PackageManager.NameNotFoundException {
173         doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
174         doThrow(new PackageManager.NameNotFoundException()).when(mPackageManger).getPackageInfo(
175                 anyString(), anyInt());
176 
177         mController.retrieveAppEntry();
178 
179         assertThat(mController.mAppEntry).isNotNull();
180         assertThat(mController.mPackageInfo).isNull();
181     }
182 
183     @Test
testUpdateUninstallButton_isSystemApp_handleAsDisableableButton()184     public void testUpdateUninstallButton_isSystemApp_handleAsDisableableButton() {
185         doReturn(false).when(mController).handleDisableable(any());
186         mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
187 
188         mController.updateUninstallButton();
189 
190         verify(mController).handleDisableable(any());
191         verify(mUninstallButton).setEnabled(false);
192     }
193 
194     @Test
testIsAvailable_nonInstantApp()195     public void testIsAvailable_nonInstantApp() throws Exception {
196         mController.mAppEntry = mAppEntry;
197         ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
198                 new InstantAppDataProvider() {
199                     @Override
200                     public boolean isInstantApp(ApplicationInfo info) {
201                         return false;
202                     }
203                 });
204         assertThat(mController.isAvailable()).isTrue();
205     }
206 
207     @Test
testIsAvailable_instantApp()208     public void testIsAvailable_instantApp() throws Exception {
209         mController.mAppEntry = mAppEntry;
210         ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
211                 new InstantAppDataProvider() {
212                     @Override
213                     public boolean isInstantApp(ApplicationInfo info) {
214                         return true;
215                     }
216                 });
217         assertThat(mController.isAvailable()).isFalse();
218     }
219 
220     @Test
testUpdateUninstallButton_isDeviceAdminApp_setButtonDisable()221     public void testUpdateUninstallButton_isDeviceAdminApp_setButtonDisable() {
222         doReturn(true).when(mController).handleDisableable(any());
223         mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
224         doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
225 
226         mController.updateUninstallButton();
227 
228         verify(mController).handleDisableable(any());
229         verify(mUninstallButton).setEnabled(false);
230     }
231 
232     @Test
testUpdateUninstallButton_isProfileOrDeviceOwner_setButtonDisable()233     public void testUpdateUninstallButton_isProfileOrDeviceOwner_setButtonDisable() {
234         doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
235 
236         mController.updateUninstallButton();
237 
238         verify(mUninstallButton).setEnabled(false);
239     }
240 
241     @Test
testUpdateUninstallButton_isDeviceProvisioningApp_setButtonDisable()242     public void testUpdateUninstallButton_isDeviceProvisioningApp_setButtonDisable() {
243         doReturn(true).when(mDpm).isDeviceOwnerAppOnAnyUser(anyString());
244         when(mSettingsActivity.getResources().getString(anyInt())).thenReturn(PACKAGE_NAME);
245 
246         mController.updateUninstallButton();
247 
248         verify(mUninstallButton).setEnabled(false);
249     }
250 
251     @Test
testUpdateUninstallButton_isUninstallInQueue_setButtonDisable()252     public void testUpdateUninstallButton_isUninstallInQueue_setButtonDisable() {
253         doReturn(true).when(mDpm).isUninstallInQueue(any());
254 
255         mController.updateUninstallButton();
256 
257         verify(mUninstallButton).setEnabled(false);
258     }
259 
260     @Test
testUpdateUninstallButton_isHomeAppAndBundled_setButtonDisable()261     public void testUpdateUninstallButton_isHomeAppAndBundled_setButtonDisable() {
262         mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
263         mController.mHomePackages.add(PACKAGE_NAME);
264 
265         mController.updateUninstallButton();
266 
267         verify(mUninstallButton).setEnabled(false);
268     }
269 
270     @Test
testUpdateForceStopButton_HasActiveAdmins_setButtonDisable()271     public void testUpdateForceStopButton_HasActiveAdmins_setButtonDisable() {
272         doReturn(true).when(mDpm).packageHasActiveAdmins(anyString());
273 
274         mController.updateForceStopButton();
275 
276         verify(mController).updateForceStopButtonInner(false);
277     }
278 
279     @Test
testUpdateForceStopButton_AppNotStopped_setButtonEnable()280     public void testUpdateForceStopButton_AppNotStopped_setButtonEnable() {
281         mController.updateForceStopButton();
282 
283         verify(mController).updateForceStopButtonInner(true);
284     }
285 
286     @Test
testUninstallPkg_intentSent()287     public void testUninstallPkg_intentSent() {
288         mController.uninstallPkg(PACKAGE_NAME, ALL_USERS, DISABLE_AFTER_INSTALL);
289 
290         verify(mFragment).startActivityForResult(any(), eq(REQUEST_UNINSTALL));
291         assertThat(
292                 mUninstallIntent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, true))
293                 .isEqualTo(ALL_USERS);
294         assertThat(mUninstallIntent.getAction()).isEqualTo(Intent.ACTION_UNINSTALL_PACKAGE);
295         assertThat(mController.mDisableAfterUninstall).isEqualTo(DISABLE_AFTER_INSTALL);
296     }
297 
298     @Test
testForceStopPackage_methodInvokedAndUpdated()299     public void testForceStopPackage_methodInvokedAndUpdated() {
300         final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
301         doReturn(appEntry).when(mState).getEntry(anyString(), anyInt());
302         doNothing().when(mController).updateForceStopButton();
303 
304         mController.forceStopPackage(PACKAGE_NAME);
305 
306         verify(mAm).forceStopPackage(PACKAGE_NAME);
307         assertThat(mController.mAppEntry).isSameAs(appEntry);
308         verify(mController).updateForceStopButton();
309     }
310 
311     @Test
testHandleDisableable_isHomeApp_notControllable()312     public void testHandleDisableable_isHomeApp_notControllable() {
313         mController.mHomePackages.add(PACKAGE_NAME);
314 
315         final boolean controllable = mController.handleDisableable(mUninstallButton);
316 
317         verify(mUninstallButton).setText(R.string.disable_text);
318         assertThat(controllable).isFalse();
319 
320     }
321 
322     @Test
testHandleDisableable_isAppEnabled_controllable()323     public void testHandleDisableable_isAppEnabled_controllable() {
324         mAppEntry.info.enabled = true;
325         mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
326         doReturn(false).when(mController).isSystemPackage(any(), any(), any());
327 
328         final boolean controllable = mController.handleDisableable(mUninstallButton);
329 
330         verify(mUninstallButton).setText(R.string.disable_text);
331         assertThat(controllable).isTrue();
332 
333     }
334 
335     @Test
testHandleDisableable_isAppDisabled_controllable()336     public void testHandleDisableable_isAppDisabled_controllable() {
337         mAppEntry.info.enabled = false;
338         mAppEntry.info.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
339         doReturn(false).when(mController).isSystemPackage(any(), any(), any());
340 
341         final boolean controllable = mController.handleDisableable(mUninstallButton);
342 
343         verify(mUninstallButton).setText(R.string.enable_text);
344         assertThat(controllable).isTrue();
345     }
346 
347     @Test
testRefreshUi_packageNull_shouldNotCrash()348     public void testRefreshUi_packageNull_shouldNotCrash() {
349         mController.mPackageName = null;
350 
351         // Should not crash in this method
352         assertThat(mController.refreshUi()).isFalse();
353     }
354 
355     /**
356      * The test fragment which implements
357      * {@link ButtonActionDialogFragment.AppButtonsDialogListener}
358      */
359     private static class TestFragment extends Fragment implements
360             ButtonActionDialogFragment.AppButtonsDialogListener {
361 
362         @Override
handleDialogClick(int type)363         public void handleDialogClick(int type) {
364             // Do nothing
365         }
366     }
367 }
368