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.tv.tuner; 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.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.support.v4.os.BuildCompat; 32 import android.util.Log; 33 import android.widget.Toast; 34 35 import com.android.tv.Features; 36 import com.android.tv.TvApplication; 37 import com.android.tv.tuner.R; 38 import com.android.tv.tuner.setup.TunerSetupActivity; 39 import com.android.tv.tuner.tvinput.TunerTvInputService; 40 import com.android.tv.tuner.util.TunerInputInfoUtils; 41 42 import java.util.Map; 43 44 /** 45 * Controls the package visibility of {@link TunerTvInputService}. 46 * <p> 47 * Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED}, 48 * {@code UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED} 49 * to update the connection status of the supported USB TV tuners. 50 */ 51 public class TunerInputController extends BroadcastReceiver { 52 private static final boolean DEBUG = true; 53 private static final String TAG = "TunerInputController"; 54 55 private static final TunerDevice[] TUNER_DEVICES = { 56 new TunerDevice(0x2040, 0xb123), // WinTV-HVR-955Q 57 new TunerDevice(0x07ca, 0x0837) // AverTV Volar Hybrid Q 58 }; 59 60 private static final int MSG_ENABLE_INPUT_SERVICE = 1000; 61 private static final long DVB_DRIVER_CHECK_DELAY_MS = 300; 62 63 private DvbDeviceAccessor mDvbDeviceAccessor; 64 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 65 @Override 66 public void handleMessage(Message msg) { 67 switch (msg.what) { 68 case MSG_ENABLE_INPUT_SERVICE: 69 Context context = (Context) msg.obj; 70 if (mDvbDeviceAccessor == null) { 71 mDvbDeviceAccessor = new DvbDeviceAccessor(context); 72 } 73 enableTunerTvInputService(context, mDvbDeviceAccessor.isDvbDeviceAvailable()); 74 break; 75 } 76 } 77 }; 78 79 /** 80 * Simple data holder for a USB device. Used to represent a tuner model, and compare 81 * against {@link UsbDevice}. 82 */ 83 private static class TunerDevice { 84 private final int vendorId; 85 private final int productId; 86 TunerDevice(int vendorId, int productId)87 private TunerDevice(int vendorId, int productId) { 88 this.vendorId = vendorId; 89 this.productId = productId; 90 } 91 equals(UsbDevice device)92 private boolean equals(UsbDevice device) { 93 return device.getVendorId() == vendorId && device.getProductId() == productId; 94 } 95 } 96 97 @Override onReceive(Context context, Intent intent)98 public void onReceive(Context context, Intent intent) { 99 if (DEBUG) Log.d(TAG, "Broadcast intent received:" + intent); 100 TvApplication.setCurrentRunningProcess(context, true); 101 if (!Features.TUNER.isEnabled(context)) { 102 enableTunerTvInputService(context, false); 103 return; 104 } 105 106 switch (intent.getAction()) { 107 case Intent.ACTION_BOOT_COMPLETED: 108 case TvApplication.ACTION_APPLICATION_FIRST_LAUNCHED: 109 case UsbManager.ACTION_USB_DEVICE_ATTACHED: 110 case UsbManager.ACTION_USB_DEVICE_DETACHED: 111 if (TunerInputInfoUtils.isBuiltInTuner(context)) { 112 enableTunerTvInputService(context, true); 113 break; 114 } 115 // Falls back to the below to check USB tuner devices. 116 boolean enabled = isUsbTunerConnected(context); 117 mHandler.removeMessages(MSG_ENABLE_INPUT_SERVICE); 118 if (enabled) { 119 // Need to check if DVB driver is accessible. Since the driver creation 120 // could be happen after the USB event, delay the checking by 121 // DVB_DRIVER_CHECK_DELAY_MS. 122 mHandler.sendMessageDelayed( 123 mHandler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context), 124 DVB_DRIVER_CHECK_DELAY_MS); 125 } else { 126 enableTunerTvInputService(context, false); 127 } 128 break; 129 } 130 } 131 132 /** 133 * See if any USB tuner hardware is attached in the system. 134 * 135 * @param context {@link Context} instance 136 * @return {@code true} if any tuner device we support is plugged in 137 */ isUsbTunerConnected(Context context)138 private boolean isUsbTunerConnected(Context context) { 139 UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); 140 Map<String, UsbDevice> deviceList = manager.getDeviceList(); 141 for (UsbDevice device : deviceList.values()) { 142 if (DEBUG) { 143 Log.d(TAG, "Device: " + device); 144 } 145 for (TunerDevice tuner : TUNER_DEVICES) { 146 if (tuner.equals(device)) { 147 Log.i(TAG, "Tuner found"); 148 return true; 149 } 150 } 151 } 152 return false; 153 } 154 155 /** 156 * Enable/disable the component {@link TunerTvInputService}. 157 * 158 * @param context {@link Context} instance 159 * @param enabled {@code true} to enable the service; otherwise {@code false} 160 */ enableTunerTvInputService(Context context, boolean enabled)161 private void enableTunerTvInputService(Context context, boolean enabled) { 162 if (DEBUG) Log.d(TAG, "enableTunerTvInputService: " + enabled); 163 PackageManager pm = context.getPackageManager(); 164 ComponentName componentName = new ComponentName(context, TunerTvInputService.class); 165 166 // Don't kill app by enabling/disabling TvActivity. If LC is killed by enabling/disabling 167 // TvActivity, the following pm.setComponentEnabledSetting doesn't work. 168 ((TvApplication) context.getApplicationContext()).handleInputCountChanged( 169 true, enabled, true); 170 // Since PackageManager.DONT_KILL_APP delays the operation by 10 seconds 171 // (PackageManagerService.BROADCAST_DELAY), we'd better avoid using it. It is used only 172 // when the LiveChannels app is active since we don't want to kill the running app. 173 int flags = TvApplication.getSingletons(context).getMainActivityWrapper().isCreated() 174 ? PackageManager.DONT_KILL_APP : 0; 175 int newState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 176 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 177 if (newState != pm.getComponentEnabledSetting(componentName)) { 178 // Send/cancel the USB tuner TV input setup recommendation card. 179 TunerSetupActivity.onTvInputEnabled(context, enabled); 180 // Enable/disable the USB tuner TV input. 181 pm.setComponentEnabledSetting(componentName, newState, flags); 182 if (!enabled) { 183 Toast.makeText( 184 context, R.string.msg_usb_device_detached, Toast.LENGTH_SHORT).show(); 185 } 186 if (DEBUG) Log.d(TAG, "Status updated:" + enabled); 187 } else if (enabled) { 188 // When # of USB tuners is changed or the device just boots. 189 TunerInputInfoUtils.updateTunerInputInfo(context); 190 } 191 } 192 } 193