Loading core/api/system-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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 media/java/android/media/tv/tuner/Lnb.java +73 −22 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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(); Loading @@ -164,41 +167,85 @@ 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(() -> { for (LnbCallback callback : mCallbackMap.keySet()) { Executor executor = mCallbackMap.get(callback); if (callback != null && executor != null) { executor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { mCallback.onEvent(eventType); if (callback != null) { callback.onEvent(eventType); } } }); } } } } private void onDiseqcMessage(byte[] diseqcMessage) { synchronized (mCallbackLock) { if (mExecutor != null && mCallback != null) { mExecutor.execute(() -> { for (LnbCallback callback : mCallbackMap.keySet()) { Executor executor = mCallbackMap.get(callback); if (callback != null && executor != null) { executor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { mCallback.onDiseqcMessage(diseqcMessage); if (callback != null) { callback.onDiseqcMessage(diseqcMessage); } } }); } } } } /* package */ boolean isClosed() { synchronized (mLock) { Loading Loading @@ -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(); } } } Loading media/java/android/media/tv/tuner/Tuner.java +354 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading Loading @@ -454,6 +453,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. * Loading Loading @@ -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()) { Loading Loading @@ -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(); Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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 { Loading media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,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. * Loading media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +13 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading
core/api/system-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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
media/java/android/media/tv/tuner/Lnb.java +73 −22 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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(); Loading @@ -164,41 +167,85 @@ 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(() -> { for (LnbCallback callback : mCallbackMap.keySet()) { Executor executor = mCallbackMap.get(callback); if (callback != null && executor != null) { executor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { mCallback.onEvent(eventType); if (callback != null) { callback.onEvent(eventType); } } }); } } } } private void onDiseqcMessage(byte[] diseqcMessage) { synchronized (mCallbackLock) { if (mExecutor != null && mCallback != null) { mExecutor.execute(() -> { for (LnbCallback callback : mCallbackMap.keySet()) { Executor executor = mCallbackMap.get(callback); if (callback != null && executor != null) { executor.execute(() -> { synchronized (mCallbackLock) { if (mCallback != null) { mCallback.onDiseqcMessage(diseqcMessage); if (callback != null) { callback.onDiseqcMessage(diseqcMessage); } } }); } } } } /* package */ boolean isClosed() { synchronized (mLock) { Loading Loading @@ -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(); } } } Loading
media/java/android/media/tv/tuner/Tuner.java +354 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) Loading Loading @@ -454,6 +453,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. * Loading Loading @@ -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()) { Loading Loading @@ -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(); Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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 { Loading
media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +19 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,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. * Loading
media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +13 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading