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