1 /* 2 * Copyright (C) 2016 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; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.content.pm.UserInfo; 25 import android.os.Build; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 29 import com.android.settings.SettingsRobolectricTestRunner; 30 import com.android.settings.TestConfig; 31 import com.android.settings.testutils.shadow.ShadowUserManager; 32 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.mockito.ArgumentMatcher; 37 import org.mockito.Mock; 38 import org.mockito.MockitoAnnotations; 39 import org.robolectric.annotation.Config; 40 import org.robolectric.shadows.ShadowApplication; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.Set; 46 47 import static com.android.settings.testutils.ApplicationTestUtils.buildInfo; 48 import static com.google.common.truth.Truth.assertThat; 49 import static org.mockito.Matchers.anyInt; 50 import static org.mockito.Matchers.argThat; 51 import static org.mockito.Matchers.anyObject; 52 import static org.mockito.Matchers.eq; 53 import static org.mockito.Mockito.atLeast; 54 import static org.mockito.Mockito.verify; 55 import static org.mockito.Mockito.verifyNoMoreInteractions; 56 import static org.mockito.Mockito.when; 57 58 /** 59 * Tests for {@link InstalledAppCounter}. 60 */ 61 @RunWith(SettingsRobolectricTestRunner.class) 62 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, 63 shadows = {ShadowUserManager.class}) 64 public final class InstalledAppCounterTest { 65 66 private final String APP_1 = "app1"; 67 private final String APP_2 = "app2"; 68 private final String APP_3 = "app3"; 69 private final String APP_4 = "app4"; 70 private final String APP_5 = "app5"; 71 private final String APP_6 = "app6"; 72 73 private final int MAIN_USER_ID = 0; 74 private final int MANAGED_PROFILE_ID = 10; 75 76 private final int PER_USER_UID_RANGE = 100000; 77 private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE; 78 private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE; 79 80 @Mock 81 private UserManager mUserManager; 82 @Mock 83 private Context mContext; 84 @Mock 85 private PackageManagerWrapper mPackageManager; 86 87 private int mInstalledAppCount = -1; 88 private ApplicationInfo mApp1; 89 private ApplicationInfo mApp2; 90 private ApplicationInfo mApp3; 91 private ApplicationInfo mApp4; 92 private ApplicationInfo mApp5; 93 private ApplicationInfo mApp6; 94 95 @Before setUp()96 public void setUp() { 97 MockitoAnnotations.initMocks(this); 98 when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); 99 100 mApp1 = buildInfo(MAIN_USER_APP_UID, APP_1, 101 ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */); 102 mApp2 = buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */, 103 0 /* targetSdkVersion */); 104 mApp3 = buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM, 105 0 /* targetSdkVersion */); 106 mApp4 = buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM, 107 0 /* targetSdkVersion */); 108 mApp5 = buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */, 109 0 /* targetSdkVersion */); 110 mApp6 = buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM, 111 0 /* targetSdkVersion */); 112 } 113 expectQueryIntentActivities(int userId, String packageName, boolean launchable)114 private void expectQueryIntentActivities(int userId, String packageName, boolean launchable) { 115 when(mPackageManager.queryIntentActivitiesAsUser( 116 argThat(new IsLaunchIntentFor(packageName)), 117 eq(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.MATCH_DIRECT_BOOT_AWARE 118 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE), 119 eq(userId))).thenReturn(launchable ? Arrays.asList(new ResolveInfo()) 120 : new ArrayList<ResolveInfo>()); 121 } 122 testCountInstalledAppsAcrossAllUsers(boolean async)123 private void testCountInstalledAppsAcrossAllUsers(boolean async) { 124 // There are two users. 125 when(mUserManager.getProfiles(UserHandle.myUserId())).thenReturn(Arrays.asList( 126 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), 127 new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0))); 128 configurePackageManager(); 129 130 // Count the number of all apps installed, irrespective of install reason. 131 count(InstalledAppCounter.IGNORE_INSTALL_REASON, async); 132 assertThat(mInstalledAppCount).isEqualTo(5); 133 134 // Verify that installed packages were retrieved the current user and the user's managed 135 // profile only. 136 verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID)); 137 verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), 138 eq(MANAGED_PROFILE_ID)); 139 verify(mPackageManager, atLeast(0)).queryIntentActivitiesAsUser(anyObject(), anyInt(), 140 anyInt()); 141 verifyNoMoreInteractions(mPackageManager); 142 143 // Count once more, considering apps installed by enterprise policy only. 144 count(PackageManager.INSTALL_REASON_POLICY, async); 145 assertThat(mInstalledAppCount).isEqualTo(3); 146 } 147 148 @Test testIncludeInCount()149 public void testIncludeInCount() { 150 configurePackageManager(); 151 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 152 mPackageManager, mApp1)).isTrue(); 153 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 154 mPackageManager, mApp2)).isTrue(); 155 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 156 mPackageManager, mApp3)).isTrue(); 157 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 158 mPackageManager, mApp4)).isFalse(); 159 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 160 mPackageManager, mApp5)).isTrue(); 161 assertThat(InstalledAppCounter.includeInCount(InstalledAppCounter.IGNORE_INSTALL_REASON, 162 mPackageManager, mApp6)).isTrue(); 163 164 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 165 mPackageManager, mApp1)).isTrue(); 166 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 167 mPackageManager, mApp2)).isFalse(); 168 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 169 mPackageManager, mApp3)).isTrue(); 170 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 171 mPackageManager, mApp4)).isFalse(); 172 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 173 mPackageManager, mApp5)).isTrue(); 174 assertThat(InstalledAppCounter.includeInCount(PackageManager.INSTALL_REASON_POLICY, 175 mPackageManager, mApp6)).isFalse(); 176 } 177 178 @Test testCountInstalledAppsAcrossAllUsersSync()179 public void testCountInstalledAppsAcrossAllUsersSync() { 180 testCountInstalledAppsAcrossAllUsers(false /* async */); 181 } 182 183 @Test testCountInstalledAppsAcrossAllUsersAsync()184 public void testCountInstalledAppsAcrossAllUsersAsync() { 185 testCountInstalledAppsAcrossAllUsers(true /* async */); 186 } 187 count(int installReason, boolean async)188 private void count(int installReason, boolean async) { 189 mInstalledAppCount = -1; 190 final InstalledAppCounterTestable counter = new InstalledAppCounterTestable(installReason); 191 if (async) { 192 counter.execute(); 193 // Wait for the background task to finish. 194 ShadowApplication.runBackgroundTasks(); 195 } else { 196 counter.executeInForeground(); 197 } 198 } 199 configurePackageManager()200 private void configurePackageManager() { 201 // The first user has four apps installed: 202 // * app1 is an updated system app. It should be counted. 203 // * app2 is a user-installed app. It should be counted. 204 // * app3 is a system app that provides a launcher icon. It should be counted. 205 // * app4 is a system app that provides no launcher icon. It should not be counted. 206 when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS 207 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS 208 | PackageManager.MATCH_ANY_USER, 209 MAIN_USER_ID)).thenReturn(Arrays.asList(mApp1, mApp2, mApp3, mApp4)); 210 // For system apps, InstalledAppCounter checks whether they handle the default launcher 211 // intent to decide whether to include them in the count of installed apps or not. 212 expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */); 213 expectQueryIntentActivities(MAIN_USER_ID, APP_4, false /* launchable */); 214 215 // app1, app3 and app4 are installed by enterprise policy. 216 final UserHandle mainUser = new UserHandle(MAIN_USER_ID); 217 when(mPackageManager.getInstallReason(APP_1, mainUser)) 218 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 219 when(mPackageManager.getInstallReason(APP_2, mainUser)) 220 .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); 221 when(mPackageManager.getInstallReason(APP_3, mainUser)) 222 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 223 when(mPackageManager.getInstallReason(APP_4, mainUser)) 224 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 225 226 // The second user has two apps installed: 227 // * app5 is a user-installed app. It should be counted. 228 // * app6 is a system app that provides a launcher icon. It should be counted. 229 when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS 230 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,MANAGED_PROFILE_ID)) 231 .thenReturn(Arrays.asList(mApp5, mApp6)); 232 expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */); 233 234 // app5 is installed by enterprise policy. 235 final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID); 236 when(mPackageManager.getInstallReason(APP_5, managedProfileUser)) 237 .thenReturn(PackageManager.INSTALL_REASON_POLICY); 238 when(mPackageManager.getInstallReason(APP_6, managedProfileUser)) 239 .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); 240 } 241 242 243 private class InstalledAppCounterTestable extends InstalledAppCounter { InstalledAppCounterTestable(int installReason)244 public InstalledAppCounterTestable(int installReason) { 245 super(mContext, installReason, mPackageManager); 246 } 247 248 @Override onCountComplete(int num)249 protected void onCountComplete(int num) { 250 mInstalledAppCount = num; 251 } 252 } 253 254 private static class IsLaunchIntentFor extends ArgumentMatcher<Intent> { 255 private final String mPackageName; 256 IsLaunchIntentFor(String packageName)257 IsLaunchIntentFor(String packageName) { 258 mPackageName = packageName; 259 } 260 261 @Override matches(Object i)262 public boolean matches(Object i) { 263 final Intent intent = (Intent) i; 264 if (intent == null) { 265 return false; 266 } 267 if (!Intent.ACTION_MAIN.equals(intent.getAction())) { 268 return false; 269 } 270 final Set<String> categories = intent.getCategories(); 271 if (categories == null || categories.size() != 1 || 272 !categories.contains(Intent.CATEGORY_LAUNCHER)) { 273 return false; 274 } 275 if (!mPackageName.equals(intent.getPackage())) { 276 return false; 277 } 278 return true; 279 } 280 } 281 } 282