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