Add SharedFilter to tuner frameworks.

The SharedFilter is used to share the Filter created by TIS to another
process. The TIS uses Filter.createSharedFilter() to create a token and
share it to another process. The new process will use
Tuner.openSharedFilter() to open that new token and create a
SharedFilter. The new process will be able to read the TIS owned
Filter's data with this new SharedFilter.

Bug: 196124225
Test: atest android.media.tv.tuner.cts on both AIDL and HIDL HAL.
Change-Id: Id93b30e4db242d35a3d1ae6023b967a90a78c448
This commit is contained in:
Hongguang
2021-10-04 22:13:43 -07:00
parent 12e9bb363a
commit bcca4dc8b9
15 changed files with 643 additions and 40 deletions

View File

@@ -20,6 +20,7 @@ package android {
field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
field public static final String ACCESS_TUNED_INFO = "android.permission.ACCESS_TUNED_INFO";
field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
field public static final String ACCESS_TV_SHARED_FILTER = "android.permission.ACCESS_TV_SHARED_FILTER";
field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
@@ -6125,6 +6126,7 @@ package android.media.tv.tuner {
method @Nullable public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
method @Nullable public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER) public static android.media.tv.tuner.filter.SharedFilter openSharedFilter(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.filter.SharedFilterCallback);
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
method public int setLnaEnabled(boolean);
@@ -6333,10 +6335,12 @@ package android.media.tv.tuner.filter {
public class Filter implements java.lang.AutoCloseable {
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
method @Nullable public String createSharedFilter();
method public int flush();
method @Deprecated public int getId();
method public long getIdLong();
method public int read(@NonNull byte[], long, long);
method public void releaseSharedFilter(@NonNull String);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int setMonitorEventMask(int);
method public int start();
@@ -6586,6 +6590,20 @@ package android.media.tv.tuner.filter {
method public int getType();
}
public final class SharedFilter implements java.lang.AutoCloseable {
method public void close();
method public int flush();
method public int read(@NonNull byte[], long, long);
method public int start();
method public int stop();
field public static final int STATUS_INACCESSIBLE = 128; // 0x80
}
public interface SharedFilterCallback {
method public void onFilterEvent(@NonNull android.media.tv.tuner.filter.SharedFilter, @NonNull android.media.tv.tuner.filter.FilterEvent[]);
method public void onFilterStatusChanged(@NonNull android.media.tv.tuner.filter.SharedFilter, int);
}
public class TemiEvent extends android.media.tv.tuner.filter.FilterEvent {
method @NonNull public byte[] getDescriptorData();
method public byte getDescriptorTag();

View File

@@ -5802,6 +5802,12 @@
<permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
android:protectionLevel="signature|privileged|vendorPrivileged" />
<!-- @SystemApi Allows an application to access shared filter of TV tuner HAL
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.ACCESS_TV_SHARED_FILTER"
android:protectionLevel="signature|privileged|vendorPrivileged" />
<!-- Allows an application to create trusted displays. @hide -->
<permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
android:protectionLevel="signature" />

View File

@@ -425,6 +425,7 @@ applications that come with the platform
<permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
<!-- Permissions required for CTS test - TunerTest -->
<permission name="android.permission.ACCESS_TV_DESCRAMBLER" />
<permission name="android.permission.ACCESS_TV_SHARED_FILTER" />
<permission name="android.permission.ACCESS_TV_TUNER" />
<permission name="android.permission.TUNER_RESOURCE_ACCESS" />
<!-- Permissions required for CTS test - TVInputManagerTest -->

View File

@@ -25,6 +25,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.Constant;
import android.hardware.tv.tuner.Constant64Bit;
import android.hardware.tv.tuner.FrontendScanType;
@@ -37,6 +38,8 @@ import android.media.tv.tuner.filter.Filter;
import android.media.tv.tuner.filter.Filter.Subtype;
import android.media.tv.tuner.filter.Filter.Type;
import android.media.tv.tuner.filter.FilterCallback;
import android.media.tv.tuner.filter.SharedFilter;
import android.media.tv.tuner.filter.SharedFilterCallback;
import android.media.tv.tuner.filter.TimeFilter;
import android.media.tv.tuner.frontend.Atsc3PlpInfo;
import android.media.tv.tuner.frontend.FrontendInfo;
@@ -620,6 +623,7 @@ public class Tuner implements AutoCloseable {
private native int nativeCloseFrontend(int handle);
private native int nativeClose();
private static native SharedFilter nativeOpenSharedFilter(String token);
/**
* Listener for resource lost.
@@ -1570,6 +1574,37 @@ public class Tuner implements AutoCloseable {
return dvr;
}
/**
* Open a shared filter instance.
*
* @param context the context of the caller.
* @param sharedFilterToken the token of the shared filter being opened.
* @param executor the executor on which callback will be invoked.
* @param cb the listener to receive notifications from shared filter.
* @return the opened shared filter object. {@code null} if the operation failed.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER)
@Nullable
static public SharedFilter openSharedFilter(@NonNull Context context,
@NonNull String sharedFilterToken, @CallbackExecutor @NonNull Executor executor,
@NonNull SharedFilterCallback cb) {
Objects.requireNonNull(sharedFilterToken, "sharedFilterToken must not be null");
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "SharedFilterCallback must not be null");
if (context.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_TV_SHARED_FILTER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller must have ACCESS_TV_SHAREDFILTER permission.");
}
SharedFilter filter = nativeOpenSharedFilter(sharedFilterToken);
if (filter != null) {
filter.setCallback(cb, executor);
}
return filter;
}
private boolean requestDemux() {
int[] demuxHandle = new int[1];
TunerDemuxRequest request = new TunerDemuxRequest();

View File

@@ -172,5 +172,16 @@ public final class TunerUtils {
}
}
/**
* Checks the accessibility of a resource instance.
*
* @throws IllegalStateException if the resource has already been inaccessible.
*/
public static void checkResourceAccessible(String name, boolean accessible) {
if (!accessible) {
throw new IllegalStateException(name + " is inaccessible");
}
}
private TunerUtils() {}
}

View File

@@ -28,6 +28,7 @@ import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.TunerVersionChecker;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -235,6 +236,8 @@ public class Filter implements AutoCloseable {
private Filter mSource;
private boolean mStarted;
private boolean mIsClosed = false;
private boolean mIsStarted = false;
private boolean mIsShared = false;
private final Object mLock = new Object();
private native int nativeConfigureFilter(
@@ -248,6 +251,8 @@ public class Filter implements AutoCloseable {
private native int nativeFlushFilter();
private native int nativeRead(byte[] buffer, long offset, long size);
private native int nativeClose();
private native String nativeCreateSharedFilter();
private native void nativeReleaseSharedFilter(String token);
// Called by JNI
private Filter(long id) {
@@ -312,6 +317,8 @@ public class Filter implements AutoCloseable {
* coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
* using the events from the previous configuration.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @param config the configuration of the filter.
* @return result status of the operation.
*/
@@ -319,6 +326,9 @@ public class Filter implements AutoCloseable {
public int configure(@NonNull FilterConfiguration config) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
Settings s = config.getSettings();
int subType = (s == null) ? mSubtype : s.getType();
if (mMainType != config.getType() || mSubtype != subType) {
@@ -374,6 +384,8 @@ public class Filter implements AutoCloseable {
* will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version
* information.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @param monitorEventMask Types of event to be monitored. Set corresponding bit to
* monitor it. Reset to stop monitoring.
* @return result status of the operation.
@@ -382,6 +394,9 @@ public class Filter implements AutoCloseable {
public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) {
return Tuner.RESULT_UNAVAILABLE;
@@ -398,6 +413,8 @@ public class Filter implements AutoCloseable {
* extract all protocols' header. Then a filter's data source can be output
* from another filter.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @param source the filter instance which provides data input. Switch to
* use demux as data source if the filter instance is NULL.
* @return result status of the operation.
@@ -407,6 +424,9 @@ public class Filter implements AutoCloseable {
public int setDataSource(@Nullable Filter source) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
if (mSource != null) {
throw new IllegalStateException("Data source is existing");
}
@@ -427,17 +447,25 @@ public class Filter implements AutoCloseable {
* coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
* using the events from the previous configuration.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @return result status of the operation.
*/
@Result
public int start() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeStartFilter();
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
int res = nativeStartFilter();
if (res == Tuner.RESULT_SUCCESS) {
mIsStarted = true;
}
return res;
}
}
/**
* Stops filtering data.
*
@@ -449,13 +477,22 @@ public class Filter implements AutoCloseable {
* coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
* using the events from the previous configuration.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @return result status of the operation.
*/
@Result
public int stop() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeStopFilter();
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
int res = nativeStopFilter();
if (res == Tuner.RESULT_SUCCESS) {
mIsStarted = false;
}
return res;
}
}
@@ -465,12 +502,17 @@ public class Filter implements AutoCloseable {
* <p>The data which is already produced by filter but not consumed yet will
* be cleared.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @return result status of the operation.
*/
@Result
public int flush() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsShared) {
return Tuner.RESULT_INVALID_STATE;
}
return nativeFlushFilter();
}
}
@@ -478,6 +520,8 @@ public class Filter implements AutoCloseable {
/**
* Copies filtered data from filter output to the given byte array.
*
* <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
*
* @param buffer the buffer to store the filtered data.
* @param offset the index of the first byte in {@code buffer} to write.
* @param size the maximum number of bytes to read.
@@ -486,6 +530,9 @@ public class Filter implements AutoCloseable {
public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsShared) {
return 0;
}
size = Math.min(size, buffer.length - offset);
return nativeRead(buffer, offset, size);
}
@@ -493,6 +540,10 @@ public class Filter implements AutoCloseable {
/**
* Stops filtering data and releases the Filter instance.
*
* <p>If this filter is shared, this filter will be closed and a
* {@link SharedFilterCallback#STATUS_INACCESSIBLE} event will be sent to shared filter before
* closing.
*/
@Override
public void close() {
@@ -504,8 +555,47 @@ public class Filter implements AutoCloseable {
if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "Failed to close filter.");
} else {
mIsStarted = false;
mIsClosed = true;
}
}
}
/**
* Creates a shared filter.
*
* @return a string shared filter token.
*/
@Nullable
public String createSharedFilter() {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (mIsStarted || mIsShared) {
Log.d(TAG, "Create shared filter in a wrong state, started: " +
mIsStarted + "shared: " + mIsShared);
return null;
}
String token = nativeCreateSharedFilter();
if (token != null) {
mIsShared = true;
}
return token;
}
}
/**
* Releases a shared filter.
*
* @param filterToken the token of the shared filter being released.
*/
public void releaseSharedFilter(@NonNull String filterToken) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (!mIsShared) {
return;
}
nativeReleaseSharedFilter(filterToken);
mIsShared = false;
}
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* Tuner shared data filter.
*
* <p>This class is used to filter wanted data in a different process.
*
* @hide
*/
@SystemApi
public final class SharedFilter implements AutoCloseable {
/** @hide */
@IntDef(flag = true, prefix = "STATUS_", value = {STATUS_INACCESSIBLE})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {}
/**
* The status of a shared filter that its data becomes inaccessible.
*/
public static final int STATUS_INACCESSIBLE = 1 << 7;
private static final String TAG = "SharedFilter";
private long mNativeContext;
private SharedFilterCallback mCallback;
private Executor mExecutor;
private final Object mCallbackLock = new Object();
private boolean mIsClosed = false;
private boolean mIsAccessible = true;
private final Object mLock = new Object();
private native int nativeStartSharedFilter();
private native int nativeStopSharedFilter();
private native int nativeFlushSharedFilter();
private native int nativeSharedRead(byte[] buffer, long offset, long size);
private native int nativeSharedClose();
// Called by JNI
private SharedFilter() {}
private void onFilterStatus(int status) {
synchronized (mLock) {
if (status == STATUS_INACCESSIBLE) {
mIsAccessible = false;
}
}
synchronized (mCallbackLock) {
if (mCallback != null && mExecutor != null) {
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onFilterStatusChanged(this, status);
}
}
});
}
}
}
private void onFilterEvent(FilterEvent[] events) {
synchronized (mCallbackLock) {
if (mCallback != null && mExecutor != null) {
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onFilterEvent(this, events);
}
}
});
}
}
}
/** @hide */
public void setCallback(SharedFilterCallback cb, Executor executor) {
synchronized (mCallbackLock) {
mCallback = cb;
mExecutor = executor;
}
}
/** @hide */
public SharedFilterCallback getCallback() {
synchronized (mCallbackLock) { return mCallback; }
}
/**
* Starts filtering data.
*
* <p>Does nothing if the filter is already started.
*
* @return result status of the operation.
*/
@Result
public int start() {
synchronized (mLock) {
TunerUtils.checkResourceAccessible(TAG, mIsAccessible);
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeStartSharedFilter();
}
}
/**
* Stops filtering data.
*
* <p>Does nothing if the filter is stopped or not started.
*
* @return result status of the operation.
*/
@Result
public int stop() {
synchronized (mLock) {
TunerUtils.checkResourceAccessible(TAG, mIsAccessible);
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeStopSharedFilter();
}
}
/**
* Flushes the filter.
*
* <p>The data which is already produced by filter but not consumed yet will
* be cleared.
*
* @return result status of the operation.
*/
@Result
public int flush() {
synchronized (mLock) {
TunerUtils.checkResourceAccessible(TAG, mIsAccessible);
TunerUtils.checkResourceState(TAG, mIsClosed);
return nativeFlushSharedFilter();
}
}
/**
* Copies filtered data from filter output to the given byte array.
*
* @param buffer the buffer to store the filtered data.
* @param offset the index of the first byte in {@code buffer} to write.
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
synchronized (mLock) {
TunerUtils.checkResourceAccessible(TAG, mIsAccessible);
TunerUtils.checkResourceState(TAG, mIsClosed);
size = Math.min(size, buffer.length - offset);
return nativeSharedRead(buffer, offset, size);
}
}
/**
* Stops filtering data and releases the Filter instance.
*/
@Override
public void close() {
synchronized (mLock) {
if (mIsClosed) {
return;
}
nativeSharedClose();
mIsClosed = true;
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.tuner.filter;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
/**
* Callback interface for receiving information from the corresponding shared filters.
*
* @hide
*/
@SystemApi
public interface SharedFilterCallback {
/**
* Invoked when there are filter events.
*
* @param sharedfilter the corresponding shared filter which sent the events.
* @param events the filter events sent from the filter.
*/
void onFilterEvent(@NonNull SharedFilter sharedfilter,
@SuppressLint("ArrayReturn") @NonNull FilterEvent[] events);
/**
* Invoked when filter status changed.
*
* @param sharedfilter the corresponding shared filter whose status is changed.
* @param status the new status of the filter.
*/
void onFilterStatusChanged(@NonNull SharedFilter sharedfilter, @Filter.Status int status);
}

View File

@@ -298,6 +298,7 @@ struct fields_t {
jfieldID dvrRecorderContext;
jfieldID dvrPlaybackContext;
jfieldID mediaEventContext;
jfieldID sharedFilterContext;
jmethodID frontendInitID;
jmethodID filterInitID;
jmethodID timeFilterInitID;
@@ -314,6 +315,9 @@ struct fields_t {
jmethodID descramblerInitID;
jmethodID linearBlockInitID;
jmethodID linearBlockSetInternalStateID;
jmethodID sharedFilterInitID;
jmethodID onSharedFilterStatusID;
jmethodID onSharedFilterEventID;
};
static fields_t gFields;
@@ -884,10 +888,11 @@ void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &eve
}
jobject filter(env->NewLocalRef(mFilterObj));
if (!env->IsSameObject(filter, nullptr)) {
env->CallVoidMethod(
filter,
gFields.onFilterEventID,
array);
jmethodID methodID = gFields.onFilterEventID;
if (mSharedFilter) {
methodID = gFields.onSharedFilterEventID;
}
env->CallVoidMethod(filter, methodID, array);
} else {
ALOGE("FilterClientCallbackImpl::onFilterEvent:"
"Filter object has been freed. Ignoring callback.");
@@ -899,13 +904,14 @@ void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject filter(env->NewLocalRef(mFilterObj));
if (!env->IsSameObject(filter, nullptr)) {
env->CallVoidMethod(
filter,
gFields.onFilterStatusID,
(jint)status);
jmethodID methodID = gFields.onFilterStatusID;
if (mSharedFilter) {
methodID = gFields.onSharedFilterStatusID;
}
env->CallVoidMethod(filter, methodID, (jint)static_cast<uint8_t>(status));
} else {
ALOGE("FilterClientCallbackImpl::onFilterStatus:"
"Filter object has been freed. Ignoring callback.");
"Filter object has been freed. Ignoring callback.");
}
}
@@ -914,6 +920,15 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte
// Java Object
mFilterObj = filterObj;
mFilterClient = filterClient;
mSharedFilter = false;
}
void FilterClientCallbackImpl::setSharedFilter(jweak filterObj, sp<FilterClient> filterClient) {
ALOGV("FilterClientCallbackImpl::setFilter");
// Java Object
mFilterObj = filterObj;
mFilterClient = filterClient;
mSharedFilter = true;
}
FilterClientCallbackImpl::~FilterClientCallbackImpl() {
@@ -2974,6 +2989,10 @@ static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) {
return (FilterClient *)env->GetLongField(filter, gFields.filterContext);
}
static sp<FilterClient> getSharedFilterClient(JNIEnv *env, jobject filter) {
return (FilterClient *)env->GetLongField(filter, gFields.sharedFilterContext);
}
static sp<LnbClient> getLnbClient(JNIEnv *env, jobject lnb) {
return (LnbClient *)env->GetLongField(lnb, gFields.lnbContext);
}
@@ -3049,6 +3068,14 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
env->GetMethodID(filterClazz, "onFilterEvent",
"([Landroid/media/tv/tuner/filter/FilterEvent;)V");
jclass sharedFilterClazz = env->FindClass("android/media/tv/tuner/filter/SharedFilter");
gFields.sharedFilterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
gFields.sharedFilterInitID = env->GetMethodID(sharedFilterClazz, "<init>", "()V");
gFields.onSharedFilterStatusID = env->GetMethodID(sharedFilterClazz, "onFilterStatus", "(I)V");
gFields.onSharedFilterEventID =
env->GetMethodID(sharedFilterClazz, "onFilterEvent",
"([Landroid/media/tv/tuner/filter/FilterEvent;)V");
jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter");
gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J");
gFields.timeFilterInitID = env->GetMethodID(timeFilterClazz, "<init>", "()V");
@@ -3718,7 +3745,12 @@ static jint android_media_tv_Tuner_set_filter_data_source(
}
static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
sp<FilterClient> filterClient = nullptr;
if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) {
filterClient = getSharedFilterClient(env, filter);
} else {
filterClient = getFilterClient(env, filter);
}
if (filterClient == nullptr) {
ALOGD("Failed to start filter: filter client not found");
return (int)Result::NOT_INITIALIZED;
@@ -3727,7 +3759,12 @@ static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
}
static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
sp<FilterClient> filterClient = nullptr;
if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) {
filterClient = getSharedFilterClient(env, filter);
} else {
filterClient = getFilterClient(env, filter);
}
if (filterClient == nullptr) {
ALOGD("Failed to stop filter: filter client not found");
return (int)Result::NOT_INITIALIZED;
@@ -3736,17 +3773,27 @@ static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
}
static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
sp<FilterClient> filterClient = nullptr;
if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) {
filterClient = getSharedFilterClient(env, filter);
} else {
filterClient = getFilterClient(env, filter);
}
if (filterClient == nullptr) {
ALOGD("Failed to flush filter: filter client not found");
return (int)Result::NOT_INITIALIZED;
return (jint)Result::NOT_INITIALIZED;
}
return (jint)filterClient->flush();
}
static jint android_media_tv_Tuner_read_filter_fmq(
JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
sp<FilterClient> filterClient = nullptr;
if (env->IsInstanceOf(filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"))) {
filterClient = getSharedFilterClient(env, filter);
} else {
filterClient = getFilterClient(env, filter);
}
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to read filter FMQ: filter client not found");
@@ -3766,14 +3813,57 @@ static jint android_media_tv_Tuner_read_filter_fmq(
}
static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
sp<FilterClient> filterClient = nullptr;
bool shared = env->IsInstanceOf(
filter, env->FindClass("android/media/tv/tuner/filter/SharedFilter"));
if (shared) {
filterClient = getSharedFilterClient(env, filter);
} else {
filterClient = getFilterClient(env, filter);
}
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to close filter: filter client not found");
return 0;
}
return (jint)filterClient->close();
Result r = filterClient->close();
filterClient->decStrong(filter);
env->SetLongField(filter, gFields.sharedFilterContext, 0);
if (shared) {
} else {
env->SetLongField(filter, gFields.filterContext, 0);
}
return (jint)r;
}
static jstring android_media_tv_Tuner_create_shared_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to create shared filter: filter client not found");
return nullptr;
}
string token = filterClient->createSharedFilter();
if (token.empty()) {
return nullptr;
}
return env->NewStringUTF(token.data());
}
static void android_media_tv_Tuner_release_shared_filter(
JNIEnv *env, jobject filter, jstring token) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to release shared filter: filter client not found");
return;
}
std::string filterToken(env->GetStringUTFChars(token, nullptr));
filterClient->releaseSharedFilter(filterToken);
}
static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
@@ -3843,10 +3933,8 @@ static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter)
}
Result r = timeFilterClient->close();
if (r == Result::SUCCESS) {
timeFilterClient->decStrong(filter);
env->SetLongField(filter, gFields.timeFilterContext, 0);
}
timeFilterClient->decStrong(filter);
env->SetLongField(filter, gFields.timeFilterContext, 0);
return (int)r;
}
@@ -3896,9 +3984,8 @@ static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descra
return (jint)Result::NOT_INITIALIZED;
}
Result r = descramblerClient->close();
if (r == Result::SUCCESS) {
descramblerClient->decStrong(descrambler);
}
descramblerClient->decStrong(descrambler);
env->SetLongField(descrambler, gFields.descramblerContext, 0);
return (jint)r;
}
@@ -3935,6 +4022,29 @@ static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /
return tuner->closeDemux();
}
static jobject android_media_tv_Tuner_open_shared_filter(
JNIEnv* env, jobject /* thiz */, jstring token) {
sp<TunerClient> tunerClient = new TunerClient();
std::string filterToken(env->GetStringUTFChars(token, nullptr));
sp<FilterClient> filterClient;
sp<FilterClientCallbackImpl> callback = new FilterClientCallbackImpl();
filterClient = tunerClient->openSharedFilter(filterToken, callback);
if (filterClient == nullptr) {
ALOGD("Failed to open shared filter %s", filterToken.c_str());
return nullptr;
}
jobject filterObj = env->NewObject(env->FindClass("android/media/tv/tuner/filter/SharedFilter"),
gFields.sharedFilterInitID);
filterClient->incStrong(filterObj);
env->SetLongField(filterObj, gFields.sharedFilterContext, (jlong)filterClient.get());
callback->setSharedFilter(env->NewWeakGlobalRef(filterObj), filterClient);
return filterObj;
}
static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeFrontend();
@@ -4014,7 +4124,14 @@ static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) {
ALOGD("Failed to close dvr: dvr client not found");
return (jint)Result::NOT_INITIALIZED;
}
return (jint)dvrClient->close();
Result r = dvrClient->close();
bool isRecorder =
env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
jfieldID fieldId =
isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext;
dvrClient->decStrong(dvr);
env->SetLongField(dvr, fieldId, 0);
return (jint)r;
}
static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) {
@@ -4043,10 +4160,8 @@ static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb,
static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) {
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
Result r = lnbClient->close();
if (r == Result::SUCCESS) {
lnbClient->decStrong(lnb);
env->SetLongField(lnb, gFields.lnbContext, 0);
}
lnbClient->decStrong(lnb);
env->SetLongField(lnb, gFields.lnbContext, 0);
return (jint)r;
}
@@ -4221,6 +4336,9 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
{ "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
{ "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
{"nativeOpenSharedFilter",
"(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
(void *)android_media_tv_Tuner_open_shared_filter},
};
static const JNINativeMethod gFilterMethods[] = {
@@ -4238,6 +4356,18 @@ static const JNINativeMethod gFilterMethods[] = {
{ "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
{ "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
{"nativeCreateSharedFilter", "()Ljava/lang/String;",
(void *)android_media_tv_Tuner_create_shared_filter},
{"nativeReleaseSharedFilter", "(Ljava/lang/String;)V",
(void *)android_media_tv_Tuner_release_shared_filter},
};
static const JNINativeMethod gSharedFilterMethods[] = {
{"nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
{"nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
{"nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
{"nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
{"nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
};
static const JNINativeMethod gTimeFilterMethods[] = {
@@ -4321,6 +4451,13 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) {
ALOGE("Failed to register filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
env, "android/media/tv/tuner/filter/SharedFilter",
gSharedFilterMethods,
NELEM(gSharedFilterMethods)) != JNI_OK) {
ALOGE("Failed to register shared filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
env, "android/media/tv/tuner/filter/TimeFilter",
gTimeFilterMethods,

View File

@@ -130,9 +130,12 @@ struct FilterClientCallbackImpl : public FilterClientCallback {
virtual void onFilterStatus(const DemuxFilterStatus status);
void setFilter(jweak filterObj, sp<FilterClient> filterClient);
void setSharedFilter(jweak filterObj, sp<FilterClient> filterClient);
private:
jweak mFilterObj;
sp<FilterClient> mFilterClient;
bool mSharedFilter;
void getSectionEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
void getMediaEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
void getPesEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);

View File

@@ -193,8 +193,27 @@ Result FilterClient::close() {
return Result::INVALID_STATE;
}
/////////////// TunerFilterCallback ///////////////////////
string FilterClient::createSharedFilter() {
if (mTunerFilter != nullptr) {
string filterToken;
if (mTunerFilter->createSharedFilter(&filterToken).isOk()) {
return filterToken;
}
}
return "";
}
Result FilterClient::releaseSharedFilter(const string& filterToken) {
if (mTunerFilter != nullptr) {
Status s = mTunerFilter->releaseSharedFilter(filterToken);
return ClientHelper::getServiceSpecificErrorCode(s);
}
return Result::INVALID_STATE;
}
/////////////// TunerFilterCallback ///////////////////////
TunerFilterCallback::TunerFilterCallback(sp<FilterClientCallback> filterClientCallback)
: mFilterClientCallback(filterClientCallback) {}
@@ -215,14 +234,14 @@ Status TunerFilterCallback::onFilterEvent(const vector<DemuxFilterEvent>& filter
}
Result FilterClient::getFilterMq() {
if (mFilterMQ != NULL) {
if (mFilterMQ != nullptr) {
return Result::SUCCESS;
}
AidlMQDesc aidlMqDesc;
Result res = Result::UNAVAILABLE;
if (mTunerFilter != NULL) {
if (mTunerFilter != nullptr) {
Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
if (s.isOk()) {
mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc, false/*resetPointer*/);

View File

@@ -142,6 +142,16 @@ public:
*/
Result close();
/**
* Create a new SharedFiler.
*/
string createSharedFilter();
/**
* Release SharedFiler.
*/
Result releaseSharedFilter(const string& filterToken);
private:
Result getFilterMq();
int64_t copyData(int8_t* buffer, int64_t size);

View File

@@ -33,8 +33,10 @@ int32_t TunerClient::mTunerVersion;
/////////////// TunerClient ///////////////////////
TunerClient::TunerClient() {
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
if (mTunerService == nullptr) {
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
}
if (mTunerService == nullptr) {
ALOGE("Failed to get tuner service");
} else {
@@ -43,8 +45,6 @@ TunerClient::TunerClient() {
}
TunerClient::~TunerClient() {
mTunerVersion = 0;
mTunerService = nullptr;
}
vector<int32_t> TunerClient::getFrontendIds() {
@@ -163,4 +163,26 @@ sp<LnbClient> TunerClient::openLnbByName(string lnbName) {
return nullptr;
}
sp<FilterClient> TunerClient::openSharedFilter(const string& filterToken,
sp<FilterClientCallback> cb) {
if (cb == nullptr) {
return nullptr;
}
if (mTunerService != nullptr) {
shared_ptr<ITunerFilter> tunerFilter;
shared_ptr<TunerFilterCallback> callback =
::ndk::SharedRefBase::make<TunerFilterCallback>(cb);
Status s = mTunerService->openSharedFilter(filterToken, callback, &tunerFilter);
if (!s.isOk()) {
return nullptr;
}
DemuxFilterType type;
tunerFilter->getFilterType(&type);
return new FilterClient(type, tunerFilter);
}
return nullptr;
}
} // namespace android

View File

@@ -20,10 +20,12 @@
#include <aidl/android/media/tv/tuner/ITunerService.h>
#include <android/binder_parcel_utils.h>
#include "DemuxClient.h"
#include "ClientHelper.h"
#include "FrontendClient.h"
#include "DemuxClient.h"
#include "DescramblerClient.h"
#include "FilterClient.h"
#include "FilterClientCallback.h"
#include "FrontendClient.h"
#include "LnbClient.h"
using Status = ::ndk::ScopedAStatus;
@@ -116,6 +118,15 @@ public:
*/
int32_t getHalTunerVersion() { return mTunerVersion; }
/**
* Open a new shared filter client.
*
* @param filterToken the shared filter token created by FilterClient.
* @param cb the FilterClientCallback to receive filter events.
* @return a newly created TunerFilter interface.
*/
sp<FilterClient> openSharedFilter(const string& filterToken, sp<FilterClientCallback> cb);
private:
/**
* An AIDL Tuner Service Singleton assigned at the first time the Tuner Client

View File

@@ -424,6 +424,7 @@
<!-- Permissions required for CTS test - TunerTest -->
<uses-permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" />
<uses-permission android:name="android.permission.ACCESS_TV_SHARED_FILTER" />
<uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
<uses-permission android:name="android.permission.TUNER_RESOURCE_ACCESS" />