1 /* 2 * Copyright 2021 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 android.media.tv.tuner.filter; 18 19 import android.annotation.BytesLong; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.media.tv.tuner.Tuner; 25 import android.media.tv.tuner.Tuner.Result; 26 import android.media.tv.tuner.TunerUtils; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.concurrent.Executor; 30 31 /** 32 * Tuner shared data filter. 33 * 34 * <p>This class is used to filter wanted data in a different process. 35 * 36 * @hide 37 */ 38 @SystemApi 39 public final class SharedFilter implements AutoCloseable { 40 /** @hide */ 41 @IntDef(prefix = "STATUS_", value = {STATUS_INACCESSIBLE}) 42 @Retention(RetentionPolicy.SOURCE) 43 public @interface Status {} 44 45 /** 46 * The status of a shared filter that its data becomes inaccessible. 47 */ 48 public static final int STATUS_INACCESSIBLE = 1 << 7; 49 50 private static final String TAG = "SharedFilter"; 51 52 private long mNativeContext; 53 private SharedFilterCallback mCallback; 54 private Executor mExecutor; 55 private Object mCallbackLock = null; 56 private boolean mIsClosed = false; 57 private boolean mIsAccessible = true; 58 private Object mLock = null; 59 nativeStartSharedFilter()60 private native int nativeStartSharedFilter(); nativeStopSharedFilter()61 private native int nativeStopSharedFilter(); nativeFlushSharedFilter()62 private native int nativeFlushSharedFilter(); nativeSharedRead(byte[] buffer, long offset, long size)63 private native int nativeSharedRead(byte[] buffer, long offset, long size); nativeSharedClose()64 private native int nativeSharedClose(); 65 66 // Called by JNI SharedFilter()67 private SharedFilter() { 68 mCallbackLock = new Object(); 69 mLock = new Object(); 70 } 71 onFilterStatus(int status)72 private void onFilterStatus(int status) { 73 synchronized (mLock) { 74 if (status == STATUS_INACCESSIBLE) { 75 mIsAccessible = false; 76 } 77 } 78 synchronized (mCallbackLock) { 79 if (mCallback != null && mExecutor != null) { 80 mExecutor.execute(() -> { 81 synchronized (mCallbackLock) { 82 if (mCallback != null) { 83 mCallback.onFilterStatusChanged(this, status); 84 } 85 } 86 }); 87 } 88 } 89 } 90 onFilterEvent(FilterEvent[] events)91 private void onFilterEvent(FilterEvent[] events) { 92 synchronized (mCallbackLock) { 93 if (mCallback != null && mExecutor != null) { 94 mExecutor.execute(() -> { 95 synchronized (mCallbackLock) { 96 if (mCallback != null) { 97 mCallback.onFilterEvent(this, events); 98 } else { 99 for (FilterEvent event : events) { 100 if (event instanceof MediaEvent) { 101 ((MediaEvent)event).release(); 102 } 103 } 104 } 105 } 106 }); 107 } else { 108 for (FilterEvent event : events) { 109 if (event instanceof MediaEvent) { 110 ((MediaEvent)event).release(); 111 } 112 } 113 } 114 } 115 } 116 117 /** @hide */ setCallback(SharedFilterCallback cb, Executor executor)118 public void setCallback(SharedFilterCallback cb, Executor executor) { 119 synchronized (mCallbackLock) { 120 mCallback = cb; 121 mExecutor = executor; 122 } 123 } 124 125 /** @hide */ getCallback()126 public SharedFilterCallback getCallback() { 127 synchronized (mCallbackLock) { return mCallback; } 128 } 129 130 /** 131 * Starts filtering data. 132 * 133 * <p>Does nothing if the filter is already started. 134 * 135 * @return result status of the operation. 136 */ 137 @Result start()138 public int start() { 139 synchronized (mLock) { 140 TunerUtils.checkResourceAccessible(TAG, mIsAccessible); 141 TunerUtils.checkResourceState(TAG, mIsClosed); 142 return nativeStartSharedFilter(); 143 } 144 } 145 146 /** 147 * Stops filtering data. 148 * 149 * <p>Does nothing if the filter is stopped or not started. 150 * 151 * @return result status of the operation. 152 */ 153 @Result stop()154 public int stop() { 155 synchronized (mLock) { 156 TunerUtils.checkResourceAccessible(TAG, mIsAccessible); 157 TunerUtils.checkResourceState(TAG, mIsClosed); 158 return nativeStopSharedFilter(); 159 } 160 } 161 162 /** 163 * Flushes the shared filter. 164 * 165 * <p>The data which is already produced by filter but not consumed yet will 166 * be cleared. 167 * 168 * @return result status of the operation. 169 */ 170 @Result flush()171 public int flush() { 172 synchronized (mLock) { 173 TunerUtils.checkResourceAccessible(TAG, mIsAccessible); 174 TunerUtils.checkResourceState(TAG, mIsClosed); 175 return nativeFlushSharedFilter(); 176 } 177 } 178 179 /** 180 * Copies filtered data from shared filter output to the given byte array. 181 * 182 * @param buffer the buffer to store the filtered data. 183 * @param offset the index of the first byte in {@code buffer} to write. 184 * @param size the maximum number of bytes to read. 185 * @return the number of bytes read. 186 */ read(@onNull byte[] buffer, @BytesLong long offset, @BytesLong long size)187 public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) { 188 synchronized (mLock) { 189 TunerUtils.checkResourceAccessible(TAG, mIsAccessible); 190 TunerUtils.checkResourceState(TAG, mIsClosed); 191 size = Math.min(size, buffer.length - offset); 192 return nativeSharedRead(buffer, offset, size); 193 } 194 } 195 196 /** 197 * Stops filtering data and releases the shared filter instance. 198 */ 199 @Override close()200 public void close() { 201 synchronized (mLock) { 202 if (mIsClosed) { 203 return; 204 } 205 synchronized (mCallbackLock) { 206 mCallback = null; 207 mExecutor = null; 208 } 209 nativeSharedClose(); 210 mIsClosed = true; 211 mCallbackLock = null; 212 } 213 } 214 } 215