1d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko/* 2944779887775bd950cf1abf348d2df461593f6abLive Channels Team * Copyright (C) 2017 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 14944779887775bd950cf1abf348d2df461593f6abLive Channels Team * limitations under the License. 15d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 16d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkopackage com.android.tv.dvr; 17d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 18d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.ContentProviderOperation; 19d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.ContentResolver; 20d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.Context; 21d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.content.OperationApplicationException; 22d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.database.Cursor; 23633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport android.media.tv.TvInputInfo; 24d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.net.Uri; 25d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.AsyncTask; 26d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.RemoteException; 27944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport android.support.media.tv.TvContractCompat; 28d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.util.Log; 29944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport com.android.tv.TvSingletons; 30944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport com.android.tv.common.recording.RecordingStorageStatusManager; 31944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport com.android.tv.common.util.CommonUtils; 32633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.util.TvInputManagerHelper; 33d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.io.File; 34d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.ArrayList; 35d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.List; 36d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 37944779887775bd950cf1abf348d2df461593f6abLive Channels Team/** A class for extending TV app-specific function to {@link RecordingStorageStatusManager}. */ 38944779887775bd950cf1abf348d2df461593f6abLive Channels Teampublic class DvrStorageStatusManager extends RecordingStorageStatusManager { 39d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private static final String TAG = "DvrStorageStatusManager"; 4038fef3bf253578f518d1bc727da4afb263194398Nick Chalko 41944779887775bd950cf1abf348d2df461593f6abLive Channels Team private final Context mContext; 42944779887775bd950cf1abf348d2df461593f6abLive Channels Team private CleanUpDbTask mCleanUpDbTask; 43d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 44d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private static final String[] PROJECTION = { 45944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvContractCompat.RecordedPrograms._ID, 46944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvContractCompat.RecordedPrograms.COLUMN_PACKAGE_NAME, 47944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvContractCompat.RecordedPrograms.COLUMN_RECORDING_DATA_URI 48d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko }; 4995961816a768da387f0b5523cf4363ace2044089Nick Chalko private static final int BATCH_OPERATION_COUNT = 100; 5095961816a768da387f0b5523cf4363ace2044089Nick Chalko 51944779887775bd950cf1abf348d2df461593f6abLive Channels Team public DvrStorageStatusManager(Context context) { 52944779887775bd950cf1abf348d2df461593f6abLive Channels Team super(context); 53d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mContext = context; 54d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 55d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 56944779887775bd950cf1abf348d2df461593f6abLive Channels Team @Override 57944779887775bd950cf1abf348d2df461593f6abLive Channels Team protected void cleanUpDbIfNeeded() { 58944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (mCleanUpDbTask != null) { 59944779887775bd950cf1abf348d2df461593f6abLive Channels Team mCleanUpDbTask.cancel(true); 60d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 61944779887775bd950cf1abf348d2df461593f6abLive Channels Team mCleanUpDbTask = new CleanUpDbTask(); 62944779887775bd950cf1abf348d2df461593f6abLive Channels Team mCleanUpDbTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 63d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 64d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 65633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko private class CleanUpDbTask extends AsyncTask<Void, Void, Boolean> { 66d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final ContentResolver mContentResolver; 67d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 68d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private CleanUpDbTask() { 69d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mContentResolver = mContext.getContentResolver(); 70d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 71d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 72d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 73633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko protected Boolean doInBackground(Void... params) { 74944779887775bd950cf1abf348d2df461593f6abLive Channels Team @StorageStatus int storageStatus = getDvrStorageStatus(); 75944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (storageStatus == STORAGE_STATUS_MISSING) { 76d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return null; 77d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 78944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (storageStatus == STORAGE_STATUS_TOTAL_CAPACITY_TOO_SMALL) { 79633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko return true; 80633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 81633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko List<ContentProviderOperation> ops = getDeleteOps(); 82d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (ops == null || ops.isEmpty()) { 83d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return null; 84d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 8595961816a768da387f0b5523cf4363ace2044089Nick Chalko Log.i( 8695961816a768da387f0b5523cf4363ace2044089Nick Chalko TAG, 8795961816a768da387f0b5523cf4363ace2044089Nick Chalko "New device storage mounted. # of recordings to be forgotten : " + ops.size()); 8895961816a768da387f0b5523cf4363ace2044089Nick Chalko for (int i = 0; i < ops.size() && !isCancelled(); i += BATCH_OPERATION_COUNT) { 8995961816a768da387f0b5523cf4363ace2044089Nick Chalko int toIndex = 9095961816a768da387f0b5523cf4363ace2044089Nick Chalko (i + BATCH_OPERATION_COUNT) > ops.size() 9195961816a768da387f0b5523cf4363ace2044089Nick Chalko ? ops.size() 9295961816a768da387f0b5523cf4363ace2044089Nick Chalko : (i + BATCH_OPERATION_COUNT); 93d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko ArrayList<ContentProviderOperation> batchOps = 94d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko new ArrayList<>(ops.subList(i, toIndex)); 95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko try { 96944779887775bd950cf1abf348d2df461593f6abLive Channels Team mContext.getContentResolver().applyBatch(TvContractCompat.AUTHORITY, batchOps); 97d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } catch (RemoteException | OperationApplicationException e) { 98d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Log.e(TAG, "Failed to clean up RecordedPrograms.", e); 99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 100d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 101d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return null; 102d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 103d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 104d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 105633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko protected void onPostExecute(Boolean forgetStorage) { 106633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko if (forgetStorage != null && forgetStorage == true) { 107944779887775bd950cf1abf348d2df461593f6abLive Channels Team DvrManager dvrManager = TvSingletons.getSingletons(mContext).getDvrManager(); 108633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko TvInputManagerHelper tvInputManagerHelper = 109944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvSingletons.getSingletons(mContext).getTvInputManagerHelper(); 110633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko List<TvInputInfo> tvInputInfoList = 111633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko tvInputManagerHelper.getTvInputInfos(true, false); 112633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko if (tvInputInfoList == null || tvInputInfoList.isEmpty()) { 113633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko return; 114633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 115633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko for (TvInputInfo info : tvInputInfoList) { 116944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (CommonUtils.isBundledInput(info.getId())) { 117633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko dvrManager.forgetStorage(info.getId()); 118633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 119633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 120633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko } 121d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mCleanUpDbTask == this) { 122d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mCleanUpDbTask = null; 123d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 124d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 125d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 126633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko private List<ContentProviderOperation> getDeleteOps() { 127d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko List<ContentProviderOperation> ops = new ArrayList<>(); 128d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 12995961816a768da387f0b5523cf4363ace2044089Nick Chalko try (Cursor c = 13095961816a768da387f0b5523cf4363ace2044089Nick Chalko mContentResolver.query( 131944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvContractCompat.RecordedPrograms.CONTENT_URI, 13295961816a768da387f0b5523cf4363ace2044089Nick Chalko PROJECTION, 13395961816a768da387f0b5523cf4363ace2044089Nick Chalko null, 13495961816a768da387f0b5523cf4363ace2044089Nick Chalko null, 13595961816a768da387f0b5523cf4363ace2044089Nick Chalko null)) { 136d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (c == null) { 137d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return null; 138d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 139d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko while (c.moveToNext()) { 140944779887775bd950cf1abf348d2df461593f6abLive Channels Team @StorageStatus int storageStatus = getDvrStorageStatus(); 141944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (isCancelled() || storageStatus == STORAGE_STATUS_MISSING) { 142d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko ops.clear(); 143d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko break; 144d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 145d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko String id = c.getString(0); 146d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko String packageName = c.getString(1); 147d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko String dataUriString = c.getString(2); 148d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (dataUriString == null) { 149d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko continue; 150d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 151d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Uri dataUri = Uri.parse(dataUriString); 152944779887775bd950cf1abf348d2df461593f6abLive Channels Team if (!CommonUtils.isInBundledPackageSet(packageName) 15395961816a768da387f0b5523cf4363ace2044089Nick Chalko || dataUri == null 15495961816a768da387f0b5523cf4363ace2044089Nick Chalko || dataUri.getPath() == null 155d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko || !ContentResolver.SCHEME_FILE.equals(dataUri.getScheme())) { 156d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko continue; 157d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 158d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko File recordedProgramDir = new File(dataUri.getPath()); 159633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko if (!recordedProgramDir.exists()) { 16095961816a768da387f0b5523cf4363ace2044089Nick Chalko ops.add( 16195961816a768da387f0b5523cf4363ace2044089Nick Chalko ContentProviderOperation.newDelete( 162944779887775bd950cf1abf348d2df461593f6abLive Channels Team TvContractCompat.buildRecordedProgramUri( 16395961816a768da387f0b5523cf4363ace2044089Nick Chalko Long.parseLong(id))) 16495961816a768da387f0b5523cf4363ace2044089Nick Chalko .build()); 165d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 166d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 167d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return ops; 168d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 169d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 170d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 171d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko} 172