diff options
| author | 2022-01-13 05:06:48 +0000 | |
|---|---|---|
| committer | 2022-01-13 05:06:48 +0000 | |
| commit | cc4caab6201f116c6877a082bbb33f2d768f12b0 (patch) | |
| tree | 93dd2137988e33f4b2a74733b904c83ebd8001f9 | |
| parent | e786a09b76362df9b099d7ea7a8175f160f6bef2 (diff) | |
| parent | b6580b4ac0a9917ce8f05989aeb5bdffdac8a303 (diff) | |
Merge "Enhance resource sharing and enable ownership transfer"
8 files changed, 612 insertions, 45 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a2bf33ee6aa3..3a7077dde0a5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6469,7 +6469,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); @@ -6506,6 +6508,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(); @@ -6534,6 +6537,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 diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 6a6a22c1fc0e..50a208344c3a 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -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(); } } } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 4128abf03e07..9c4a83a235c0 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -241,7 +241,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; @@ -250,7 +250,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) @@ -455,6 +454,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. * * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able @@ -547,59 +800,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()) { @@ -669,8 +977,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(); @@ -997,6 +1308,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; } @@ -1756,12 +2082,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; } @@ -1795,7 +2121,7 @@ public class Tuner implements AutoCloseable { mLnbHandle = null; } mLnb = newLnb; - mLnb.setCallback(executor, cb, this); + mLnb.setCallbackAndOwner(executor, cb, this); setLnb(mLnb); } return mLnb; @@ -2081,8 +2407,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 { diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 2adea7e44a43..5ada89e9dea7 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -418,6 +418,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. * * <p>There are three possible scenarios: diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 7b83a0365fb4..d16fc6ca1dc7 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -177,6 +177,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. * * <p>There are three possible scenarios: diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index d9feaf4013a7..1b41494814b7 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -978,7 +978,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; } @@ -1345,18 +1346,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"); @@ -3319,6 +3345,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); @@ -3333,6 +3365,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); @@ -4574,10 +4617,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 }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index f1b31e3520b1..502bd6b18413 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -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); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 481a3b546cc3..af705d597af2 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -289,6 +289,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 { enforceTunerAccessPermission("requestDemux"); @@ -388,7 +405,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."); } @@ -991,6 +1012,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) { |