1 /* 2 * Copyright (C) 2019 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.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyLong; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.when; 28 29 import android.app.usage.UsageStats; 30 import android.app.usage.UsageStatsManager; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.ModuleInfo; 35 import android.content.pm.PackageManager; 36 import android.content.pm.ResolveInfo; 37 import android.os.PowerManager; 38 import android.os.UserHandle; 39 import android.os.UserManager; 40 41 import com.android.settingslib.applications.AppUtils; 42 import com.android.settingslib.applications.ApplicationsState; 43 import com.android.settingslib.applications.instantapps.InstantAppDataProvider; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.Mock; 49 import org.mockito.MockitoAnnotations; 50 import org.robolectric.RobolectricTestRunner; 51 import org.robolectric.RuntimeEnvironment; 52 import org.robolectric.util.ReflectionHelpers; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 57 @RunWith(RobolectricTestRunner.class) 58 public class RecentAppStatsMixinTest { 59 60 @Mock 61 private UsageStatsManager mUsageStatsManager; 62 @Mock 63 private UserManager mUserManager; 64 @Mock 65 private ApplicationsState mAppState; 66 @Mock 67 private PackageManager mPackageManager; 68 @Mock 69 private ApplicationsState.AppEntry mAppEntry; 70 @Mock 71 private ApplicationInfo mApplicationInfo; 72 @Mock 73 private PowerManager mPowerManager; 74 75 private RecentAppStatsMixin mRecentAppStatsMixin; 76 77 @Before setUp()78 public void setUp() { 79 MockitoAnnotations.initMocks(this); 80 final Context context = spy(RuntimeEnvironment.application); 81 when(context.getApplicationContext()).thenReturn(context); 82 ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", mAppState); 83 doReturn(mUsageStatsManager).when(context).getSystemService(Context.USAGE_STATS_SERVICE); 84 doReturn(mUserManager).when(context).getSystemService(Context.USER_SERVICE); 85 doReturn(mPackageManager).when(context).getPackageManager(); 86 doReturn(mPowerManager).when(context).getSystemService(PowerManager.class); 87 when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{}); 88 89 mRecentAppStatsMixin = new RecentAppStatsMixin(context, 3 /* maximumApps */); 90 } 91 92 @Test loadDisplayableRecentApps_oneValidRecentAppSet_shouldHaveOneRecentApp()93 public void loadDisplayableRecentApps_oneValidRecentAppSet_shouldHaveOneRecentApp() { 94 final List<UsageStats> stats = new ArrayList<>(); 95 final UsageStats stat1 = new UsageStats(); 96 stat1.mLastTimeUsed = System.currentTimeMillis(); 97 stat1.mPackageName = "pkg.class"; 98 stats.add(stat1); 99 // stat1 is valid app. 100 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) 101 .thenReturn(mAppEntry); 102 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 103 .thenReturn(new ResolveInfo()); 104 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 105 .thenReturn(stats); 106 mAppEntry.info = mApplicationInfo; 107 108 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 109 110 assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1); 111 } 112 113 @Test loadDisplayableRecentApps_threeValidRecentAppsSet_shouldHaveThreeRecentApps()114 public void loadDisplayableRecentApps_threeValidRecentAppsSet_shouldHaveThreeRecentApps() { 115 final List<UsageStats> stats = new ArrayList<>(); 116 final UsageStats stat1 = new UsageStats(); 117 final UsageStats stat2 = new UsageStats(); 118 final UsageStats stat3 = new UsageStats(); 119 stat1.mLastTimeUsed = System.currentTimeMillis(); 120 stat1.mPackageName = "pkg.class"; 121 stats.add(stat1); 122 123 stat2.mLastTimeUsed = System.currentTimeMillis(); 124 stat2.mPackageName = "pkg.class2"; 125 stats.add(stat2); 126 127 stat3.mLastTimeUsed = System.currentTimeMillis(); 128 stat3.mPackageName = "pkg.class3"; 129 stats.add(stat3); 130 131 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) 132 .thenReturn(mAppEntry); 133 when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())) 134 .thenReturn(mAppEntry); 135 when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId())) 136 .thenReturn(mAppEntry); 137 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 138 .thenReturn(new ResolveInfo()); 139 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 140 .thenReturn(stats); 141 mAppEntry.info = mApplicationInfo; 142 143 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 144 145 assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(3); 146 } 147 148 @Test loadDisplayableRecentApps_oneValidAndTwoInvalidSet_shouldHaveOneRecentApp()149 public void loadDisplayableRecentApps_oneValidAndTwoInvalidSet_shouldHaveOneRecentApp() { 150 final List<UsageStats> stats = new ArrayList<>(); 151 final UsageStats stat1 = new UsageStats(); 152 final UsageStats stat2 = new UsageStats(); 153 final UsageStats stat3 = new UsageStats(); 154 stat1.mLastTimeUsed = System.currentTimeMillis(); 155 stat1.mPackageName = "pkg.class"; 156 stats.add(stat1); 157 158 stat2.mLastTimeUsed = System.currentTimeMillis(); 159 stat2.mPackageName = "com.android.settings"; 160 stats.add(stat2); 161 162 stat3.mLastTimeUsed = System.currentTimeMillis(); 163 stat3.mPackageName = "pkg.class3"; 164 stats.add(stat3); 165 166 // stat1, stat2 are valid apps. stat3 is invalid. 167 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) 168 .thenReturn(mAppEntry); 169 when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())) 170 .thenReturn(mAppEntry); 171 when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId())) 172 .thenReturn(null); 173 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 174 .thenReturn(new ResolveInfo()); 175 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 176 .thenReturn(stats); 177 mAppEntry.info = mApplicationInfo; 178 179 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 180 181 // Only stat1. stat2 is skipped because of the package name, stat3 skipped because 182 // it's invalid app. 183 assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1); 184 } 185 186 @Test loadDisplayableRecentApps_oneInstantAppSet_shouldHaveOneRecentApp()187 public void loadDisplayableRecentApps_oneInstantAppSet_shouldHaveOneRecentApp() { 188 final List<UsageStats> stats = new ArrayList<>(); 189 // Instant app. 190 final UsageStats stat = new UsageStats(); 191 stat.mLastTimeUsed = System.currentTimeMillis() + 200; 192 stat.mPackageName = "com.foo.barinstant"; 193 stats.add(stat); 194 195 ApplicationsState.AppEntry statEntry = mock(ApplicationsState.AppEntry.class); 196 statEntry.info = mApplicationInfo; 197 198 when(mAppState.getEntry(stat.mPackageName, UserHandle.myUserId())).thenReturn(statEntry); 199 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 200 .thenReturn(stats); 201 202 // Make sure stat is considered an instant app. 203 ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", 204 (InstantAppDataProvider) (ApplicationInfo info) -> info == statEntry.info); 205 206 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 207 208 assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1); 209 } 210 211 @Test loadDisplayableRecentApps_withNullAppEntryOrInfo_shouldNotCrash()212 public void loadDisplayableRecentApps_withNullAppEntryOrInfo_shouldNotCrash() { 213 final List<UsageStats> stats = new ArrayList<>(); 214 final UsageStats stat1 = new UsageStats(); 215 final UsageStats stat2 = new UsageStats(); 216 stat1.mLastTimeUsed = System.currentTimeMillis(); 217 stat1.mPackageName = "pkg.class"; 218 stats.add(stat1); 219 220 stat2.mLastTimeUsed = System.currentTimeMillis(); 221 stat2.mPackageName = "pkg.class2"; 222 stats.add(stat2); 223 224 // app1 has AppEntry with null info, app2 has null AppEntry. 225 mAppEntry.info = null; 226 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) 227 .thenReturn(mAppEntry); 228 when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())) 229 .thenReturn(null); 230 231 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 232 .thenReturn(stats); 233 234 // We should not crash here. 235 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 236 } 237 238 @Test loadDisplayableRecentApps_hiddenSystemModuleSet_shouldNotHaveHiddenSystemModule()239 public void loadDisplayableRecentApps_hiddenSystemModuleSet_shouldNotHaveHiddenSystemModule() { 240 final List<UsageStats> stats = new ArrayList<>(); 241 // Regular app. 242 final UsageStats stat1 = new UsageStats(); 243 stat1.mLastTimeUsed = System.currentTimeMillis(); 244 stat1.mPackageName = "com.foo.bar"; 245 stats.add(stat1); 246 247 // Hidden system module. 248 final UsageStats stat2 = new UsageStats(); 249 stat2.mLastTimeUsed = System.currentTimeMillis() + 200; 250 stat2.mPackageName = "com.foo.hidden"; 251 stats.add(stat2); 252 253 ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class); 254 ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class); 255 stat1Entry.info = mApplicationInfo; 256 stat2Entry.info = mApplicationInfo; 257 258 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry); 259 when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry); 260 261 final ModuleInfo moduleInfo1 = new ModuleInfo(); 262 moduleInfo1.setPackageName(stat1.mPackageName); 263 moduleInfo1.setHidden(false); 264 265 final ModuleInfo moduleInfo2 = new ModuleInfo(); 266 moduleInfo2.setPackageName(stat2.mPackageName); 267 moduleInfo2.setHidden(true); 268 269 ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", null); 270 final List<ModuleInfo> modules = new ArrayList<>(); 271 modules.add(moduleInfo2); 272 when(mPackageManager.getInstalledModules(anyInt() /* flags */)) 273 .thenReturn(modules); 274 275 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 276 .thenReturn(new ResolveInfo()); 277 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 278 .thenReturn(stats); 279 280 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 281 282 assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1); 283 } 284 285 @Test loadDisplayableRecentApps_powerSaverModeOn_shouldHaveEmptyList()286 public void loadDisplayableRecentApps_powerSaverModeOn_shouldHaveEmptyList() { 287 when(mPowerManager.isPowerSaveMode()).thenReturn(true); 288 289 final List<UsageStats> stats = new ArrayList<>(); 290 final UsageStats stat1 = new UsageStats(); 291 292 stat1.mLastTimeUsed = System.currentTimeMillis(); 293 stat1.mPackageName = "pkg.class"; 294 stats.add(stat1); 295 296 // stat1, stat2 are valid apps. stat3 is invalid. 297 when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())) 298 .thenReturn(mAppEntry); 299 when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) 300 .thenReturn(new ResolveInfo()); 301 when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) 302 .thenReturn(stats); 303 mAppEntry.info = mApplicationInfo; 304 305 mRecentAppStatsMixin.loadDisplayableRecentApps(3); 306 307 assertThat(mRecentAppStatsMixin.mRecentApps).isEmpty(); 308 } 309 } 310