Merge "TIF: Add content ratings for Netherlands" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 425675b..b2a73fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17057,22 +17057,6 @@
method public void onInputStateChanged(java.lang.String, int);
}
- public abstract class TvInputPassthroughWrapperService extends android.media.tv.TvInputService {
- ctor public TvInputPassthroughWrapperService();
- method public abstract java.lang.String getPassthroughInputId(java.lang.String);
- method public abstract android.media.tv.TvInputPassthroughWrapperService.PassthroughWrapperSession onCreatePassthroughWrapperSession();
- method public final android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
- }
-
- public abstract class TvInputPassthroughWrapperService.PassthroughWrapperSession extends android.media.tv.TvInputService.Session {
- ctor public TvInputPassthroughWrapperService.PassthroughWrapperSession();
- method public abstract void onPassthroughSessionCreationFailed();
- method public abstract void onPassthroughSessionReleased();
- method public abstract void onPassthroughVideoAvailable();
- method public abstract void onPassthroughVideoUnavailable(int);
- method public final boolean onSetSurface(android.view.Surface);
- }
-
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
@@ -17081,6 +17065,14 @@
field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
}
+ public abstract class TvInputService.HardwareSession extends android.media.tv.TvInputService.Session {
+ ctor public TvInputService.HardwareSession();
+ method public abstract java.lang.String getHardwareInputId();
+ method public void onHardwareVideoAvailable();
+ method public void onHardwareVideoUnavailable(int);
+ method public final boolean onSetSurface(android.view.Surface);
+ }
+
public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
ctor public TvInputService.Session();
method public void notifyChannelRetuned(android.net.Uri);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 50c5566..2311e67 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4865,6 +4865,15 @@
<!-- TV content rating system strings for CO TV -->
<!-- TV content rating system strings for DE TV -->
+ <string name="display_name_detv" translatable="false">DE-TV</string>
+ <string name="display_name_detv_all" translatable="false">ab 0 Jahren</string>
+ <string name="display_name_detv_12" translatable="false">ab 12 Jahren</string>
+ <string name="display_name_detv_16" translatable="false">ab 16 Jahren</string>
+ <string name="display_name_detv_18" translatable="false">ab 18 Jahren</string>
+ <string name="description_detv_all">Die nachfolgende Sendung ist für alle alters geeignet.</string>
+ <string name="description_detv_12">Die nachfolgende Sendung ist für Zuschauer unter 12 Jahren nicht geeignet.</string>
+ <string name="description_detv_16">Die nachfolgende Sendung ist für Zuschauer unter 16 Jahren nicht geeignet.</string>
+ <string name="description_detv_18">Die nachfolgende Sendung ist für Zuschauer unter 18 Jahren nicht geeignet.</string>
<!-- TV content rating system strings for DK TV -->
@@ -4873,6 +4882,17 @@
<!-- TV content rating system strings for FI TV -->
<!-- TV content rating system strings for FR TV -->
+ <string name="display_name_frtv" translatable="false">FR-TV</string>
+ <string name="display_name_frtv_all" translatable="false">Les programmes tous publics</string>
+ <string name="display_name_frtv_10" translatable="false">Déconseillé aux -10 ans</string>
+ <string name="display_name_frtv_12" translatable="false">Déconseillé aux -12 ans</string>
+ <string name="display_name_frtv_16" translatable="false">Déconseillé aux -16 ans</string>
+ <string name="display_name_frtv_18" translatable="false">Déconseillé aux -18 ans</string>
+ <string name="description_frtv_all">Les programmes tous publics</string>
+ <string name="description_frtv_10">Programmes comportant certaines scènes susceptibles de heurter les -10 ans.</string>
+ <string name="description_frtv_12">Programmes pouvant troubler les -12 ans, notamment lorsque le scénario recourt de façon systématique et répétée à la violence physique ou psychologique.</string>
+ <string name="description_frtv_16">Programmes à caractère érotique ou de grande violence, susceptibles de nuire à l’épanouissement physique, mental ou moral des -16 ans.</string>
+ <string name="description_frtv_18">Programmes pornographiques ou de très grande violence, réservés à un public adulte averti et susceptibles de nuire à l’épanouissement physique, mental ou moral des -18 ans.</string>
<!-- TV content rating system strings for GR TV -->
diff --git a/core/res/res/xml/tv_content_rating_systems.xml b/core/res/res/xml/tv_content_rating_systems.xml
index 6dcd10cc..2df091d 100644
--- a/core/res/res/xml/tv_content_rating_systems.xml
+++ b/core/res/res/xml/tv_content_rating_systems.xml
@@ -36,6 +36,32 @@
<!-- TV content rating system for CO TV -->
<!-- TV content rating system for DE TV -->
+ <rating-system-definition id="DE_TV"
+ displayName="@string/display_name_detv"
+ country="DE">
+ <rating-definition id="DE_TV_ALL"
+ displayName="@string/display_name_detv_all"
+ description="@string/description_detv_all"
+ ageHint="0" />
+ <rating-definition id="DE_TV_12"
+ displayName="@string/display_name_detv_12"
+ description="@string/description_detv_12"
+ ageHint="12" />
+ <rating-definition id="DE_TV_16"
+ displayName="@string/display_name_detv_16"
+ description="@string/description_detv_16"
+ ageHint="16" />
+ <rating-definition id="DE_TV_18"
+ displayName="@string/display_name_detv_18"
+ description="@string/description_detv_18"
+ ageHint="18" />
+ <order>
+ <rating id="DE_TV_ALL" />
+ <rating id="DE_TV_12" />
+ <rating id="DE_TV_16" />
+ <rating id="DE_TV_18" />
+ </order>
+ </rating-system-definition>
<!-- TV content rating system for DK TV -->
@@ -44,6 +70,41 @@
<!-- TV content rating system for FI TV -->
<!-- TV content rating system for FR TV -->
+ <rating-system-definition id="FR_TV"
+ displayName="@string/display_name_frtv"
+ country="FR">
+ <rating-definition id="FR_TV_ALL"
+ displayName="@string/display_name_frtv_all"
+ description="@string/description_frtv_all"
+ ageHint="0" />
+ <rating-definition id="FR_TV_10"
+ displayName="@string/display_name_frtv_10"
+ description="@string/description_frtv_10"
+ ageHint="10">
+ </rating-definition>
+ <rating-definition id="FR_TV_12"
+ displayName="@string/display_name_frtv_12"
+ description="@string/description_frtv_12"
+ ageHint="12">
+ </rating-definition>
+ <rating-definition id="FR_TV_16"
+ displayName="@string/display_name_frtv_16"
+ description="@string/description_frtv_16"
+ ageHint="16">
+ </rating-definition>
+ <rating-definition id="FR_TV_18"
+ displayName="@string/display_name_frtv_18"
+ description="@string/description_frtv_18"
+ ageHint="18">
+ </rating-definition>
+ <order>
+ <rating id="FR_TV_ALL" />
+ <rating id="FR_TV_10" />
+ <rating id="FR_TV_12" />
+ <rating id="FR_TV_16" />
+ <rating id="FR_TV_18" />
+ </order>
+ </rating-system-definition>
<!-- TV content rating system for GR TV -->
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 26a0d20..df648e7 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -27,5 +27,4 @@
void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo);
void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo);
void removeTvInput(in String inputId);
- void setWrappedInputId(in String inputId, in String wrappedInputId);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index f8c22fc..8a918e1 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -27,7 +27,7 @@
* @hide
*/
oneway interface ITvInputSessionCallback {
- void onSessionCreated(ITvInputSession session);
+ void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
void onSessionEvent(in String name, in Bundle args);
void onChannelRetuned(in Uri channelUri);
void onTracksChanged(in List<TvTrackInfo> tracks);
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 2b3ac5d..fc3ff81 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -124,10 +124,10 @@
* <td>CO_TV</td>
* <td></td>
* </tr-->
- * <!--tr>
+ * <tr>
* <td>DE_TV</td>
- * <td></td>
- * </tr-->
+ * <td>The Germany television rating system</td>
+ * </tr>
* <!--tr>
* <td>DK_TV</td>
* <td></td>
@@ -140,10 +140,10 @@
* <td>FI_TV</td>
* <td></td>
* </tr-->
- * <!--tr>
+ * <tr>
* <td>FR_TV</td>
- * <td></td>
- * </tr-->
+ * <td>The content rating system in French</td>
+ * </tr>
* <!--tr>
* <td>GR_TV</td>
* <td></td>
@@ -316,10 +316,31 @@
* <td>CO_TV_ALL</td>
* <td></td>
* </tr-->
- * <!--tr>
+ * <tr>
* <td>DE_TV_ALL</td>
- * <td></td>
- * </tr-->
+ * <td>Without restriction. There are time schedules and certain age groups which have to be
+ * considered. {@code DE_TV_ALL} is scheduled in daytime (6:00AM – 8:00PM). However, cinema
+ * films classified with "12" may be shown during the daytime, if they are not considered
+ * harmful to younger children.</td>
+ * </tr>
+ * <tr>
+ * <td>DE_TV_12</td>
+ * <td>Suitable for 12 years and above. There are time schedules and certain age groups
+ * which have to be considered. {@code DE_TV_12} is scheduled in primetime (from 8:00PM
+ * – 10.00 p.m.).</td>
+ * </tr>
+ * <tr>
+ * <td>DE_TV_16</td>
+ * <td>Suitable for 16 years and above. There are time schedules and certain age groups
+ * which have to be considered. {@code DE_TV_16} is scheduled in late evening (from 10:00PM
+ * - 11:00PM). </td>
+ * </tr>
+ * <tr>
+ * <td>DE_TV_18</td>
+ * <td>Suitable for 18 years and above. There are time schedules and certain age groups
+ * which have to be considered. {@code DE_TV_18} is scheduled in late night (from 11:00PM
+ * - 6:00AM). </td>
+ * </tr>
* <!--tr>
* <td>DK_TV_ALL</td>
* <td></td>
@@ -332,10 +353,36 @@
* <td>FI_TV_ALL</td>
* <td></td>
* </tr-->
- * <!--tr>
+ * <tr>
* <td>FR_TV_ALL</td>
- * <td></td>
- * </tr-->
+ * <td>A rating string for {@code FR_TV}. According to CSA in France, if no rating appears,
+ * the program is most likely appropriate for all ages. In Android TV, however,
+ * {@code RATING_FR_ALL} is used for handling that case.</td>
+ * </tr>
+ * <tr>
+ * <td>FR_TV_10</td>
+ * <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ * recommended for children under 10.</td>
+ * </tr>
+ * <tr>
+ * <td>FR_TV_12</td>
+ * <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ * recommended for children under 12. Programs rated this are not allowed to air before
+ * 10:00 pm (Some channels and programs are subject to exception). </td>
+ * </tr>
+ * <tr>
+ * <td>FR_TV_16</td>
+ * <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ * recommended for children under 16. Programs rated this are not allowed to air before
+ * 10:30 pm (Some channels and programs are subject to exception). </td>
+ * </tr>
+ * <tr>
+ * <td>FR_TV_18</td>
+ * <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ * recommended for persons under 18. Programs rated this are allowed between midnight and
+ * 5 am and only on some channels. The access to these programs is locked by a personal
+ * password.</td>
+ * </tr>
* <!--tr>
* <td>GR_TV_ALL</td>
* <td></td>
@@ -603,10 +650,6 @@
* <td></td>
* </tr-->
* <!--tr>
- * <td>DE_TV_</td>
- * <td></td>
- * </tr-->
- * <!--tr>
* <td>DK_TV_</td>
* <td></td>
* </tr-->
@@ -619,10 +662,6 @@
* <td></td>
* </tr-->
* <!--tr>
- * <td>FR_TV_</td>
- * <td></td>
- * </tr-->
- * <!--tr>
* <td>GR_TV_</td>
* <td></td>
* </tr-->
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 9c4f121..fdf0d9c 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1454,6 +1454,10 @@
mPendingEventPool.release(p);
}
+ IBinder getToken() {
+ return mToken;
+ }
+
private void releaseInternal() {
mToken = null;
synchronized (mHandler) {
diff --git a/media/java/android/media/tv/TvInputPassthroughWrapperService.java b/media/java/android/media/tv/TvInputPassthroughWrapperService.java
deleted file mode 100644
index 08c802f6..0000000
--- a/media/java/android/media/tv/TvInputPassthroughWrapperService.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2014 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.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.Surface;
-
-/**
- * TvInputPassthroughWrapperService represents a TV input which controls an external device
- * connected to a pass-through TV input (e.g. HDMI 1).
- * <p>
- * This service wraps around a pass-through TV input and delegates the {@link Surface} to the
- * connected TV input so that the application can show the pass-through TV input while
- * TvInputPassthroughWrapperService controls the underlying external device via a separate
- * connection. In the setup activity, the TV input should get the pass-through TV input ID, around
- * which this service will wrap. The service implementation should pass the ID via
- * {@link TvInputPassthroughWrapperService#getPassthroughInputId(String)}. In addition,
- * it needs to implement {@link TvInputPassthroughWrapperService#onCreatePassthroughWrapperSession}
- * to handle requests from the application.
- * </p>
- */
-public abstract class TvInputPassthroughWrapperService extends TvInputService {
- private static final String TAG = "TvInputPassthroughWrapperService";
- // STOPSHIP: Turn debugging off.
- private static final boolean DEBUG = true;
- private TvInputManager mTvInputManager;
- private Handler mHandler;
-
- @Override
- public void onCreate() {
- if (DEBUG) Log.d(TAG, "onCreate()");
- super.onCreate();
- mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
- mHandler = new Handler();
- }
-
- @Override
- public final Session onCreateSession(String inputId) {
- if (DEBUG) Log.d(TAG, "onCreateSession()");
- // Checks the pass-through TV input is properly setup.
- String passthroughInputId = getPassthroughInputId(inputId);
- if (passthroughInputId == null) {
- Log.w(TAG, "The passthrough TV input for input id(" + inputId + ") is not setup yet.");
- return null;
- }
- // Checks if input id from the derived class is really pass-through type.
- TvInputInfo info = mTvInputManager.getTvInputInfo(passthroughInputId);
- if (info == null || !info.isPassthroughInputType()) {
- Log.w(TAG, "Invalid TV input id from derived class: " + passthroughInputId);
- return null;
- }
- // Creates a PassthroughWrapperSession.
- PassthroughWrapperSession session = onCreatePassthroughWrapperSession();
- if (session == null) {
- return null;
- }
- // Connects to the pass-through input the external device is connected to.
- if (!session.connect(passthroughInputId)) {
- throw new IllegalStateException("WrapperSession cannot be reused.");
- }
- notifyWrappedInputId(inputId, passthroughInputId);
- return session;
- }
-
- /**
- * Returns an implementation of {@link PassthroughWrapperSession}.
- * <p>
- * May return {@code null} if {@link TvInputPassthroughWrapperService} fails to create a
- * session.
- * </p>
- */
- public abstract PassthroughWrapperSession onCreatePassthroughWrapperSession();
-
- /**
- * Returns the TV input id the external device is connected to.
- * <p>
- * {@link TvInputPassthroughWrapperService} is expected to identify the pass-though TV
- * input the external device is connected to in the setup phase of this TV input.
- * May return {@code null} if the pass-though TV input is not identified yet.
- * </p>
- * @param inputId The ID of the TV input which controls the external device.
- */
- public abstract String getPassthroughInputId(String inputId);
-
- /**
- * Base session class for derived classes to handle the request from the application. This
- * creates additional session to the pass-through TV input internally and delegates the
- * {@link Surface} given from the application.
- */
- public abstract class PassthroughWrapperSession extends Session {
- private static final float VOLUME_ON = 1.0f;
- private static final float VOLUME_OFF = 0f;
- private TvInputManager.Session mSession;
- private Surface mSurface;
- private Float mVolume;
- private boolean mReleased;
- private int mSurfaceFormat;
- private int mSurfaceWidth;
- private int mSurfaceHeight;
- private boolean mSurfaceChanged;
- private boolean mConnectionRequested;
-
- private final TvInputManager.SessionCallback mSessionCallback =
- new TvInputManager.SessionCallback() {
- @Override
- public void onSessionCreated(TvInputManager.Session session) {
- if (session == null) {
- Log.w(TAG, "Failed to create session.");
- onPassthroughSessionCreationFailed();
- return;
- }
- if (mReleased) {
- session.release();
- return;
- }
- if (mSurface != null) {
- session.setSurface(mSurface);
- mSurface = null;
- }
- if (mVolume != null) {
- session.setStreamVolume(mVolume);
- mVolume = null;
- }
- if (mSurfaceChanged) {
- session.dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
- mSurfaceChanged = false;
- }
- mSession = session;
- }
-
- @Override
- public void onSessionReleased(TvInputManager.Session session) {
- mReleased = true;
- mSession = null;
- onPassthroughSessionReleased();
- }
-
- @Override
- public void onVideoAvailable(TvInputManager.Session session) {
- if (mSession == session) {
- onPassthroughVideoAvailable();
- }
- }
-
- @Override
- public void onVideoUnavailable(TvInputManager.Session session, int reason) {
- if (mSession == session) {
- onPassthroughVideoUnavailable(reason);
- }
- }
-
- @Override
- public void onSessionEvent(TvInputManager.Session session, String eventType,
- Bundle eventArgs) {
- if (mSession == session) {
- notifySessionEvent(eventType, eventArgs);
- }
- }
- };
-
- /**
- * Called when failed to create a session for pass-through TV input.
- */
- public abstract void onPassthroughSessionCreationFailed();
-
- /**
- * Called when the pass-through TV input session is released. This typically happens when
- * the process hosting the pass-through TV input has crashed or been killed.
- */
- public abstract void onPassthroughSessionReleased();
-
- /**
- * Called when the underlying pass-through TV input session calls
- * {@link #notifyVideoAvailable()}.
- */
- public abstract void onPassthroughVideoAvailable();
-
- /**
- * Called when the underlying pass-through TV input session calls
- * {@link #notifyVideoUnavailable(int)}.
- *
- * @param reason The reason why the pass-through TV input stopped the playback.
- */
- public abstract void onPassthroughVideoUnavailable(int reason);
-
- @Override
- public final boolean onSetSurface(Surface surface) {
- if (DEBUG) Log.d(TAG, "onSetSurface(" + surface + ")");
- if (mSession == null) {
- mSurface = surface;
- } else {
- mSession.setSurface(surface);
- }
- return true;
- }
-
- private boolean connect(String inputId) {
- if (mConnectionRequested) {
- return false;
- }
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
- mConnectionRequested = true;
- return true;
- }
-
- @Override
- void release() {
- super.release();
- mReleased = true;
- if (mSession != null) {
- mSession.release();
- }
- }
-
- @Override
- void dispatchSurfaceChanged(int format, int width, int height) {
- super.dispatchSurfaceChanged(format, width, height);
- if (mSession == null) {
- mSurfaceFormat = format;
- mSurfaceWidth = width;
- mSurfaceHeight = height;
- mSurfaceChanged = true;
- } else {
- mSession.dispatchSurfaceChanged(format, width, height);
- }
- }
-
- @Override
- void setStreamVolume(float volume) {
- super.setStreamVolume(volume);
- // Here, we let the pass-through TV input know only whether volume is on or off and
- // make the fine control done in the derived class to prevent that the volume is
- // controlled in the both side.
- float volumeForPassthriughInput = (volume > 0.0f) ? VOLUME_ON : VOLUME_OFF;
- if (mSession == null) {
- mVolume = Float.valueOf(volumeForPassthriughInput);
- } else {
- mSession.setStreamVolume(volumeForPassthriughInput);
- }
- }
- }
-}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d084cf7..0f90c2d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -31,6 +31,7 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.InputChannel;
@@ -161,6 +162,8 @@
* Returns a concrete implementation of {@link Session}.
* <p>
* May return {@code null} if this TV input service fails to create a session for some reason.
+ * If TV input represents an external device connected to a hardware TV input,
+ * {@link HardwareSession} should be returned.
* </p>
* @param inputId The ID of the TV input associated with the session.
*/
@@ -219,21 +222,6 @@
}
/**
- * Notify wrapped TV input ID of current input to TV input framework manager
- *
- * @param inputId The TV input ID of {@link TvInputPassthroughWrapperService}
- * @param wrappedInputId The ID of the wrapped TV input such as external pass-though TV input
- * @hide
- */
- public final void notifyWrappedInputId(String inputId, String wrappedInputId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = inputId;
- args.arg2 = wrappedInputId;
- mHandler.obtainMessage(TvInputService.ServiceHandler.DO_SET_WRAPPED_TV_INPUT_ID,
- args).sendToTarget();
- }
-
- /**
* Base class for derived classes to implement to provide a TV input session.
*/
public abstract class Session implements KeyEvent.Callback {
@@ -398,7 +386,7 @@
* Informs the application that video is not available, so the TV input cannot continue
* playing the TV stream.
*
- * @param reason The reason why the TV input stopped the playback:
+ * @param reason The reason that the TV input stopped the playback:
* <ul>
* <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
* <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
@@ -987,6 +975,97 @@
}
}
+ /**
+ * Base class for a TV input session which represents an external device connected to a
+ * hardware TV input. Once TV input returns an implementation of this class on
+ * {@link #onCreateSession(String)}, the framework will create a hardware session and forward
+ * the application's surface to the hardware TV input.
+ * @see #onCreateSession(String)
+ */
+ public abstract class HardwareSession extends Session {
+
+ private TvInputManager.Session mHardwareSession;
+ private ITvInputSession mProxySession;
+ private ITvInputSessionCallback mProxySessionCallback;
+
+ /**
+ * Returns the hardware TV input ID the external device is connected to.
+ * <p>
+ * TV input is expected to provide {@link android.R.attr#setupActivity} so that
+ * the application can launch it before using this TV input. The setup activity may let
+ * the user select the hardware TV input to which the external device is connected. The ID
+ * of the selected one should be stored in the TV input so that it can be returned here.
+ * </p>
+ */
+ public abstract String getHardwareInputId();
+
+ private final TvInputManager.SessionCallback mHardwareSessionCallback =
+ new TvInputManager.SessionCallback() {
+ @Override
+ public void onSessionCreated(TvInputManager.Session session) {
+ mHardwareSession = session;
+ SomeArgs args = SomeArgs.obtain();
+ if (session != null) {
+ args.arg1 = mProxySession;
+ args.arg2 = mProxySessionCallback;
+ args.arg3 = session.getToken();
+ } else {
+ args.arg1 = null;
+ args.arg2 = mProxySessionCallback;
+ args.arg3 = null;
+ onRelease();
+ }
+ mHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onVideoAvailable(final TvInputManager.Session session) {
+ if (mHardwareSession == session) {
+ onHardwareVideoAvailable();
+ }
+ }
+
+ @Override
+ public void onVideoUnavailable(final TvInputManager.Session session,
+ final int reason) {
+ if (mHardwareSession == session) {
+ onHardwareVideoUnavailable(reason);
+ }
+ }
+ };
+
+ /**
+ * This method will not be called in {@link HardwareSession}. Framework will
+ * forward the application's surface to the hardware TV input.
+ */
+ @Override
+ public final boolean onSetSurface(Surface surface) {
+ Log.e(TAG, "onSetSurface() should not be called in HardwareProxySession.");
+ return false;
+ }
+
+ /**
+ * Called when the underlying hardware TV input session calls
+ * {@link TvInputService.Session#notifyVideoAvailable()}.
+ */
+ public void onHardwareVideoAvailable() { }
+
+ /**
+ * Called when the underlying hardware TV input session calls
+ * {@link TvInputService.Session#notifyVideoUnavailable(int)}.
+ *
+ * @param reason The reason that the hardware TV input stopped the playback:
+ * <ul>
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
+ * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
+ * </ul>
+ */
+ public void onHardwareVideoUnavailable(int reason) { }
+ }
+
/** @hide */
public static boolean isNavigationKey(int keyCode) {
switch (keyCode) {
@@ -1010,11 +1089,11 @@
@SuppressLint("HandlerLeak")
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
- private static final int DO_ADD_HARDWARE_TV_INPUT = 2;
- private static final int DO_REMOVE_HARDWARE_TV_INPUT = 3;
- private static final int DO_ADD_HDMI_CEC_TV_INPUT = 4;
- private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 5;
- private static final int DO_SET_WRAPPED_TV_INPUT_ID = 6;
+ private static final int DO_NOTIFY_SESSION_CREATED = 2;
+ private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
+ private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
+ private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5;
+ private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6;
private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
int n = mCallbacks.beginBroadcast();
@@ -1053,18 +1132,6 @@
mCallbacks.finishBroadcast();
}
- private void broadcastSetWrappedTvInputId(String inputId, String wrappedInputId) {
- int n = mCallbacks.beginBroadcast();
- for (int i = 0; i < n; ++i) {
- try {
- mCallbacks.getBroadcastItem(i).setWrappedInputId(inputId, wrappedInputId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting.", e);
- }
- }
- mCallbacks.finishBroadcast();
- }
-
@Override
public final void handleMessage(Message msg) {
switch (msg.what) {
@@ -1073,17 +1140,58 @@
InputChannel channel = (InputChannel) args.arg1;
ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
String inputId = (String) args.arg3;
- try {
- Session sessionImpl = onCreateSession(inputId);
- if (sessionImpl == null) {
+ Session sessionImpl = onCreateSession(inputId);
+ args.recycle();
+ if (sessionImpl == null) {
+ try {
// Failed to create a session.
- cb.onSessionCreated(null);
- } else {
- sessionImpl.setSessionCallback(cb);
- ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
- sessionImpl, channel);
- cb.onSessionCreated(stub);
+ cb.onSessionCreated(null, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onSessionCreated");
}
+ } else {
+ sessionImpl.setSessionCallback(cb);
+ ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+ sessionImpl, channel);
+ if (sessionImpl instanceof HardwareSession) {
+ HardwareSession proxySession =
+ ((HardwareSession) sessionImpl);
+ String harewareInputId = proxySession.getHardwareInputId();
+ if (!TextUtils.isEmpty(harewareInputId)) {
+ // TODO: check if the given ID is really hardware TV input.
+ proxySession.mProxySession = stub;
+ proxySession.mProxySessionCallback = cb;
+ TvInputManager manager = (TvInputManager) getSystemService(
+ Context.TV_INPUT_SERVICE);
+ manager.createSession(harewareInputId,
+ proxySession.mHardwareSessionCallback, mHandler);
+ } else {
+ sessionImpl.onRelease();
+ Log.w(TAG, "Hardware input id is not setup yet.");
+ try {
+ cb.onSessionCreated(null, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onSessionCreated");
+ }
+ }
+ } else {
+ SomeArgs someArgs = SomeArgs.obtain();
+ someArgs.arg1 = stub;
+ someArgs.arg2 = cb;
+ someArgs.arg3 = null;
+ mHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
+ someArgs).sendToTarget();
+ }
+ }
+ return;
+ }
+ case DO_NOTIFY_SESSION_CREATED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ ITvInputSession stub = (ITvInputSession) args.arg1;
+ ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
+ IBinder hardwareSessionToken = (IBinder) args.arg3;
+ try {
+ cb.onSessionCreated(stub, hardwareSessionToken);
} catch (RemoteException e) {
Log.e(TAG, "error in onSessionCreated");
}
@@ -1122,13 +1230,6 @@
}
return;
}
- case DO_SET_WRAPPED_TV_INPUT_ID: {
- SomeArgs args = (SomeArgs) msg.obj;
- String inputId = (String) args.arg1;
- String wrappedInputId = (String) args.arg2;
- broadcastSetWrappedTvInputId(inputId, wrappedInputId);
- return;
- }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 495d3a9..530ad4b 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -272,8 +272,7 @@
synchronized (mLocationSinkLock) {
// only one sink is allowed at the moment
if (mLocationSink != null) {
- throw new RuntimeException(
- "IFusedLocationHardware does not support multiple sinks");
+ Log.e(TAG, "Replacing an existing IFusedLocationHardware sink");
}
mLocationSink = eventSink;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 73b4a54..18446ae 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -479,12 +479,13 @@
// Set up a callback to send the session token.
ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
@Override
- public void onSessionCreated(ITvInputSession session) {
+ public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
if (DEBUG) {
Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
}
synchronized (mLock) {
sessionState.mSession = session;
+ sessionState.mHardwareSessionToken = harewareSessionToken;
if (session == null) {
removeSessionStateLocked(sessionToken, userId);
sendSessionTokenToClientLocked(sessionState.mClient,
@@ -1107,8 +1108,14 @@
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
- surface);
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ if (sessionState.mHardwareSessionToken == null) {
+ getSessionLocked(sessionState).setSurface(surface);
+ } else {
+ getSessionLocked(sessionState.mHardwareSessionToken,
+ Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "error in setSurface", e);
}
@@ -1132,8 +1139,13 @@
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId)
- .dispatchSurfaceChanged(format, width, height);
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
+ if (sessionState.mHardwareSessionToken != null) {
+ getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+ resolvedUserId).dispatchSurfaceChanged(format, width, height);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "error in dispatchSurfaceChanged", e);
}
@@ -1145,6 +1157,8 @@
@Override
public void setVolume(IBinder sessionToken, float volume, int userId) {
+ final float REMOTE_VOLUME_ON = 1.0f;
+ final float REMOTE_VOLUME_OFF = 0f;
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "setVolume");
@@ -1152,8 +1166,16 @@
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
- volume);
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).setVolume(volume);
+ if (sessionState.mHardwareSessionToken != null) {
+ // Here, we let the hardware session know only whether volume is on or
+ // off to prevent that the volume is controlled in the both side.
+ getSessionLocked(sessionState.mHardwareSessionToken,
+ Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
+ ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "error in setVolume", e);
}
@@ -1457,13 +1479,24 @@
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "captureFrame");
try {
- final String wrappedInputId;
+ String hardwareInputId = null;
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
- wrappedInputId = userState.wrappedInputMap.get(inputId);
+ if (userState.inputMap.get(inputId) == null) {
+ Slog.e(TAG, "Input not found for " + inputId);
+ return false;
+ }
+ for (SessionState sessionState : userState.sessionStateMap.values()) {
+ if (sessionState.mInfo.getId().equals(inputId)
+ && sessionState.mHardwareSessionToken != null) {
+ hardwareInputId = userState.sessionStateMap.get(
+ sessionState.mHardwareSessionToken).mInfo.getId();
+ break;
+ }
+ }
}
return mTvInputHardwareManager.captureFrame(
- (wrappedInputId != null) ? wrappedInputId : inputId,
+ (hardwareInputId != null) ? hardwareInputId : inputId,
surface, config, callingUid, resolvedUserId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1581,6 +1614,7 @@
pw.println("mSessionToken: " + session.mSessionToken);
pw.println("mSession: " + session.mSession);
pw.println("mLogUri: " + session.mLogUri);
+ pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
pw.decreaseIndent();
}
pw.decreaseIndent();
@@ -1670,9 +1704,6 @@
private final Set<ITvInputManagerCallback> callbackSet =
new HashSet<ITvInputManagerCallback>();
- // A mapping from the TV input id to wrapped input id.
- private final Map<String, String> wrappedInputMap = new HashMap<String, String>();
-
// The token of a "main" TV input session.
private IBinder mainSessionToken = null;
@@ -1747,9 +1778,11 @@
private final IBinder mSessionToken;
private ITvInputSession mSession;
private Uri mLogUri;
+ // Not null if this session represents an external device connected to a hardware TV input.
+ private IBinder mHardwareSessionToken;
- private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, int seq,
- int callingUid, int userId) {
+ private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
+ int seq, int callingUid, int userId) {
mSessionToken = sessionToken;
mInfo = info;
mClient = client;
@@ -1769,6 +1802,22 @@
Slog.e(TAG, "error in onSessionReleased", e);
}
}
+ // If there are any other sessions based on this session, they should be released.
+ UserState userState = getUserStateLocked(mUserId);
+ for (SessionState sessionState : userState.sessionStateMap.values()) {
+ if (mSession != null && mSession == sessionState.mHardwareSessionToken) {
+ try {
+ sessionState.mSession.release();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in release", e);
+ }
+ try {
+ sessionState.mClient.onSessionReleased(sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onSessionReleased", e);
+ }
+ }
+ }
removeSessionStateLocked(mSessionToken, mUserId);
}
}
@@ -1870,10 +1919,8 @@
for (TvInputState inputState : userState.inputMap.values()) {
if (inputState.mInfo.getComponent().equals(component)) {
- String inputId = inputState.mInfo.getId();
- notifyInputStateChangedLocked(userState, inputId,
+ notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
INPUT_STATE_DISCONNECTED, null);
- userState.wrappedInputMap.remove(inputId);
}
}
updateServiceConnectionLocked(mComponent, mUserId);
@@ -1952,27 +1999,6 @@
}
}
}
-
- @Override
- public void setWrappedInputId(String inputId, String wrappedInputId) {
- synchronized (mLock) {
- if (!hasInputIdLocked(inputId)) {
- return;
- }
- UserState userState = getUserStateLocked(mUserId);
- userState.wrappedInputMap.put(inputId, wrappedInputId);
- }
- }
-
- private boolean hasInputIdLocked(String inputId) {
- ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- for (TvInputInfo inputInfo : serviceState.mInputList) {
- if (inputInfo.getId().equals(inputId)) {
- return true;
- }
- }
- return false;
- }
}
private final class LogHandler extends Handler {