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