1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.storagemanager.automatic; 18 19import static com.google.common.truth.Truth.assertThat; 20 21import static org.mockito.Mockito.any; 22import static org.mockito.Mockito.anyInt; 23import static org.mockito.Mockito.eq; 24import static org.mockito.Mockito.mock; 25import static org.mockito.Mockito.never; 26import static org.mockito.Mockito.spy; 27import static org.mockito.Mockito.verify; 28import static org.mockito.Mockito.when; 29 30import android.app.NotificationManager; 31import android.app.job.JobParameters; 32import android.app.usage.StorageStatsManager; 33import android.content.ContentResolver; 34import android.content.Context; 35import android.content.Intent; 36import android.content.res.Resources; 37import android.os.BatteryManager; 38import android.os.storage.StorageManager; 39import android.os.storage.VolumeInfo; 40import android.provider.Settings; 41 42import com.android.settingslib.deviceinfo.StorageVolumeProvider; 43import com.android.storagemanager.overlay.FeatureFactory; 44import com.android.storagemanager.overlay.StorageManagementJobProvider; 45import com.android.storagemanager.testing.TestingConstants; 46 47import org.junit.Before; 48import org.junit.Test; 49import org.junit.runner.RunWith; 50import org.mockito.Mock; 51import org.mockito.MockitoAnnotations; 52import org.robolectric.Robolectric; 53import org.robolectric.RobolectricTestRunner; 54import org.robolectric.annotation.Config; 55import org.robolectric.shadows.ShadowApplication; 56import org.robolectric.util.ReflectionHelpers; 57 58import java.io.File; 59import java.util.ArrayList; 60import java.util.List; 61 62import static com.google.common.truth.Truth.assertThat; 63import static org.mockito.Mockito.anyInt; 64import static org.mockito.Mockito.eq; 65import static org.mockito.Mockito.mock; 66import static org.mockito.Mockito.nullable; 67import static org.mockito.Mockito.spy; 68import static org.mockito.Mockito.verify; 69import static org.mockito.Mockito.when; 70 71@RunWith(RobolectricTestRunner.class) 72@Config(manifest=TestingConstants.MANIFEST, sdk=TestingConstants.SDK_VERSION) 73public class AutomaticStorageManagementJobServiceTest { 74 @Mock private BatteryManager mBatteryManager; 75 @Mock private NotificationManager mNotificationManager; 76 @Mock private VolumeInfo mVolumeInfo; 77 @Mock private File mFile; 78 @Mock private JobParameters mJobParameters; 79 @Mock private StorageManagementJobProvider mStorageManagementJobProvider; 80 @Mock private FeatureFactory mFeatureFactory; 81 @Mock private StorageVolumeProvider mStorageVolumeProvider; 82 @Mock private AutomaticStorageManagementJobService.Clock mClock; 83 private AutomaticStorageManagementJobService mJobService; 84 private ShadowApplication mApplication; 85 private List<VolumeInfo> mVolumes; 86 87 @Before 88 public void setUp() throws Exception { 89 MockitoAnnotations.initMocks(this); 90 91 when(mJobParameters.getJobId()).thenReturn(0); 92 93 // Let's set up our system services to act like a device that has the following conditions: 94 // 1. We're plugged in and charging. 95 // 2. We have a completely full device. 96 // 3. ASM is disabled. 97 when(mBatteryManager.isCharging()).thenReturn(true); 98 mVolumes = new ArrayList<>(); 99 when(mVolumeInfo.getPath()).thenReturn(mFile); 100 when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE); 101 when(mVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIMARY_PHYSICAL); 102 when(mVolumeInfo.isMountedReadable()).thenReturn(true); 103 mVolumes.add(mVolumeInfo); 104 when(mStorageVolumeProvider.getPrimaryStorageSize()).thenReturn(100L); 105 when(mStorageVolumeProvider.getVolumes()).thenReturn(mVolumes); 106 when(mStorageVolumeProvider.getFreeBytes( 107 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 108 .thenReturn(0L); 109 when(mStorageVolumeProvider.getTotalBytes( 110 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 111 .thenReturn(100L); 112 113 mApplication = ShadowApplication.getInstance(); 114 mApplication.setSystemService(Context.BATTERY_SERVICE, mBatteryManager); 115 mApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); 116 117 // This is a hack-y injection of our own FeatureFactory. 118 // By default, the Storage Manager has a FeatureFactory which returns null for all features. 119 // Using reflection, we can inject our own FeatureFactory which returns a mock for the 120 // StorageManagementJobProvider feature. This lets us observe when the ASMJobService 121 // actually tries to run the job. 122 when(mFeatureFactory.getStorageManagementJobProvider()) 123 .thenReturn(mStorageManagementJobProvider); 124 when(mStorageManagementJobProvider.onStartJob( 125 nullable(Context.class), nullable(JobParameters.class), anyInt())) 126 .thenReturn(false); 127 ReflectionHelpers.setStaticField(FeatureFactory.class, "sFactory", mFeatureFactory); 128 129 // And we can't forget to initialize the actual job service. 130 mJobService = spy(Robolectric.setupService(AutomaticStorageManagementJobService.class)); 131 mJobService.setStorageVolumeProvider(mStorageVolumeProvider); 132 mJobService.setClock(mClock); 133 134 Resources fakeResources = mock(Resources.class); 135 when(fakeResources.getInteger( 136 com.android.internal.R.integer.config_storageManagerDaystoRetainDefault)) 137 .thenReturn(90); 138 139 when(mJobService.getResources()).thenReturn(fakeResources); 140 } 141 142 @Test 143 public void testJobRequiresCharging() { 144 when(mBatteryManager.isCharging()).thenReturn(false); 145 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 146 // The job should report that it needs to be retried, if not charging. 147 assertJobFinished(true); 148 149 when(mBatteryManager.isCharging()).thenReturn(true); 150 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 151 assertJobFinished(false); 152 } 153 154 @Test 155 public void testStartJobTriesUpsellWhenASMDisabled() { 156 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 157 assertJobFinished(false); 158 mApplication.runBackgroundTasks(); 159 160 List<Intent> broadcastedIntents = mApplication.getBroadcastIntents(); 161 assertThat(broadcastedIntents.size()).isEqualTo(1); 162 163 Intent lastIntent = broadcastedIntents.get(0); 164 assertThat(lastIntent.getAction()) 165 .isEqualTo(NotificationController.INTENT_ACTION_SHOW_NOTIFICATION); 166 assertThat(lastIntent.getComponent().getClassName()) 167 .isEqualTo(NotificationController.class.getCanonicalName()); 168 169 assertStorageManagerJobDidNotRun(); 170 } 171 172 @Test 173 public void testASMJobRunsWithValidConditions() { 174 activateASM(); 175 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 176 assertStorageManagerJobRan(); 177 } 178 179 @Test 180 public void testJobDoesntRunIfStorageNotFull() throws Exception { 181 activateASM(); 182 when(mStorageVolumeProvider.getFreeBytes( 183 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 184 .thenReturn(100L); 185 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 186 assertStorageManagerJobDidNotRun(); 187 } 188 189 @Test 190 public void testJobOnlyRunsIfFreeStorageIsUnder15Percent() throws Exception { 191 activateASM(); 192 when(mStorageVolumeProvider.getFreeBytes( 193 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 194 .thenReturn(15L); 195 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 196 assertStorageManagerJobDidNotRun(); 197 198 when(mStorageVolumeProvider.getFreeBytes( 199 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 200 .thenReturn(14L); 201 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 202 assertStorageManagerJobRan(); 203 } 204 205 @Test 206 public void testNonDefaultDaysToRetain() { 207 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 208 Settings.Secure.putInt(resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, 209 30); 210 activateASM(); 211 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 212 assertStorageManagerJobRan(30); 213 } 214 215 @Test 216 public void testNonPrivateDrivesIgnoredForFreeSpaceCalculation() throws Exception { 217 File notPrivate = mock(File.class); 218 VolumeInfo nonPrivateVolume = mock(VolumeInfo.class); 219 when(nonPrivateVolume.getPath()).thenReturn(notPrivate); 220 when(nonPrivateVolume.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC); 221 mVolumes.add(nonPrivateVolume); 222 when(mStorageVolumeProvider.getFreeBytes( 223 nullable(StorageStatsManager.class), eq(nonPrivateVolume))) 224 .thenReturn(0L); 225 when(mStorageVolumeProvider.getTotalBytes( 226 nullable(StorageStatsManager.class), eq(nonPrivateVolume))) 227 .thenReturn(100L); 228 activateASM(); 229 when(mStorageVolumeProvider.getFreeBytes( 230 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 231 .thenReturn(15L); 232 233 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 234 assertStorageManagerJobDidNotRun(); 235 } 236 237 @Test 238 public void testMultiplePrivateVolumesCountedForASMActivationThreshold() throws Exception { 239 File privateVolume = mock(File.class); 240 VolumeInfo privateVolumeInfo = mock(VolumeInfo.class); 241 when(privateVolumeInfo.getPath()).thenReturn(privateVolume); 242 when(privateVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE); 243 when(privateVolumeInfo.isMountedReadable()).thenReturn(true); 244 when(privateVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIVATE_INTERNAL); 245 when(mStorageVolumeProvider.getFreeBytes( 246 nullable(StorageStatsManager.class), eq(privateVolumeInfo))) 247 .thenReturn(0L); 248 when(mStorageVolumeProvider.getTotalBytes( 249 nullable(StorageStatsManager.class), eq(privateVolumeInfo))) 250 .thenReturn(100L); 251 mVolumes.add(privateVolumeInfo); 252 activateASM(); 253 when(mStorageVolumeProvider.getFreeBytes( 254 nullable(StorageStatsManager.class), eq(mVolumeInfo))) 255 .thenReturn(15L); 256 257 assertThat(mJobService.onStartJob(mJobParameters)).isFalse(); 258 assertStorageManagerJobRan(); 259 } 260 261 @Test 262 public void disableSmartStorageIfPastThreshold() throws Exception { 263 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 264 activateASM(); 265 266 AutomaticStorageManagementJobService.Clock fakeClock = 267 mock(AutomaticStorageManagementJobService.Clock.class); 268 when(fakeClock.currentTimeMillis()).thenReturn(1001L); 269 when(mStorageManagementJobProvider.getDisableThresholdMillis(any(ContentResolver.class))) 270 .thenReturn(1000L); 271 AutomaticStorageManagementJobService.maybeDisableDueToPolicy( 272 mStorageManagementJobProvider, resolver, fakeClock); 273 274 assertThat( 275 Settings.Secure.getInt( 276 resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED)) 277 .isEqualTo(0); 278 } 279 280 @Test 281 public void dontDisableSmartStorageIfPastThresholdAndDisabledInThePast() throws Exception { 282 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 283 activateASM(); 284 Settings.Secure.putInt( 285 resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY, 1); 286 287 AutomaticStorageManagementJobService.Clock fakeClock = 288 mock(AutomaticStorageManagementJobService.Clock.class); 289 when(fakeClock.currentTimeMillis()).thenReturn(1001L); 290 when(mStorageManagementJobProvider.getDisableThresholdMillis(any(ContentResolver.class))) 291 .thenReturn(1000L); 292 AutomaticStorageManagementJobService.maybeDisableDueToPolicy( 293 mStorageManagementJobProvider, resolver, fakeClock); 294 295 assertThat( 296 Settings.Secure.getInt( 297 resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED)) 298 .isNotEqualTo(0); 299 } 300 301 @Test 302 public void logDisabledByPolicyIfPastThreshold() throws Exception { 303 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 304 activateASM(); 305 306 AutomaticStorageManagementJobService.Clock fakeClock = 307 mock(AutomaticStorageManagementJobService.Clock.class); 308 when(fakeClock.currentTimeMillis()).thenReturn(1001L); 309 when(mStorageManagementJobProvider.getDisableThresholdMillis(any(ContentResolver.class))) 310 .thenReturn(1000L); 311 AutomaticStorageManagementJobService.maybeDisableDueToPolicy( 312 mStorageManagementJobProvider, resolver, fakeClock); 313 314 assertThat( 315 Settings.Secure.getInt( 316 resolver, 317 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY)) 318 .isGreaterThan(0); 319 } 320 321 @Test 322 public void dontDisableSmartStorageIfNotPastThreshold() throws Exception { 323 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 324 activateASM(); 325 326 AutomaticStorageManagementJobService.Clock fakeClock = 327 mock(AutomaticStorageManagementJobService.Clock.class); 328 when(fakeClock.currentTimeMillis()).thenReturn(999L); 329 when(mStorageManagementJobProvider.getDisableThresholdMillis(any(ContentResolver.class))) 330 .thenReturn(1000L); 331 AutomaticStorageManagementJobService.maybeDisableDueToPolicy( 332 mStorageManagementJobProvider, resolver, fakeClock); 333 334 assertThat( 335 Settings.Secure.getInt( 336 resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED)) 337 .isNotEqualTo(0); 338 } 339 340 private void assertJobFinished(boolean retryNeeded) { 341 verify(mJobService).jobFinished(nullable(JobParameters.class), eq(retryNeeded)); 342 } 343 344 private void assertStorageManagerJobRan() { 345 assertStorageManagerJobRan( 346 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT); 347 } 348 349 private void assertStorageManagerJobRan(int daysToRetain) { 350 verify(mStorageManagementJobProvider).onStartJob(eq(mJobService), eq(mJobParameters), 351 eq(daysToRetain)); 352 } 353 354 private void assertStorageManagerJobDidNotRun() { 355 verify(mStorageManagementJobProvider, never()) 356 .onStartJob(any(Context.class), any(JobParameters.class), anyInt()); 357 } 358 359 private void activateASM() { 360 ContentResolver resolver = mApplication.getApplicationContext().getContentResolver(); 361 Settings.Secure.putInt(resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 1); 362 } 363} 364