11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/* 21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project 31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License. 61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at 71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software 111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and 141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License. 151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.exoplayer.buffer; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context; 20d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport android.os.AsyncTask; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.provider.Settings; 226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.support.annotation.NonNull; 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Pair; 241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.common.SoftPreconditions; 266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.File; 281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList; 296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport java.util.List; 301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.SortedMap; 311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Manages Trickplay storage. 341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic class TrickplayStorageManager implements BufferManager.StorageManager { 366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko // TODO: Support multi-sessions. 3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final String BUFFER_DIR = "timeshift"; 381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Copied from android.provider.Settings.Global (hidden fields) 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String 411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage"; 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String 431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes"; 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // Copied from android.os.StorageManager 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 47ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500L * 1024 * 1024; 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 496ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static AsyncTask<Void, Void, Void> sLastCacheCleanUpTask; 506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static File sBufferDir; 516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static long sStorageBufferBytes; 526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 5365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final long mMaxBufferSize; 541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private static void initParamsIfNeeded(Context context, @NonNull File path) { 566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko // TODO: Support multi-sessions. 576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko SoftPreconditions.checkState( 586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sBufferDir == null || sBufferDir.equals(path)); 596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (path.equals(sBufferDir)) { 606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return; 616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sBufferDir = path; 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko long lowPercentage = Settings.Global.getInt(context.getContentResolver(), 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko long lowPercentageToBytes = path.getTotalSpace() * lowPercentage / 100; 661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko long maxLowBytes = Settings.Global.getLong(context.getContentResolver(), 671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sStorageBufferBytes = Math.min(lowPercentageToBytes, maxLowBytes); 691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko public TrickplayStorageManager(Context context, @NonNull File baseDir, long maxBufferSize) { 726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko initParamsIfNeeded(context, new File(baseDir, BUFFER_DIR)); 736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sBufferDir.mkdirs(); 7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mMaxBufferSize = maxBufferSize; 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko clearStorage(); 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private void clearStorage() { 796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko long now = System.currentTimeMillis(); 806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (sLastCacheCleanUpTask != null) { 816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sLastCacheCleanUpTask.cancel(true); 82d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sLastCacheCleanUpTask = new AsyncTask<Void, Void, Void>() { 846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko @Override 856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko protected Void doInBackground(Void... params) { 866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (isCancelled()) { 876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return null; 886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko File files[] = sBufferDir.listFiles(); 906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (files == null || files.length == 0) { 916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return null; 926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko for (File file : files) { 946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (isCancelled()) { 956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko break; 966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko long lastModified = file.lastModified(); 986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (lastModified != 0 && lastModified < now) { 99d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko file.delete(); 100d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 101d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 1026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return null; 103d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 1046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko }; 1056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko sLastCacheCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 10965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public File getBufferDir() { 1106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return sBufferDir; 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean isPersistent() { 1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return false; 1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 11965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public boolean reachedStorageMax(long bufferSize, long pendingDelete) { 12065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return bufferSize - pendingDelete > mMaxBufferSize; 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public boolean hasEnoughBuffer(long pendingDelete) { 1256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko return sBufferDir.getUsableSpace() + pendingDelete >= sStorageBufferBytes; 1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko public List<BufferManager.TrackFormat> readTrackInfoFiles(boolean isAudio) { 1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return null; 1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko public ArrayList<BufferManager.PositionHolder> readIndexFile(String trackId) { 1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return null; 1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio) { 1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko public void writeIndexFile(String trackName, 1446ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko SortedMap<Long, Pair<SampleChunk, Integer>> index) { 1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 148