diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 34a9265984669..4c034b3169bdb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1107,6 +1107,11 @@
+
+
+
CREATOR =
+ new Parcelable.Creator() {
+ @Override
+ public DvbDeviceInfo createFromParcel(Parcel source) {
+ try {
+ return new DvbDeviceInfo(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating DvbDeviceInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public DvbDeviceInfo[] newArray(int size) {
+ return new DvbDeviceInfo[size];
+ }
+ };
+
+ private final int mAdapterId;
+ private final int mDeviceId;
+
+ private DvbDeviceInfo(Parcel source) {
+ mAdapterId = source.readInt();
+ mDeviceId = source.readInt();
+ }
+
+ /**
+ * Constructs a new {@link DvbDeviceInfo} with the given adapter ID and device ID.
+ */
+ public DvbDeviceInfo(int adapterId, int deviceId) {
+ mAdapterId = adapterId;
+ mDeviceId = deviceId;
+ }
+
+ /**
+ * Returns the adapter ID of DVB device, in terms of enumerating the DVB device adapters
+ * installed in the system. The adapter ID counts from zero.
+ */
+ public int getAdapterId() {
+ return mAdapterId;
+ }
+
+ /**
+ * Returns the device ID of DVB device, in terms of enumerating the DVB devices attached to
+ * the same device adapter. The device ID counts from zero.
+ */
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAdapterId);
+ dest.writeInt(mDeviceId);
+ }
+}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 078fb2fa946c3..6fe5dbbc8a13d 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -18,6 +18,7 @@ package android.media.tv;
import android.content.ComponentName;
import android.graphics.Rect;
+import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
@@ -29,6 +30,7 @@ import android.media.tv.TvStreamConfig;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.view.Surface;
/**
@@ -91,4 +93,8 @@ interface ITvInputManager {
boolean captureFrame(in String inputId, in Surface surface, in TvStreamConfig config,
int userId);
boolean isSingleSessionActive(int userId);
+
+ // For DVB device binding
+ List getDvbDeviceList();
+ ParcelFileDescriptor openDvbDevice(in DvbDeviceInfo info, int device);
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 705aa3d646bdb..e74860ee92851 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -21,12 +21,14 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.media.MediaPlayer;
+import android.media.tv.DvbDeviceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -42,6 +44,7 @@ import android.view.View;
import com.android.internal.util.Preconditions;
+import java.lang.IllegalArgumentException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
@@ -55,6 +58,25 @@ import java.util.Map;
public final class TvInputManager {
private static final String TAG = "TvInputManager";
+ static final int DVB_DEVICE_START = 0;
+ static final int DVB_DEVICE_END = 2;
+
+ /**
+ * A demux device of DVB API for controlling the filters of DVB hardware/software.
+ * @hide
+ */
+ public static final int DVB_DEVICE_DEMUX = DVB_DEVICE_START;
+ /**
+ * A DVR device of DVB API for reading transport streams.
+ * @hide
+ */
+ public static final int DVB_DEVICE_DVR = 1;
+ /**
+ * A frontend device of DVB API for controlling the tuner and DVB demodulator hardware.
+ * @hide
+ */
+ public static final int DVB_DEVICE_FRONTEND = DVB_DEVICE_END;
+
static final int VIDEO_UNAVAILABLE_REASON_START = 0;
static final int VIDEO_UNAVAILABLE_REASON_END = 4;
@@ -1257,6 +1279,43 @@ public final class TvInputManager {
}
}
+ /**
+ * Returns the list of currently available DVB devices on the system.
+ *
+ * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
+ * @hide
+ */
+ public List getDvbDeviceList() {
+ try {
+ return mService.getDvbDeviceList();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns a {@link ParcelFileDescriptor} of a specified DVB device for a given
+ * {@link DvbDeviceInfo}
+ *
+ * @param info A {@link DvbDeviceInfo} to open a DVB device.
+ * @param device A DVB device. The DVB device can be {@link DVB_DEVICE_DEMUX},
+ * {@link DVB_DEVICE_DVR} or {@link DVB_DEVICE_FRONTEND}.
+ * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
+ * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} was
+ * invalid or the specified DVB device was busy with a previous request.
+ * @hide
+ */
+ public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) {
+ try {
+ if (DVB_DEVICE_START > device || DVB_DEVICE_END < device) {
+ throw new IllegalArgumentException("Invalid DVB device: " + device);
+ }
+ return mService.openDvbDevice(info, device);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* The Session provides the per-session functionality of TV inputs.
* @hide
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e649e48472815..a869c206bc4eb 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -39,6 +39,7 @@ import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
@@ -64,6 +65,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -80,23 +82,34 @@ import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.IllegalArgumentException;
+import java.lang.Integer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/** This class provides a system service that manages television inputs. */
public final class TvInputManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "TvInputManagerService";
+ // Pattern for selecting the DVB frontend devices from the list of files in the /dev directory.
+ private static final Pattern sFrontEndDevicePattern =
+ Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
+
private final Context mContext;
private final TvInputHardwareManager mTvInputHardwareManager;
@@ -1506,6 +1519,74 @@ public final class TvInputManagerService extends SystemService {
}
}
+ @Override
+ public List getDvbDeviceList() throws RemoteException {
+ if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DVB_DEVICE permission");
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ ArrayList deviceInfos = new ArrayList<>();
+ File devDirectory = new File("/dev");
+ for (String fileName : devDirectory.list()) {
+ Matcher matcher = sFrontEndDevicePattern.matcher(fileName);
+ if (matcher.find()) {
+ int adapterId = Integer.parseInt(matcher.group(1));
+ int deviceId = Integer.parseInt(matcher.group(2));
+ deviceInfos.add(new DvbDeviceInfo(adapterId, deviceId));
+ }
+ }
+ return Collections.unmodifiableList(deviceInfos);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires DVB_DEVICE permission");
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ String deviceFileName;
+ switch (device) {
+ case TvInputManager.DVB_DEVICE_DEMUX:
+ deviceFileName = String.format("/dev/dvb%d.demux%d", info.getAdapterId(),
+ info.getDeviceId());
+ break;
+ case TvInputManager.DVB_DEVICE_DVR:
+ deviceFileName = String.format("/dev/dvb%d.dvr%d", info.getAdapterId(),
+ info.getDeviceId());
+ break;
+ case TvInputManager.DVB_DEVICE_FRONTEND:
+ deviceFileName = String.format("/dev/dvb%d.frontend%d", info.getAdapterId(),
+ info.getDeviceId());
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid DVB device: " + device);
+ }
+ try {
+ // The DVB frontend device only needs to be opened in read/write mode, which
+ // allows performing tuning operations. The DVB demux and DVR device are enough
+ // to be opened in read only mode.
+ return ParcelFileDescriptor.open(new File(deviceFileName),
+ TvInputManager.DVB_DEVICE_FRONTEND == device
+ ? ParcelFileDescriptor.MODE_READ_WRITE
+ : ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public List getAvailableTvStreamConfigList(String inputId, int userId)
throws RemoteException {