165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko/* 265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Copyright (C) 2016 The Android Open Source Project 365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * you may not use this file except in compliance with the License. 665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * You may obtain a copy of the License at 765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 1065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Unless required by applicable law or agreed to in writing, software 1165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 1265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * See the License for the specific language governing permissions and 1465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * limitations under the License. 1565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 1665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.source; 1865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.content.Context; 2065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 2165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.common.AutoCloseableUtils; 2265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.common.SoftPreconditions; 2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.TunerHal; 2465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.TunerChannel; 2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.tvinput.EventDetector; 2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.HashMap; 2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.HashSet; 2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Iterator; 3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Map; 3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Set; 3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 3365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko/** 3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Manages {@link TunerTsStreamer} for playback and recording. 3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * The class hides handling of {@link TunerHal} from other classes. 36d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * This class is used by {@link TsDataSourceManager}. Don't use this class directly. 3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoclass TunerTsStreamerManager { 3965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The lock will protect mStreamerFinder, mSourceToStreamerMap and some part of TsStreamCreator 4065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // to support timely {@link TunerTsStreamer} cancellation due to a new tune request from 4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // the same session. 4265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Object mCancelLock = new Object(); 4365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final StreamerFinder mStreamerFinder = new StreamerFinder(); 4465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<Integer, TsStreamerCreator> mCreators = new HashMap<>(); 456ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private final Map<Integer, EventDetector.EventListener> mListeners = new HashMap<>(); 46d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private final Map<TsDataSource, TunerTsStreamer> mSourceToStreamerMap = new HashMap<>(); 4765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final TunerHalManager mTunerHalManager = new TunerHalManager(); 4865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static TunerTsStreamerManager sInstance; 4965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 5065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 5165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Returns the singleton instance for the class 5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * @return TunerTsStreamerManager 5365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 5465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko static synchronized TunerTsStreamerManager getInstance() { 5565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (sInstance == null) { 5665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko sInstance = new TunerTsStreamerManager(); 5765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return sInstance; 5965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 6065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 6165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TunerTsStreamerManager() { } 6265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 63d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko synchronized TsDataSource createDataSource( 6465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Context context, TunerChannel channel, EventDetector.EventListener listener, 6565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko int sessionId, boolean reuse) { 6665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TsStreamerCreator creator; 6765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 6865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mStreamerFinder.containsLocked(channel)) { 6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStreamerFinder.appendSessionLocked(channel, sessionId); 7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerTsStreamer streamer = mStreamerFinder.getStreamerLocked(channel); 71d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TsDataSource source = streamer.createDataSource(); 726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mListeners.put(sessionId, listener); 736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko streamer.registerListener(listener); 7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSourceToStreamerMap.put(source, streamer); 7565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return source; 7665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 7765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko creator = new TsStreamerCreator(context, channel, listener); 7865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCreators.put(sessionId, creator); 7965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 8065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerTsStreamer streamer = creator.create(sessionId, reuse); 8165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 8265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCreators.remove(sessionId); 8365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (streamer == null) { 8465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return null; 8565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!creator.isCancelledLocked()) { 8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStreamerFinder.putLocked(channel, sessionId, streamer); 88d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TsDataSource source = streamer.createDataSource(); 896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mListeners.put(sessionId, listener); 9065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSourceToStreamerMap.put(source, streamer); 9165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return source; 9265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 9365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Created streamer was cancelled by a new tune request. 9565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko streamer.stopStream(); 9665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal hal = streamer.getTunerHal(); 9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko hal.setHasPendingTune(false); 9865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHalManager.releaseTunerHal(hal, sessionId, reuse); 9965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return null; 10065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 10165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 102d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko synchronized void releaseDataSource(TsDataSource source, int sessionId, 10365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko boolean reuse) { 10465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerTsStreamer streamer; 10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 10665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko streamer = mSourceToStreamerMap.get(source); 10765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSourceToStreamerMap.remove(source); 10865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (streamer == null) { 10965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 11065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 1116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko EventDetector.EventListener listener = mListeners.remove(sessionId); 1126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko streamer.unregisterListener(listener); 11365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerChannel channel = streamer.getChannel(); 11465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko SoftPreconditions.checkState(channel != null); 11565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStreamerFinder.removeSessionLocked(channel, sessionId); 11665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mStreamerFinder.containsLocked(channel)) { 11765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 11865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 11965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 12065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko streamer.stopStream(); 12165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal hal = streamer.getTunerHal(); 12265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko hal.setHasPendingTune(false); 12365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHalManager.releaseTunerHal(hal, sessionId, reuse); 12465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 12565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 12665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void setHasPendingTune(int sessionId) { 12765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 12865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mCreators.containsKey(sessionId)) { 12965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCreators.get(sessionId).cancelLocked(); 13065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 1346ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko /** 1356ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko * Add tuner hal into TunerHalManager for test. 1366ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko */ 1376ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko void addTunerHal(TunerHal tunerHal, int sessionId) { 1386ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mTunerHalManager.addTunerHal(tunerHal, sessionId); 1396ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 1406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 14165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized void release(int sessionId) { 14265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHalManager.releaseCachedHal(sessionId); 14365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 14465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 14565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private class StreamerFinder { 14665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<TunerChannel, Set<Integer>> mSessions = new HashMap<>(); 14765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<TunerChannel, TunerTsStreamer> mStreamers = new HashMap<>(); 14865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 14965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 15065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void putLocked(TunerChannel channel, int sessionId, TunerTsStreamer streamer) { 15165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Set<Integer> sessions = new HashSet<>(); 15265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko sessions.add(sessionId); 15365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessions.put(channel, sessions); 15465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStreamers.put(channel, streamer); 15565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 15665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 15765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 15865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void appendSessionLocked(TunerChannel channel, int sessionId) { 15965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mSessions.containsKey(channel)) { 16065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessions.get(channel).add(sessionId); 16165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 16265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 16365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 16465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 16565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void removeSessionLocked(TunerChannel channel, int sessionId) { 16665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Set<Integer> sessions = mSessions.get(channel); 16765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko sessions.remove(sessionId); 16865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (sessions.size() == 0) { 16965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessions.remove(channel); 17065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStreamers.remove(channel); 17165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 17465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 17565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean containsLocked(TunerChannel channel) { 17665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mSessions.containsKey(channel); 17765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 17865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 17965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TunerTsStreamer getStreamerLocked(TunerChannel channel) { 18165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mStreamers.containsKey(channel) ? mStreamers.get(channel) : null; 18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 18465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 18565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 18665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * {@link TunerTsStreamer} creation can be cancelled by a new tune request for the same 18765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * session. The class supports the cancellation in creating new {@link TunerTsStreamer}. 18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 18965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private class TsStreamerCreator { 19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Context mContext; 19165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final TunerChannel mChannel; 19265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final EventDetector.EventListener mEventListener; 19365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // mCancelled will be {@code true} if a new tune request for the same session 19465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // cancels create(). 19565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean mCancelled; 19665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TunerHal mTunerHal; 19765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 19865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TsStreamerCreator(Context context, TunerChannel channel, 19965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko EventDetector.EventListener listener) { 20065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mContext = context; 20165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mChannel = channel; 20265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mEventListener = listener; 20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TunerTsStreamer create(int sessionId, boolean reuse) { 20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal hal = mTunerHalManager.getOrCreateTunerHal(mContext, sessionId); 20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (hal == null) { 20865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return null; 20965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko boolean canceled = false; 21165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 21265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!mCancelled) { 21365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHal = hal; 21465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 21565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko canceled = true; 21665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!canceled) { 219d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko TunerTsStreamer tsStreamer = new TunerTsStreamer(hal, mEventListener, mContext); 22065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (tsStreamer.startStream(mChannel)) { 22165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return tsStreamer; 22265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mCancelLock) { 22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHal = null; 22565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 22665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 22765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko hal.setHasPendingTune(false); 22865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Since TunerTsStreamer is not properly created, closes TunerHal. 22965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // And do not re-use TunerHal when it is not cancelled. 23065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHalManager.releaseTunerHal(hal, sessionId, mCancelled && reuse); 23165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return null; 23265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void cancelLocked() { 23665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mCancelled) { 23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCancelled = true; 24065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mTunerHal != null) { 24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHal.setHasPendingTune(true); 24265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // @GuardedBy("mCancelLock") 24665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean isCancelledLocked() { 24765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mCancelled; 24865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 25265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Supports sharing {@link TunerHal} among multiple sessions. 25365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * The class also supports session affinity for {@link TunerHal} allocation. 25465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 25565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private class TunerHalManager { 25665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<Integer, TunerHal> mTunerHals = new HashMap<>(); 25765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 25865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TunerHal getOrCreateTunerHal(Context context, int sessionId) { 25965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Handles session affinity. 26065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal hal = mTunerHals.get(sessionId); 26165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (hal != null) { 26265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHals.remove(sessionId); 26365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return hal; 26465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 26565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Finds a TunerHal which is cached for other sessions. 26665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Iterator it = mTunerHals.keySet().iterator(); 26765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (it.hasNext()) { 26865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Integer key = (Integer) it.next(); 26965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko hal = mTunerHals.get(key); 27065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHals.remove(key); 27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return hal; 27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return TunerHal.createInstance(context); 27465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 27665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void releaseTunerHal(TunerHal hal, int sessionId, boolean reuse) { 2776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (!reuse || !hal.isReusable()) { 27865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko AutoCloseableUtils.closeQuietly(hal); 27965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 28065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal cachedHal = mTunerHals.get(sessionId); 28265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (cachedHal != hal) { 28365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHals.put(sessionId, hal); 2846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (cachedHal != null) { 2856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko AutoCloseableUtils.closeQuietly(cachedHal); 2866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 28765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 29065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void releaseCachedHal(int sessionId) { 29165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TunerHal hal = mTunerHals.get(sessionId); 29265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (hal != null) { 29365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTunerHals.remove(sessionId); 29465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 29565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (hal != null) { 29665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko AutoCloseableUtils.closeQuietly(hal); 29765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 29865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 2996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko 3006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko private void addTunerHal(TunerHal tunerHal, int sessionId) { 3016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mTunerHals.put(sessionId, tunerHal); 3026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 30365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 30465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko}