blob: 0ccc893cc80a0294aad35f29eb7470f80a505040 [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 static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.nullable;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationManager;
import android.app.job.JobParameters;
import android.app.usage.StorageStatsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.BatteryManager;
import android.os.SystemProperties;
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 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.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.util.ReflectionHelpers;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
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;
@Mock private AutomaticStorageManagementJobService.Clock mClock;
private AutomaticStorageManagementJobService mJobService;
private Context mContext;
private ShadowApplication mApplication;
private List<VolumeInfo> mVolumes;
@Before
public void setUp() throws Exception {
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);
when(mVolumeInfo.getPath()).thenReturn(mFile);
when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
when(mVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIMARY_PHYSICAL);
when(mVolumeInfo.isMountedReadable()).thenReturn(true);
mVolumes = new ArrayList<>();
mVolumes.add(mVolumeInfo);
when(mStorageVolumeProvider.getPrimaryStorageSize()).thenReturn(100L);
when(mStorageVolumeProvider.getVolumes()).thenReturn(mVolumes);
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(0L);
when(mStorageVolumeProvider.getTotalBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(100L);
mContext = RuntimeEnvironment.application;
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(
nullable(Context.class), nullable(JobParameters.class), anyInt()))
.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.onBind(null);
mJobService.setStorageVolumeProvider(mStorageVolumeProvider);
mJobService.setClock(mClock);
Resources fakeResources = mock(Resources.class);
when(fakeResources.getInteger(
com.android.internal.R.integer.config_storageManagerDaystoRetainDefault))
.thenReturn(90);
when(mJobService.getResources()).thenReturn(fakeResources);
}
@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 testASMJobRunsWithValidConditionsIfEnabledByDefaultAndUnset() {
SystemProperties.set("ro.storage_manager.enabled", "true");
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void testJobDoesntRunIfStorageNotFull() throws Exception {
activateASM();
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(100L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
}
@Test
public void testJobOnlyRunsIfFreeStorageIsUnder15Percent() throws Exception {
activateASM();
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(14L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void testNonDefaultDaysToRetain() {
ContentResolver resolver = mContext.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() throws Exception {
File notPrivate = mock(File.class);
VolumeInfo nonPrivateVolume = mock(VolumeInfo.class);
when(nonPrivateVolume.getPath()).thenReturn(notPrivate);
when(nonPrivateVolume.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
mVolumes.add(nonPrivateVolume);
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(nonPrivateVolume)))
.thenReturn(0L);
when(mStorageVolumeProvider.getTotalBytes(
nullable(StorageStatsManager.class), eq(nonPrivateVolume)))
.thenReturn(100L);
activateASM();
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobDidNotRun();
}
@Test
public void testMultiplePrivateVolumesCountedForASMActivationThreshold() throws Exception {
File privateVolume = mock(File.class);
VolumeInfo privateVolumeInfo = mock(VolumeInfo.class);
when(privateVolumeInfo.getPath()).thenReturn(privateVolume);
when(privateVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
when(privateVolumeInfo.isMountedReadable()).thenReturn(true);
when(privateVolumeInfo.getFsUuid()).thenReturn(StorageManager.UUID_PRIVATE_INTERNAL);
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(privateVolumeInfo)))
.thenReturn(0L);
when(mStorageVolumeProvider.getTotalBytes(
nullable(StorageStatsManager.class), eq(privateVolumeInfo)))
.thenReturn(100L);
mVolumes.add(privateVolumeInfo);
activateASM();
when(mStorageVolumeProvider.getFreeBytes(
nullable(StorageStatsManager.class), eq(mVolumeInfo)))
.thenReturn(15L);
assertThat(mJobService.onStartJob(mJobParameters)).isFalse();
assertStorageManagerJobRan();
}
@Test
public void disableSmartStorageIfPastThreshold() throws Exception {
ContentResolver resolver = mContext.getContentResolver();
activateASM();
AutomaticStorageManagementJobService.Clock fakeClock =
mock(AutomaticStorageManagementJobService.Clock.class);
when(fakeClock.currentTimeMillis()).thenReturn(1001L);
when(mStorageManagementJobProvider.getDisableThresholdMillis(any(Context.class)))
.thenReturn(1000L);
AutomaticStorageManagementJobService.maybeDisableDueToPolicy(
mStorageManagementJobProvider, mContext, fakeClock);
assertThat(
Settings.Secure.getInt(
resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED))
.isEqualTo(0);
}
@Test
public void dontDisableSmartStorageIfPastThresholdAndDisabledInThePast() throws Exception {
ContentResolver resolver = mContext.getContentResolver();
activateASM();
Settings.Secure.putInt(
resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY, 1);
AutomaticStorageManagementJobService.Clock fakeClock =
mock(AutomaticStorageManagementJobService.Clock.class);
when(fakeClock.currentTimeMillis()).thenReturn(1001L);
when(mStorageManagementJobProvider.getDisableThresholdMillis(any(Context.class)))
.thenReturn(1000L);
AutomaticStorageManagementJobService.maybeDisableDueToPolicy(
mStorageManagementJobProvider, mContext, fakeClock);
assertThat(
Settings.Secure.getInt(
resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED))
.isNotEqualTo(0);
}
@Test
public void logDisabledByPolicyIfPastThreshold() throws Exception {
ContentResolver resolver = mContext.getContentResolver();
activateASM();
AutomaticStorageManagementJobService.Clock fakeClock =
mock(AutomaticStorageManagementJobService.Clock.class);
when(fakeClock.currentTimeMillis()).thenReturn(1001L);
when(mStorageManagementJobProvider.getDisableThresholdMillis(any(Context.class)))
.thenReturn(1000L);
AutomaticStorageManagementJobService.maybeDisableDueToPolicy(
mStorageManagementJobProvider, mContext, fakeClock);
assertThat(
Settings.Secure.getInt(
resolver,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY))
.isGreaterThan(0);
}
@Test
public void dontDisableSmartStorageIfNotPastThreshold() throws Exception {
ContentResolver resolver = mContext.getContentResolver();
activateASM();
AutomaticStorageManagementJobService.Clock fakeClock =
mock(AutomaticStorageManagementJobService.Clock.class);
when(fakeClock.currentTimeMillis()).thenReturn(999L);
when(mStorageManagementJobProvider.getDisableThresholdMillis(any(Context.class)))
.thenReturn(1000L);
AutomaticStorageManagementJobService.maybeDisableDueToPolicy(
mStorageManagementJobProvider, mContext, fakeClock);
assertThat(
Settings.Secure.getInt(
resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED))
.isNotEqualTo(0);
}
private void assertJobFinished(boolean retryNeeded) {
verify(mJobService).jobFinished(nullable(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() {
verify(mStorageManagementJobProvider, never())
.onStartJob(any(Context.class), any(JobParameters.class), anyInt());
}
private void activateASM() {
ContentResolver resolver = mContext.getContentResolver();
Settings.Secure.putInt(resolver, Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 1);
}
}