TunerStorageCleanUpService.java revision d41f0075a7d2ea826204e81fcec57d0aa57171a9
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.tv.tuner.tvinput; 18 19import android.app.job.JobParameters; 20import android.app.job.JobService; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.database.Cursor; 24import android.media.tv.TvContract; 25import android.net.Uri; 26import android.os.AsyncTask; 27 28import com.android.tv.TvApplication; 29import com.android.tv.dvr.DvrStorageStatusManager; 30import com.android.tv.util.Utils; 31 32import java.io.File; 33import java.io.IOException; 34import java.util.HashSet; 35import java.util.Set; 36import java.util.concurrent.TimeUnit; 37 38/** 39 * Creates {@link JobService} to clean up recorded program files which are not referenced 40 * from database. 41 */ 42public class TunerStorageCleanUpService extends JobService { 43 private CleanUpStorageTask mTask; 44 45 @Override 46 public void onCreate() { 47 TvApplication.setCurrentRunningProcess(this, false); 48 super.onCreate(); 49 mTask = new CleanUpStorageTask(this, this); 50 } 51 52 @Override 53 public boolean onStartJob(JobParameters params) { 54 mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 55 return true; 56 } 57 58 @Override 59 public boolean onStopJob(JobParameters params) { 60 return false; 61 } 62 63 /** 64 * Cleans up recorded program files which are not referenced from database. 65 * Cleaning up will be done periodically. 66 */ 67 public static class CleanUpStorageTask extends AsyncTask<JobParameters, Void, JobParameters[]> { 68 private final static String[] mProjection = { 69 TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME, 70 TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI 71 }; 72 private final static long ELAPSED_MILLIS_TO_DELETE = TimeUnit.DAYS.toMillis(1); 73 74 private final Context mContext; 75 private final DvrStorageStatusManager mDvrStorageStatusManager; 76 private final JobService mJobService; 77 private final ContentResolver mContentResolver; 78 79 /** 80 * Creates a recurring storage cleaning task. 81 * 82 * @param context {@link Context} 83 * @param jobService {@link JobService} 84 */ 85 public CleanUpStorageTask(Context context, JobService jobService) { 86 mContext = context; 87 mDvrStorageStatusManager = 88 TvApplication.getSingletons(mContext).getDvrStorageStatusManager(); 89 mJobService = jobService; 90 mContentResolver = mContext.getContentResolver(); 91 } 92 93 private Set<String> getRecordedProgramsDirs() { 94 try (Cursor c = mContentResolver.query( 95 TvContract.RecordedPrograms.CONTENT_URI, mProjection, null, null, null)) { 96 if (c == null) { 97 return null; 98 } 99 Set<String> recordedProgramDirs = new HashSet<>(); 100 while (c.moveToNext()) { 101 String packageName = c.getString(0); 102 String dataUriString = c.getString(1); 103 if (dataUriString == null) { 104 continue; 105 } 106 Uri dataUri = Uri.parse(dataUriString); 107 if (!Utils.isInBundledPackageSet(packageName) 108 || dataUri == null || dataUri.getPath() == null 109 || !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) { 110 continue; 111 } 112 File recordedProgramDir = new File(dataUri.getPath()); 113 try { 114 recordedProgramDirs.add(recordedProgramDir.getCanonicalPath()); 115 } catch (IOException | SecurityException e) { 116 } 117 } 118 return recordedProgramDirs; 119 } 120 } 121 122 @Override 123 protected JobParameters[] doInBackground(JobParameters... params) { 124 if (mDvrStorageStatusManager.getDvrStorageStatus() 125 == DvrStorageStatusManager.STORAGE_STATUS_MISSING) { 126 return params; 127 } 128 File dvrRecordingDir = mDvrStorageStatusManager.getRecordingRootDataDirectory(); 129 if (dvrRecordingDir == null || !dvrRecordingDir.isDirectory()) { 130 return params; 131 } 132 Set<String> recordedProgramDirs = getRecordedProgramsDirs(); 133 if (recordedProgramDirs == null) { 134 return params; 135 } 136 File[] files = dvrRecordingDir.listFiles(); 137 if (files == null || files.length == 0) { 138 return params; 139 } 140 for (File recordingDir : files) { 141 try { 142 if (!recordedProgramDirs.contains(recordingDir.getCanonicalPath())) { 143 long lastModified = recordingDir.lastModified(); 144 long now = System.currentTimeMillis(); 145 if (lastModified != 0 146 && lastModified < now - ELAPSED_MILLIS_TO_DELETE) { 147 // To prevent current recordings from being deleted, 148 // deletes recordings which was not modified for long enough time. 149 Utils.deleteDirOrFile(recordingDir); 150 } 151 } 152 } catch (IOException | SecurityException e) { 153 // would not happen 154 } 155 } 156 return params; 157 } 158 159 @Override 160 protected void onPostExecute(JobParameters[] params) { 161 for (JobParameters param : params) { 162 mJobService.jobFinished(param, false); 163 } 164 } 165 } 166} 167