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.applications.appinfo; 18 19 import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.ARG_PACKAGE_NAME; 20 import static com.android.settings.applications.appinfo.AppInfoDashboardFragment 21 .UNINSTALL_ALL_USERS_MENU; 22 import static com.android.settings.applications.appinfo.AppInfoDashboardFragment.UNINSTALL_UPDATES; 23 import static com.google.common.truth.Truth.assertThat; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.nullable; 26 import static org.mockito.Mockito.doNothing; 27 import static org.mockito.Mockito.doReturn; 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.times; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.app.admin.DevicePolicyManager; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.pm.ApplicationInfo; 39 import android.content.pm.PackageInfo; 40 import android.content.pm.PackageManager; 41 import android.content.pm.UserInfo; 42 import android.os.Bundle; 43 import android.os.UserManager; 44 import android.view.Menu; 45 import android.view.MenuItem; 46 47 import com.android.settings.SettingsActivity; 48 import com.android.settings.SettingsPreferenceFragment; 49 import com.android.settings.testutils.SettingsRobolectricTestRunner; 50 import com.android.settingslib.applications.AppUtils; 51 import com.android.settingslib.applications.ApplicationsState.AppEntry; 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.robolectric.RuntimeEnvironment; 63 import org.robolectric.annotation.Config; 64 import org.robolectric.util.ReflectionHelpers; 65 66 import java.util.ArrayList; 67 import java.util.List; 68 69 @RunWith(SettingsRobolectricTestRunner.class) 70 public final class AppInfoDashboardFragmentTest { 71 72 private static final String PACKAGE_NAME = "test_package_name"; 73 74 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 75 private UserManager mUserManager; 76 @Mock 77 private SettingsActivity mActivity; 78 @Mock 79 private DevicePolicyManager mDevicePolicyManager; 80 @Mock 81 private PackageManager mPackageManager; 82 83 private AppInfoDashboardFragment mFragment; 84 private Context mShadowContext; 85 86 @Before 87 public void setUp() { 88 MockitoAnnotations.initMocks(this); 89 mShadowContext = RuntimeEnvironment.application; 90 mFragment = spy(new AppInfoDashboardFragment()); 91 doReturn(mActivity).when(mFragment).getActivity(); 92 doReturn(mShadowContext).when(mFragment).getContext(); 93 doReturn(mPackageManager).when(mActivity).getPackageManager(); 94 95 // Default to not considering any apps to be instant (individual tests can override this). 96 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 97 (InstantAppDataProvider) (i -> false)); 98 } 99 100 @Test 101 public void shouldShowUninstallForAll_installForOneOtherUserOnly_shouldReturnTrue() { 102 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 103 when(mUserManager.getUsers().size()).thenReturn(2); 104 ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); 105 ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); 106 final ApplicationInfo info = new ApplicationInfo(); 107 info.enabled = true; 108 final AppEntry appEntry = mock(AppEntry.class); 109 appEntry.info = info; 110 final PackageInfo packageInfo = mock(PackageInfo.class); 111 ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); 112 113 assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isTrue(); 114 } 115 116 @Test 117 public void shouldShowUninstallForAll_installForSelfOnly_shouldReturnFalse() { 118 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 119 when(mUserManager.getUsers().size()).thenReturn(2); 120 ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); 121 ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); 122 final ApplicationInfo info = new ApplicationInfo(); 123 info.flags = ApplicationInfo.FLAG_INSTALLED; 124 info.enabled = true; 125 final AppEntry appEntry = mock(AppEntry.class); 126 appEntry.info = info; 127 final PackageInfo packageInfo = mock(PackageInfo.class); 128 ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); 129 130 assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse(); 131 } 132 133 @Test 134 public void onPrepareOptionsMenu_setUpdateMenuVisible_byDefaultForSystemApps_shouldBeTrue() { 135 Menu menu = onPrepareOptionsMenuTestsSetup(); 136 mFragment.onPrepareOptionsMenu(menu); 137 138 verify(menu.findItem(UNINSTALL_UPDATES), times(1)).setVisible(true); 139 } 140 141 @Test 142 @Config(qualifiers = "mcc999") 143 public void onPrepareOptionsMenu_setUpdateMenuVisible_ifDisabledByDevice_shouldBeFalse() { 144 Menu menu = onPrepareOptionsMenuTestsSetup(); 145 mFragment.onPrepareOptionsMenu(menu); 146 147 verify(menu.findItem(UNINSTALL_UPDATES), times(1)).setVisible(false); 148 } 149 150 private Menu onPrepareOptionsMenuTestsSetup() { 151 // Menu mocking 152 Menu menu = mock(Menu.class); 153 final MenuItem uninstallUpdatesMenuItem = mock(MenuItem.class); 154 final MenuItem uninstallForAllMenuItem = mock(MenuItem.class); 155 when(menu.findItem(UNINSTALL_UPDATES)).thenReturn(uninstallUpdatesMenuItem); 156 when(menu.findItem(UNINSTALL_ALL_USERS_MENU)).thenReturn(uninstallForAllMenuItem); 157 158 // Setup work to prevent NPE 159 final ApplicationInfo info = new ApplicationInfo(); 160 info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 161 info.enabled = true; 162 final AppEntry appEntry = mock(AppEntry.class); 163 appEntry.info = info; 164 mFragment.setAppEntry(appEntry); 165 166 return menu; 167 } 168 169 @Test 170 public void launchFragment_hasNoPackageInfo_shouldFinish() { 171 ReflectionHelpers.setField(mFragment, "mPackageInfo", null); 172 173 assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isFalse(); 174 verify(mActivity).finishAndRemoveTask(); 175 } 176 177 @Test 178 public void launchFragment_hasPackageInfo_shouldReturnTrue() { 179 final PackageInfo packageInfo = mock(PackageInfo.class); 180 ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); 181 182 assertThat(mFragment.ensurePackageInfoAvailable(mActivity)).isTrue(); 183 verify(mActivity, never()).finishAndRemoveTask(); 184 } 185 186 @Test 187 public void packageSizeChange_isOtherPackage_shouldNotRefreshUi() { 188 ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); 189 mFragment.onPackageSizeChanged("Not_" + PACKAGE_NAME); 190 191 verify(mFragment, never()).refreshUi(); 192 } 193 194 @Test 195 public void packageSizeChange_isOwnPackage_shouldRefreshUi() { 196 doReturn(Boolean.TRUE).when(mFragment).refreshUi(); 197 ReflectionHelpers.setField(mFragment, "mPackageName", PACKAGE_NAME); 198 199 mFragment.onPackageSizeChanged(PACKAGE_NAME); 200 201 verify(mFragment).refreshUi(); 202 } 203 204 // Tests that we don't show the "uninstall for all users" button for instant apps. 205 @Test 206 public void instantApps_noUninstallForAllButton() { 207 // Make this app appear to be instant. 208 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 209 (InstantAppDataProvider) (i -> true)); 210 when(mDevicePolicyManager.packageHasActiveAdmins(nullable(String.class))).thenReturn(false); 211 when(mUserManager.getUsers().size()).thenReturn(2); 212 213 final ApplicationInfo info = new ApplicationInfo(); 214 info.enabled = true; 215 final AppEntry appEntry = mock(AppEntry.class); 216 appEntry.info = info; 217 final PackageInfo packageInfo = mock(PackageInfo.class); 218 219 ReflectionHelpers.setField(mFragment, "mDpm", mDevicePolicyManager); 220 ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); 221 ReflectionHelpers.setField(mFragment, "mPackageInfo", packageInfo); 222 223 assertThat(mFragment.shouldShowUninstallForAll(appEntry)).isFalse(); 224 } 225 226 @Test 227 public void onActivityResult_uninstalledUpdates_shouldInvalidateOptionsMenu() { 228 doReturn(true).when(mFragment).refreshUi(); 229 230 mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class)); 231 232 verify(mActivity).invalidateOptionsMenu(); 233 } 234 235 @Test 236 public void onActivityResult_packageUninstalled_shouldFinishAndRemoveTask() { 237 doReturn(false).when(mFragment).refreshUi(); 238 239 mFragment.onActivityResult(mFragment.REQUEST_UNINSTALL, 0, mock(Intent.class)); 240 241 verify(mActivity).finishAndRemoveTask(); 242 } 243 244 @Test 245 public void getPreferenceControllers_noPackageInfo_shouldReturnNull() { 246 doNothing().when(mFragment).retrieveAppEntry(); 247 248 assertThat(mFragment.createPreferenceControllers(mShadowContext)).isNull(); 249 } 250 251 @Test 252 public void getNumberOfUserWithPackageInstalled_twoUsersInstalled_shouldReturnTwo() 253 throws PackageManager.NameNotFoundException { 254 final String packageName = "Package1"; 255 final int userID1 = 1; 256 final int userID2 = 2; 257 final List<UserInfo> userInfos = new ArrayList<>(); 258 userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY)); 259 userInfos.add(new UserInfo(userID2, "yue", UserInfo.FLAG_GUEST)); 260 when(mUserManager.getUsers(true)).thenReturn(userInfos); 261 ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); 262 final ApplicationInfo appInfo = new ApplicationInfo(); 263 appInfo.flags = ApplicationInfo.FLAG_INSTALLED; 264 when(mPackageManager.getApplicationInfoAsUser( 265 packageName, PackageManager.GET_META_DATA, userID1)) 266 .thenReturn(appInfo); 267 when(mPackageManager.getApplicationInfoAsUser( 268 packageName, PackageManager.GET_META_DATA, userID2)) 269 .thenReturn(appInfo); 270 ReflectionHelpers.setField(mFragment, "mPm", mPackageManager); 271 272 assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(2); 273 } 274 275 @Test 276 public void getNumberOfUserWithPackageInstalled_oneUserInstalled_shouldReturnOne() 277 throws PackageManager.NameNotFoundException { 278 final String packageName = "Package1"; 279 final int userID1 = 1; 280 final int userID2 = 2; 281 final List<UserInfo> userInfos = new ArrayList<>(); 282 userInfos.add(new UserInfo(userID1, "User1", UserInfo.FLAG_PRIMARY)); 283 userInfos.add(new UserInfo(userID2, "yue", UserInfo.FLAG_GUEST)); 284 when(mUserManager.getUsers(true)).thenReturn(userInfos); 285 ReflectionHelpers.setField(mFragment, "mUserManager", mUserManager); 286 final ApplicationInfo appInfo = new ApplicationInfo(); 287 appInfo.flags = ApplicationInfo.FLAG_INSTALLED; 288 when(mPackageManager.getApplicationInfoAsUser( 289 packageName, PackageManager.GET_META_DATA, userID1)) 290 .thenReturn(appInfo); 291 when(mPackageManager.getApplicationInfoAsUser( 292 packageName, PackageManager.GET_META_DATA, userID2)) 293 .thenThrow(new PackageManager.NameNotFoundException()); 294 ReflectionHelpers.setField(mFragment, "mPm", mPackageManager); 295 296 assertThat(mFragment.getNumberOfUserWithPackageInstalled(packageName)).isEqualTo(1); 297 } 298 299 @Test 300 public void onDestroy_shouldUnregisterReceiver() { 301 final Context context = mock(Context.class); 302 doReturn(context).when(mFragment).getContext(); 303 ReflectionHelpers.setField(mFragment, "mLifecycle", mock(Lifecycle.class)); 304 ReflectionHelpers.setField(mFragment, "mCheckedForLoaderManager", true); 305 mFragment.startListeningToPackageRemove(); 306 307 mFragment.onDestroy(); 308 309 verify(context).unregisterReceiver(mFragment.mPackageRemovedReceiver); 310 } 311 312 @Test 313 public void startAppInfoFragment_noCrashOnNullArgs() { 314 final SettingsPreferenceFragment caller = mock(SettingsPreferenceFragment.class); 315 final SettingsActivity sa = mock(SettingsActivity.class); 316 when(caller.getActivity()).thenReturn(sa); 317 when(caller.getContext()).thenReturn(sa); 318 final AppEntry appEntry = mock(AppEntry.class); 319 appEntry.info = mock(ApplicationInfo.class); 320 321 AppInfoDashboardFragment.startAppInfoFragment(AppInfoDashboardFragment.class, 0, null, 322 caller, appEntry); 323 } 324 325 @Test 326 public void startAppInfoFragment_includesNewAndOldArgs() { 327 final SettingsPreferenceFragment caller = mock(SettingsPreferenceFragment.class); 328 final SettingsActivity sa = mock(SettingsActivity.class); 329 when(caller.getContext()).thenReturn(sa); 330 final AppEntry appEntry = mock(AppEntry.class); 331 appEntry.info = mock(ApplicationInfo.class); 332 333 final Bundle bundle = new Bundle(); 334 bundle.putString("test", "test"); 335 336 AppInfoDashboardFragment.startAppInfoFragment(AppInfoDashboardFragment.class, 0, bundle, 337 caller, appEntry); 338 339 final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); 340 341 verify(caller).startActivityForResult(intent.capture(), any(Integer.class)); 342 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 343 .containsKey("test")) 344 .isTrue(); 345 assertThat(intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) 346 .containsKey(ARG_PACKAGE_NAME)) 347 .isTrue(); 348 } 349 } 350