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