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:
parent
238f9d577d
commit
b6580b4ac0
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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 },
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user