diff options
Diffstat (limited to 'src/com/android/tv/tuner/TunerInputControllerImpl.java')
-rw-r--r-- | src/com/android/tv/tuner/TunerInputControllerImpl.java | 567 |
1 files changed, 0 insertions, 567 deletions
diff --git a/src/com/android/tv/tuner/TunerInputControllerImpl.java b/src/com/android/tv/tuner/TunerInputControllerImpl.java deleted file mode 100644 index 6594cd35..00000000 --- a/src/com/android/tv/tuner/TunerInputControllerImpl.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tv.tuner; - -import static com.android.tv.TvFeatures.TUNER; -import static com.android.tv.tuner.TunerFeatures.NETWORK_TUNER; - -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.util.Log; -import android.widget.Toast; -import com.android.tv.R; -import com.android.tv.Starter; -import com.android.tv.TvApplication; -import com.android.tv.TvFeatures; -import com.android.tv.TvSingletons; -import com.android.tv.common.BuildConfig; -import com.android.tv.common.util.SystemPropertiesProxy; - - -import com.android.tv.tuner.setup.BaseTunerSetupActivity; -import com.android.tv.tuner.util.TunerInputInfoUtils; -import com.android.tv.tunerinputcontroller.TunerInputController; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -/** - * Controls the package visibility of {@link BaseTunerTvInputService}. - * - * <p>Listens to broadcast intent for {@link Intent#ACTION_BOOT_COMPLETED}, {@code - * UsbManager.ACTION_USB_DEVICE_ATTACHED}, and {@code UsbManager.ACTION_USB_DEVICE_ATTACHED} to - * update the connection status of the supported USB TV tuners. - */ -public class TunerInputControllerImpl implements TunerInputController { - private static final boolean DEBUG = false; - private static final String TAG = "TunerInputController"; - private static final String PREFERENCE_IS_NETWORK_TUNER_ATTACHED = "network_tuner"; - private static final String SECURITY_PATCH_LEVEL_KEY = "ro.build.version.security_patch"; - private static final String SECURITY_PATCH_LEVEL_FORMAT = "yyyy-MM-dd"; - private static final String PLAY_STORE_LINK_TEMPLATE = "market://details?id=%s"; - - /** Action of {@link Intent} to check network connection repeatedly when it is necessary. */ - private static final String CHECKING_NETWORK_TUNER_STATUS = - "com.android.tv.action.CHECKING_NETWORK_TUNER_STATUS"; - - private static final String EXTRA_CHECKING_DURATION = - "com.android.tv.action.extra.CHECKING_DURATION"; - private static final String EXTRA_DEVICE_IP = "com.android.tv.action.extra.DEVICE_IP"; - - private static final long INITIAL_CHECKING_DURATION_MS = TimeUnit.SECONDS.toMillis(10); - private static final long MAXIMUM_CHECKING_DURATION_MS = TimeUnit.MINUTES.toMillis(10); - private static final String NOTIFICATION_CHANNEL_ID = "tuner_discovery_notification"; - - // TODO: Load settings from XML file - private static final TunerDevice[] TUNER_DEVICES = { - new TunerDevice(0x2040, 0xb123, null), // WinTV-HVR-955Q - new TunerDevice(0x07ca, 0x0837, null), // AverTV Volar Hybrid Q - // WinTV-dualHD (bulk) will be supported after 2017 April security patch. - new TunerDevice(0x2040, 0x826d, "2017-04-01"), // WinTV-dualHD (bulk) - new TunerDevice(0x2040, 0x0264, null), - }; - - private static final int MSG_ENABLE_INPUT_SERVICE = 1000; - private static final long DVB_DRIVER_CHECK_DELAY_MS = 300; - - private final ComponentName usbTunerComponent; - private final ComponentName networkTunerComponent; - private final ComponentName builtInTunerComponent; - private final Map<TunerDevice, ComponentName> mTunerServiceMapping = new HashMap<>(); - - private final Map<ComponentName, String> mTunerApplicationNames = new HashMap<>(); - private final Map<ComponentName, String> mNotificationMessages = new HashMap<>(); - private final Map<ComponentName, Bitmap> mNotificationLargeIcons = new HashMap<>(); - - private final CheckDvbDeviceHandler mHandler = new CheckDvbDeviceHandler(this); - - public TunerInputControllerImpl(ComponentName embeddedTuner) { - usbTunerComponent = embeddedTuner; - networkTunerComponent = usbTunerComponent; - builtInTunerComponent = usbTunerComponent; - for (TunerDevice device : TUNER_DEVICES) { - mTunerServiceMapping.put(device, usbTunerComponent); - } - } - - /** Checks status of USB devices to see if there are available USB tuners connected. */ - @Override - public void onCheckingUsbTunerStatus(Context context, String action) { - onCheckingUsbTunerStatus(context, action, mHandler); - } - - private void onCheckingUsbTunerStatus( - Context context, String action, @NonNull CheckDvbDeviceHandler handler) { - Set<TunerDevice> connectedUsbTuners = getConnectedUsbTuners(context); - handler.removeMessages(MSG_ENABLE_INPUT_SERVICE); - if (!connectedUsbTuners.isEmpty()) { - // Need to check if DVB driver is accessible. Since the driver creation - // could be happen after the USB event, delay the checking by - // DVB_DRIVER_CHECK_DELAY_MS. - handler.sendMessageDelayed( - handler.obtainMessage(MSG_ENABLE_INPUT_SERVICE, context), - DVB_DRIVER_CHECK_DELAY_MS); - } else { - handleTunerStatusChanged( - context, - false, - connectedUsbTuners, - TextUtils.equals(action, UsbManager.ACTION_USB_DEVICE_DETACHED) - ? TunerHal.TUNER_TYPE_USB - : null); - } - } - - private void onNetworkTunerChanged(Context context, boolean enabled) { - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(context); - if (sharedPreferences.contains(PREFERENCE_IS_NETWORK_TUNER_ATTACHED) - && sharedPreferences.getBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false) - == enabled) { - // the status is not changed - return; - } - if (enabled) { - sharedPreferences.edit().putBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, true).apply(); - } else { - sharedPreferences - .edit() - .putBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false) - .apply(); - } - // Network tuner detection is initiated by UI. So the app should not - // be killed. - handleTunerStatusChanged( - context, true, getConnectedUsbTuners(context), TunerHal.TUNER_TYPE_NETWORK); - } - - /** - * See if any USB tuner hardware is attached in the system. - * - * @param context {@link Context} instance - * @return {@code true} if any tuner device we support is plugged in - */ - private Set<TunerDevice> getConnectedUsbTuners(Context context) { - UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); - Map<String, UsbDevice> deviceList = manager.getDeviceList(); - String currentSecurityLevel = - SystemPropertiesProxy.getString(SECURITY_PATCH_LEVEL_KEY, null); - - Set<TunerDevice> devices = new HashSet<>(); - for (UsbDevice device : deviceList.values()) { - if (DEBUG) { - Log.d(TAG, "Device: " + device); - } - for (TunerDevice tuner : TUNER_DEVICES) { - if (tuner.equalsTo(device) && tuner.isSupported(currentSecurityLevel)) { - Log.i(TAG, "Tuner found"); - devices.add(tuner); - } - } - } - return devices; - } - - private void handleTunerStatusChanged( - Context context, - boolean forceDontKillApp, - Set<TunerDevice> connectedUsbTuners, - Integer triggerType) { - Map<ComponentName, Integer> serviceToEnable = new HashMap<>(); - Set<ComponentName> serviceToDisable = new HashSet<>(); - serviceToDisable.add(builtInTunerComponent); - serviceToDisable.add(networkTunerComponent); - if (TUNER.isEnabled(context)) { - // TODO: support both built-in tuner and other tuners at the same time? - if (TunerHal.useBuiltInTuner(context)) { - enableTunerTvInputService( - context, true, false, TunerHal.TUNER_TYPE_BUILT_IN, builtInTunerComponent); - return; - } - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(context); - if (sharedPreferences.getBoolean(PREFERENCE_IS_NETWORK_TUNER_ATTACHED, false)) { - serviceToEnable.put(networkTunerComponent, TunerHal.TUNER_TYPE_NETWORK); - } - } - for (TunerDevice device : TUNER_DEVICES) { - if (TUNER.isEnabled(context) && connectedUsbTuners.contains(device)) { - serviceToEnable.put(mTunerServiceMapping.get(device), TunerHal.TUNER_TYPE_USB); - } else { - serviceToDisable.add(mTunerServiceMapping.get(device)); - } - } - serviceToDisable.removeAll(serviceToEnable.keySet()); - for (ComponentName serviceComponent : serviceToEnable.keySet()) { - if (isTunerPackageInstalled(context, serviceComponent)) { - enableTunerTvInputService( - context, - true, - forceDontKillApp, - serviceToEnable.get(serviceComponent), - serviceComponent); - } else { - sendNotificationToInstallPackage(context, serviceComponent); - } - } - for (ComponentName serviceComponent : serviceToDisable) { - if (isTunerPackageInstalled(context, serviceComponent)) { - enableTunerTvInputService( - context, false, forceDontKillApp, triggerType, serviceComponent); - } else { - cancelNotificationToInstallPackage(context, serviceComponent); - } - } - } - - /** - * Enable/disable the component {@link BaseTunerTvInputService}. - * - * @param context {@link Context} instance - * @param enabled {@code true} to enable the service; otherwise {@code false} - */ - private static void enableTunerTvInputService( - Context context, - boolean enabled, - boolean forceDontKillApp, - Integer tunerType, - ComponentName serviceComponent) { - if (DEBUG) Log.d(TAG, "enableTunerTvInputService: " + enabled); - PackageManager pm = context.getPackageManager(); - int newState = - enabled - ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED - : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - if (newState != pm.getComponentEnabledSetting(serviceComponent)) { - int flags = forceDontKillApp ? PackageManager.DONT_KILL_APP : 0; - if (serviceComponent.getPackageName().equals(context.getPackageName())) { - // Don't kill APP when handling input count changing. Or the following - // setComponentEnabledSetting() call won't work. - ((TvApplication) context.getApplicationContext()) - .handleInputCountChanged(true, enabled, true); - // Bundled input. Don't kill app if LiveChannels app is active since we don't want - // to kill the running app. - if (TvSingletons.getSingletons(context).getMainActivityWrapper().isCreated()) { - flags |= PackageManager.DONT_KILL_APP; - } - // Send/cancel the USB tuner TV input setup notification. - BaseTunerSetupActivity.onTvInputEnabled(context, enabled, tunerType); - if (!enabled && tunerType != null) { - if (tunerType == TunerHal.TUNER_TYPE_USB) { - Toast.makeText( - context, - R.string.msg_usb_tuner_disconnected, - Toast.LENGTH_SHORT) - .show(); - } else if (tunerType == TunerHal.TUNER_TYPE_NETWORK) { - Toast.makeText( - context, - R.string.msg_network_tuner_disconnected, - Toast.LENGTH_SHORT) - .show(); - } - } - } - // Enable/disable the USB tuner TV input. - pm.setComponentEnabledSetting(serviceComponent, newState, flags); - if (DEBUG) Log.d(TAG, "Status updated:" + enabled); - } else if (TvFeatures.TUNER.isEnabled(context) - && enabled - && serviceComponent.getPackageName().equals(context.getPackageName())) { - // When # of tuners is changed or the tuner input service is switching from/to using - // network tuners or the device just boots. - TunerInputInfoUtils.updateTunerInputInfo(context); - } - } - - /** - * Discovers a network tuner. If the network connection is down, it won't repeatedly checking. - */ - @Override - public void executeNetworkTunerDiscoveryAsyncTask(final Context context) { - executeNetworkTunerDiscoveryAsyncTask(context, 0, 0); - } - - /** - * Discovers a network tuner. - * - * @param context {@link Context} - * @param repeatedDurationMs The time length to wait to repeatedly check network status to start - * finding network tuner when the network connection is not available. {@code 0} to disable - * repeatedly checking. - * @param deviceIp The previous discovered device IP, 0 if none. - */ - private void executeNetworkTunerDiscoveryAsyncTask( - final Context context, final long repeatedDurationMs, final int deviceIp) { - // TODO(76197781): Allow network tuner separate from DVB tuner - if (!NETWORK_TUNER.isEnabled(context) || !TUNER.isEnabled(context)) { - return; - } - final Intent networkCheckingIntent = new Intent(context, IntentReceiver.class); - networkCheckingIntent.setAction(CHECKING_NETWORK_TUNER_STATUS); - if (!isNetworkConnected(context) && repeatedDurationMs > 0) { - sendCheckingAlarm(context, networkCheckingIntent, repeatedDurationMs); - } else { - new AsyncTask<Void, Void, Boolean>() { - @Override - protected Boolean doInBackground(Void... params) { - Boolean result = null; - // Implement and execute network tuner discovery AsyncTask here. - return result; - } - - @Override - protected void onPostExecute(Boolean foundNetworkTuner) { - if (foundNetworkTuner == null) { - return; - } - sendCheckingAlarm( - context, - networkCheckingIntent, - foundNetworkTuner ? INITIAL_CHECKING_DURATION_MS : repeatedDurationMs); - onNetworkTunerChanged(context, foundNetworkTuner); - } - }.execute(); - } - } - - private static boolean isNetworkConnected(Context context) { - ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - return networkInfo != null && networkInfo.isConnected(); - } - - private static void sendCheckingAlarm(Context context, Intent intent, long delayMs) { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - intent.putExtra(EXTRA_CHECKING_DURATION, delayMs); - PendingIntent alarmIntent = - PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - alarmManager.set( - AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + delayMs, - alarmIntent); - } - - private static boolean isTunerPackageInstalled( - Context context, ComponentName serviceComponent) { - try { - context.getPackageManager().getPackageInfo(serviceComponent.getPackageName(), 0); - return true; - } catch (NameNotFoundException e) { - return false; - } - } - - private void sendNotificationToInstallPackage(Context context, ComponentName serviceComponent) { - if (!BuildConfig.ENG) { - return; - } - String applicationName = mTunerApplicationNames.get(serviceComponent); - if (applicationName == null) { - applicationName = context.getString(R.string.tuner_install_default_application_name); - } - String contentTitle = - context.getString( - R.string.tuner_install_notification_content_title, applicationName); - String contentText = mNotificationMessages.get(serviceComponent); - if (contentText == null) { - contentText = context.getString(R.string.tuner_install_notification_content_text); - } - Bitmap largeIcon = mNotificationLargeIcons.get(serviceComponent); - if (largeIcon == null) { - // TODO: Make a better default image. - largeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_store); - } - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) { - createNotificationChannel(context, notificationManager); - } - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData( - Uri.parse( - String.format( - PLAY_STORE_LINK_TEMPLATE, serviceComponent.getPackageName()))); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID); - builder.setAutoCancel(true) - .setSmallIcon(R.drawable.ic_launcher_s) - .setLargeIcon(largeIcon) - .setContentTitle(contentTitle) - .setContentText(contentText) - .setCategory(Notification.CATEGORY_RECOMMENDATION) - .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)); - notificationManager.notify(serviceComponent.getPackageName(), 0, builder.build()); - } - - private static void cancelNotificationToInstallPackage( - Context context, ComponentName serviceComponent) { - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(serviceComponent.getPackageName(), 0); - } - - private static void createNotificationChannel( - Context context, NotificationManager notificationManager) { - notificationManager.createNotificationChannel( - new NotificationChannel( - NOTIFICATION_CHANNEL_ID, - context.getResources() - .getString(R.string.ut_setup_notification_channel_name), - NotificationManager.IMPORTANCE_HIGH)); - } - - public static class IntentReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) Log.d(TAG, "Broadcast intent received:" + intent); - Starter.start(context); - TunerInputControllerImpl tunerInputController = - (TunerInputControllerImpl) - TvSingletons.getSingletons(context).getTunerInputController().get(); - if (!TUNER.isEnabled(context)) { - tunerInputController.handleTunerStatusChanged( - context, false, Collections.emptySet(), null); - return; - } - switch (intent.getAction()) { - case Intent.ACTION_BOOT_COMPLETED: - tunerInputController.executeNetworkTunerDiscoveryAsyncTask( - context, INITIAL_CHECKING_DURATION_MS, 0); - // fall through - case TvApplication.ACTION_APPLICATION_FIRST_LAUNCHED: - case UsbManager.ACTION_USB_DEVICE_ATTACHED: - case UsbManager.ACTION_USB_DEVICE_DETACHED: - tunerInputController.onCheckingUsbTunerStatus(context, intent.getAction()); - break; - case CHECKING_NETWORK_TUNER_STATUS: - long repeatedDurationMs = - intent.getLongExtra( - EXTRA_CHECKING_DURATION, INITIAL_CHECKING_DURATION_MS); - tunerInputController.executeNetworkTunerDiscoveryAsyncTask( - context, - Math.min(repeatedDurationMs * 2, MAXIMUM_CHECKING_DURATION_MS), - intent.getIntExtra(EXTRA_DEVICE_IP, 0)); - break; - default: // fall out - } - } - } - - /** - * Simple data holder for a USB device. Used to represent a tuner model, and compare against - * {@link UsbDevice}. - */ - private static class TunerDevice { - private final int vendorId; - private final int productId; - - // security patch level from which the specific tuner type is supported. - private final String minSecurityLevel; - - private TunerDevice(int vendorId, int productId, String minSecurityLevel) { - this.vendorId = vendorId; - this.productId = productId; - this.minSecurityLevel = minSecurityLevel; - } - - private boolean equalsTo(UsbDevice device) { - return device.getVendorId() == vendorId && device.getProductId() == productId; - } - - private boolean isSupported(String currentSecurityLevel) { - if (minSecurityLevel == null) { - return true; - } - - long supportSecurityLevelTimeStamp = 0; - long currentSecurityLevelTimestamp = 0; - try { - SimpleDateFormat format = new SimpleDateFormat(SECURITY_PATCH_LEVEL_FORMAT); - supportSecurityLevelTimeStamp = format.parse(minSecurityLevel).getTime(); - currentSecurityLevelTimestamp = format.parse(currentSecurityLevel).getTime(); - } catch (ParseException e) { - } - return supportSecurityLevelTimeStamp != 0 - && supportSecurityLevelTimeStamp <= currentSecurityLevelTimestamp; - } - } - - private static class CheckDvbDeviceHandler extends Handler { - - private final TunerInputControllerImpl mTunerInputController; - private DvbDeviceAccessor mDvbDeviceAccessor; - - CheckDvbDeviceHandler(TunerInputControllerImpl tunerInputController) { - super(Looper.getMainLooper()); - this.mTunerInputController = tunerInputController; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_ENABLE_INPUT_SERVICE: - Context context = (Context) msg.obj; - if (mDvbDeviceAccessor == null) { - mDvbDeviceAccessor = new DvbDeviceAccessor(context); - } - boolean enabled = mDvbDeviceAccessor.isDvbDeviceAvailable(); - mTunerInputController.handleTunerStatusChanged( - context, - false, - enabled - ? mTunerInputController.getConnectedUsbTuners(context) - : Collections.emptySet(), - TunerHal.TUNER_TYPE_USB); - break; - default: // fall out - } - } - } -} |