1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.usbtuner; 18 19 import android.content.BroadcastReceiver; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.hardware.usb.UsbDevice; 25 import android.hardware.usb.UsbManager; 26 import android.media.tv.TvInputInfo; 27 import android.media.tv.TvInputManager; 28 import android.media.tv.TvInputService; 29 import android.os.Build; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.support.v4.os.BuildCompat; 34 import android.util.Log; 35 36 import com.android.tv.Features; 37 import com.android.tv.TvApplication; 38 import com.android.usbtuner.setup.TunerSetupActivity; 39 import com.android.usbtuner.tvinput.UsbTunerTvInputService; 40 41 import java.util.Map; 42 43 /** 44 * Controls the package visibility of {@link UsbTunerTvInputService}. 45 * <p> 46 * Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED}, 47 * {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED} 48 * to update the connection status of the supported USB TV tuners. 49 */ 50 public class UsbInputController extends BroadcastReceiver { 51 private static final boolean DEBUG = true; 52 private static final String TAG = "UsbInputController"; 53 54 private static final TunerDevice[] TUNER_DEVICES = { 55 new TunerDevice(0x2040, 0xb123), // WinTV-HVR-955Q 56 new TunerDevice(0x07ca, 0x0837) // AverTV Volar Hybrid Q 57 }; 58 59 private static final boolean IS_MNC_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; 60 61 private static final int MSG_ENABLE_INPUT_SERVICE = 1000; 62 private static final long DVB_DRIVER_CHECK_DELAY_MS = 300; 63 64 private DvbDeviceAccessor mDvbDeviceAccessor; 65 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 66 @Override 67 public void handleMessage(Message msg) { 68 switch (msg.what) { 69 case MSG_ENABLE_INPUT_SERVICE: 70 Context context = (Context) msg.obj; 71 if (mDvbDeviceAccessor == null) { 72 mDvbDeviceAccessor = new DvbDeviceAccessor(context); 73 } 74 enableUsbTunerTvInputService(context, 75 mDvbDeviceAccessor.isDvbDeviceAvailable()); 76 break; 77 } 78 } 79 }; 80 81 /** 82 * Simple data holder for a USB device. Used to represent a tuner model, and compare 83 * against {@link UsbDevice}. 84 */ 85 private static class TunerDevice { 86 private final int vendorId; 87 private final int productId; 88 TunerDevice(int vendorId, int productId)89 private TunerDevice(int vendorId, int productId) { 90 this.vendorId = vendorId; 91 this.productId = productId; 92 } 93 equals(UsbDevice device)94 private boolean equals(UsbDevice device) { 95 return device.getVendorId() == vendorId && device.getProductId() == productId; 96 } 97 } 98 99 @Override onReceive(Context context, Intent intent)100 public void onReceive(Context context, Intent intent) { 101 if (DEBUG) Log.d(TAG, "Broadcast intent received:" + intent); 102 103 if (!Features.USB_TUNER.isEnabled(context)) { 104 enableUsbTunerTvInputService(context, false); 105 return; 106 } 107 108 switch (intent.getAction()) { 109 case Intent.ACTION_BOOT_COMPLETED: 110 case UsbManager.ACTION_USB_DEVICE_ATTACHED: 111 case UsbManager.ACTION_USB_DEVICE_DETACHED: 112 // Tuner is supported on MNC and later version only. 113 boolean enabled = IS_MNC_OR_LATER && isTunerConnected(context); 114 mHandler.removeMessages(MSG_ENABLE_INPUT_SERVICE); 115 if (enabled) { 116 // Need to check if DVB driver is accessible. Since the driver creation 117 // could be happen after the USB event, delay the checking by 118 // DVB_DRIVER_CHECK_DELAY_MS. 119 mHandler.sendMessageDelayed( 120 mHandler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context), 121 DVB_DRIVER_CHECK_DELAY_MS); 122 } else { 123 enableUsbTunerTvInputService(context, false); 124 } 125 break; 126 } 127 } 128 129 /** 130 * See if any USB tuner hardware is attached in the system. 131 * 132 * @param context {@link Context} instance 133 * @return {@code true} if any tuner device we support is plugged in 134 */ isTunerConnected(Context context)135 private boolean isTunerConnected(Context context) { 136 UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); 137 Map<String, UsbDevice> deviceList = manager.getDeviceList(); 138 for (UsbDevice device : deviceList.values()) { 139 if (DEBUG) { 140 Log.d(TAG, "Device: " + device); 141 } 142 for (TunerDevice tuner : TUNER_DEVICES) { 143 if (tuner.equals(device)) { 144 Log.i(TAG, "Tuner found"); 145 return true; 146 } 147 } 148 } 149 return false; 150 } 151 152 /** 153 * Enable/disable the component {@link UsbTunerTvInputService}. 154 * 155 * @param context {@link Context} instance 156 * @param enabled {@code true} to enable the service; otherwise {@code false} 157 */ enableUsbTunerTvInputService(Context context, boolean enabled)158 private void enableUsbTunerTvInputService(Context context, boolean enabled) { 159 PackageManager pm = context.getPackageManager(); 160 ComponentName USBTUNER = new ComponentName(context, UsbTunerTvInputService.class); 161 162 // Don't kill app by enabling/disabling TvActivity. If LC is killed by enabling/disabling 163 // TvActivity, the following pm.setComponentEnabledSetting doesn't work. 164 ((TvApplication) context.getApplicationContext()).handleInputCountChanged( 165 true, enabled, true); 166 // Since PackageManager.DONT_KILL_APP delays the operation by 10 seconds 167 // (PackageManagerService.BROADCAST_DELAY), we'd better avoid using it. It is used only 168 // when the LiveChannels app is active since we don't want to kill the running app. 169 int flags = TvApplication.getSingletons(context).getMainActivityWrapper().isCreated() 170 ? PackageManager.DONT_KILL_APP : 0; 171 int newState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 172 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 173 if (newState != pm.getComponentEnabledSetting(USBTUNER)) { 174 // Send/cancel the USB tuner TV input setup recommendation card. 175 TunerSetupActivity.onTvInputEnabled(context, enabled); 176 // Enable/disable the USB tuner TV input. 177 pm.setComponentEnabledSetting(USBTUNER, newState, flags); 178 if (DEBUG) Log.d(TAG, "Status updated:" + enabled); 179 } 180 if (enabled && BuildCompat.isAtLeastN()) { 181 TvInputInfo info = mDvbDeviceAccessor.buildTvInputInfo(context); 182 if (info != null) { 183 Log.i(TAG, "TvInputInfo updated: " + info.toString()); 184 ((TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE)) 185 .updateTvInputInfo(info); 186 } 187 } 188 } 189 } 190