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