1d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko/* 2d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Copyright (C) 2016 The Android Open Source Project 3d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * 4d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 5d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * you may not use this file except in compliance with the License. 6d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * You may obtain a copy of the License at 7d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * 8d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 9d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * 10d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Unless required by applicable law or agreed to in writing, software 11d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 12d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * See the License for the specific language governing permissions and 14d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * limitations under the License 15d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 16d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 17d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkopackage com.android.tv.tuner.tvinput; 18d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 19d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.app.job.JobParameters; 20d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.app.job.JobService; 21d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.ContentResolver; 22d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.Context; 23d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.database.Cursor; 24d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.media.tv.TvContract; 25d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.net.Uri; 26d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.AsyncTask; 276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.util.Log; 28d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 29d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.TvApplication; 30d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.dvr.DvrStorageStatusManager; 31d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.util.Utils; 32d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 33d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.io.File; 34d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.io.IOException; 35d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.HashSet; 36d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.Set; 37d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.concurrent.TimeUnit; 38d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 39d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko/** 40d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Creates {@link JobService} to clean up recorded program files which are not referenced 41d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * from database. 42d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 43d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkopublic class TunerStorageCleanUpService extends JobService { 446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static final String TAG = "TunerStorageCleanUpService"; 456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 46d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private CleanUpStorageTask mTask; 47d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 48d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 49d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public void onCreate() { 506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!TvApplication.getSingletons(this).getTvInputManagerHelper().hasTvInputManager()) { 516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Log.wtf(TAG, "Stopping because device does not have a TvInputManager"); 526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko this.stopSelf(); 536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return; 546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 55d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TvApplication.setCurrentRunningProcess(this, false); 56d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko super.onCreate(); 57d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mTask = new CleanUpStorageTask(this, this); 58d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 59d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 60d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 61d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public boolean onStartJob(JobParameters params) { 62d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 63d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return true; 64d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 65d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 66d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 67d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public boolean onStopJob(JobParameters params) { 68d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return false; 69d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 70d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 71d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 72d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Cleans up recorded program files which are not referenced from database. 73d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Cleaning up will be done periodically. 74d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 75d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public static class CleanUpStorageTask extends AsyncTask<JobParameters, Void, JobParameters[]> { 76d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final static String[] mProjection = { 77d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TvContract.RecordedPrograms.COLUMN_PACKAGE_NAME, 78d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI 79d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko }; 80d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final static long ELAPSED_MILLIS_TO_DELETE = TimeUnit.DAYS.toMillis(1); 81d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 82d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final Context mContext; 83d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final DvrStorageStatusManager mDvrStorageStatusManager; 84d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final JobService mJobService; 85d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final ContentResolver mContentResolver; 86d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 87d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 88d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Creates a recurring storage cleaning task. 89d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * 90d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * @param context {@link Context} 91d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * @param jobService {@link JobService} 92d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 93d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public CleanUpStorageTask(Context context, JobService jobService) { 94d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mContext = context; 95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mDvrStorageStatusManager = 96d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TvApplication.getSingletons(mContext).getDvrStorageStatusManager(); 97d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mJobService = jobService; 98d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mContentResolver = mContext.getContentResolver(); 99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 100d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 101d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private Set<String> getRecordedProgramsDirs() { 102d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko try (Cursor c = mContentResolver.query( 103d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TvContract.RecordedPrograms.CONTENT_URI, mProjection, null, null, null)) { 104d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (c == null) { 105d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return null; 106d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 107d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Set<String> recordedProgramDirs = new HashSet<>(); 108d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko while (c.moveToNext()) { 109d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko String packageName = c.getString(0); 110d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko String dataUriString = c.getString(1); 111d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (dataUriString == null) { 112d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko continue; 113d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 114d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Uri dataUri = Uri.parse(dataUriString); 115d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (!Utils.isInBundledPackageSet(packageName) 116d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko || dataUri == null || dataUri.getPath() == null 117d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko || !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) { 118d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko continue; 119d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 120d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko File recordedProgramDir = new File(dataUri.getPath()); 121d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko try { 122d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko recordedProgramDirs.add(recordedProgramDir.getCanonicalPath()); 123d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } catch (IOException | SecurityException e) { 124d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 125d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 126d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return recordedProgramDirs; 127d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 128d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 129d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 130d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 131d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko protected JobParameters[] doInBackground(JobParameters... params) { 132d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mDvrStorageStatusManager.getDvrStorageStatus() 133d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko == DvrStorageStatusManager.STORAGE_STATUS_MISSING) { 134d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return params; 135d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 136d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko File dvrRecordingDir = mDvrStorageStatusManager.getRecordingRootDataDirectory(); 137d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (dvrRecordingDir == null || !dvrRecordingDir.isDirectory()) { 138d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return params; 139d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 140d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Set<String> recordedProgramDirs = getRecordedProgramsDirs(); 141d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (recordedProgramDirs == null) { 142d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return params; 143d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 144d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko File[] files = dvrRecordingDir.listFiles(); 145d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (files == null || files.length == 0) { 146d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return params; 147d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 148d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko for (File recordingDir : files) { 149d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko try { 150d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (!recordedProgramDirs.contains(recordingDir.getCanonicalPath())) { 151d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko long lastModified = recordingDir.lastModified(); 152d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko long now = System.currentTimeMillis(); 153d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (lastModified != 0 154d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko && lastModified < now - ELAPSED_MILLIS_TO_DELETE) { 155d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // To prevent current recordings from being deleted, 156d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // deletes recordings which was not modified for long enough time. 157d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Utils.deleteDirOrFile(recordingDir); 158d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 159d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 160d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } catch (IOException | SecurityException e) { 161d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // would not happen 162d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 163d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 164d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return params; 165d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 166d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 167d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 168d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko protected void onPostExecute(JobParameters[] params) { 169d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko for (JobParameters param : params) { 170d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mJobService.jobFinished(param, false); 171d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 172d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 173d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 174d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko} 175