11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
171abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopackage com.android.usbtuner;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.BroadcastReceiver;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.ComponentName;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Intent;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.pm.PackageManager;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.hardware.usb.UsbDevice;
251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.hardware.usb.UsbManager;
2648dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.tv.TvInputInfo;
2748dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.tv.TvInputManager;
2848dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.media.tv.TvInputService;
291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Build;
301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Handler;
311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Looper;
321abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Message;
3348dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport android.support.v4.os.BuildCompat;
341abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
361abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.Features;
371abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.TvApplication;
38ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.usbtuner.setup.TunerSetupActivity;
391abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.usbtuner.tvinput.UsbTunerTvInputService;
401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
411abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Map;
421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Controls the package visibility of {@link UsbTunerTvInputService}.
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED},
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * to update the connection status of the supported USB TV tuners.
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
501abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class UsbInputController extends BroadcastReceiver {
5148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private static final boolean DEBUG = true;
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "UsbInputController";
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final TunerDevice[] TUNER_DEVICES = {
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        new TunerDevice(0x2040, 0xb123),  // WinTV-HVR-955Q
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        new TunerDevice(0x07ca, 0x0837)   // AverTV Volar Hybrid Q
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean IS_MNC_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int MSG_ENABLE_INPUT_SERVICE = 1000;
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final long DVB_DRIVER_CHECK_DELAY_MS = 300;
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private DvbDeviceAccessor mDvbDeviceAccessor;
6548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        @Override
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        public void handleMessage(Message msg) {
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            switch (msg.what) {
691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case MSG_ENABLE_INPUT_SERVICE:
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Context context = (Context) msg.obj;
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    if (mDvbDeviceAccessor == null) {
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        mDvbDeviceAccessor = new DvbDeviceAccessor(context);
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    enableUsbTunerTvInputService(context,
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            mDvbDeviceAccessor.isDvbDeviceAvailable());
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Simple data holder for a USB device. Used to represent a tuner model, and compare
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * against {@link UsbDevice}.
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static class TunerDevice {
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private final int vendorId;
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private final int productId;
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private TunerDevice(int vendorId, int productId) {
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.vendorId = vendorId;
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            this.productId = productId;
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        private boolean equals(UsbDevice device) {
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return device.getVendorId() == vendorId && device.getProductId() == productId;
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    @Override
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void onReceive(Context context, Intent intent) {
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) Log.d(TAG, "Broadcast intent received:" + intent);
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!Features.USB_TUNER.isEnabled(context)) {
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            enableUsbTunerTvInputService(context, false);
1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (intent.getAction()) {
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case Intent.ACTION_BOOT_COMPLETED:
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case UsbManager.ACTION_USB_DEVICE_ATTACHED:
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case UsbManager.ACTION_USB_DEVICE_DETACHED:
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // Tuner is supported on MNC and later version only.
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                boolean enabled = IS_MNC_OR_LATER && isTunerConnected(context);
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mHandler.removeMessages(MSG_ENABLE_INPUT_SERVICE);
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (enabled) {
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    // Need to check if DVB driver is accessible. Since the driver creation
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    // could be happen after the USB event, delay the checking by
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    // DVB_DRIVER_CHECK_DELAY_MS.
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    mHandler.sendMessageDelayed(
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            mHandler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context),
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            DVB_DRIVER_CHECK_DELAY_MS);
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                } else {
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    enableUsbTunerTvInputService(context, false);
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * See if any USB tuner hardware is attached in the system.
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * @param context {@link Context} instance
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * @return {@code true} if any tuner device we support is plugged in
1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean isTunerConnected(Context context) {
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Map<String, UsbDevice> deviceList = manager.getDeviceList();
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (UsbDevice device : deviceList.values()) {
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "Device: " + device);
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (TunerDevice tuner : TUNER_DEVICES) {
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (tuner.equals(device)) {
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.i(TAG, "Tuner found");
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return true;
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return false;
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /**
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * Enable/disable the component {@link UsbTunerTvInputService}.
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     *
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * @param context {@link Context} instance
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * @param enabled {@code true} to enable the service; otherwise {@code false}
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void enableUsbTunerTvInputService(Context context, boolean enabled) {
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        PackageManager pm  = context.getPackageManager();
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ComponentName USBTUNER = new ComponentName(context, UsbTunerTvInputService.class);
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
16248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // Don't kill app by enabling/disabling TvActivity. If LC is killed by enabling/disabling
16348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        // TvActivity, the following pm.setComponentEnabledSetting doesn't work.
16448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        ((TvApplication) context.getApplicationContext()).handleInputCountChanged(
16548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                true, enabled, true);
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Since PackageManager.DONT_KILL_APP delays the operation by 10 seconds
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // (PackageManagerService.BROADCAST_DELAY), we'd better avoid using it. It is used only
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // when the LiveChannels app is active since we don't want to kill the running app.
169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        int flags = TvApplication.getSingletons(context).getMainActivityWrapper().isCreated()
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ? PackageManager.DONT_KILL_APP : 0;
1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int newState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (newState != pm.getComponentEnabledSetting(USBTUNER)) {
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Send/cancel the USB tuner TV input setup recommendation card.
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TunerSetupActivity.onTvInputEnabled(context, enabled);
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Enable/disable the USB tuner TV input.
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pm.setComponentEnabledSetting(USBTUNER, newState, flags);
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) Log.d(TAG, "Status updated:" + enabled);
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
18048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        if (enabled && BuildCompat.isAtLeastN()) {
18148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            TvInputInfo info = mDvbDeviceAccessor.buildTvInputInfo(context);
18248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            if (info != null) {
18348dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                Log.i(TAG, "TvInputInfo updated: " + info.toString());
18448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                ((TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE))
18548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho                        .updateTvInputInfo(info);
18648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho            }
18748dadb49248271b01997862e1335912a4f2e189fYoungsang Cho        }
1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
190