Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17c4a944 authored by Kensuke Miyagi's avatar Kensuke Miyagi
Browse files

Fix race condition betweeen Tuner APIs and with onReclaimResources

- Added acquire/releaseLock() in ITunerResourceManager.aidl for Tuner
  API implementations to utilize.
- Added more lock around resource related operations that have
  race condition with releaseAll() in Tuner.java
- This fix addresses race condition when Tuner object dies as well.
- Added these calls into Tuner API implementations

Also, suppress FLAG_ONEWAY warning for onReclaimResources as it is only
called against OEM/SoC packages that are part of image that goes throug
Google certification.

Bug: 206725267
Bug: 206726459
Bug: 206723996
Test: TunerTest#testResourceReclaimedDifferentProcess() and
testResourceReclaimedDifferentThread()

Change-Id: Ic9aee1e15f53b2d4984c3b4214a392c2c4d5731e
parent f5c3a290
Loading
Loading
Loading
Loading
+471 −213
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;

/**
 * This class is used to interact with hardware tuners devices.
@@ -248,6 +249,7 @@ 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)
@@ -304,6 +306,11 @@ public class Tuner implements AutoCloseable {
    private final Object mOnTuneEventLock = new Object();
    private final Object mScanCallbackLock = new Object();
    private final Object mOnResourceLostListenerLock = new Object();
    private final ReentrantLock mFrontendLock = new ReentrantLock();
    private final ReentrantLock mLnbLock = new ReentrantLock();
    private final ReentrantLock mFrontendCiCamLock = new ReentrantLock();
    private final ReentrantLock mDemuxLock = new ReentrantLock();
    private int mRequestedCiCamId;

    private Integer mDemuxHandle;
    private Integer mFrontendCiCamHandle;
@@ -391,7 +398,12 @@ public class Tuner implements AutoCloseable {

    /** @hide */
    public List<Integer> getFrontendIds() {
        mFrontendLock.lock();
        try {
            return nativeGetFrontendIds();
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -426,6 +438,9 @@ public class Tuner implements AutoCloseable {
     * @param tuner the Tuner instance to share frontend resource with.
     */
    public void shareFrontendFromTuner(@NonNull Tuner tuner) {
        acquireTRMSLock("shareFrontendFromTuner()");
        mFrontendLock.lock();
        try {
            mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
            synchronized (mIsSharedFrontend) {
                mFrontendHandle = tuner.mFrontendHandle;
@@ -433,6 +448,10 @@ public class Tuner implements AutoCloseable {
                mIsSharedFrontend = true;
            }
            nativeShareFrontend(mFrontend.mId);
        } finally {
            releaseTRMSLock();
            mFrontendLock.unlock();
        }
    }

    /**
@@ -498,11 +517,18 @@ public class Tuner implements AutoCloseable {
     */
    @Override
    public void close() {
        acquireTRMSLock("close()");
        try {
            releaseAll();
            TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner");
        } finally {
            releaseTRMSLock();
        }
    }

    private void releaseAll() {
        mFrontendLock.lock();
        try {
            if (mFrontendHandle != null) {
                synchronized (mIsSharedFrontend) {
                    if (!mIsSharedFrontend) {
@@ -520,9 +546,21 @@ public class Tuner implements AutoCloseable {
                mFrontendHandle = null;
                mFrontend = null;
            }
        } finally {
            mFrontendLock.unlock();
        }

        mLnbLock.lock();
        try {
            if (mLnb != null) {
                mLnb.close();
            }
        } finally {
            mLnbLock.unlock();
        }

        mFrontendCiCamLock.lock();
        try {
            if (mFrontendCiCamHandle != null) {
                int result = nativeUnlinkCiCam(mFrontendCiCamId);
                if (result == RESULT_SUCCESS) {
@@ -531,6 +569,10 @@ public class Tuner implements AutoCloseable {
                    mFrontendCiCamHandle = null;
                }
            }
        } finally {
            mFrontendCiCamLock.unlock();
        }

        synchronized (mDescramblers) {
            if (!mDescramblers.isEmpty()) {
                for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
@@ -543,6 +585,7 @@ public class Tuner implements AutoCloseable {
                mDescramblers.clear();
            }
        }

        synchronized (mFilters) {
            if (!mFilters.isEmpty()) {
                for (WeakReference<Filter> weakFilter : mFilters) {
@@ -554,6 +597,9 @@ public class Tuner implements AutoCloseable {
                mFilters.clear();
            }
        }

        mDemuxLock.lock();
        try {
            if (mDemuxHandle != null) {
                int res = nativeCloseDemux(mDemuxHandle);
                if (res != Tuner.RESULT_SUCCESS) {
@@ -562,6 +608,9 @@ public class Tuner implements AutoCloseable {
                mTunerResourceManager.releaseDemux(mDemuxHandle, mClientId);
                mDemuxHandle = null;
            }
        } finally {
            mDemuxLock.unlock();
        }

        mTunerResourceManager.unregisterClientProfile(mClientId);

@@ -763,9 +812,12 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int tune(@NonNull FrontendSettings settings) {
        mFrontendLock.lock();
        try {
            final int type = settings.getType();
            if (mFrontendHandle != null && type != mFrontendType) {
            Log.e(TAG, "Frontend was opened with type " + mFrontendType + ", new type is " + type);
                Log.e(TAG, "Frontend was opened with type " + mFrontendType
                        + ", new type is " + type);
                return RESULT_INVALID_STATE;
            }
            Log.d(TAG, "Tune to " + settings.getFrequencyLong());
@@ -776,16 +828,22 @@ public class Tuner implements AutoCloseable {
                    return RESULT_UNAVAILABLE;
                }
            }
        if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {

            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
                mFrontendInfo = null;
                Log.d(TAG, "Write Stats Log for tuning.");
                FrameworkStatsLog
                        .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
                            FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__TUNING);
            return nativeTune(settings.getType(), settings);
        }
                int res = nativeTune(settings.getType(), settings);
                return res;
            } else {
                return RESULT_UNAVAILABLE;
            }
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
     * Stops a previous tuning.
@@ -797,7 +855,12 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int cancelTuning() {
        mFrontendLock.lock();
        try {
            return nativeStopTune();
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -824,11 +887,15 @@ public class Tuner implements AutoCloseable {
    @Result
    public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
            @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {

        mFrontendLock.lock();
        try {
            synchronized (mScanCallbackLock) {
                // Scan can be called again for blink scan if scanCallback and executor are same as
                //before.
                if (((mScanCallback != null) && (mScanCallback != scanCallback))
                || ((mScanCallbackExecutor != null) && (mScanCallbackExecutor != executor))) {
                        || ((mScanCallbackExecutor != null)
                            && (mScanCallbackExecutor != executor))) {
                    throw new IllegalStateException(
                        "Different Scan session already in progress.  stopScan must be called "
                            + "before a new scan session can be " + "started.");
@@ -841,7 +908,8 @@ public class Tuner implements AutoCloseable {
                        return RESULT_UNAVAILABLE;
                    }
                }
            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
                if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
                          mFrontendLock)) {
                    mScanCallback = scanCallback;
                    mScanCallbackExecutor = executor;
                    mFrontendInfo = null;
@@ -852,6 +920,9 @@ public class Tuner implements AutoCloseable {
                }
                return RESULT_UNAVAILABLE;
            }
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -867,6 +938,8 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int cancelScanning() {
        mFrontendLock.lock();
        try {
            synchronized (mScanCallbackLock) {
                FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCAN_STOPPED);
@@ -876,6 +949,9 @@ public class Tuner implements AutoCloseable {
                mScanCallbackExecutor = null;
                return retVal;
            }
        } finally {
            mFrontendLock.unlock();
        }
    }

    private boolean requestFrontend() {
@@ -903,7 +979,12 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    private int setLnb(@NonNull Lnb lnb) {
        mLnbLock.lock();
        try {
            return nativeSetLnb(lnb);
        } finally {
            mLnbLock.unlock();
        }
    }

    /**
@@ -929,10 +1010,15 @@ public class Tuner implements AutoCloseable {
     */
    @Nullable
    public FrontendStatus getFrontendStatus(@NonNull @FrontendStatusType int[] statusTypes) {
        mFrontendLock.lock();
        try {
            if (mFrontend == null) {
                throw new IllegalStateException("frontend is not initialized");
            }
            return nativeGetFrontendStatus(statusTypes);
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -942,11 +1028,16 @@ public class Tuner implements AutoCloseable {
     * @return the id of hardware A/V sync.
     */
    public int getAvSyncHwId(@NonNull Filter filter) {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return INVALID_AV_SYNC_ID;
            }
            Integer id = nativeGetAvSyncHwId(filter);
            return id == null ? INVALID_AV_SYNC_ID : id;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -959,11 +1050,16 @@ public class Tuner implements AutoCloseable {
     * @return the current timestamp of hardware A/V sync.
     */
    public long getAvSyncTime(int avSyncHwId) {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return INVALID_TIMESTAMP;
            }
            Long time = nativeGetAvSyncTime(avSyncHwId);
            return time == null ? INVALID_TIMESTAMP : time;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -980,10 +1076,15 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int connectCiCam(int ciCamId) {
        if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return nativeConnectCiCam(ciCamId);
            }
            return RESULT_UNAVAILABLE;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1011,14 +1112,30 @@ public class Tuner implements AutoCloseable {
     *         {@link TunerVersionChecker#getTunerVersion()}.
     */
    public int connectFrontendToCiCam(int ciCamId) {
        if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
        // TODO: change this so TRMS lock is held only when the resource handles for
        // CiCam/Frontend is null. Current implementation can only handle one local lock for that.
        acquireTRMSLock("connectFrontendToCiCam()");
        mFrontendCiCamLock.lock();
        mFrontendLock.lock();
        try {
            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
                    TunerVersionChecker.TUNER_VERSION_1_1,
                    "linkFrontendToCiCam")) {
            if (checkCiCamResource(ciCamId)
                    && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
                mRequestedCiCamId = ciCamId;
                // No need to unlock mFrontendCiCamLock and mFrontendLock below becauase
                // TRMS lock is already acquired. Pass null to disable lock related operations
                if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, null)
                        && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, null)
                    ) {
                    return nativeLinkCiCam(ciCamId);
                }
            }
            return INVALID_LTS_ID;
        } finally {
            releaseTRMSLock();
            mFrontendCiCamLock.unlock();
            mFrontendLock.unlock();
        }
    }

    /**
@@ -1033,10 +1150,15 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int disconnectCiCam() {
        mDemuxLock.lock();
        try {
            if (mDemuxHandle != null) {
                return nativeDisconnectCiCam();
            }
            return RESULT_UNAVAILABLE;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1057,8 +1179,12 @@ public class Tuner implements AutoCloseable {
     */
    @Result
    public int disconnectFrontendToCiCam(int ciCamId) {
        if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
        acquireTRMSLock("disconnectFrontendToCiCam()");
        try {
            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
                    TunerVersionChecker.TUNER_VERSION_1_1,
                    "unlinkFrontendToCiCam")) {
                mFrontendCiCamLock.lock();
                if (mFrontendCiCamHandle != null && mFrontendCiCamId != null
                        && mFrontendCiCamId == ciCamId) {
                    int result = nativeUnlinkCiCam(ciCamId);
@@ -1071,6 +1197,12 @@ public class Tuner implements AutoCloseable {
                }
            }
            return RESULT_UNAVAILABLE;
        } finally {
            if (mFrontendCiCamLock.isLocked()) {
                mFrontendCiCamLock.unlock();
            }
            releaseTRMSLock();
        }
    }

    /**
@@ -1082,7 +1214,9 @@ public class Tuner implements AutoCloseable {
     */
    @Nullable
    public FrontendInfo getFrontendInfo() {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
        mFrontendLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
                return null;
            }
            if (mFrontend == null) {
@@ -1092,6 +1226,9 @@ public class Tuner implements AutoCloseable {
                mFrontendInfo = getFrontendInfoById(mFrontend.mId);
            }
            return mFrontendInfo;
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -1114,7 +1251,12 @@ public class Tuner implements AutoCloseable {

    /** @hide */
    public FrontendInfo getFrontendInfoById(int id) {
        mFrontendLock.lock();
        try {
            return nativeGetFrontendInfo(id);
        } finally {
            mFrontendLock.unlock();
        }
    }

    /**
@@ -1125,7 +1267,12 @@ public class Tuner implements AutoCloseable {
     */
    @Nullable
    public DemuxCapabilities getDemuxCapabilities() {
        mDemuxLock.lock();
        try {
            return nativeGetDemuxCapabilities();
        } finally {
            mDemuxLock.unlock();
        }
    }

    private void onFrontendEvent(int eventType) {
@@ -1417,7 +1564,9 @@ public class Tuner implements AutoCloseable {
    public Filter openFilter(@Type int mainType, @Subtype int subType,
            @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
            @Nullable FilterCallback cb) {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return null;
            }
            Filter filter = nativeOpenFilter(
@@ -1443,6 +1592,9 @@ public class Tuner implements AutoCloseable {
                }
            }
            return filter;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1457,18 +1609,24 @@ public class Tuner implements AutoCloseable {
     */
    @Nullable
    public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
        mLnbLock.lock();
        try {
            Objects.requireNonNull(executor, "executor must not be null");
            Objects.requireNonNull(cb, "LnbCallback must not be null");
            if (mLnb != null) {
                mLnb.setCallback(executor, cb, this);
                return mLnb;
            }
        if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB) && mLnb != null) {
            if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock)
                    && mLnb != null) {
                mLnb.setCallback(executor, cb, this);
                setLnb(mLnb);
                return mLnb;
            }
            return null;
        } finally {
            mLnbLock.unlock();
        }
    }

    /**
@@ -1483,6 +1641,8 @@ public class Tuner implements AutoCloseable {
    @Nullable
    public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
            @NonNull LnbCallback cb) {
        mLnbLock.lock();
        try {
            Objects.requireNonNull(name, "LNB name must not be null");
            Objects.requireNonNull(executor, "executor must not be null");
            Objects.requireNonNull(cb, "LnbCallback must not be null");
@@ -1497,6 +1657,9 @@ public class Tuner implements AutoCloseable {
                setLnb(mLnb);
            }
            return mLnb;
        } finally {
            mLnbLock.unlock();
        }
    }

    private boolean requestLnb() {
@@ -1518,10 +1681,15 @@ public class Tuner implements AutoCloseable {
     */
    @Nullable
    public TimeFilter openTimeFilter() {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return null;
            }
            return nativeOpenTimeFilter();
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1532,10 +1700,15 @@ public class Tuner implements AutoCloseable {
    @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
    @Nullable
    public Descrambler openDescrambler() {
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
        mDemuxLock.lock();
        try {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return null;
            }
            return requestDescrambler();
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1553,14 +1726,19 @@ public class Tuner implements AutoCloseable {
            @BytesLong long bufferSize,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OnRecordStatusChangedListener l) {
        mDemuxLock.lock();
        try {
            Objects.requireNonNull(executor, "executor must not be null");
            Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null");
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return null;
            }
            DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize);
            dvr.setListener(executor, l);
            return dvr;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1578,14 +1756,19 @@ public class Tuner implements AutoCloseable {
            @BytesLong long bufferSize,
            @CallbackExecutor @NonNull Executor executor,
            @NonNull OnPlaybackStatusChangedListener l) {
        mDemuxLock.lock();
        try {
            Objects.requireNonNull(executor, "executor must not be null");
            Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null");
        if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
                return null;
            }
            DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
            dvr.setListener(executor, l);
            return dvr;
        } finally {
            mDemuxLock.unlock();
        }
    }

    /**
@@ -1602,6 +1785,8 @@ public class Tuner implements AutoCloseable {
    static public SharedFilter openSharedFilter(@NonNull Context context,
            @NonNull String sharedFilterToken, @CallbackExecutor @NonNull Executor executor,
            @NonNull SharedFilterCallback cb) {
        // TODO: check what happenes when onReclaimResources() is called and see if
        // this needs to be protected with TRMS lock
        Objects.requireNonNull(sharedFilterToken, "sharedFilterToken must not be null");
        Objects.requireNonNull(executor, "executor must not be null");
        Objects.requireNonNull(cb, "SharedFilterCallback must not be null");
@@ -1665,22 +1850,28 @@ public class Tuner implements AutoCloseable {
        return granted;
    }

    private boolean checkResource(int resourceType)  {
    private boolean checkResource(int resourceType, ReentrantLock localLock)  {
        switch (resourceType) {
            case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
                if (mFrontendHandle == null && !requestFrontend()) {
                if (mFrontendHandle == null && !requestResource(resourceType, localLock)) {
                    return false;
                }
                break;
            }
            case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
                if (mLnb == null && !requestLnb()) {
                if (mLnb == null && !requestResource(resourceType, localLock)) {
                    return false;
                }
                break;
            }
            case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
                if (mDemuxHandle == null && !requestDemux()) {
                if (mDemuxHandle == null && !requestResource(resourceType, localLock)) {
                    return false;
                }
                break;
            }
            case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: {
                if (mFrontendCiCamHandle == null && !requestResource(resourceType, localLock)) {
                    return false;
                }
                break;
@@ -1691,24 +1882,91 @@ public class Tuner implements AutoCloseable {
        return true;
    }

    private boolean checkCiCamResource(int ciCamId) {
        if (mFrontendCiCamHandle == null && !requestFrontendCiCam(ciCamId)) {
    // Expected flow of how to use this function is:
    // 1) lock the localLock and check if the resource is already held
    // 2) if yes, no need to call this function and continue with the handle with the lock held
    // 3) if no, then first release the held lock and grab the TRMS lock to avoid deadlock
    // 4) grab the local lock again and release the TRMS lock
    // If localLock is null, we'll assume the caller does not want the lock related operations
    private boolean requestResource(int resourceType, ReentrantLock localLock)  {
        boolean enableLockOperations = localLock != null;

        // release the local lock first to avoid deadlock
        if (enableLockOperations) {
            if (localLock.isLocked()) {
                localLock.unlock();
            } else {
                throw new IllegalStateException("local lock must be locked beforehand");
            }
        }

        // now safe to grab TRMS lock
        if (enableLockOperations) {
            acquireTRMSLock("requestResource:" + resourceType);
        }

        try {
            // lock the local lock
            if (enableLockOperations) {
                localLock.lock();
            }
            switch (resourceType) {
                case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
                    return requestFrontend();
                }
                case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
                    return requestLnb();
                }
                case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
                    return requestDemux();
                }
                case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: {
                    return requestFrontendCiCam(mRequestedCiCamId);
                }
                default:
                    return false;
            }
        return true;
        } finally {
            if (enableLockOperations) {
                releaseTRMSLock();
            }
        }
    }

    /* package */ void releaseLnb() {
        acquireTRMSLock("releaseLnb()");
        mLnbLock.lock();
        try {
            if (mLnbHandle != null) {
                // LNB handle can be null if it's opened by name.
                mTunerResourceManager.releaseLnb(mLnbHandle, mClientId);
                mLnbHandle = null;
            }
            mLnb = null;
        } finally {
            releaseTRMSLock();
            mLnbLock.unlock();
        }
    }

    /** @hide */
    public int getClientId() {
        return mClientId;
    }

    private void acquireTRMSLock(String functionNameForLog) {
        if (DEBUG) {
            Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
                    + "for clientId:" + mClientId);
        }
        if (!mTunerResourceManager.acquireLock(mClientId)) {
            Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog
                    + " for clientId:" + mClientId + " - this can cause deadlock between"
                    + " Tuner API calls and onReclaimResources()");
        }
    }

    private void releaseTRMSLock() {
        mTunerResourceManager.releaseLock(mClientId);
    }
}
+42 −0

File changed.

Preview size limit exceeded, changes collapsed.

+30 −0

File changed.

Preview size limit exceeded, changes collapsed.

+222 −3

File changed.

Preview size limit exceeded, changes collapsed.