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 {