diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7617c394e80..112af28223d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2971,6 +2971,22 @@ + + + + + + + + + + diff --git a/res/values/bools.xml b/res/values/bools.xml index 6f0445712f2..ffbc863f811 100644 --- a/res/values/bools.xml +++ b/res/values/bools.xml @@ -43,4 +43,7 @@ false + + + false diff --git a/res/values/strings.xml b/res/values/strings.xml index ff0c2feed65..63cdcb66339 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7649,4 +7649,7 @@ Storage manager + + Automatic Storage Management Service + diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageBroadcastReceiver.java b/src/com/android/settings/deletionhelper/AutomaticStorageBroadcastReceiver.java new file mode 100644 index 00000000000..e7b04693083 --- /dev/null +++ b/src/com/android/settings/deletionhelper/AutomaticStorageBroadcastReceiver.java @@ -0,0 +1,49 @@ +/* + * 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.settings.deletionhelper; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.text.format.DateUtils; + +/** + * A {@link BroadcastReceiver} listening for {@link Intent#ACTION_BOOT_COMPLETED} broadcasts to + * schedule an automatic storage management job. Automatic storage management jobs are only + * scheduled once a day for a plugged in device. + */ +public class AutomaticStorageBroadcastReceiver extends BroadcastReceiver { + private static final int AUTOMATIC_STORAGE_JOB_ID = 0; + private static final long PERIOD = DateUtils.DAY_IN_MILLIS; + + @Override + public void onReceive(Context context, Intent intent) { + JobScheduler jobScheduler = + (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + ComponentName component = new ComponentName(context, + AutomaticStorageManagementJobService.class); + JobInfo job = new JobInfo.Builder(AUTOMATIC_STORAGE_JOB_ID, component) + .setRequiresCharging(true) + .setRequiresDeviceIdle(true) + .setPeriodic(PERIOD) + .build(); + jobScheduler.schedule(job); + } +} diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagementJobService.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagementJobService.java new file mode 100644 index 00000000000..990e328f51f --- /dev/null +++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagementJobService.java @@ -0,0 +1,90 @@ +/* + * 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.settings.deletionhelper; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.provider.Settings; +import android.util.Log; +import com.android.settings.overlay.FeatureFactory; +import com.android.settings.overlay.StorageManagementJobProvider; + +import java.io.File; + +/** + * {@link JobService} class to start automatic storage clearing jobs to free up space. The job only + * starts if the device is under a certain percent of free storage. + */ +public class AutomaticStorageManagementJobService extends JobService { + private static final String TAG = "AsmJobService"; + private static final String SHARED_PREFRENCES_NAME = "automatic_storage_manager_settings"; + private static final String KEY_DAYS_TO_RETAIN = "days_to_retain"; + + private static final long DEFAULT_LOW_FREE_PERCENT = 15; + + private StorageManagementJobProvider mProvider; + + @Override + public boolean onStartJob(JobParameters args) { + boolean isEnabled = + Settings.Secure.getInt(getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0; + if (!isEnabled) { + return false; + } + + StorageManager manager = getSystemService(StorageManager.class); + VolumeInfo internalVolume = manager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); + + final File dataPath = internalVolume.getPath(); + if (!volumeNeedsManagement(dataPath)) { + Log.i(TAG, "Skipping automatic storage management."); + return false; + } + mProvider = FeatureFactory.getFactory(this).getStorageManagementJobProvider(); + if (mProvider != null) { + return mProvider.onStartJob(this, args, getDaysToRetain()); + } + + return false; + } + + @Override + public boolean onStopJob(JobParameters args) { + if (mProvider != null) { + return mProvider.onStopJob(this, args); + } + + return false; + } + + private int getDaysToRetain() { + SharedPreferences sharedPreferences = + getSharedPreferences(SHARED_PREFRENCES_NAME, Context.MODE_PRIVATE); + return sharedPreferences.getInt(KEY_DAYS_TO_RETAIN, + AutomaticStorageManagerSettings.DEFAULT_DAYS_TO_RETAIN); + } + + private boolean volumeNeedsManagement(final File dataPath) { + long lowStorageThreshold = (dataPath.getTotalSpace() * DEFAULT_LOW_FREE_PERCENT) / 100; + return dataPath.getFreeSpace() < lowStorageThreshold; + } +} \ No newline at end of file diff --git a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java index abd0ec13c07..6163576a0bb 100644 --- a/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java +++ b/src/com/android/settings/deletionhelper/AutomaticStorageManagerSettings.java @@ -17,6 +17,9 @@ package com.android.settings.deletionhelper; import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; import android.os.Bundle; import android.provider.Settings; import android.util.Log; @@ -41,9 +44,13 @@ import com.android.settings.widget.SwitchBar.OnSwitchChangeListener; */ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment implements OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + public static final int DEFAULT_DAYS_TO_RETAIN = 90; + + private static final String SHARED_PREFRENCES_NAME = "automatic_storage_manager_settings"; private static final String KEY_DAYS = "days"; private static final String KEY_DELETION_HELPER = "deletion_helper"; private static final String KEY_STORAGE_MANAGER_SWITCH = "storage_manager_active"; + private static final String KEY_DAYS_TO_RETAIN = "days_to_retain"; private DropDownPreference mDaysToRetain; private Preference mDeletionHelper; @@ -70,6 +77,14 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0; mStorageManagerSwitch.setChecked(isChecked); mStorageManagerSwitch.setOnPreferenceChangeListener(this); + + SharedPreferences sharedPreferences = + getContext().getSharedPreferences(SHARED_PREFRENCES_NAME, + Context.MODE_PRIVATE); + int value = sharedPreferences.getInt(KEY_DAYS_TO_RETAIN, DEFAULT_DAYS_TO_RETAIN); + String[] stringValues = + getResources().getStringArray(R.array.automatic_storage_management_days_values); + mDaysToRetain.setValue(stringValues[daysValueToIndex(value, stringValues)]); } @Override @@ -88,8 +103,11 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, checked ? 1 : 0); break; case KEY_DAYS: - // TODO: Configure a setting which controls how many days of data the storage manager - // should retain. + SharedPreferences.Editor editor = + getContext().getSharedPreferences(SHARED_PREFRENCES_NAME, + Context.MODE_PRIVATE).edit(); + editor.putInt(KEY_DAYS_TO_RETAIN, Integer.parseInt((String) newValue)); + editor.apply(); break; } return true; @@ -108,4 +126,14 @@ public class AutomaticStorageManagerSettings extends SettingsPreferenceFragment } return true; } + + private static int daysValueToIndex(int value, String[] indices) { + for (int i = 0; i < indices.length; i++) { + int thisValue = Integer.parseInt(indices[i]); + if (value == thisValue) { + return i; + } + } + return indices.length - 1; + } } diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 3b307e51de1..04f2f810961 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -65,6 +65,7 @@ public abstract class FeatureFactory { * Return a provider which adds additional deletion services to the Deletion Helper. */ public abstract DeletionHelperFeatureProvider getDeletionHelperFeatureProvider(); + public abstract StorageManagementJobProvider getStorageManagementJobProvider(); public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 425320ac5bf..054724787ac 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -33,4 +33,9 @@ public final class FeatureFactoryImpl extends FeatureFactory { return null; } + @Override + public StorageManagementJobProvider getStorageManagementJobProvider() { + return null; + } + } diff --git a/src/com/android/settings/overlay/StorageManagementJobProvider.java b/src/com/android/settings/overlay/StorageManagementJobProvider.java new file mode 100644 index 00000000000..80f8737a597 --- /dev/null +++ b/src/com/android/settings/overlay/StorageManagementJobProvider.java @@ -0,0 +1,40 @@ +/* + * 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.settings.overlay; + +import android.app.job.JobParameters; +import android.content.Context; + +/** + * Feature provider for automatic storage management jobs. + */ +public interface StorageManagementJobProvider { + /** + * Starts an asynchronous deletion job to clear out storage older than + * @param params Standard JobService parameters. + * @param daysToRetain Number of days of information to retain on the device. + * @return If the job needs to process the work on a separate thread. + */ + boolean onStartJob(Context context, JobParameters params, int daysToRetain); + + /** + * Attempt to stop the execution of the job. + * @param params Parameters specifying info about this job. + * @return If the job should be rescheduled. + */ + boolean onStopJob(Context context, JobParameters params); +}