Enhance resource sharing and enable ownership transfer

New public APIs
- Tuner.closeFrontend()
  - Enables TIS app to continue using other resource while changing
    frontend resource
  - close & release Frontend resource when called on the owner, while
    it unshares Frontend resource when called on the sharee.
- Lnb.addCallback(Callback, Executor) and Lnb.removeCallback(Callback)
  - Enables TIS app to receive callback from the sharee
  - Also helps with the ownership transfer
- Tuner.transferOwner(Tuner newOwner)
  - Transfers the ownership of Frontend, CiCam, and Lnb resource

Additionally, added the following:
- Call nativeSetLnb() in requestFrontend() in case mLnb resource
  is already held. (this use case becomes a possibility now that we
  support Tuner.closeFrontend())

Bug: 192010866
Test: cts.TunerTest#testTransferOwner, testLnbAddAndRemoveSharee

Change-Id: I4c39c3726f0dd7bd1c153975ad01393ff2773005
This commit is contained in:
Kensuke Miyagi 2021-12-13 21:17:47 -08:00
parent 238f9d577d
commit b6580b4ac0
8 changed files with 612 additions and 45 deletions

View File

@ -6338,7 +6338,9 @@ package android.media.tv.tuner {
}
public class Lnb implements java.lang.AutoCloseable {
method public void addCallback(@NonNull android.media.tv.tuner.LnbCallback, @NonNull java.util.concurrent.Executor);
method public void close();
method public boolean removeCallback(@NonNull android.media.tv.tuner.LnbCallback);
method public int sendDiseqcMessage(@NonNull byte[]);
method public int setSatellitePosition(int);
method public int setTone(int);
@ -6375,6 +6377,7 @@ package android.media.tv.tuner {
method public void clearOnTuneEventListener();
method public void clearResourceLostListener();
method public void close();
method public void closeFrontend();
method public int connectCiCam(int);
method public int connectFrontendToCiCam(int);
method public int disconnectCiCam();
@ -6400,6 +6403,7 @@ package android.media.tv.tuner {
method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method public int transferOwner(@NonNull android.media.tv.tuner.Tuner);
method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff

View File

@ -28,6 +28,9 @@ import android.media.tv.tuner.Tuner.Result;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
@ -145,9 +148,9 @@ public class Lnb implements AutoCloseable {
private static final String TAG = "Lnb";
LnbCallback mCallback;
Executor mExecutor;
Tuner mTuner;
Map<LnbCallback, Executor> mCallbackMap =
new HashMap<LnbCallback, Executor>();
Tuner mOwner;
private final Object mCallbackLock = new Object();
@ -164,38 +167,82 @@ public class Lnb implements AutoCloseable {
private Lnb() {}
void setCallback(Executor executor, @Nullable LnbCallback callback, Tuner tuner) {
void setCallbackAndOwner(Executor executor, @Nullable LnbCallback callback, Tuner tuner) {
synchronized (mCallbackLock) {
mCallback = callback;
mExecutor = executor;
mTuner = tuner;
if (callback != null && executor != null) {
addCallback(callback, executor);
}
}
setOwner(tuner);
}
/**
* Adds LnbCallback
*
* @param callback the callback to receive notifications from LNB.
* @param executor the executor on which callback will be invoked. Cannot be null.
*/
public void addCallback(@NonNull LnbCallback callback, @NonNull Executor executor) {
Objects.requireNonNull(callback, "callback must not be null");
Objects.requireNonNull(executor, "executor must not be null");
synchronized (mCallbackLock) {
mCallbackMap.put(callback, executor);
}
}
/**
* Removes LnbCallback
*
* @param callback the callback be removed for callback
*
* @return {@code true} when successful. {@code false} otherwise.
*/
public boolean removeCallback(@NonNull LnbCallback callback) {
Objects.requireNonNull(callback, "callback must not be null");
synchronized (mCallbackLock) {
boolean result = (mCallbackMap.remove(callback) != null);
return result;
}
}
// allow owner transfer independent of whether callback is registered or not
/* package */ void setOwner(@NonNull Tuner newOwner) {
Objects.requireNonNull(newOwner, "newOwner must not be null");
synchronized (mLock) {
mOwner = newOwner;
}
}
private void onEvent(int eventType) {
synchronized (mCallbackLock) {
if (mExecutor != null && mCallback != null) {
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onEvent(eventType);
for (LnbCallback callback : mCallbackMap.keySet()) {
Executor executor = mCallbackMap.get(callback);
if (callback != null && executor != null) {
executor.execute(() -> {
synchronized (mCallbackLock) {
if (callback != null) {
callback.onEvent(eventType);
}
}
}
});
});
}
}
}
}
private void onDiseqcMessage(byte[] diseqcMessage) {
synchronized (mCallbackLock) {
if (mExecutor != null && mCallback != null) {
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
mCallback.onDiseqcMessage(diseqcMessage);
for (LnbCallback callback : mCallbackMap.keySet()) {
Executor executor = mCallbackMap.get(callback);
if (callback != null && executor != null) {
executor.execute(() -> {
synchronized (mCallbackLock) {
if (callback != null) {
callback.onDiseqcMessage(diseqcMessage);
}
}
}
});
});
}
}
}
}
@ -279,7 +326,11 @@ public class Lnb implements AutoCloseable {
TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
} else {
mIsClosed = true;
mTuner.releaseLnb();
if (mOwner != null) {
mOwner.releaseLnb();
mOwner = null;
}
mCallbackMap.clear();
}
}
}

View File

@ -240,7 +240,7 @@ public class Tuner implements AutoCloseable {
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MSG_RESOURCE_LOST = 1;
private static final int MSG_ON_FILTER_EVENT = 2;
@ -249,7 +249,6 @@ public class Tuner implements AutoCloseable {
private static final int FILTER_CLEANUP_THRESHOLD = 256;
/** @hide */
@IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
@Retention(RetentionPolicy.SOURCE)
@ -453,6 +452,260 @@ public class Tuner implements AutoCloseable {
}
}
/**
* Transfers the ownership of shared frontend and its associated resources.
*
* @param newOwner the Tuner instance to be the new owner.
*
* @return result status of tune operation.
*/
public int transferOwner(@NonNull Tuner newOwner) {
acquireTRMSLock("transferOwner()");
mFrontendLock.lock();
mFrontendCiCamLock.lock();
mLnbLock.lock();
try {
if (!isFrontendOwner() || !isNewOwnerQualifiedForTransfer(newOwner)) {
return RESULT_INVALID_STATE;
}
int res = transferFeOwner(newOwner);
if (res != RESULT_SUCCESS) {
return res;
}
res = transferCiCamOwner(newOwner);
if (res != RESULT_SUCCESS) {
return res;
}
res = transferLnbOwner(newOwner);
if (res != RESULT_SUCCESS) {
return res;
}
} finally {
mFrontendLock.unlock();
mFrontendCiCamLock.unlock();
mLnbLock.unlock();
releaseTRMSLock();
}
return RESULT_SUCCESS;
}
/**
* Resets or copies Frontend related settings.
*/
private void replicateFrontendSettings(@Nullable Tuner src) {
mFrontendLock.lock();
try {
if (src == null) {
if (DEBUG) {
Log.d(TAG, "resetting Frontend params for " + mClientId);
}
mFrontend = null;
mFrontendHandle = null;
mFrontendInfo = null;
mFrontendType = FrontendSettings.TYPE_UNDEFINED;
} else {
if (DEBUG) {
Log.d(TAG, "copying Frontend params from " + src.mClientId
+ " to " + mClientId);
}
mFrontend = src.mFrontend;
mFrontendHandle = src.mFrontendHandle;
mFrontendInfo = src.mFrontendInfo;
mFrontendType = src.mFrontendType;
}
} finally {
mFrontendLock.unlock();
}
}
/**
* Sets the frontend owner. mFeOwnerTuner should be null for the owner Tuner instance.
*/
private void setFrontendOwner(Tuner owner) {
mFrontendLock.lock();
try {
mFeOwnerTuner = owner;
} finally {
mFrontendLock.unlock();
}
}
/**
* Resets or copies the CiCam related settings.
*/
private void replicateCiCamSettings(@Nullable Tuner src) {
mFrontendCiCamLock.lock();
try {
if (src == null) {
if (DEBUG) {
Log.d(TAG, "resetting CiCamParams: " + mClientId);
}
mFrontendCiCamHandle = null;
mFrontendCiCamId = null;
} else {
if (DEBUG) {
Log.d(TAG, "copying CiCamParams from " + src.mClientId + " to " + mClientId);
Log.d(TAG, "mFrontendCiCamHandle:" + src.mFrontendCiCamHandle + ", "
+ "mFrontendCiCamId:" + src.mFrontendCiCamId);
}
mFrontendCiCamHandle = src.mFrontendCiCamHandle;
mFrontendCiCamId = src.mFrontendCiCamId;
}
} finally {
mFrontendCiCamLock.unlock();
}
}
/**
* Resets or copies Lnb related settings.
*/
private void replicateLnbSettings(@Nullable Tuner src) {
mLnbLock.lock();
try {
if (src == null) {
if (DEBUG) {
Log.d(TAG, "resetting Lnb params");
}
mLnb = null;
mLnbHandle = null;
} else {
if (DEBUG) {
Log.d(TAG, "copying Lnb params from " + src.mClientId + " to " + mClientId);
}
mLnb = src.mLnb;
mLnbHandle = src.mLnbHandle;
}
} finally {
mLnbLock.unlock();
}
}
/**
* Checks if it is a frontend resource owner.
* Proper mutex must be held prior to calling this.
*/
private boolean isFrontendOwner() {
boolean notAnOwner = (mFeOwnerTuner != null);
if (notAnOwner) {
Log.e(TAG, "transferOwner() - cannot be called on the non-owner");
return false;
}
return true;
}
/**
* Checks if the newOwner is qualified.
* Proper mutex must be held prior to calling this.
*/
private boolean isNewOwnerQualifiedForTransfer(@NonNull Tuner newOwner) {
// new owner must be the current sharee
boolean newOwnerIsTheCurrentSharee = (newOwner.mFeOwnerTuner == this)
&& (newOwner.mFrontendHandle.equals(mFrontendHandle));
if (!newOwnerIsTheCurrentSharee) {
Log.e(TAG, "transferOwner() - new owner must be the current sharee");
return false;
}
// new owner must not be holding any of the to-be-shared resources
boolean newOwnerAlreadyHoldsToBeSharedResource =
(newOwner.mFrontendCiCamHandle != null || newOwner.mLnb != null);
if (newOwnerAlreadyHoldsToBeSharedResource) {
Log.e(TAG, "transferOwner() - new owner cannot be holding CiCam"
+ " nor Lnb resource");
return false;
}
return true;
}
/**
* Transfers the ownership of the already held frontend resource.
* Proper mutex must be held prior to calling this.
*/
private int transferFeOwner(@NonNull Tuner newOwner) {
// handle native resource first
newOwner.nativeUpdateFrontend(getNativeContext());
nativeUpdateFrontend(0);
// transfer frontend related settings
newOwner.replicateFrontendSettings(this);
// transfer the frontend owner info
setFrontendOwner(newOwner);
newOwner.setFrontendOwner(null);
// handle TRM
if (mTunerResourceManager.transferOwner(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
mClientId, newOwner.mClientId)) {
return RESULT_SUCCESS;
} else {
return RESULT_UNKNOWN_ERROR;
}
}
/**
* Transfers the ownership of CiCam resource.
* This is a no-op if the CiCam resource is not held.
* Proper mutex must be held prior to calling this.
*/
private int transferCiCamOwner(Tuner newOwner) {
boolean notAnOwner = (mFrontendCiCamHandle == null);
if (notAnOwner) {
// There is nothing to do here if there is no CiCam
return RESULT_SUCCESS;
}
// no need to handle at native level
// transfer the CiCam info at Tuner level
newOwner.replicateCiCamSettings(this);
replicateCiCamSettings(null);
// handle TRM
if (mTunerResourceManager.transferOwner(
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM,
mClientId, newOwner.mClientId)) {
return RESULT_SUCCESS;
} else {
return RESULT_UNKNOWN_ERROR;
}
}
/**
* Transfers the ownership of Lnb resource.
* This is a no-op if the Lnb resource is not held.
* Proper mutex must be held prior to calling this.
*/
private int transferLnbOwner(Tuner newOwner) {
boolean notAnOwner = (mLnb == null);
if (notAnOwner) {
// There is nothing to do here if there is no Lnb
return RESULT_SUCCESS;
}
// no need to handle at native level
// set the new owner
mLnb.setOwner(newOwner);
newOwner.replicateLnbSettings(this);
replicateLnbSettings(null);
// handle TRM
if (mTunerResourceManager.transferOwner(
TunerResourceManager.TUNER_RESOURCE_TYPE_LNB,
mClientId, newOwner.mClientId)) {
return RESULT_SUCCESS;
} else {
return RESULT_UNKNOWN_ERROR;
}
}
/**
* Updates client priority with an arbitrary value along with a nice value.
*
@ -546,59 +799,114 @@ public class Tuner implements AutoCloseable {
}
}
/**
* Either unshares the frontend resource (for sharee) or release Frontend (for owner)
*/
public void closeFrontend() {
acquireTRMSLock("closeFrontend()");
try {
releaseFrontend();
} finally {
releaseTRMSLock();
}
}
/**
* Releases frontend resource for the owner. Unshares frontend resource for the sharee.
*/
private void releaseFrontend() {
if (DEBUG) {
Log.d(TAG, "Tuner#releaseFrontend");
}
mFrontendLock.lock();
try {
if (mFrontendHandle != null) {
if (DEBUG) {
Log.d(TAG, "mFrontendHandle not null");
}
if (mFeOwnerTuner != null) {
if (DEBUG) {
Log.d(TAG, "mFeOwnerTuner not null - sharee");
}
// unregister self from the Frontend callback
mFeOwnerTuner.unregisterFrontendCallbackListener(this);
mFeOwnerTuner = null;
nativeUnshareFrontend();
} else {
if (DEBUG) {
Log.d(TAG, "mFeOwnerTuner null - owner");
}
// close resource as owner
int res = nativeCloseFrontend(mFrontendHandle);
if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "failed to close frontend");
}
mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
}
if (DEBUG) {
Log.d(TAG, "call TRM#releaseFrontend :" + mFrontendHandle + ", " + mClientId);
}
mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
FrameworkStatsLog
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
mFrontendHandle = null;
mFrontend = null;
replicateFrontendSettings(null);
}
} finally {
mFrontendLock.unlock();
}
}
/**
* Releases CiCam resource if held. No-op otherwise.
*/
private void releaseCiCam() {
mFrontendCiCamLock.lock();
try {
if (mFrontendCiCamHandle != null) {
if (DEBUG) {
Log.d(TAG, "unlinking CiCam : " + mFrontendCiCamHandle + " for " + mClientId);
}
int result = nativeUnlinkCiCam(mFrontendCiCamId);
if (result == RESULT_SUCCESS) {
mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
replicateCiCamSettings(null);
} else {
Log.e(TAG, "nativeUnlinkCiCam(" + mFrontendCiCamHandle + ") for mClientId:"
+ mClientId + "failed with result:" + result);
}
} else {
if (DEBUG) {
Log.d(TAG, "NOT unlinking CiCam : " + mClientId);
}
}
} finally {
mFrontendCiCamLock.unlock();
}
}
private void releaseAll() {
// release CiCam before frontend because frontend handle is needed to unlink CiCam
releaseCiCam();
releaseFrontend();
mLnbLock.lock();
try {
// mLnb will be non-null only for owner tuner
if (mLnb != null) {
if (DEBUG) {
Log.d(TAG, "calling mLnb.close() : " + mClientId);
}
mLnb.close();
} else {
if (DEBUG) {
Log.d(TAG, "NOT calling mLnb.close() : " + mClientId);
}
}
} finally {
mLnbLock.unlock();
}
mFrontendCiCamLock.lock();
try {
if (mFrontendCiCamHandle != null) {
int result = nativeUnlinkCiCam(mFrontendCiCamId);
if (result == RESULT_SUCCESS) {
mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
mFrontendCiCamId = null;
mFrontendCiCamHandle = null;
}
}
} finally {
mFrontendCiCamLock.unlock();
}
synchronized (mDescramblers) {
if (!mDescramblers.isEmpty()) {
@ -668,8 +976,11 @@ public class Tuner implements AutoCloseable {
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeShareFrontend(int id);
private native int nativeUnshareFrontend();
private native void nativeRegisterFeCbListener(long nativeContext);
private native void nativeUnregisterFeCbListener(long nativeContext);
// nativeUpdateFrontend must be called on the new owner first
private native void nativeUpdateFrontend(long nativeContext);
@Result
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
@ -993,6 +1304,21 @@ public class Tuner implements AutoCloseable {
mFrontendHandle = feHandle[0];
mFrontend = nativeOpenFrontendByHandle(mFrontendHandle);
}
// For satellite type, set Lnb if valid handle exists.
// This is necessary as now that we support closeFrontend().
if (mFrontendType == FrontendSettings.TYPE_DVBS
|| mFrontendType == FrontendSettings.TYPE_ISDBS
|| mFrontendType == FrontendSettings.TYPE_ISDBS3) {
mLnbLock.lock();
try {
if (mLnbHandle != null && mLnb != null) {
nativeSetLnb(mLnb);
}
} finally {
mLnbLock.unlock();
}
}
return granted;
}
@ -1643,12 +1969,12 @@ public class Tuner implements AutoCloseable {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
if (mLnb != null) {
mLnb.setCallback(executor, cb, this);
mLnb.setCallbackAndOwner(executor, cb, this);
return mLnb;
}
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock)
&& mLnb != null) {
mLnb.setCallback(executor, cb, this);
mLnb.setCallbackAndOwner(executor, cb, this);
setLnb(mLnb);
return mLnb;
}
@ -1682,7 +2008,7 @@ public class Tuner implements AutoCloseable {
mLnbHandle = null;
}
mLnb = newLnb;
mLnb.setCallback(executor, cb, this);
mLnb.setCallbackAndOwner(executor, cb, this);
setLnb(mLnb);
}
return mLnb;
@ -1968,8 +2294,15 @@ public class Tuner implements AutoCloseable {
try {
if (mLnbHandle != null) {
// LNB handle can be null if it's opened by name.
if (DEBUG) {
Log.d(TAG, "releasing Lnb");
}
mTunerResourceManager.releaseLnb(mLnbHandle, mClientId);
mLnbHandle = null;
} else {
if (DEBUG) {
Log.d(TAG, "NOT releasing Lnb because mLnbHandle is null");
}
}
mLnb = null;
} finally {

View File

@ -414,6 +414,25 @@ public class TunerResourceManager {
}
}
/**
* Transfers the ownership of shared resource.
*
* <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner.
*
* @param resourceType the type of the resource to transfer the ownership for.
* @param currentOwnerId the id of the current owner client.
* @param newOwnerId the id of the new owner client.
*
* @return true if successful and false otherwise.
*/
public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) {
try {
return mService.transferOwner(resourceType, currentOwnerId, newOwnerId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Requests a Tuner Demux resource.
*

View File

@ -176,6 +176,19 @@ interface ITunerResourceManager {
*/
void shareFrontend(in int selfClientId, in int targetClientId);
/*
* Transfers the ownership of the shared resource.
*
* <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner.
*
* @param resourceType the type of resource to transfer the ownership for.
* @param currentOwnerId the id of the current owner client.
* @param newOwnerId the id of the new owner client.
*
* @return true if successful. false otherwise.
*/
boolean transferOwner(in int resourceType, in int currentOwnerId, in int newOwnerId);
/*
* This API is used by the Tuner framework to request an available demux from the TunerHAL.
*

View File

@ -959,7 +959,8 @@ FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak lis
void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jweak listenerRef = env->NewWeakGlobalRef(listener);
ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this);
ALOGV("addCallbackListener() with listener:%p and ref:%p @%p",
listener, listenerRef, this);
std::scoped_lock<std::mutex> lock(mMutex);
mListenersMap[jtuner] = listenerRef;
}
@ -1314,18 +1315,43 @@ int JTuner::shareFrontend(int feId) {
return (int)Result::SUCCESS;
}
int JTuner::unshareFrontend() {
if (mFeClient != nullptr) {
ALOGE("Cannot unshare frontend because this session is already holding %d"
" as an owner instead of as a sharee", mFeClient->getId());
return (int)Result::INVALID_STATE;
}
mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
return (int)Result::SUCCESS;
}
void JTuner::registerFeCbListener(JTuner* jtuner) {
ALOGV("registerFeCbListener: %p", jtuner);
if (mFeClientCb != nullptr && jtuner != nullptr) {
mFeClientCb->addCallbackListener(jtuner, jtuner->getObject());
}
}
void JTuner::unregisterFeCbListener(JTuner* jtuner) {
ALOGV("unregisterFeCbListener: %p", jtuner);
if (mFeClientCb != nullptr && jtuner != nullptr) {
mFeClientCb->removeCallbackListener(jtuner);
}
}
void JTuner::updateFrontend(JTuner* jtuner) {
if (jtuner == nullptr) {
ALOGV("JTuner::updateFrontend(null) called for previous owner: %p", this);
mFeClient = nullptr;
mFeClientCb = nullptr;
} else {
ALOGV("JTuner::updateFrontend(%p) called for new owner: %p", jtuner, this);
mFeClient = jtuner->mFeClient;
mFeClientCb = jtuner->mFeClientCb;
}
}
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@ -3245,6 +3271,12 @@ static int android_media_tv_Tuner_share_frontend(
return tuner->shareFrontend(id);
}
static int android_media_tv_Tuner_unshare_frontend(
JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->unshareFrontend();
}
static void android_media_tv_Tuner_register_fe_cb_listener(
JNIEnv *env, jobject thiz, jlong shareeJTuner) {
sp<JTuner> tuner = getTuner(env, thiz);
@ -3259,6 +3291,17 @@ static void android_media_tv_Tuner_unregister_fe_cb_listener(
tuner->unregisterFeCbListener(jtuner);
}
static void android_media_tv_Tuner_update_frontend(JNIEnv *env, jobject thiz, jlong jtunerPtr) {
sp<JTuner> tuner = getTuner(env, thiz);
JTuner *jtuner;
if (jtunerPtr == 0) {
jtuner = nullptr;
} else {
jtuner = (JTuner *) jtunerPtr;
}
tuner->updateFrontend(jtuner);
}
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
@ -4432,10 +4475,14 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeShareFrontend", "(I)I",
(void *)android_media_tv_Tuner_share_frontend },
{ "nativeUnshareFrontend", "()I",
(void *)android_media_tv_Tuner_unshare_frontend },
{ "nativeRegisterFeCbListener", "(J)V",
(void*)android_media_tv_Tuner_register_fe_cb_listener },
{ "nativeUnregisterFeCbListener", "(J)V",
(void*)android_media_tv_Tuner_unregister_fe_cb_listener },
{ "nativeUpdateFrontend", "(J)V",
(void*)android_media_tv_Tuner_update_frontend },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },

View File

@ -178,8 +178,10 @@ struct JTuner : public RefBase {
jobject getFrontendIds();
jobject openFrontendByHandle(int feHandle);
int shareFrontend(int feId);
int unshareFrontend();
void registerFeCbListener(JTuner* jtuner);
void unregisterFeCbListener(JTuner* jtuner);
void updateFrontend(JTuner* jtuner);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);

View File

@ -287,6 +287,23 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
}
@Override
public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) {
enforceTunerAccessPermission("transferOwner");
enforceTrmAccessPermission("transferOwner");
synchronized (mLock) {
if (!checkClientExists(currentOwnerId)) {
Slog.e(TAG, "currentOwnerId:" + currentOwnerId + " does not exit");
return false;
}
if (!checkClientExists(newOwnerId)) {
Slog.e(TAG, "newOwnerId:" + newOwnerId + " does not exit");
return false;
}
return transferOwnerInternal(resourceType, currentOwnerId, newOwnerId);
}
}
@Override
public boolean requestDemux(@NonNull TunerDemuxRequest request,
@NonNull int[] demuxHandle) throws RemoteException {
@ -387,7 +404,11 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (fe == null) {
throw new RemoteException("Releasing frontend does not exist.");
}
if (fe.getOwnerClientId() != clientId) {
int ownerClientId = fe.getOwnerClientId();
ClientProfile ownerProfile = getClientProfile(ownerClientId);
if (ownerClientId != clientId
&& (ownerProfile != null
&& !ownerProfile.getShareFeClientIds().contains(clientId))) {
throw new RemoteException(
"Client is not the current owner of the releasing fe.");
}
@ -969,6 +990,83 @@ public class TunerResourceManagerService extends SystemService implements IBinde
getClientProfile(targetClientId).shareFrontend(selfClientId);
}
private boolean transferFeOwner(int currentOwnerId, int newOwnerId) {
ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
// change the owner of all the inUse frontend
newOwnerProfile.shareFrontend(currentOwnerId);
currentOwnerProfile.stopSharingFrontend(newOwnerId);
for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
getFrontendResource(inUseHandle).setOwner(newOwnerId);
}
// double check there is no other resources tied to the previous owner
for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
int ownerId = getFrontendResource(inUseHandle).getOwnerClientId();
if (ownerId != newOwnerId) {
Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle
+ ", " + ownerId + ", " + newOwnerId);
return false;
}
}
return true;
}
private boolean transferFeCiCamOwner(int currentOwnerId, int newOwnerId) {
ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
// link ciCamId to the new profile
int ciCamId = currentOwnerProfile.getInUseCiCamId();
newOwnerProfile.useCiCam(ciCamId);
// set the new owner Id
CiCamResource ciCam = getCiCamResource(ciCamId);
ciCam.setOwner(newOwnerId);
// unlink cicam resource from the original owner profile
currentOwnerProfile.releaseCiCam();
return true;
}
private boolean transferLnbOwner(int currentOwnerId, int newOwnerId) {
ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId);
ClientProfile newOwnerProfile = getClientProfile(newOwnerId);
Set<Integer> inUseLnbHandles = new HashSet<>();
for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) {
// link lnb handle to the new profile
newOwnerProfile.useLnb(lnbHandle);
// set new owner Id
LnbResource lnb = getLnbResource(lnbHandle);
lnb.setOwner(newOwnerId);
inUseLnbHandles.add(lnbHandle);
}
// unlink lnb handles from the original owner
for (Integer lnbHandle : inUseLnbHandles) {
currentOwnerProfile.releaseLnb(lnbHandle);
}
return true;
}
@VisibleForTesting
protected boolean transferOwnerInternal(int resourceType, int currentOwnerId, int newOwnerId) {
switch (resourceType) {
case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND:
return transferFeOwner(currentOwnerId, newOwnerId);
case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM:
return transferFeCiCamOwner(currentOwnerId, newOwnerId);
case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB:
return transferLnbOwner(currentOwnerId, newOwnerId);
default:
Slog.e(TAG, "transferOwnerInternal. unsupported resourceType: " + resourceType);
return false;
}
}
@VisibleForTesting
protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
if (DEBUG) {