Home | History | Annotate | Download | only in automatic
      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 
     17 package com.android.storagemanager.automatic;
     18 
     19 import static com.google.common.truth.Truth.assertThat;
     20 
     21 import static org.mockito.Mockito.any;
     22 import static org.mockito.Mockito.anyInt;
     23 import static org.mockito.Mockito.eq;
     24 import static org.mockito.Mockito.mock;
     25 import static org.mockito.Mockito.never;
     26 import static org.mockito.Mockito.spy;
     27 import static org.mockito.Mockito.verify;
     28 import static org.mockito.Mockito.when;
     29 
     30 import android.app.NotificationManager;
     31 import android.app.job.JobParameters;
     32 import android.app.usage.StorageStatsManager;
     33 import android.content.ContentResolver;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.content.res.Resources;
     37 import android.os.BatteryManager;
     38 import android.os.storage.StorageManager;
     39 import android.os.storage.VolumeInfo;
     40 import android.provider.Settings;
     41 
     42 import com.android.settingslib.deviceinfo.StorageVolumeProvider;
     43 import com.android.storagemanager.overlay.FeatureFactory;
     44 import com.android.storagemanager.overlay.StorageManagementJobProvider;
     45 import com.android.storagemanager.testing.TestingConstants;
     46 
     47 import org.junit.Before;
     48 import org.junit.Test;
     49 import org.junit.runner.RunWith;
     50 import org.mockito.Mock;
     51 import org.mockito.MockitoAnnotations;
     52 import org.robolectric.Robolectric;
     53 import org.robolectric.RobolectricTestRunner;
     54 import org.robolectric.annotation.Config;
     55 import org.robolectric.shadows.ShadowApplication;
     56 import org.robolectric.util.ReflectionHelpers;
     57 
     58 import java.io.File;
     59 import java.util.ArrayList;
     60 import java.util.List;
     61 
     62 import static com.google.common.truth.Truth.assertThat;
     63 import static org.mockito.Mockito.anyInt;
     64 import static org.mockito.Mockito.eq;
     65 import static org.mockito.Mockito.mock;
     66 import static org.mockito.Mockito.nullable;
     67 import static org.mockito.Mockito.spy;
     68 import static org.mockito.Mockito.verify;
     69 import static org.mockito.Mockito.when;
     70 
     71 @RunWith(RobolectricTestRunner.class)
     72 @Config(manifest=TestingConstants.MANIFEST, sdk=TestingConstants.SDK_VERSION)
     73 public 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