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.annotation.TargetApi; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.media.tv.TvInputInfo; 23 import android.media.tv.TvInputManager; 24 import android.os.Build; 25 import android.os.ParcelFileDescriptor; 26 import android.support.annotation.IntDef; 27 import android.support.annotation.NonNull; 28 import android.support.annotation.Nullable; 29 import android.support.v4.os.BuildCompat; 30 import android.util.Log; 31 32 import com.android.tv.common.SoftPreconditions; 33 import com.android.tv.common.feature.CommonFeatures; 34 import com.android.tv.common.recording.RecordingCapability; 35 import com.android.usbtuner.tvinput.UsbTunerTvInputService; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.lang.reflect.InvocationTargetException; 40 import java.lang.reflect.Method; 41 import java.util.ArrayList; 42 import java.util.Collections; 43 import java.util.List; 44 45 /** 46 * Provides with the file descriptors to access DVB device. 47 */ 48 public class DvbDeviceAccessor { 49 private static final String TAG = "DvbDeviceAccessor"; 50 51 @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND}) 52 @Retention(RetentionPolicy.SOURCE) 53 public @interface DvbDevice {} 54 public static final int DVB_DEVICE_DEMUX = 0; // TvInputManager.DVB_DEVICE_DEMUX; 55 public static final int DVB_DEVICE_DVR = 1; // TvInputManager.DVB_DEVICE_DVR; 56 public static final int DVB_DEVICE_FRONTEND = 2; // TvInputManager.DVB_DEVICE_FRONTEND; 57 58 private static Method sGetDvbDeviceListMethod; 59 private static Method sOpenDvbDeviceMethod; 60 61 private final TvInputManager mTvInputManager; 62 63 static { 64 try { 65 Class tvInputManagerClass = Class.forName("android.media.tv.TvInputManager"); 66 Class dvbDeviceInfoClass = Class.forName("android.media.tv.DvbDeviceInfo"); 67 sGetDvbDeviceListMethod = tvInputManagerClass.getDeclaredMethod("getDvbDeviceList"); 68 sGetDvbDeviceListMethod.setAccessible(true); 69 sOpenDvbDeviceMethod = tvInputManagerClass.getDeclaredMethod( 70 "openDvbDevice", dvbDeviceInfoClass, Integer.TYPE); 71 sOpenDvbDeviceMethod.setAccessible(true); 72 } catch (ClassNotFoundException e) { 73 Log.e(TAG, "Couldn't find class", e); 74 } catch (NoSuchMethodException e) { 75 Log.e(TAG, "Couldn't find method", e); 76 } 77 } 78 DvbDeviceAccessor(Context context)79 public DvbDeviceAccessor(Context context) { 80 mTvInputManager = (TvInputManager) context.getSystemService(context.TV_INPUT_SERVICE); 81 } 82 getDvbDeviceList()83 public List<DvbDeviceInfoWrapper> getDvbDeviceList() { 84 try { 85 List<DvbDeviceInfoWrapper> wrapperList = new ArrayList<>(); 86 List dvbDeviceInfoList = (List) sGetDvbDeviceListMethod.invoke(mTvInputManager); 87 for (Object dvbDeviceInfo : dvbDeviceInfoList) { 88 wrapperList.add(new DvbDeviceInfoWrapper(dvbDeviceInfo)); 89 } 90 Collections.sort(wrapperList); 91 return wrapperList; 92 } catch (IllegalAccessException e) { 93 Log.e(TAG, "Couldn't access", e); 94 } catch (InvocationTargetException e) { 95 Log.e(TAG, "Couldn't invoke", e); 96 } 97 return null; 98 } 99 isDvbDeviceAvailable()100 public boolean isDvbDeviceAvailable() { 101 try { 102 List dvbDeviceInfoList = (List) sGetDvbDeviceListMethod.invoke(mTvInputManager); 103 return (!dvbDeviceInfoList.isEmpty()); 104 } catch (IllegalAccessException e) { 105 Log.e(TAG, "Couldn't access", e); 106 } catch (InvocationTargetException e) { 107 Log.e(TAG, "Couldn't invoke", e); 108 } 109 return false; 110 } 111 openDvbDevice(DvbDeviceInfoWrapper deviceInfo, @DvbDevice int device)112 public ParcelFileDescriptor openDvbDevice(DvbDeviceInfoWrapper deviceInfo, 113 @DvbDevice int device) { 114 try { 115 return (ParcelFileDescriptor) sOpenDvbDeviceMethod.invoke( 116 mTvInputManager, deviceInfo.getDvbDeviceInfo(), device); 117 } catch (IllegalAccessException e) { 118 Log.e(TAG, "Couldn't access", e); 119 } catch (InvocationTargetException e) { 120 Log.e(TAG, "Couldn't invoke", e); 121 } 122 return null; 123 } 124 125 /** 126 * Returns the current recording capability for USB tuner. 127 * @param inputId the input id to use. 128 */ getRecordingCapability(String inputId)129 public RecordingCapability getRecordingCapability(String inputId) { 130 List<DvbDeviceInfoWrapper> deviceList = getDvbDeviceList(); 131 // TODO(DVR) implement accurate capabilities and updating values when needed. 132 return RecordingCapability.builder() 133 .setInputId(inputId) 134 .setMaxConcurrentPlayingSessions(1) 135 .setMaxConcurrentTunedSessions(deviceList.size()) 136 .setMaxConcurrentSessionsOfAllTypes(deviceList.size() + 1) 137 .build(); 138 } 139 140 @Nullable 141 @TargetApi(Build.VERSION_CODES.N) buildTvInputInfo(Context context)142 public TvInputInfo buildTvInputInfo(Context context) { 143 List<DvbDeviceInfoWrapper> deviceList = getDvbDeviceList(); 144 TvInputInfo.Builder builder = new TvInputInfo.Builder(context, new ComponentName(context, 145 UsbTunerTvInputService.class)); 146 if (deviceList.size() > 0) { 147 return builder.setCanRecord( 148 CommonFeatures.DVR.isEnabled(context) && BuildCompat.isAtLeastN()) 149 .setTunerCount(deviceList.size()) 150 .build(); 151 } else { 152 return null; 153 } 154 } 155 156 public static class DvbDeviceInfoWrapper implements Comparable<DvbDeviceInfoWrapper> { 157 private static Method sGetAdapterIdMethod; 158 private static Method sGetDeviceIdMethod; 159 private final Object mDvbDeviceInfo; 160 private final int mAdapterId; 161 private final int mDeviceId; 162 private final long mId; 163 164 static { 165 try { 166 Class dvbDeviceInfoClass = Class.forName("android.media.tv.DvbDeviceInfo"); 167 sGetAdapterIdMethod = dvbDeviceInfoClass.getDeclaredMethod("getAdapterId"); 168 sGetAdapterIdMethod.setAccessible(true); 169 sGetDeviceIdMethod = dvbDeviceInfoClass.getDeclaredMethod("getDeviceId"); 170 sGetDeviceIdMethod.setAccessible(true); 171 } catch (ClassNotFoundException e) { 172 Log.e(TAG, "Couldn't find class", e); 173 } catch (NoSuchMethodException e) { 174 Log.e(TAG, "Couldn't find method", e); 175 } 176 } 177 DvbDeviceInfoWrapper(Object dvbDeviceInfo)178 public DvbDeviceInfoWrapper(Object dvbDeviceInfo) { 179 mDvbDeviceInfo = dvbDeviceInfo; 180 mAdapterId = initAdapterId(); 181 mDeviceId = initDeviceId(); 182 mId = (((long) getAdapterId()) << 32) | (getDeviceId() & 0xffffffffL); 183 } 184 getId()185 public long getId() { 186 return mId; 187 } 188 getAdapterId()189 public int getAdapterId() { 190 return mAdapterId; 191 } 192 initAdapterId()193 private int initAdapterId() { 194 try { 195 return (int) sGetAdapterIdMethod.invoke(mDvbDeviceInfo); 196 } catch (InvocationTargetException e) { 197 Log.e(TAG, "Couldn't invoke", e); 198 } catch (IllegalAccessException e) { 199 Log.e(TAG, "Couldn't access", e); 200 } 201 return -1; 202 } 203 getDeviceId()204 public int getDeviceId() { 205 return mDeviceId; 206 } 207 initDeviceId()208 private int initDeviceId() { 209 try { 210 return (int) sGetDeviceIdMethod.invoke(mDvbDeviceInfo); 211 } catch (InvocationTargetException e) { 212 Log.e(TAG, "Couldn't invoke", e); 213 } catch (IllegalAccessException e) { 214 Log.e(TAG, "Couldn't access", e); 215 } 216 return -1; 217 } 218 getDvbDeviceInfo()219 public Object getDvbDeviceInfo() { 220 return mDvbDeviceInfo; 221 } 222 223 @Override compareTo(@onNull DvbDeviceInfoWrapper another)224 public int compareTo(@NonNull DvbDeviceInfoWrapper another) { 225 if (getAdapterId() != another.getAdapterId()) { 226 return getAdapterId() - another.getAdapterId(); 227 } 228 return getDeviceId() - another.getDeviceId(); 229 } 230 231 @Override toString()232 public String toString() { 233 return String.format("DvbDeviceInfo {adapterId: %d, deviceId: %d}", getAdapterId(), 234 getDeviceId()); 235 } 236 } 237 } 238