17d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko/*
27d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Copyright (C) 2015 The Android Open Source Project
37d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
47d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
57d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * you may not use this file except in compliance with the License.
67d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * You may obtain a copy of the License at
77d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
87d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
97d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
107d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Unless required by applicable law or agreed to in writing, software
117d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
127d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * See the License for the specific language governing permissions and
147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * limitations under the License.
157d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko */
167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
177d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkopackage com.android.tv;
187d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
197d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.app.Activity;
207d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.ActivityNotFoundException;
216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.content.Context;
227d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.content.Intent;
237d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.media.tv.TvInputInfo;
247d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.os.Bundle;
256ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.os.Handler;
266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.os.Looper;
276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport android.support.annotation.MainThread;
287d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.util.Log;
297d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.common.SoftPreconditions;
317d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.common.TvCommonConstants;
326ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.data.ChannelDataManager;
336ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.data.ChannelDataManager.Listener;
3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.data.epg.EpgFetcher;
3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.experiments.Experiments;
367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.SetupUtils;
377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.util.TvInputManagerHelper;
3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.util.Utils;
397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport java.util.concurrent.TimeUnit;
416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko/**
437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * An activity to launch a TV input setup activity.
447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko *
457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * <p> After setup activity is finished, all channels will be browsable.
467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko */
477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkopublic class SetupPassthroughActivity extends Activity {
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "SetupPassthroughAct";
497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private static final boolean DEBUG = false;
507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private static final int REQUEST_START_SETUP_ACTIVITY = 200;
527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static ScanTimeoutMonitor sScanTimeoutMonitor;
546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
557d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private TvInputInfo mTvInputInfo;
567d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private Intent mActivityAfterCompletion;
576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private boolean mEpgFetcherDuringScan;
587d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
597d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    @Override
607d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public void onCreate(Bundle savedInstanceState) {
616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (DEBUG) Log.d(TAG, "onCreate");
627d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        super.onCreate(savedInstanceState);
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(this);
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        TvInputManagerHelper inputManager = appSingletons.getTvInputManagerHelper();
656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        Intent intent = getIntent();
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String inputId = intent.getStringExtra(TvCommonConstants.EXTRA_INPUT_ID);
677d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mTvInputInfo = inputManager.getTvInputInfo(inputId);
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mActivityAfterCompletion = intent.getParcelableExtra(
697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                TvCommonConstants.EXTRA_ACTIVITY_AFTER_COMPLETION);
706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        boolean needToFetchEpg = Utils.isInternalTvInput(this, mTvInputInfo.getId())
716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                && Experiments.CLOUD_EPG.get();
726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (needToFetchEpg) {
736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // In case when the activity is restored, this flag should be restored as well.
746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mEpgFetcherDuringScan = true;
7565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        }
766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (savedInstanceState == null) {
776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            SoftPreconditions.checkState(
786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    intent.getAction().equals(TvCommonConstants.INTENT_ACTION_INPUT_SETUP));
796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (DEBUG) Log.d(TAG, "TvInputId " + inputId + " / TvInputInfo " + mTvInputInfo);
806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (mTvInputInfo == null) {
816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.w(TAG, "There is no input with the ID " + inputId + ".");
826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                finish();
836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                return;
846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            Intent setupIntent =
866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    intent.getExtras().getParcelable(TvCommonConstants.EXTRA_SETUP_INTENT);
876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (DEBUG) Log.d(TAG, "Setup activity launch intent: " + setupIntent);
886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (setupIntent == null) {
896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.w(TAG, "The input (" + mTvInputInfo.getId() + ") doesn't have setup.");
906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                finish();
916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                return;
926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            SetupUtils.grantEpgPermission(this, mTvInputInfo.getServiceInfo().packageName);
946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (DEBUG) Log.d(TAG, "Activity after completion " + mActivityAfterCompletion);
956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // If EXTRA_SETUP_INTENT is not removed, an infinite recursion happens during
966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            // setupIntent.putExtras(intent.getExtras()).
976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            Bundle extras = intent.getExtras();
986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            extras.remove(TvCommonConstants.EXTRA_SETUP_INTENT);
996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            setupIntent.putExtras(extras);
1006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            try {
1016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                startActivityForResult(setupIntent, REQUEST_START_SETUP_ACTIVITY);
1026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            } catch (ActivityNotFoundException e) {
1036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.e(TAG, "Can't find activity: " + setupIntent.getComponent());
1046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                finish();
1056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                return;
1066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (needToFetchEpg) {
1086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                if (sScanTimeoutMonitor == null) {
1096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                    sScanTimeoutMonitor = new ScanTimeoutMonitor(this);
1106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                }
1116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                sScanTimeoutMonitor.startMonitoring();
1126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                EpgFetcher.getInstance(this).onChannelScanStarted();
1136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
114721bd0da688cd552737fbb753a00597f95103b95Adrian Roos        }
115721bd0da688cd552737fbb753a00597f95103b95Adrian Roos    }
116721bd0da688cd552737fbb753a00597f95103b95Adrian Roos
117721bd0da688cd552737fbb753a00597f95103b95Adrian Roos    @Override
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void onActivityResult(int requestCode, final int resultCode, final Intent data) {
1196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (DEBUG) Log.d(TAG, "onActivityResult");
1206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (sScanTimeoutMonitor != null) {
1216ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            sScanTimeoutMonitor.stopMonitoring();
1226ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
1236ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        // Note: It's not guaranteed that this method is always called after scanning.
124ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        boolean setupComplete = requestCode == REQUEST_START_SETUP_ACTIVITY
125ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                && resultCode == Activity.RESULT_OK;
1266ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        // Tells EpgFetcher that channel source setup is finished.
1276ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        if (mEpgFetcherDuringScan) {
1286ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            EpgFetcher.getInstance(this).onChannelScanFinished();
1296ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
130ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        if (!setupComplete) {
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            setResult(resultCode, data);
1327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            finish();
1337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            return;
1347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        }
1357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        SetupUtils.getInstance(this).onTvInputSetupFinished(mTvInputInfo.getId(), new Runnable() {
1367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            @Override
1377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            public void run() {
1387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                if (mActivityAfterCompletion != null) {
1397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    try {
1407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        startActivity(mActivityAfterCompletion);
1417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    } catch (ActivityNotFoundException e) {
1427d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        Log.w(TAG, "Activity launch failed", e);
1437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
1447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                setResult(resultCode, data);
1467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                finish();
1477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        });
1497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
1506ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1516ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    /**
1526ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * Monitors the scan progress and notifies the timeout of the scanning.
1536ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * The purpose of this monitor is to call EpgFetcher.onChannelScanFinished() in case when
1546ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     * SetupPassthroughActivity.onActivityResult() is not called properly. b/36008534
1556ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko     */
1566ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    @MainThread
1576ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    private static class ScanTimeoutMonitor {
1586ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        // Set timeout long enough. The message in Sony TV says the scanning takes about 30 minutes.
1596ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private static final long SCAN_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(30);
1606ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private final Context mContext;
1626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private final ChannelDataManager mChannelDataManager;
1636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private final Handler mHandler = new Handler(Looper.getMainLooper());
1646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private final Runnable mScanTimeoutRunnable = new Runnable() {
1656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
1666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            public void run() {
1676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                Log.w(TAG, "No channels has been added for a while." +
1686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                        " The scan might have finished unexpectedly.");
1696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                onScanTimedOut();
1706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1716ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        };
1726ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private final Listener mChannelDataManagerListener = new Listener() {
1736ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
1746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            public void onLoadFinished() {
1756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                setupTimer();
1766ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1776ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1786ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
1796ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            public void onChannelListUpdated() {
1806ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                setupTimer();
1816ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1826ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1836ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            @Override
1846ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            public void onChannelBrowsableChanged() { }
1856ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        };
1866ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private boolean mStarted;
1876ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1886ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private ScanTimeoutMonitor(Context context) {
1896ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mContext = context.getApplicationContext();
1906ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mChannelDataManager = TvApplication.getSingletons(context).getChannelDataManager();
1916ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
1926ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
1936ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void startMonitoring() {
1946ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (!mStarted) {
1956ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mStarted = true;
1966ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mChannelDataManager.addListener(mChannelDataManagerListener);
1976ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
1986ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (mChannelDataManager.isDbLoadFinished()) {
1996ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                setupTimer();
2006ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
2016ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
2026ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2036ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void stopMonitoring() {
2046ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            if (mStarted) {
2056ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mStarted = false;
2066ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mHandler.removeCallbacks(mScanTimeoutRunnable);
2076ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko                mChannelDataManager.removeListener(mChannelDataManagerListener);
2086ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            }
2096ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
2106ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2116ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void setupTimer() {
2126ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mHandler.removeCallbacks(mScanTimeoutRunnable);
2136ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            mHandler.postDelayed(mScanTimeoutRunnable, SCAN_TIMEOUT_MS);
2146ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
2156ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko
2166ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        private void onScanTimedOut() {
2176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            stopMonitoring();
2186ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko            EpgFetcher.getInstance(mContext).onChannelScanFinished();
2196ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko        }
2206ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko    }
221721bd0da688cd552737fbb753a00597f95103b95Adrian Roos}
222