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 
DvbDeviceAccessor(Context context)72     public DvbDeviceAccessor(Context context) {
73         mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
74     }
75 
getDvbDeviceList()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      */
getNumOfDvbDevices()96     public int getNumOfDvbDevices() {
97         List<DvbDeviceInfoWrapper> dvbDeviceList = getDvbDeviceList();
98         return dvbDeviceList == null ? 0 : dvbDeviceList.size();
99     }
100 
isDvbDeviceAvailable()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 
openDvbDevice(DvbDeviceInfoWrapper deviceInfo, @DvbDevice int device)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      */
getRecordingCapability(String inputId)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 
DvbDeviceInfoWrapper(Object dvbDeviceInfo)163         public DvbDeviceInfoWrapper(Object dvbDeviceInfo) {
164             mDvbDeviceInfo = dvbDeviceInfo;
165             mAdapterId = initAdapterId();
166             mDeviceId = initDeviceId();
167             mId = (((long) getAdapterId()) << 32) | (getDeviceId() & 0xffffffffL);
168         }
169 
getId()170         public long getId() {
171             return mId;
172         }
173 
getAdapterId()174         public int getAdapterId() {
175             return mAdapterId;
176         }
177 
initAdapterId()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 
getDeviceId()189         public int getDeviceId() {
190             return mDeviceId;
191         }
192 
initDeviceId()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 
getDvbDeviceInfo()204         public Object getDvbDeviceInfo() {
205             return mDvbDeviceInfo;
206         }
207 
208         @Override
compareTo(@onNull DvbDeviceInfoWrapper another)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
toString()217         public String toString() {
218             return String.format(Locale.US, "DvbDeviceInfo {adapterId: %d, deviceId: %d}",
219                     getAdapterId(),
220                     getDeviceId());
221         }
222     }
223 }
224