OSDN Git Service

TIF: Add DVB device handling to TvInputManagerService
authorJaesung Chung <jaesung@google.com>
Fri, 24 Apr 2015 10:39:59 +0000 (19:39 +0900)
committerJaesung Chung <jaesung@google.com>
Thu, 14 May 2015 02:00:06 +0000 (02:00 +0000)
Added an API to pass an open file descriptor of DVB devices and
addressed the security issue of setting the permissions on DVB devices
to 0666.

Bug: 20436120
Change-Id: I4649e76084f3356ec22b7e776fb87c6a8fdc00d6

core/res/AndroidManifest.xml
media/java/android/media/tv/DvbDeviceInfo.aidl [new file with mode: 0644]
media/java/android/media/tv/DvbDeviceInfo.java [new file with mode: 0644]
media/java/android/media/tv/ITvInputManager.aidl
media/java/android/media/tv/TvInputManager.java
services/core/java/com/android/server/tv/TvInputManagerService.java

index edff537..5977a01 100644 (file)
     <permission android:name="android.permission.CAPTURE_TV_INPUT"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- @hide Allows TvInputService to access DVB device.
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DVB_DEVICE"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- @hide Allows enabling/disabling OEM unlock
    <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.OEM_UNLOCK_STATE"
diff --git a/media/java/android/media/tv/DvbDeviceInfo.aidl b/media/java/android/media/tv/DvbDeviceInfo.aidl
new file mode 100644 (file)
index 0000000..4851050
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable DvbDeviceInfo;
\ No newline at end of file
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
new file mode 100644 (file)
index 0000000..1885a34
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.media.tv.TvInputManager;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+
+/**
+ * Simple container for information about DVB device.
+ * Not for third-party developers.
+ *
+ * @hide
+ */
+public final class DvbDeviceInfo implements Parcelable {
+    static final String TAG = "DvbDeviceInfo";
+
+    public static final Parcelable.Creator<DvbDeviceInfo> CREATOR =
+            new Parcelable.Creator<DvbDeviceInfo>() {
+                @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);
+    }
+}
index 078fb2f..6fe5dbb 100644 (file)
@@ -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<DvbDeviceInfo> getDvbDeviceList();
+    ParcelFileDescriptor openDvbDevice(in DvbDeviceInfo info, int device);
 }
index 705aa3d..e74860e 100644 (file)
@@ -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;
 
@@ -1258,6 +1280,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<DvbDeviceInfo> 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
      */
index e649e48..a869c20 100644 (file)
@@ -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;
 
@@ -1507,6 +1520,74 @@ public final class TvInputManagerService extends SystemService {
         }
 
         @Override
+        public List<DvbDeviceInfo> 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<DvbDeviceInfo> 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<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int userId)
                 throws RemoteException {
             if (mContext.checkCallingPermission(