1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.storagemanager.automatic;
18
19import android.app.job.JobParameters;
20import android.app.job.JobService;
21import android.content.Context;
22import android.os.BatteryManager;
23import android.os.storage.StorageManager;
24import android.os.storage.VolumeInfo;
25import android.provider.Settings;
26import android.util.Log;
27import com.android.storagemanager.overlay.FeatureFactory;
28import com.android.storagemanager.overlay.StorageManagementJobProvider;
29
30import java.io.File;
31
32/**
33 * {@link JobService} class to start automatic storage clearing jobs to free up space. The job only
34 * starts if the device is under a certain percent of free storage.
35 */
36public class AutomaticStorageManagementJobService extends JobService {
37    private static final String TAG = "AsmJobService";
38
39    private static final long DEFAULT_LOW_FREE_PERCENT = 15;
40
41    private StorageManagementJobProvider mProvider;
42
43    @Override
44    public boolean onStartJob(JobParameters args) {
45        // We need to double-check the precondition shere because they are not enforced for a
46        // periodic job.
47        if (!preconditionsFulfilled()) {
48            // By telling the system to re-schedule the job, it will attempt to execute again at a
49            // later idle window -- possibly one where we are charging.
50            jobFinished(args, true);
51            return false;
52        }
53
54        StorageManager manager = getSystemService(StorageManager.class);
55        VolumeInfo internalVolume = manager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
56        final File dataPath = internalVolume.getPath();
57        if (!volumeNeedsManagement(dataPath)) {
58            Log.i(TAG, "Skipping automatic storage management.");
59            Settings.Secure.putLong(getContentResolver(),
60                    Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
61                    System.currentTimeMillis());
62            jobFinished(args, false);
63            return false;
64        }
65
66        boolean isEnabled =
67                Settings.Secure.getInt(getContentResolver(),
68                        Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0;
69        if (!isEnabled) {
70            NotificationController.maybeShowNotification(getApplicationContext());
71            jobFinished(args, false);
72            return false;
73        }
74
75        mProvider = FeatureFactory.getFactory(this).getStorageManagementJobProvider();
76        if (mProvider != null) {
77            return mProvider.onStartJob(this, args, getDaysToRetain());
78        }
79
80        jobFinished(args, false);
81        return false;
82    }
83
84    @Override
85    public boolean onStopJob(JobParameters args) {
86        if (mProvider != null) {
87            return mProvider.onStopJob(this, args);
88        }
89
90        return false;
91    }
92
93    private int getDaysToRetain() {
94        return Settings.Secure.getInt(getContentResolver(),
95                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
96                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT);
97    }
98
99    private boolean volumeNeedsManagement(final File dataPath) {
100        long lowStorageThreshold = (dataPath.getTotalSpace() * DEFAULT_LOW_FREE_PERCENT) / 100;
101        return dataPath.getFreeSpace() < lowStorageThreshold;
102    }
103
104    private boolean preconditionsFulfilled() {
105        // NOTE: We don't check the idle state here because this job should be running in idle
106        // maintenance windows. During the idle maintenance window, the device is -technically- not
107        // idle. For more information, see PowerManager.isDeviceIdleMode().
108
109        boolean isCharging = false;
110        BatteryManager batteryManager = (BatteryManager) getSystemService(Context.BATTERY_SERVICE);
111        if (batteryManager != null) {
112            isCharging = batteryManager.isCharging();
113        }
114
115        return isCharging;
116    }
117}