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