EpgDataCleanupService.java revision d9f937e4769668da59d614600ea3405ee0353ef6
1/*
2 * Copyright (C) 2014 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.providers.tv;
18
19import android.app.IntentService;
20import android.content.Intent;
21import android.database.Cursor;
22import android.media.tv.TvContract.Programs;
23import android.media.tv.TvContract.WatchedPrograms;
24import android.os.SystemClock;
25import android.text.format.DateUtils;
26import android.util.Log;
27
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.util.concurrent.TimeUnit;
31
32/**
33 * A service that cleans up EPG data.
34 */
35public class EpgDataCleanupService extends IntentService {
36    private static final boolean DEBUG = true;
37    private static final String TAG = "EpgDataCleanupService";
38
39    static final String ACTION_CLEAN_UP_EPG_DATA =
40            "com.android.providers.tv.intent.CLEAN_UP_EPG_DATA";
41
42    public EpgDataCleanupService() {
43        super("EpgDataCleanupService");
44    }
45
46    @Override
47    protected void onHandleIntent(Intent intent) {
48        if (DEBUG) {
49            Log.d(TAG, "Received intent: " + intent);
50        }
51        final String action = intent.getAction();
52        if (!ACTION_CLEAN_UP_EPG_DATA.equals(action)) {
53            return;
54        }
55
56        long nowMillis = System.currentTimeMillis();
57
58        int maxProgramAgeInDays = getResources().getInteger(R.integer.max_program_age_in_days);
59        if (maxProgramAgeInDays > 0) {
60            clearOldPrograms(nowMillis - TimeUnit.DAYS.toMillis(maxProgramAgeInDays));
61        }
62
63        int maxWatchedProgramAgeInDays =
64                getResources().getInteger(R.integer.max_watched_program_age_in_days);
65        if (maxWatchedProgramAgeInDays > 0) {
66            clearOldWatchHistory(nowMillis - TimeUnit.DAYS.toMillis(maxWatchedProgramAgeInDays));
67        }
68
69        int maxWatchedProgramEntryCount =
70                getResources().getInteger(R.integer.max_watched_program_entry_count);
71        if (maxWatchedProgramEntryCount > 0) {
72            clearOverflowWatchHistory(maxWatchedProgramEntryCount);
73        }
74    }
75
76    /**
77     * Clear program info that ended before {@code maxEndTimeMillis}.
78     */
79    @VisibleForTesting
80    void clearOldPrograms(long maxEndTimeMillis) {
81        int deleteCount = getContentResolver().delete(
82                Programs.CONTENT_URI,
83                Programs.COLUMN_END_TIME_UTC_MILLIS + "<?",
84                new String[] { String.valueOf(maxEndTimeMillis) });
85        if (DEBUG && deleteCount > 0) {
86            Log.d(TAG, "Deleted " + deleteCount + " programs"
87                  + " (reason: ended before "
88                  + DateUtils.getRelativeTimeSpanString(this, maxEndTimeMillis) + ")");
89        }
90    }
91
92    /**
93     * Clear watch history whose watch started before {@code maxStartTimeMillis}.
94     * In theory, history entry for currently watching program can be deleted
95     * (e.g., have been watching since before {@code maxStartTimeMillis}).
96     */
97    @VisibleForTesting
98    void clearOldWatchHistory(long maxStartTimeMillis) {
99        int deleteCount = getContentResolver().delete(
100                WatchedPrograms.CONTENT_URI,
101                WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS + "<?",
102                new String[] { String.valueOf(maxStartTimeMillis) });
103        if (DEBUG && deleteCount > 0) {
104            Log.d(TAG, "Deleted " + deleteCount + " watched programs"
105                  + " (reason: started before "
106                  + DateUtils.getRelativeTimeSpanString(this, maxStartTimeMillis) + ")");
107        }
108    }
109
110    /**
111     * Clear watch history except last {@code maxEntryCount} entries.
112     * "Last" here is based on watch start time, and so, in theory, history entry for program
113     * that user was watching until recent reboot can be deleted earlier than other entries
114     * which ended before.
115     */
116    @VisibleForTesting
117    void clearOverflowWatchHistory(int maxEntryCount) {
118        Cursor cursor = getContentResolver().query(
119                WatchedPrograms.CONTENT_URI,
120                new String[] { WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS }, null, null,
121                WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS);
122        if (cursor == null) {
123            Log.e(TAG, "Failed to query watched program");
124            return;
125        }
126        int totalCount;
127        long maxStartTimeMillis;
128        try {
129            totalCount = cursor.getCount();
130            int overflowCount = totalCount - maxEntryCount;
131            if (overflowCount <= 0) {
132                return;
133            }
134            if (!cursor.moveToPosition(overflowCount - 1)) {
135                Log.e(TAG, "Failed to query watched program");
136                return;
137            }
138            maxStartTimeMillis = cursor.getLong(0);
139        } finally {
140            cursor.close();
141        }
142
143        int deleteCount = getContentResolver().delete(
144                WatchedPrograms.CONTENT_URI,
145                WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS + "<?",
146                new String[] { String.valueOf(maxStartTimeMillis + 1) });
147        if (DEBUG && deleteCount > 0) {
148            Log.d(TAG, "Deleted " + deleteCount + " of " + totalCount + " watched programs"
149                  + " (reason: entry count > " + maxEntryCount + ")");
150        }
151    }
152}
153