1 /* 2 * Copyright (C) 2015 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.tv.tuner.exoplayer.buffer; 18 19 import android.content.Context; 20 import android.os.AsyncTask; 21 import android.provider.Settings; 22 import android.support.annotation.NonNull; 23 import android.util.Pair; 24 25 import com.android.tv.common.SoftPreconditions; 26 27 import java.io.File; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.SortedMap; 31 32 /** 33 * Manages Trickplay storage. 34 */ 35 public class TrickplayStorageManager implements BufferManager.StorageManager { 36 // TODO: Support multi-sessions. 37 private static final String BUFFER_DIR = "timeshift"; 38 39 // Copied from android.provider.Settings.Global (hidden fields) 40 private static final String 41 SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage"; 42 private static final String 43 SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes"; 44 45 // Copied from android.os.StorageManager 46 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 47 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500L * 1024 * 1024; 48 49 private static AsyncTask<Void, Void, Void> sLastCacheCleanUpTask; 50 private static File sBufferDir; 51 private static long sStorageBufferBytes; 52 53 private final long mMaxBufferSize; 54 55 private static void initParamsIfNeeded(Context context, @NonNull File path) { 56 // TODO: Support multi-sessions. 57 SoftPreconditions.checkState( 58 sBufferDir == null || sBufferDir.equals(path)); 59 if (path.equals(sBufferDir)) { 60 return; 61 } 62 sBufferDir = path; 63 long lowPercentage = Settings.Global.getInt(context.getContentResolver(), 64 SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 65 long lowPercentageToBytes = path.getTotalSpace() * lowPercentage / 100; 66 long maxLowBytes = Settings.Global.getLong(context.getContentResolver(), 67 SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 68 sStorageBufferBytes = Math.min(lowPercentageToBytes, maxLowBytes); 69 } 70 71 public TrickplayStorageManager(Context context, @NonNull File baseDir, long maxBufferSize) { 72 initParamsIfNeeded(context, new File(baseDir, BUFFER_DIR)); 73 sBufferDir.mkdirs(); 74 mMaxBufferSize = maxBufferSize; 75 clearStorage(); 76 } 77 78 private void clearStorage() { 79 long now = System.currentTimeMillis(); 80 if (sLastCacheCleanUpTask != null) { 81 sLastCacheCleanUpTask.cancel(true); 82 } 83 sLastCacheCleanUpTask = new AsyncTask<Void, Void, Void>() { 84 @Override 85 protected Void doInBackground(Void... params) { 86 if (isCancelled()) { 87 return null; 88 } 89 File files[] = sBufferDir.listFiles(); 90 if (files == null || files.length == 0) { 91 return null; 92 } 93 for (File file : files) { 94 if (isCancelled()) { 95 break; 96 } 97 long lastModified = file.lastModified(); 98 if (lastModified != 0 && lastModified < now) { 99 file.delete(); 100 } 101 } 102 return null; 103 } 104 }; 105 sLastCacheCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 106 } 107 108 @Override 109 public File getBufferDir() { 110 return sBufferDir; 111 } 112 113 @Override 114 public boolean isPersistent() { 115 return false; 116 } 117 118 @Override 119 public boolean reachedStorageMax(long bufferSize, long pendingDelete) { 120 return bufferSize - pendingDelete > mMaxBufferSize; 121 } 122 123 @Override 124 public boolean hasEnoughBuffer(long pendingDelete) { 125 return sBufferDir.getUsableSpace() + pendingDelete >= sStorageBufferBytes; 126 } 127 128 @Override 129 public List<BufferManager.TrackFormat> readTrackInfoFiles(boolean isAudio) { 130 return null; 131 } 132 133 @Override 134 public ArrayList<BufferManager.PositionHolder> readIndexFile(String trackId) { 135 return null; 136 } 137 138 @Override 139 public void writeTrackInfoFiles(List<BufferManager.TrackFormat> formatList, boolean isAudio) { 140 } 141 142 @Override 143 public void writeIndexFile(String trackName, 144 SortedMap<Long, Pair<SampleChunk, Integer>> index) { 145 } 146 147 } 148