blob: c0e1cd4e593b9df0cdee1f8c9a9e1a728da2435e [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.storagemanager.automatic;
import android.app.NotificationManager;
import android.app.job.JobParameters;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import com.android.storagemanager.overlay.FeatureFactory;
import com.android.storagemanager.overlay.StorageManagementJobProvider;
import com.android.storagemanager.testing.TestingConstants;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.util.ReflectionHelpers;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
@Config(manifest=TestingConstants.MANIFEST, sdk=TestingConstants.SDK_VERSION)
public class AutomaticStorageManagementJobServiceTest {
@Mock private BatteryManager mBatteryManager;
@Mock private NotificationManager mNotificationManager;
@Mock private VolumeInfo mVolumeInfo;
@Mock private File mFile;
@Mock private JobParameters mJobParameters;
@Mock private StorageManagementJobProvider mStorageManagementJobProvider;
@Mock private FeatureFactory mFeatureFactory;
@Mock private StorageVolumeProvider mStorageVolumeProvider;
private AutomaticStorageManagementJobService mJobService;
private ShadowApplication mApplication;
private List<VolumeInfo> mVolumes;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mJobParameters.getJobId()).thenReturn(0);
// Let's set up our system services to act like a device that has the following conditions:
// 1. We're plugged in and charging.
// 2. We have a completely full device.
// 3. ASM is disabled.
when(mBatteryManager.isCharging()).thenReturn(true);
mVolumes = new ArrayList<>();
when(mVolumeInfo.getPath()).thenReturn(mFile);
when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
when(mVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIMARY_PHYSICAL);
when(mFile.getTotalSpace()).thenReturn(100L);
when(mFile.getFreeSpace()).thenReturn(0L);
mVolumes.add(mVolumeInfo);
when(mStorageVolumeProvider.getPrimaryStorageSize()).thenReturn(100L);
when(mStorageVolumeProvider.getVolumes()).thenReturn(mVolumes);
mApplication = ShadowApplication.getInstance();
mApplication.setSystemService(Context.BATTERY_SERVICE, mBatteryManager);
mApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
// This is a hack-y injection of our own FeatureFactory.
// By default, the Storage Manager has a FeatureFactory which returns null for all features.
// Using reflection, we can inject our own FeatureFactory which returns a mock for the
// StorageManagementJobProvider feature. This lets us observe when the ASMJobService
// actually tries to run the job.
when(mFeatureFactory.getStorageManagementJobProvider())
.thenReturn(mStorageManagementJobProvider);
when(mStorageManagementJobProvider.onStartJob(any(Context.class),
any(JobParameters.class), any(Integer.class))).thenReturn(false);
ReflectionHelpers.setStaticField(FeatureFactory.class, "sFactory", mFeatureFactory);
// And we can't forget to initialize the actual job service.
mJobService = spy(Robolectric.setupService(AutomaticStorageManagementJobService.class));
mJobService.setStorageVolumeProvider(mStorageVolumeProvider);
}
@Test
public void testJobRequiresCharging() {
when(mBatteryManager.isCharging()).thenReturn(false);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
// The job should report that it needs to be retried, if not charging.
assertJobFinished(true);
when(mBatteryManager.isCharging()).thenReturn(true);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertJobFinished(false);
}
@Test
public void testStartJobTriesUpsellWhenASMDisabled() {
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertJobFinished(false);
mApplication.runBackgroundTasks();
List<Intent> broadcastedIntents = mApplication.getBroadcastIntents();
assertThat(broadcastedIntents.size()).isEqualTo(1);
Intent lastIntent = broadcastedIntents.get(0);
assertThat(lastIntent.getAction())
.isEqualTo(NotificationController.INTENT_ACTION_SHOW_NOTIFICATION);
assertThat(lastIntent.getComponent().getClassName())
.isEqualTo(NotificationController.class.getCanonicalName());
assertStorageManagerJobDidNotRun();
}
@Test
public void testASMJobRunsWithValidConditions() {
activateASM();
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void testJobDoesntRunIfStorageNotFull() {
activateASM();
when(mFile.getFreeSpace()).thenReturn(100L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
}
@Test
public void testJobOnlyRunsIfFreeStorageIsUnder15Percent() {
activateASM();
when(mFile.getFreeSpace()).thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
when(mFile.getFreeSpace()).thenReturn(14L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void testNonDefaultDaysToRetain() {
ContentResolver resolver = mApplication.getApplicationContext().getContentResolver();
Settings.Secure.putInt(resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
30);
activateASM();
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan(30);
}
@Test
public void testNonPrivateDrivesIgnoredForFreeSpaceCalculation() {
File notPrivate = mock(File.class);
VolumeInfo nonPrivateVolume = mock(VolumeInfo.class);
when(nonPrivateVolume.getPath()).thenReturn(notPrivate);
when(nonPrivateVolume.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
when(notPrivate.getTotalSpace()).thenReturn(100L);
when(notPrivate.getFreeSpace()).thenReturn(0L);
mVolumes.add(nonPrivateVolume);
activateASM();
when(mFile.getFreeSpace()).thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
}
@Test
public void testMultiplePrivateVolumesCountedForASMActivationThrsehold() {
File privateVolume = mock(File.class);
VolumeInfo privateVolumeInfo = mock(VolumeInfo.class);
when(privateVolumeInfo.getPath()).thenReturn(privateVolume);
when(privateVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
when(privateVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIVATE_INTERNAL);
when(privateVolume.getTotalSpace()).thenReturn(100L);
when(privateVolume.getFreeSpace()).thenReturn(0L);
mVolumes.add(privateVolumeInfo);
activateASM();
when(mFile.getFreeSpace()).thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void testPrimaryPrivateStorageDefersToStorageManager() {
// The Storage Manager has a more accurate calculation for the UUID_PRIVATE_INTERNAL
// volume, so we don't use the getTotalSpace() method to find it.
// If the job accidentally uses the getTotalSpace(), it will run the job accidentally.
when(mVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIVATE_INTERNAL);
when(mFile.getTotalSpace()).thenReturn(1000L);
when(mFile.getFreeSpace()).thenReturn(15L);
activateASM();
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
}
private void assertJobFinished(boolean retryNeeded) {
verify(mJobService).jobFinished(any(JobParameters.class), eq(retryNeeded));
}
private void assertStorageManagerJobRan() {
assertStorageManagerJobRan(
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT);
}
private void assertStorageManagerJobRan(int daysToRetain) {
verify(mStorageManagementJobProvider).onStartJob(eq(mJobService), eq(mJobParameters),
eq(daysToRetain));
}
private void assertStorageManagerJobDidNotRun() {
verifyNoMoreInteractions(mStorageManagementJobProvider);
}
private void activateASM() {
ContentResolver resolver = mApplication.getApplicationContext().getContentResolver();
Settings.Secure.putInt(resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 1);
}
}