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.settingslib.applications; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.anyString; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 import static org.robolectric.shadow.api.Shadow.extract; 25 26 import android.annotation.UserIdInt; 27 import android.app.ApplicationPackageManager; 28 import android.app.usage.IStorageStatsManager; 29 import android.app.usage.StorageStats; 30 import android.app.usage.StorageStatsManager; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.content.pm.ActivityInfo; 36 import android.content.pm.ApplicationInfo; 37 import android.content.pm.PackageManager; 38 import android.content.pm.ResolveInfo; 39 import android.graphics.drawable.ColorDrawable; 40 import android.graphics.drawable.Drawable; 41 import android.os.Handler; 42 import android.os.UserHandle; 43 import android.util.IconDrawableFactory; 44 45 import com.android.settingslib.applications.ApplicationsState.AppEntry; 46 import com.android.settingslib.applications.ApplicationsState.Callbacks; 47 import com.android.settingslib.applications.ApplicationsState.Session; 48 import com.android.settingslib.SettingsLibRobolectricTestRunner; 49 import com.android.settingslib.testutils.shadow.ShadowUserManager; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.mockito.ArgumentCaptor; 55 import org.mockito.ArgumentMatchers; 56 import org.mockito.Captor; 57 import org.mockito.Mock; 58 import org.mockito.MockitoAnnotations; 59 import org.robolectric.RuntimeEnvironment; 60 import org.robolectric.annotation.Config; 61 import org.robolectric.annotation.Implementation; 62 import org.robolectric.annotation.Implements; 63 import org.robolectric.shadow.api.Shadow; 64 import org.robolectric.shadows.ShadowContextImpl; 65 import org.robolectric.shadows.ShadowLooper; 66 67 import java.util.ArrayList; 68 import java.util.List; 69 import java.util.UUID; 70 71 @RunWith(SettingsLibRobolectricTestRunner.class) 72 @Config(shadows = {ShadowUserManager.class, 73 ApplicationsStateRoboTest.ShadowIconDrawableFactory.class, 74 ApplicationsStateRoboTest.ShadowPackageManager.class}) 75 public class ApplicationsStateRoboTest { 76 77 private final static String HOME_PACKAGE_NAME = "com.android.home"; 78 private final static String LAUNCHABLE_PACKAGE_NAME = "com.android.launchable"; 79 80 /** Class under test */ 81 private ApplicationsState mApplicationsState; 82 83 @Mock 84 private Callbacks mCallbacks; 85 @Captor 86 private ArgumentCaptor<ArrayList<AppEntry>> mAppEntriesCaptor; 87 @Mock 88 private StorageStatsManager mStorageStatsManager; 89 90 @Implements(value = IconDrawableFactory.class, inheritImplementationMethods = true) 91 public static class ShadowIconDrawableFactory { 92 93 @Implementation 94 public Drawable getBadgedIcon(ApplicationInfo appInfo) { 95 return new ColorDrawable(0); 96 } 97 } 98 99 @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) 100 public static class ShadowPackageManager extends 101 org.robolectric.shadows.ShadowApplicationPackageManager { 102 103 @Implementation 104 public ComponentName getHomeActivities(List<ResolveInfo> outActivities) { 105 ResolveInfo resolveInfo = new ResolveInfo(); 106 resolveInfo.activityInfo = new ActivityInfo(); 107 resolveInfo.activityInfo.packageName = HOME_PACKAGE_NAME; 108 resolveInfo.activityInfo.enabled = true; 109 outActivities.add(resolveInfo); 110 return ComponentName.createRelative(resolveInfo.activityInfo.packageName, "foo"); 111 } 112 113 public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, 114 @PackageManager.ResolveInfoFlags int flags, @UserIdInt int userId) { 115 List<ResolveInfo> resolveInfos = new ArrayList<>(); 116 ResolveInfo resolveInfo = new ResolveInfo(); 117 resolveInfo.activityInfo = new ActivityInfo(); 118 resolveInfo.activityInfo.packageName = LAUNCHABLE_PACKAGE_NAME; 119 resolveInfo.activityInfo.enabled = true; 120 resolveInfo.filter = new IntentFilter(); 121 resolveInfo.filter.addCategory(Intent.CATEGORY_LAUNCHER); 122 resolveInfos.add(resolveInfo); 123 return resolveInfos; 124 } 125 } 126 127 @Before 128 public void setUp() throws Exception { 129 MockitoAnnotations.initMocks(this); 130 131 // Robolectric does not know about the StorageStatsManager as a system service. 132 // Registering a mock of this service as a replacement. 133 ShadowContextImpl shadowContext = Shadow.extract( 134 RuntimeEnvironment.application.getBaseContext()); 135 shadowContext.setSystemService(Context.STORAGE_STATS_SERVICE, mStorageStatsManager); 136 StorageStats storageStats = new StorageStats(); 137 storageStats.codeBytes = 10; 138 storageStats.dataBytes = 20; 139 storageStats.cacheBytes = 30; 140 when(mStorageStatsManager.queryStatsForPackage(ArgumentMatchers.any(UUID.class), 141 anyString(), ArgumentMatchers.any(UserHandle.class))).thenReturn(storageStats); 142 143 mApplicationsState = ApplicationsState.getInstance(RuntimeEnvironment.application); 144 mApplicationsState.clearEntries(); 145 } 146 147 private ApplicationInfo createApplicationInfo(String packageName) { 148 ApplicationInfo appInfo = new ApplicationInfo(); 149 appInfo.sourceDir = "foo"; 150 appInfo.flags |= ApplicationInfo.FLAG_INSTALLED; 151 appInfo.storageUuid = UUID.randomUUID(); 152 appInfo.packageName = packageName; 153 return appInfo; 154 } 155 156 private AppEntry createAppEntry(ApplicationInfo appInfo, int id) { 157 AppEntry appEntry = new AppEntry(RuntimeEnvironment.application, appInfo, id); 158 appEntry.label = "label"; 159 appEntry.mounted = true; 160 return appEntry; 161 } 162 163 private void addApp(String packageName, int id) { 164 ApplicationInfo appInfo = createApplicationInfo(packageName); 165 AppEntry appEntry = createAppEntry(appInfo, id); 166 mApplicationsState.mAppEntries.add(appEntry); 167 mApplicationsState.mEntriesMap.get(0).put(appInfo.packageName, appEntry); 168 } 169 170 private void processAllMessages() { 171 Handler mainHandler = mApplicationsState.mMainHandler; 172 Handler bkgHandler = mApplicationsState.mBackgroundHandler; 173 ShadowLooper shadowBkgLooper = extract(bkgHandler.getLooper()); 174 ShadowLooper shadowMainLooper = extract(mainHandler.getLooper()); 175 shadowBkgLooper.idle(); 176 shadowMainLooper.idle(); 177 } 178 179 private AppEntry findAppEntry(List<AppEntry> appEntries, long id) { 180 for (AppEntry appEntry : appEntries) { 181 if (appEntry.id == id) { 182 return appEntry; 183 } 184 } 185 return null; 186 } 187 188 @Test 189 public void testDefaultSessionLoadsAll() { 190 Session session = mApplicationsState.newSession(mCallbacks); 191 session.onResume(); 192 193 addApp(HOME_PACKAGE_NAME,1); 194 addApp(LAUNCHABLE_PACKAGE_NAME,2); 195 session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 196 processAllMessages(); 197 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 198 199 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 200 assertThat(appEntries.size()).isEqualTo(2); 201 202 for (AppEntry appEntry : appEntries) { 203 assertThat(appEntry.size).isGreaterThan(0L); 204 assertThat(appEntry.icon).isNotNull(); 205 } 206 207 AppEntry homeEntry = findAppEntry(appEntries, 1); 208 assertThat(homeEntry.isHomeApp).isTrue(); 209 assertThat(homeEntry.hasLauncherEntry).isFalse(); 210 211 AppEntry launchableEntry = findAppEntry(appEntries, 2); 212 assertThat(launchableEntry.hasLauncherEntry).isTrue(); 213 assertThat(launchableEntry.launcherEntryEnabled).isTrue(); 214 session.onDestroy(); 215 } 216 217 @Test 218 public void testCustomSessionLoadsIconsOnly() { 219 Session session = mApplicationsState.newSession(mCallbacks); 220 session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS); 221 session.onResume(); 222 223 addApp(LAUNCHABLE_PACKAGE_NAME,1); 224 session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 225 processAllMessages(); 226 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 227 228 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 229 assertThat(appEntries.size()).isEqualTo(1); 230 231 AppEntry launchableEntry = findAppEntry(appEntries, 1); 232 assertThat(launchableEntry.icon).isNotNull(); 233 assertThat(launchableEntry.size).isEqualTo(-1); 234 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 235 session.onDestroy(); 236 } 237 238 @Test 239 public void testCustomSessionLoadsSizesOnly() { 240 Session session = mApplicationsState.newSession(mCallbacks); 241 session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_SIZES); 242 session.onResume(); 243 244 addApp(LAUNCHABLE_PACKAGE_NAME,1); 245 session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 246 processAllMessages(); 247 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 248 249 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 250 assertThat(appEntries.size()).isEqualTo(1); 251 252 AppEntry launchableEntry = findAppEntry(appEntries, 1); 253 assertThat(launchableEntry.icon).isNull(); 254 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 255 assertThat(launchableEntry.size).isGreaterThan(0L); 256 session.onDestroy(); 257 } 258 259 @Test 260 public void testCustomSessionLoadsHomeOnly() { 261 Session session = mApplicationsState.newSession(mCallbacks); 262 session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_HOME_APP); 263 session.onResume(); 264 265 addApp(HOME_PACKAGE_NAME,1); 266 session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 267 processAllMessages(); 268 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 269 270 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 271 assertThat(appEntries.size()).isEqualTo(1); 272 273 AppEntry launchableEntry = findAppEntry(appEntries, 1); 274 assertThat(launchableEntry.icon).isNull(); 275 assertThat(launchableEntry.hasLauncherEntry).isFalse(); 276 assertThat(launchableEntry.size).isEqualTo(-1); 277 assertThat(launchableEntry.isHomeApp).isTrue(); 278 session.onDestroy(); 279 } 280 281 @Test 282 public void testCustomSessionLoadsLeanbackOnly() { 283 Session session = mApplicationsState.newSession(mCallbacks); 284 session.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER); 285 session.onResume(); 286 287 addApp(LAUNCHABLE_PACKAGE_NAME,1); 288 session.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR); 289 processAllMessages(); 290 verify(mCallbacks).onRebuildComplete(mAppEntriesCaptor.capture()); 291 292 List<AppEntry> appEntries = mAppEntriesCaptor.getValue(); 293 assertThat(appEntries.size()).isEqualTo(1); 294 295 AppEntry launchableEntry = findAppEntry(appEntries, 1); 296 assertThat(launchableEntry.icon).isNull(); 297 assertThat(launchableEntry.size).isEqualTo(-1); 298 assertThat(launchableEntry.isHomeApp).isFalse(); 299 assertThat(launchableEntry.hasLauncherEntry).isTrue(); 300 assertThat(launchableEntry.launcherEntryEnabled).isTrue(); 301 session.onDestroy(); 302 } 303 } 304