Merge "Clean up certain state transitions in DreamService." into klp-modular-dev
diff --git a/api/current.txt b/api/current.txt
index 5a23f53..9ded80d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11022,11 +11022,16 @@
field public static final int MESSAGE_VENDOR_COMMAND_WITH_ID = 160; // 0xa0
field public static final int MESSAGE_VENDOR_REMOTE_BUTTON_DOWN = 138; // 0x8a
field public static final int MESSAGE_VENDOR_REMOTE_BUTTON_UP = 139; // 0x8b
+ field public static final int POWER_STATUS_ON = 0; // 0x0
+ field public static final int POWER_STATUS_STANDBY = 1; // 0x1
+ field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
+ field public static final int POWER_TRANSIENT_TO_ON = 2; // 0x2
+ field public static final int POWER_TRANSIENT_TO_STANDBY = 3; // 0x3
}
public final class HdmiCecClient {
+ method public boolean isTvOn();
method public void sendActiveSource();
- method public void sendGiveDevicePowerStatus(int);
method public void sendImageViewOn();
method public void sendInactiveSource();
method public void sendTextViewOn();
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 38d9de4..8578a32 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -160,6 +160,12 @@
public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2;
public static final int MESSAGE_ABORT = 0xFF;
+ public static final int POWER_STATUS_UNKNOWN = -1;
+ public static final int POWER_STATUS_ON = 0;
+ public static final int POWER_STATUS_STANDBY = 1;
+ public static final int POWER_TRANSIENT_TO_ON = 2;
+ public static final int POWER_TRANSIENT_TO_STANDBY = 3;
+
private static final int[] ADDRESS_TO_TYPE = {
DEVICE_TV, // ADDR_TV
DEVICE_RECORDER, // ADDR_RECORDER_1
diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java
index d7f4a72..1f382e6 100644
--- a/core/java/android/hardware/hdmi/HdmiCecClient.java
+++ b/core/java/android/hardware/hdmi/HdmiCecClient.java
@@ -110,16 +110,20 @@
}
/**
- * Send <GiveDevicePowerStatus> message.
+ * Returns true if the TV or attached display is powered on.
+ * <p>
+ * The result of this method is only meaningful on playback devices (where the device
+ * type is {@link HdmiCec#DEVICE_PLAYBACK}).
+ * </p>
*
- * @param address logical address of the device to send the message to, such as
- * {@link HdmiCec#ADDR_TV}.
+ * @return true if TV is on; otherwise false.
*/
- public void sendGiveDevicePowerStatus(int address) {
+ public boolean isTvOn() {
try {
- mService.sendGiveDevicePowerStatus(mBinder, address);
+ return mService.isTvOn(mBinder);
} catch (RemoteException e) {
- Log.e(TAG, "sendGiveDevicePowerStatus threw exception ", e);
+ Log.e(TAG, "isTvOn threw exception ", e);
}
+ return false;
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java
index 575785d..10b058c 100644
--- a/core/java/android/hardware/hdmi/HdmiCecManager.java
+++ b/core/java/android/hardware/hdmi/HdmiCecManager.java
@@ -45,6 +45,9 @@
* @return {@link HdmiCecClient} instance. {@code null} on failure.
*/
public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) {
+ if (mService == null) {
+ return null;
+ }
try {
IBinder b = mService.allocateLogicalDevice(type, getListenerWrapper(listener));
return HdmiCecClient.create(mService, b);
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
index 6fefcf8..b5df131 100644
--- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
@@ -29,12 +29,11 @@
interface IHdmiCecService {
IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
void removeServiceListener(IBinder b, IHdmiCecListener listener);
- void setOsdName(IBinder b, String name);
void sendActiveSource(IBinder b);
void sendInactiveSource(IBinder b);
void sendImageViewOn(IBinder b);
void sendTextViewOn(IBinder b);
- void sendGiveDevicePowerStatus(IBinder b, int address);
+ boolean isTvOn(IBinder b);
void sendMessage(IBinder b, in HdmiCecMessage message);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 086a2b1..b67a5a3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2519,6 +2519,13 @@
android:description="@string/permdesc_accessNetworkConditions"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to provision and access DRM certificates
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
+ android:label="@string/permlab_accessDrmCertificates"
+ android:description="@string/permdesc_accessDrmCertificates"
+ android:protectionLevel="signature|system" />
+
<!-- The system process is explicitly the only one allowed to launch the
confirmation UI for full backup/restore -->
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 30243a4..5748e72 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1985,6 +1985,11 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_accessDrmCertificates">access DRM certificates</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_accessDrmCertificates">Allows an application to provision and use DRM certficates. Should never be needed for normal apps.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 73d5b74..0a234aa 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -368,7 +368,10 @@
<span class="en">Media Playback</span></a>
</li>
<li><a href="<?cs var:toroot ?>guide/topics/media/mediarouter.html">
- <span class="en">MediaRouter</span></a>
+ <span class="en">Media Router</span></a>
+ </li>
+ <li><a href="<?cs var:toroot ?>guide/topics/media/mediarouteprovider.html">
+ <span class="en">Media Route Provider</span></a>
</li>
<li><a href="<?cs var:toroot ?>guide/appendix/media-formats.html">
<span class="en">Supported Media Formats</span></a>
diff --git a/docs/html/guide/topics/media/mediarouteprovider.jd b/docs/html/guide/topics/media/mediarouteprovider.jd
new file mode 100644
index 0000000..389fbfb
--- /dev/null
+++ b/docs/html/guide/topics/media/mediarouteprovider.jd
@@ -0,0 +1,453 @@
+page.title=Media Route Provider
+page.tags="mediarouteprovider","mediacontrolintent"
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#overview">Overview</a>
+ <ol>
+ <li><a href="#dist">Distribution of route providers</a></li>
+ <li><a href="#playback-types">Types of playback</a></li>
+ <li><a href="#mr-packages">Media router packages</a></li>
+ </ol>
+ </li>
+ <li><a href="#provider-service">Creating a Provider Service</a></li>
+ <li><a href="#route-caps">Specifying Route Capabilities</a>
+ <ol>
+ <li><a href="#route-cat">Route categories</a></li>
+ <li><a href="#media-types">Media types and protocols</a></li>
+ <li><a href="#playback-ctrls">Playback controls</a></li>
+ <li><a href="#mrpd">MediaRouteProviderDescriptor</a></li>
+ </ol>
+ </li>
+ <li><a href="#ctrl-routes">Controlling Routes</a></li>
+ </ol>
+ <h2>Key Classes</h2>
+ <ol>
+ <li>{@link android.support.v7.media.MediaRouteProvider}</li>
+ <li>{@link android.support.v7.media.MediaRouteProviderDescriptor}</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController RouteController}</li>
+ </ol>
+ <h2>Related Samples</h2>
+ <ol>
+ <li><a href="{@docRoot}samples/MediaRouter/index.html">MediaRouter</a></li>
+ </ol>
+ </div>
+</div>
+
+<p>Users want to play media content from their Android devices bigger, brighter, and louder on
+ connected playback devices such as televisions, stereos,
+ and home theater equipment. As a manufacturer of these devices, allowing Android users to
+ instantly show a picture, play a song, or share a video for friends and family using your product
+ can make it much more compelling and engaging.</p>
+
+<p>The Android media router framework allows manufacturers to enable playback on their devices
+ through a standardized interface called a {@link android.support.v7.media.MediaRouteProvider}.
+ A route provider defines a common interface for playing media on a receiver device, making it
+ possible to play media on your equipment from any Android application that supports media
+ routes.</p>
+
+<p>This guide discusses how to create a media route provider for a receiver device and make it
+ available to other media playback applications that run on Android.</p>
+
+<h2 id="overview">Overview</h2>
+
+<p>The Android media router framework enables media app developers and media playback device
+ manufacturers to connect through a common API and common user interface. App developers that
+ implement a {@link android.support.v7.media.MediaRouter} interface can then connect to the
+ framework and play content to devices that participate in the media router framework. Media
+ playback device manufacturers can participate in the framework by publishing a {@link
+ android.support.v7.media.MediaRouteProvider} that allows other applications to connect to and
+ play media on the receiver devices. Figure 1 illustrates how an app connects to a receiving
+ device through the media router framework.</p>
+
+<img src="{@docRoot}images/mediarouter/media-route-provider-framework.png" alt="" id="figure1"/>
+<p class="img-caption">
+ <strong>Figure 1.</strong> Overview of how media route provider classes provide communication
+ from a media app to a receiver device.
+</p>
+
+<p>When you build a media route provider for your receiver device, the provider serves the
+following purposes:</p>
+
+<ul>
+ <li>Describe and publish the capabilities of the receiver device so other apps can discover it
+ and use its playback features.</li>
+ <li>Wrap the programming interface of the receiver device and its communication
+ transport mechanisms to make the device compatible with the media router framework.</li>
+</ul>
+
+
+<h3 id="dist">Distribution of route providers</h3>
+
+<p>A media route provider is distributed as part of an Android app. Your route provider can be
+ made available to other apps by extending
+ {@link android.support.v7.media.MediaRouteProviderService} or wrapping your implementation of
+ {@link android.support.v7.media.MediaRouteProvider} with your own service and declaring an intent
+ filter for the media route provider. These steps allow other apps to discover and make use of
+ your media route.</p>
+
+<p>
+ <strong>Note:</strong> The app containing the media route provider can also include a
+ <a href="{@docRoot}guide/topics/media/mediarouter.html">MediaRouter</a> interface to the
+ route provider, but this is not required.
+</p>
+
+
+<h3 id="playback-types">Types of playback</h3>
+
+<p>There are two main types of playback supported by the media router framework. A media route
+ provider can support one or both types of playback, depending on the capabilities of your playback
+ equipment and the functionality you want to support:</p>
+
+<ul>
+ <li><strong>Remote Playback</strong> — This approach uses the receiver device to handle the
+ content data retrieval, decoding, and playback, while an Android device in the user's hand is
+ used as a remote control. This approach is used by Android apps that support
+ <a href="https://developers.google.com/cast/">Google Cast</a>.</li>
+ <li><strong>Secondary Output</strong> — With this approach, the Android media application
+ retrieves, renders and streams video or music directly to the receiver device. This approach is
+ used to support Wireless Display output on Android.</li>
+</ul>
+
+
+<h3 id="mr-packages">Media router packages</h3>
+
+<p>
+ The media router APIs are provided as part of the Android Support Library version 18 and higher,
+ in the <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter">v7-mediarouter</a>
+ support library. You should use the classes in the
+ {@link android.support.v7.media} package for media route provider functions.
+ These APIs are compatible with devices running Android 2.1 (API level 7) and higher.
+</p>
+
+<p class="caution">
+ <strong>Caution:</strong> There is another set of media router APIs provided in the
+ {@link android.media} class package that have been superseded by the
+ <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter">v7-mediarouter</a>
+ support library. You <em>should not</em> use the {@link android.media} classes for
+ implementing media route provider functions.
+</p>
+
+<p>In order to use the {@link android.support.v7.media} media router classes, you
+ must add the <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter"
+ >v7-mediarouter support library package</a> to your app development project. For more
+ information on adding support libraries to your app development project, see
+ <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a>.
+</p>
+
+
+<h2 id="provider-service">Creating a Provider Service</h2>
+
+<p>The media router framework must be able to discover and connect to your media route provider
+ to allow other applications to use your route. In order to do this, the media router framework
+ looks for apps that declare a media route provider intent action. When another app wants to
+ connect to your provider, the framework must be able to invoke and connect to it, so your provider
+ must be encapsulated in a {@link android.app.Service}.</p>
+
+<p>The following example code shows the declaration of a media route provider service and the
+ intent filter in a manifest, which allows it to be discovered and used by the media router
+ framework:</p>
+
+<pre>
+<service android:name=".provider.SampleMediaRouteProviderService"
+ android:label="@string/sample_media_route_provider_service"
+ android:process=":mrp">
+ <intent-filter>
+ <action android:name="android.media.MediaRouteProviderService" />
+ </intent-filter>
+</service>
+</pre>
+
+<p>This manifest example declares a service that wraps the actual media route provider classes.
+ The Android media router framework provides the
+ {@link android.support.v7.media.MediaRouteProviderService} class for use as a service wrapper for
+ media route providers. The following example code demonstrates how to use this wrapper
+ class:</p>
+
+<pre>
+public class SampleMediaRouteProviderService extends MediaRouteProviderService {
+
+ @Override
+ public MediaRouteProvider onCreateMediaRouteProvider() {
+ return new SampleMediaRouteProvider(this);
+ }
+}
+</pre>
+
+
+<h2 id="route-caps">Specifying Route Capabilities</h2>
+
+<p>Apps connecting to the media router framework can discover your media route through your
+ app's manifest declarations, but they also need to know the capabilities of the media routes you
+ are providing. Media routes can be of different types and have different features, and other apps
+ need to be able to discover these details to determine if they are compatible with your route.</p>
+
+<p>The media router framework allows you to define and publish the capabilities of your media
+ route through {@link android.content.IntentFilter} objects, {@link
+ android.support.v7.media.MediaRouteDescriptor} objects and a {@link
+ android.support.v7.media.MediaRouteProviderDescriptor}. This section explains how to use these
+ classes to publish the details of your media route for other apps.</p>
+
+
+<h3 id="route-cat">Route categories</h3>
+
+<p>As part of the programmatic description of your media route provider, you must specify
+ whether your provider supports remote playback, secondary output, or both. These are the route
+ categories provided by the media router framework:</p>
+
+<ul>
+ <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_LIVE_AUDIO CATEGORY_LIVE_AUDIO}
+ — Output of audio to a secondary output device, such as a wireless-enabled music system.
+ </li>
+ <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_LIVE_VIDEO CATEGORY_LIVE_VIDEO}
+ — Output of video to a secondary output device, such as Wireless Display devices.</li>
+ <li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_REMOTE_PLAYBACK
+ CATEGORY_REMOTE_PLAYBACK} — Play video or audio on a separate device which handles media
+ retrieval, decoding, and playback, such as
+ <a href="https://www.google.com/url?q=http://www.google.com/chromecast">Chromecast</a> devices.
+ </li>
+</ul>
+
+<p>In order to include these settings in a description of your media route, you insert them into
+ an {@link android.content.IntentFilter} object, which you later add to a
+ {@link android.support.v7.media.MediaRouteDescriptor} object:</p>
+
+<pre>
+public final class SampleMediaRouteProvider extends MediaRouteProvider {
+ private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
+ static {
+ IntentFilter videoPlayback = new IntentFilter();
+ <strong>videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);</strong>
+ CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
+ CONTROL_FILTERS_BASIC.add(videoPlayback);
+ }
+}
+
+</pre>
+
+<p>If you specify the {@link android.support.v7.media.MediaControlIntent#CATEGORY_REMOTE_PLAYBACK
+ CATEGORY_REMOTE_PLAYBACK} intent, you must also define what media types and
+ playback controls are supported by your media route provider. The next section describes how to
+ specify these settings for your device.</p>
+
+
+<h3 id="media-types">Media types and protocols</h3>
+
+<p>A media route provider for a remote playback device must specify the media types and transfer
+ protocols it supports. You specify these settings using the {@link android.content.IntentFilter}
+ class and the {@link android.content.IntentFilter#addDataScheme addDataScheme()} and
+ {@link android.content.IntentFilter#addDataType addDataType()} methods of that object. The
+ following code snippet demonstrates how to define an intent filter for supporting remote video
+ playback using http, https, and Real Time Streaming Protocol (RTSP):</p>
+
+<pre>
+public final class SampleMediaRouteProvider extends MediaRouteProvider {
+
+ private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
+
+ static {
+ IntentFilter videoPlayback = new IntentFilter();
+ videoPlayback.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+ videoPlayback.addAction(MediaControlIntent.ACTION_PLAY);
+ videoPlayback.addDataScheme("http");
+ videoPlayback.addDataScheme("https");
+ videoPlayback.addDataScheme("rtsp");
+ addDataTypeUnchecked(videoPlayback, "video/*");
+ CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
+ CONTROL_FILTERS_BASIC.add(videoPlayback);
+ }
+ ...
+
+ private static void addDataTypeUnchecked(IntentFilter filter, String type) {
+ try {
+ filter.addDataType(type);
+ } catch (MalformedMimeTypeException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
+
+</pre>
+
+
+<h3 id="playback-ctrls">Playback controls</h3>
+
+<p>A media route provider that offers remote playback must specify the types of media controls
+ it supports. These are the general types of control that media routes can provide:</p>
+
+<ul>
+ <li><strong>Playback controls</strong>, such as play, pause, rewind, and fast-forward.</li>
+ <li><strong>Queuing features</strong>, which allow the sending app to add and remove items
+ from a playlist which is maintained by the receiver device.</li>
+ <li><strong>Session features</strong>, which prevent sending apps from interfering with each
+ other by having the receiver device provide a session id to the requesting app and then checking
+ that id with each subsequent playback control request.</li>
+</ul>
+
+<p>The following code example demonstrates how to construct an intent filter for supporting
+ basic media route playback controls:</p>
+
+<pre>
+public final class SampleMediaRouteProvider extends MediaRouteProvider {
+ private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
+ static {
+ ...
+ IntentFilter playControls = new IntentFilter();
+ playControls.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
+ playControls.addAction(MediaControlIntent.ACTION_SEEK);
+ playControls.addAction(MediaControlIntent.ACTION_GET_STATUS);
+ playControls.addAction(MediaControlIntent.ACTION_PAUSE);
+ playControls.addAction(MediaControlIntent.ACTION_RESUME);
+ playControls.addAction(MediaControlIntent.ACTION_STOP);
+ CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
+ CONTROL_FILTERS_BASIC.add(videoPlayback);
+ CONTROL_FILTERS_BASIC.add(playControls);
+ }
+ ...
+}
+</pre>
+
+<p>For more information about the available playback control intents, see the
+ {@link android.support.v7.media.MediaControlIntent} class.</p>
+
+
+<h3 id="mrpd">MediaRouteProviderDescriptor</h3>
+
+<p>After defining the capabilities of your media route using {@link
+ android.content.IntentFilter} objects, you can then create a descriptor object for publishing to
+ the Android media router framework. This descriptor object contains the specifics of your media
+ route's capabilities so that other applications can determine how to interact with your media
+ route.</p>
+
+<p>The following example code demonstrates how to add the previously created intent filters to a
+ {@link android.support.v7.media.MediaRouteProviderDescriptor} and set the descriptor for use by
+ the media router framework:</p>
+
+<pre>
+public SampleMediaRouteProvider(Context context) {
+ super(context);
+ publishRoutes();
+}
+
+private void publishRoutes() {
+ Resources r = getContext().getResources();
+ // Create a route descriptor using previously created IntentFilters
+ MediaRouteDescriptor routeDescriptor = new MediaRouteDescriptor.Builder(
+ VARIABLE_VOLUME_BASIC_ROUTE_ID,
+ r.getString(R.string.variable_volume_basic_route_name))
+ .setDescription(r.getString(R.string.sample_route_description))
+ .addControlFilters(CONTROL_FILTERS_BASIC)
+ .setPlaybackStream(AudioManager.STREAM_MUSIC)
+ .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
+ .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
+ .setVolumeMax(VOLUME_MAX)
+ .setVolume(mVolume)
+ .build();
+ // Add the route descriptor to the provider descriptor
+ MediaRouteProviderDescriptor providerDescriptor =
+ new MediaRouteProviderDescriptor.Builder()
+ .addRoute(routeDescriptor)
+ .build();
+
+ // Publish the descriptor to the framework
+ setDescriptor(providerDescriptor);
+}
+</pre>
+
+<p>For more information on the available descriptor settings, see the reference documentation
+ for {@link android.support.v7.media.MediaRouteDescriptor} and {@link
+ android.support.v7.media.MediaRouteProviderDescriptor}.</p>
+
+
+<h2 id="ctrl-routes">Controlling Routes</h2>
+
+<p>When an application connects to your media route provider, the provider receives playback
+ commands through the media router framework sent to your route by other apps. To handle these
+ requests, you must provide an implementation of a {@link
+ android.support.v7.media.MediaRouteProvider.RouteController} class, which processes the commands
+ and handles the actual communication to your receiver device.</p>
+
+<p>The media router framework calls the {@link
+ android.support.v7.media.MediaRouteProvider#onCreateRouteController onCreateRouteController()}
+ method of your route provider to obtain an instance of this class and then routes requests to it.
+ These are the key methods of the {@link
+ android.support.v7.media.MediaRouteProvider.RouteController} class, which you must implement for
+ your media route provider:</p>
+
+<ul>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onSelect onSelect()}
+ — Called when an application selects your route for playback. You use this method to do
+ any preparation work that may be required before media playback begins.</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onControlRequest
+ onControlRequest()} — Sends specific playback commands to the receiving device.</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onSetVolume
+ onSetVolume()} — Sends a request to the receiving device to set the playback volume to a
+ specific value.</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onUpdateVolume
+ onUpdateVolume()} — Sends a request to the receiving device to modify the playback
+ volume by a specified amount.</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect
+ onUnselect()} — Called when an application unselects a route.</li>
+ <li>{@link android.support.v7.media.MediaRouteProvider.RouteController#onRelease onRelease()}
+ — Called when the route is no longer needed by the framework, allowing it to free its
+ resources.</li>
+</ul>
+
+<p>All playback control requests, except for volume changes, are directed to the {@link
+ android.support.v7.media.MediaRouteProvider.RouteController#onControlRequest onControlRequest()}
+ method. Your implementation of this method must parse the control requests and respond to them
+ appropriately. Here is an example implementation of this method which processes commands for a
+ remote playback media route:</p>
+
+<pre>
+private final class SampleRouteController extends
+ MediaRouteProvider.RouteController {
+ ...
+
+ @Override
+ public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
+
+ String action = intent.getAction();
+
+ if (intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
+ boolean success = false;
+ if (action.equals(MediaControlIntent.ACTION_PLAY)) {
+ success = handlePlay(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
+ success = handleEnqueue(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_REMOVE)) {
+ success = handleRemove(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_SEEK)) {
+ success = handleSeek(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_GET_STATUS)) {
+ success = handleGetStatus(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_PAUSE)) {
+ success = handlePause(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_RESUME)) {
+ success = handleResume(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_STOP)) {
+ success = handleStop(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_START_SESSION)) {
+ success = handleStartSession(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_GET_SESSION_STATUS)) {
+ success = handleGetSessionStatus(intent, callback);
+ } else if (action.equals(MediaControlIntent.ACTION_END_SESSION)) {
+ success = handleEndSession(intent, callback);
+ }
+
+ Log.d(TAG, mSessionManager.toString());
+ return success;
+ }
+ return false;
+ }
+ ...
+}
+</pre>
+
+<p>It is important to understand that the {@link
+ android.support.v7.media.MediaRouteProvider.RouteController} class is intended to act as a wrapper
+ for the API to your media playback equipment. The implementation of the methods in this class is
+ entirely dependent on the programmatic interface provided by your receiving device.</p>
diff --git a/docs/html/guide/topics/media/mediarouter.jd b/docs/html/guide/topics/media/mediarouter.jd
index 1b10265..e0bf889 100644
--- a/docs/html/guide/topics/media/mediarouter.jd
+++ b/docs/html/guide/topics/media/mediarouter.jd
@@ -1,5 +1,5 @@
-page.title=MediaRouter
-page.tags="cast","chromecast","wireless display","miracast"
+page.title=Media Router
+page.tags="mediarouter","cast","chromecast","wireless display","miracast"
@jd:body
<div id="qv-wrapper">
@@ -36,6 +36,10 @@
<li>{@link android.support.v7.media.MediaRouter.Callback}</li>
<li>{@link android.support.v7.media.MediaRouteProvider}</li>
</ol>
+ <h2>Related Samples</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/media/mediarouter.html">MediaRouter</a></li>
+ </ol>
</div>
</div>
@@ -105,15 +109,17 @@
(API level 7) and higher.
</p>
-<p class="note">
- <strong>Note:</strong> There is another set of media router APIs provided in the
+<p class="caution">
+ <strong>Caution:</strong> There is another set of media router APIs provided in the
{@link android.media} that have been superseded by the v7-mediarouter support library.
You <em>should not</em> use the {@link android.media} classes for media router functions.
</p>
<p>In order to use the {@link android.support.v7.media} media router classes, you must add
the <a href="{@docRoot}tools/support-library/features.html#v7-mediarouter">v7-mediarouter
- support library package</a> to your app development project.
+ support library package</a> to your app development project. For more
+ information on adding support libraries to your app development project, see
+ <a href="{@docRoot}tools/support-library/setup.html">Support Library Setup</a>.
</p>
@@ -211,9 +217,9 @@
CATEGORY_LIVE_VIDEO} — Output of video to a secondary output device, such as Wireless
Display devices.</li>
<li>{@link android.support.v7.media.MediaControlIntent#CATEGORY_REMOTE_PLAYBACK
- CATEGORY_REMOTE_PLAYBACK} — Play video or audio on a separate device that supports the
- <a href="https://developers.google.com/cast/">Google Cast</a> remote control protocol, such
- as <a href="https://www.google.com/url?q=http://www.google.com/chromecast">Chromecast</a>.
+ CATEGORY_REMOTE_PLAYBACK} — Play video or audio on a separate device that handles media
+ retrieval, decoding, and playback, such as
+ <a href="https://www.google.com/url?q=http://www.google.com/chromecast">Chromecast</a> devices.
</li>
</ul>
@@ -279,7 +285,7 @@
<p>In order to connect to a media route selected by the user, your app must obtain the {@link
android.support.v7.media.MediaRouter} framework object and then attach a {@link
android.support.v7.media.MediaRouter.Callback} object. The callback object receives messages
- from the media router framework when a route selected, changed or disconnected by the user.</p>
+ from the media router framework when a route is selected, changed, or disconnected by the user.</p>
<p>To obtain an instance of the {@link android.support.v7.media.MediaRouter} framework object,
call {@link android.support.v7.media.MediaRouter#getInstance MediaRouter.getInstance()}
@@ -299,11 +305,11 @@
<p>The media router framework communicates with an app through a callback object that
you attach to the {@link android.support.v7.media.MediaRouter} framework object. An app
that uses the media router framework must extend the {@link
- android.support.v7.media.MediaRouter.Callback} object to receive messages when a media route is
- connected and provide content to the connected device through that route.</p>
+ android.support.v7.media.MediaRouter.Callback} object in order to receive messages when a
+ media route is connected.</p>
-<p>There are several methods in the callback that can be overwritten to receive messages about
- media router events. At the minimum, your implementation of the {@link
+<p>There are several methods in the callback that you can override to receive information about
+ media router events. At minimum, your implementation of the {@link
android.support.v7.media.MediaRouter.Callback} class should override the following
methods:</p>
@@ -440,12 +446,12 @@
<p class="note">
<strong>Note:</strong> The media route framework also provides a
- {@link android.support.v7.app.MediaRouteDiscoveryFragment} class which takes care of adding and
- removing the call back for an activity.
+ {@link android.support.v7.app.MediaRouteDiscoveryFragment} class, which takes care of adding and
+ removing the callback for an activity.
</p>
<p>Now when you run your application, you should see a Cast button appear in your activity.
- When you press the button the media router framework, a route selection dialog appears as shown
+ When you touch the button, a route selection dialog appears as shown
in figure 3, allowing your user to select an available media route. Make sure you have a
supported device available on your local network when testing this interface.</p>
diff --git a/docs/html/images/mediarouter/media-route-provider-framework.png b/docs/html/images/mediarouter/media-route-provider-framework.png
new file mode 100644
index 0000000..60cc29a
--- /dev/null
+++ b/docs/html/images/mediarouter/media-route-provider-framework.png
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._data.plist b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._data.plist
new file mode 100644
index 0000000..d82ea05
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._data.plist
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._image1.png b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._image1.png
new file mode 100644
index 0000000..3435e35
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/._image1.png
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-route-provider-framework.graffle/data.plist b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/data.plist
new file mode 100644
index 0000000..07791df
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/data.plist
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-route-provider-framework.graffle/image1.png b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/image1.png
new file mode 100644
index 0000000..d6e3e95
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-route-provider-framework.graffle/image1.png
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-router-framework.graffle/data.plist b/docs/image_sources/mediarouter/media-router-framework.graffle/data.plist
new file mode 100644
index 0000000..ffd8212
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-router-framework.graffle/data.plist
Binary files differ
diff --git a/docs/image_sources/mediarouter/media-router-framework.graffle/image1.png b/docs/image_sources/mediarouter/media-router-framework.graffle/image1.png
new file mode 100644
index 0000000..d6e3e95
--- /dev/null
+++ b/docs/image_sources/mediarouter/media-router-framework.graffle/image1.png
Binary files differ
diff --git a/libs/input/EventHub.cpp b/libs/input/EventHub.cpp
index fc324f8..ed63b2d 100644
--- a/libs/input/EventHub.cpp
+++ b/libs/input/EventHub.cpp
@@ -1059,10 +1059,6 @@
AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE,
- AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4,
- AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8,
- AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12,
- AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16,
};
status_t EventHub::openDeviceLocked(const char *devicePath) {
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 9bf48ce..532e39a 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1,4 +1,4 @@
- /*
+/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,6 @@
import android.os.Bundle;
import android.os.Parcel;
import android.util.Log;
-import android.content.Context;
/**
* MediaDrm can be used to obtain keys for decrypting protected media streams, in
@@ -100,6 +99,8 @@
private final static String TAG = "MediaDrm";
+ private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
+
private EventHandler mEventHandler;
private OnEventListener mOnEventListener;
@@ -154,7 +155,7 @@
}
private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid,
- String mimeType);
+ String mimeType);
/**
* Instantiate a MediaDrm object
@@ -178,7 +179,7 @@
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaDrm>(this),
- getByteArrayFromUUID(uuid));
+ getByteArrayFromUUID(uuid));
}
/**
@@ -287,7 +288,7 @@
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediadrm_ref,
- int eventType, int extra, Object obj)
+ int eventType, int extra, Object obj)
{
MediaDrm md = (MediaDrm)((WeakReference)mediadrm_ref).get();
if (md == null) {
@@ -387,9 +388,8 @@
* problem with the certifcate
*/
public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
- String mimeType, int keyType,
- HashMap<String, String> optionalParameters)
- throws NotProvisionedException;
+ String mimeType, int keyType, HashMap<String, String> optionalParameters)
+ throws NotProvisionedException;
/**
@@ -413,7 +413,7 @@
* @throws ResourceBusyException if required resources are in use
*/
public native byte[] provideKeyResponse(byte[] scope, byte[] response)
- throws NotProvisionedException, DeniedByServerException;
+ throws NotProvisionedException, DeniedByServerException;
/**
@@ -480,7 +480,7 @@
}
private native ProvisionRequest getProvisionRequestNative(int certType,
- String certAuthority);
+ String certAuthority);
/**
* After a provision response is received by the app, it is provided to the DRM
@@ -493,12 +493,12 @@
* server rejected the request
*/
public void provideProvisionResponse(byte[] response)
- throws DeniedByServerException {
+ throws DeniedByServerException {
provideProvisionResponseNative(response);
}
private native Certificate provideProvisionResponseNative(byte[] response)
- throws DeniedByServerException;
+ throws DeniedByServerException;
/**
* A means of enforcing limits on the number of concurrent streams per subscriber
@@ -585,23 +585,22 @@
private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId,
- String algorithm);
+ String algorithm);
private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId,
- String algorithm);
+ String algorithm);
private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] input, byte[] iv);
+ byte[] keyId, byte[] input, byte[] iv);
private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] input, byte[] iv);
+ byte[] keyId, byte[] input, byte[] iv);
private static final native byte[] signNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] message);
+ byte[] keyId, byte[] message);
private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId,
- byte[] keyId, byte[] message,
- byte[] signature);
+ byte[] keyId, byte[] message, byte[] signature);
/**
* In addition to supporting decryption of DASH Common Encrypted Media, the
@@ -631,7 +630,7 @@
private byte[] mSessionId;
CryptoSession(MediaDrm drm, byte[] sessionId,
- String cipherAlgorithm, String macAlgorithm)
+ String cipherAlgorithm, String macAlgorithm)
{
mSessionId = sessionId;
mDrm = drm;
@@ -706,8 +705,7 @@
* "algorithms".
*/
public CryptoSession getCryptoSession(byte[] sessionId,
- String cipherAlgorithm,
- String macAlgorithm)
+ String cipherAlgorithm, String macAlgorithm)
{
return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
}
@@ -753,11 +751,11 @@
* @hide - not part of the public API at this time
*/
public CertificateRequest getCertificateRequest(int certType,
- String certAuthority)
+ String certAuthority)
{
ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority);
return new CertificateRequest(provisionRequest.getData(),
- provisionRequest.getDefaultUrl());
+ provisionRequest.getDefaultUrl());
}
/**
@@ -802,18 +800,16 @@
* @hide - not part of the public API at this time
*/
public Certificate provideCertificateResponse(byte[] response)
- throws DeniedByServerException {
+ throws DeniedByServerException {
return provideProvisionResponseNative(response);
}
private static final native byte[] signRSANative(MediaDrm drm, byte[] sessionId,
- String algorithm, byte[] wrappedKey,
- byte[] message);
+ String algorithm, byte[] wrappedKey, byte[] message);
/**
* Sign data using an RSA key
*
- * @param context the app context
* @param sessionId a sessionId obtained from openSession on the MediaDrm object
* @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1"
* @param wrappedKey - the wrapped (encrypted) RSA private key obtained
@@ -822,7 +818,8 @@
*
* @hide - not part of the public API at this time
*/
- public byte[] signRSA(Context context, byte[] sessionId, String algorithm, byte[] wrappedKey, byte[] message) {
+ public byte[] signRSA(byte[] sessionId, String algorithm,
+ byte[] wrappedKey, byte[] message) {
return signRSANative(this, sessionId, algorithm, wrappedKey, message);
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 1dbaa3a..7c45682 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -570,7 +570,7 @@
FIND_CLASS(clazz, "android/media/MediaDrm$Certificate");
GET_FIELD_ID(gFields.certificate.wrappedPrivateKey, clazz, "mWrappedKey", "[B");
GET_FIELD_ID(gFields.certificate.certificateData, clazz, "mCertificateData", "[B");
- gFields.certificateClassId = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
+ gFields.certificateClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_CLASS(clazz, "java/util/ArrayList");
GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
@@ -595,13 +595,13 @@
GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;");
FIND_CLASS(clazz, "java/util/HashMap");
- gFields.hashmapClassId = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
+ gFields.hashmapClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_CLASS(clazz, "java/lang/String");
- gFields.stringClassId = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
+ gFields.stringClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
FIND_CLASS(clazz, "java/util/ArrayList");
- gFields.arraylistClassId = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
+ gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
}
static void android_media_MediaDrm_native_setup(
diff --git a/media/lib/signer/java/com/android/mediadrm/signer/MediaDrmSigner.java b/media/lib/signer/java/com/android/mediadrm/signer/MediaDrmSigner.java
index d971afb..0a2897f 100644
--- a/media/lib/signer/java/com/android/mediadrm/signer/MediaDrmSigner.java
+++ b/media/lib/signer/java/com/android/mediadrm/signer/MediaDrmSigner.java
@@ -16,7 +16,6 @@
package com.android.mediadrm.signer;
-import android.content.Context;
import android.media.MediaDrm;
import android.media.DeniedByServerException;
@@ -37,7 +36,7 @@
* server
*/
public final static class CertificateRequest {
- private MediaDrm.CertificateRequest mCertRequest;
+ private final MediaDrm.CertificateRequest mCertRequest;
CertificateRequest(MediaDrm.CertificateRequest certRequest) {
mCertRequest = certRequest;
@@ -65,7 +64,7 @@
* with a certificate.
*/
public final static class Certificate {
- private MediaDrm.Certificate mCertificate;
+ private final MediaDrm.Certificate mCertificate;
Certificate(MediaDrm.Certificate certificate) {
mCertificate = certificate;
@@ -97,7 +96,7 @@
* the chain of authority.
*/
public static CertificateRequest getCertificateRequest(MediaDrm drm, int certType,
- String certAuthority) {
+ String certAuthority) {
return new CertificateRequest(drm.getCertificateRequest(certType, certAuthority));
}
@@ -117,14 +116,13 @@
* server rejected the request
*/
public static Certificate provideCertificateResponse(MediaDrm drm, byte[] response)
- throws DeniedByServerException {
+ throws DeniedByServerException {
return new Certificate(drm.provideCertificateResponse(response));
}
/**
* Sign data using an RSA key
*
- * @param context the App context
* @param drm the MediaDrm object
* @param sessionId a sessionId obtained from openSession on the MediaDrm object
* @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1"
@@ -132,8 +130,8 @@
* from provideCertificateResponse
* @param message the data for which a signature is to be computed
*/
- public static byte[] signRSA(Context context, MediaDrm drm, byte[] sessionId,
- String algorithm, byte[] wrappedKey, byte[] message) {
- return drm.signRSA(context, sessionId, algorithm, wrappedKey, message);
+ public static byte[] signRSA(MediaDrm drm, byte[] sessionId,
+ String algorithm, byte[] wrappedKey, byte[] message) {
+ return drm.signRSA(sessionId, algorithm, wrappedKey, message);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
index 9916435..64b51c9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
@@ -27,8 +27,12 @@
import java.util.List;
/**
- * CecDevice class represents a CEC logical device characterized
- * by its device type. A physical device can contain the functions of
+ * HdmiCecDevice class represents a CEC logical device characterized
+ * by its device type. It is a superclass of those serving concrete device type.
+ * Currently we're interested in playback(one of sources), display(sink) device type
+ * only. The support for the other types like recorder, audio system will come later.
+ *
+ * <p>A physical device can contain the functions of
* more than one logical device, in which case it should create
* as many logical devices as necessary.
*
@@ -41,7 +45,7 @@
*
* <p>Declared as package-private, accessed by HdmiCecService only.
*/
-final class HdmiCecDevice {
+abstract class HdmiCecDevice {
private static final String TAG = "HdmiCecDevice";
private final int mType;
@@ -49,19 +53,38 @@
// List of listeners to the message/event coming to the device.
private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
private final Binder mBinder = new Binder();
+ private final HdmiCecService mService;
- private String mName;
private boolean mIsActiveSource;
/**
+ * Factory method that creates HdmiCecDevice instance to the device type.
+ */
+ public static HdmiCecDevice create(HdmiCecService service, int type) {
+ if (type == HdmiCec.DEVICE_PLAYBACK) {
+ return new HdmiCecDevicePlayback(service, type);
+ } else if (type == HdmiCec.DEVICE_TV) {
+ return new HdmiCecDeviceTv(service, type);
+ }
+ return null;
+ }
+
+ /**
* Constructor.
*/
- public HdmiCecDevice(int type) {
+ public HdmiCecDevice(HdmiCecService service, int type) {
+ mService = service;
mType = type;
mIsActiveSource = false;
}
/**
+ * Called right after the class is instantiated. This method can be used to
+ * implement any initialization tasks for the instance.
+ */
+ abstract public void initialize();
+
+ /**
* Return the binder token that identifies this instance.
*/
public Binder getToken() {
@@ -69,6 +92,13 @@
}
/**
+ * Return the service instance.
+ */
+ public HdmiCecService getService() {
+ return mService;
+ }
+
+ /**
* Return the type of this device.
*/
public int getType() {
@@ -76,24 +106,6 @@
}
/**
- * Set the name of the device. The name will be transferred via the message
- * <Set OSD Name> to other HDMI-CEC devices connected through HDMI
- * cables and shown on TV screen to identify the devicie.
- *
- * @param name name of the device
- */
- public void setName(String name) {
- mName = name;
- }
-
- /**
- * Return the name of this device.
- */
- public String getName() {
- return mName;
- }
-
- /**
* Register a listener to be invoked when events occur.
*
* @param listener the listern that will run
@@ -128,6 +140,7 @@
if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
mIsActiveSource = false;
}
+
if (mListeners.size() == 0) {
return;
}
@@ -167,4 +180,13 @@
public void setIsActiveSource(boolean state) {
mIsActiveSource = state;
}
+
+ /**
+ * Check if the connected sink device is in powered-on state. The default implementation
+ * simply returns false. Should be overriden by subclass to report the correct state.
+ */
+ public boolean isSinkDeviceOn() {
+ Log.w(TAG, "Not valid for the device type: " + mType);
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java
new file mode 100644
index 0000000..0310264
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+
+/**
+ * Class for the logical device of playback type. Devices such as DVD/Blueray player
+ * that support 'playback' feature are classified as playback device. It is common
+ * that they don't have built-in display, therefore need to talk, stream their contents
+ * to TV/display device which is connected through HDMI cable.
+ *
+ * <p>It closely monitors the status of display device (other devices can be of interest
+ * too, but with much less priority), declares itself as 'active source' to have
+ * display show its output, switch the source state as ordered by display that may be
+ * talking to many other devices connected to it. It also receives commands from display
+ * such as remote control signal, standby, status report, playback mode.
+ *
+ * <p>Declared as package-private, accessed by HdmiCecService only.
+ */
+final class HdmiCecDevicePlayback extends HdmiCecDevice {
+ private static final String TAG = "HdmiCecDevicePlayback";
+
+ private int mSinkDevicePowerStatus;
+
+ /**
+ * Constructor.
+ */
+ public HdmiCecDevicePlayback(HdmiCecService service, int type) {
+ super(service, type);
+ mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN;
+ }
+
+ @Override
+ public void initialize() {
+ // Playback device tries to obtain the power status of TV/display when created,
+ // and maintains it all through its lifecycle. CEC spec says there is
+ // a maximum 1 second response time. Therefore it should be kept in mind
+ // that there can be as much amount of period of time the power status
+ // of the display remains unknown after the query is sent out.
+ queryTvPowerStatus();
+ }
+
+ private void queryTvPowerStatus() {
+ getService().sendMessage(getType(), HdmiCec.ADDR_TV,
+ HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, HdmiCecService.EMPTY_PARAM);
+ }
+
+ @Override
+ public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+ // Updates power status of display. The cases are:
+ // 1) Response for the queried power status request arrives. Update the status.
+ // 2) Broadcast or direct <Standby> command from TV, which is sent as TV itself is going
+ // into standby mode too.
+ // 3) Broadcast <Report Physical Address> command from TV, which is sent while it boots up.
+ if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) {
+ mSinkDevicePowerStatus = params[0];
+ } else if (srcAddress == HdmiCec.ADDR_TV) {
+ if (opcode == HdmiCec.MESSAGE_STANDBY) {
+ mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_STANDBY;
+ } else if (opcode == HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS) {
+ mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_ON;
+ }
+ }
+ super.handleMessage(srcAddress, dstAddress, opcode, params);
+ }
+
+ @Override
+ public void handleHotplug(boolean connected) {
+ // If cable get disconnected sink device becomes unreachable. Switch the status
+ // to unknown, and query the status once the cable gets connected back.
+ if (!connected) {
+ mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN;
+ } else {
+ queryTvPowerStatus();
+ }
+ super.handleHotplug(connected);
+ }
+
+ @Override
+ public boolean isSinkDeviceOn() {
+ return mSinkDevicePowerStatus == HdmiCec.POWER_STATUS_ON;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java
new file mode 100644
index 0000000..09ff3ca
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.server.hdmi;
+
+/**
+ * Class for logical device of TV type.
+ */
+final class HdmiCecDeviceTv extends HdmiCecDevice {
+ private static final String TAG = "HdmiCecDeviceTv";
+
+ /**
+ * Constructor.
+ */
+ public HdmiCecDeviceTv(HdmiCecService service, int type) {
+ super(service, type);
+ }
+
+ public void initialize() {
+ // TODO: Do the initialization task for TV device here.
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
index 0a7236c..aa496c5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -23,6 +23,7 @@
import android.hardware.hdmi.IHdmiCecListener;
import android.hardware.hdmi.IHdmiCecService;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
@@ -30,6 +31,7 @@
import android.util.SparseArray;
import com.android.server.SystemService;
+import libcore.util.EmptyArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -37,8 +39,6 @@
import java.util.ArrayList;
import java.util.Locale;
-import libcore.util.EmptyArray;
-
/**
* Provides a service for sending and processing HDMI-CEC messages, and providing
* the information on HDMI settings in general.
@@ -65,7 +65,7 @@
private static final String PERMISSION = "android.permission.HDMI_CEC";
- private static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
+ static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
public HdmiCecService(Context context) {
super(context);
@@ -76,7 +76,12 @@
@Override
public void onStart() {
mNativePtr = nativeInit(this);
- publishBinderService(Context.HDMI_CEC_SERVICE, new BinderService());
+ if (mNativePtr != 0) {
+ // TODO: Consider using a dedicated, configurable identifier for OSD name, maybe from
+ // Settings. It should be ASCII only, not a very long one (limited to 15 chars).
+ setOsdNameLocked(Build.MODEL);
+ publishBinderService(Context.HDMI_CEC_SERVICE, new BinderService());
+ }
}
/**
@@ -88,7 +93,6 @@
// but better be handled in service by turning off the screen
// or putting the device into suspend mode. List up such messages
// and handle them here.
- int type = HdmiCec.getTypeFromAddress(dstAddress);
synchronized (mLock) {
if (dstAddress == HdmiCec.ADDR_BROADCAST) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
@@ -96,6 +100,7 @@
params);
}
} else {
+ int type = HdmiCec.getTypeFromAddress(dstAddress);
HdmiCecDevice device = mLogicalDevices.get(type);
if (device == null) {
Log.w(TAG, "logical device not found. type: " + type);
@@ -138,22 +143,6 @@
}
/**
- * Called by native when a request for the device OSD name was received.
- * The native part uses the return value to generate the message
- * <Set Osd Name> in response.
- */
- private byte[] getOsdName(int type) {
- // TODO: Consider getting the OSD name from device name instead.
- synchronized (mLock) {
- HdmiCecDevice device = mLogicalDevices.get(type);
- if (device != null) {
- return device.getName().getBytes(Charset.forName("US-ASCII"));
- }
- }
- return null;
- }
-
- /**
* Called by native when a request for the menu language of the device was
* received. The native part uses the return value to generate the message
* <Set Menu Language> in response. The language should be of
@@ -174,8 +163,7 @@
synchronized (mLock) {
for (int i = 0; i < mLogicalDevices.size(); ++i) {
HdmiCecDevice device = mLogicalDevices.valueAt(i);
- pw.println("Device: name=" + device.getName() +
- ", type=" + device.getType() +
+ pw.println("Device: type=" + device.getType() +
", active=" + device.isActiveSource());
}
}
@@ -205,6 +193,15 @@
throw new IllegalArgumentException("Device not found");
}
+ // package-private. Used by HdmiCecDevice and its subclasses only.
+ void sendMessage(int type, int address, int opcode, byte[] params) {
+ nativeSendMessage(mNativePtr, type, address, opcode, params);
+ }
+
+ private void setOsdNameLocked(String name) {
+ nativeSetOsdName(mNativePtr, name.getBytes(Charset.forName("US-ASCII")));
+ }
+
private final class ListenerRecord implements IBinder.DeathRecipient {
private final IHdmiCecListener mListener;
private final int mType;
@@ -248,8 +245,12 @@
Log.e(TAG, "Logical address was not allocated");
return null;
} else {
- device = new HdmiCecDevice(type);
- device.setName(HdmiCec.getDefaultDeviceName(address));
+ device = HdmiCecDevice.create(HdmiCecService.this, type);
+ if (device == null) {
+ Log.e(TAG, "Device type not supported yet.");
+ return null;
+ }
+ device.initialize();
mLogicalDevices.put(type, device);
}
}
@@ -272,18 +273,6 @@
}
@Override
- public void setOsdName(IBinder b, String name) {
- enforceAccessPermission();
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null");
- }
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- device.setName(name);
- }
- }
-
- @Override
public void sendActiveSource(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
@@ -331,12 +320,11 @@
}
@Override
- public void sendGiveDevicePowerStatus(IBinder b, int address) {
+ public boolean isTvOn(IBinder b) {
enforceAccessPermission();
synchronized (mLock) {
HdmiCecDevice device = getLogicalDeviceLocked(b);
- nativeSendMessage(mNativePtr, device.getType(), address,
- HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, EMPTY_PARAM);
+ return device.isSinkDeviceOn();
}
}
@@ -398,4 +386,5 @@
private static native void nativeSendMessage(long handler, int deviceType, int destination,
int opcode, byte[] params);
private static native int nativeGetPhysicalAddress(long handler);
+ private static native void nativeSetOsdName(long handler, byte[] name);
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
index 61edda8..a00aaa8 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -20,7 +20,7 @@
#include "ScopedPrimitiveArray.h"
-#include <cstring>
+#include <string>
#include <deque>
#include <map>
@@ -34,7 +34,6 @@
jmethodID handleMessage;
jmethodID handleHotplug;
jmethodID getActiveSource;
- jmethodID getOsdName;
jmethodID getLanguage;
} gHdmiCecServiceClassInfo;
@@ -84,6 +83,7 @@
void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
void sendCecMessage(const cec_message_t& message);
+ void setOsdName(const char* name, size_t len);
private:
enum {
@@ -156,6 +156,7 @@
std::deque<MessageEntry> mMessageQueue;
uint16_t mPhysicalAddress;
+ std::string mOsdName;
};
@@ -373,6 +374,10 @@
mDevice->send_message(mDevice, &message);
}
+void HdmiCecHandler::setOsdName(const char* name, size_t len) {
+ mOsdName.assign(name, min(len, CEC_MESSAGE_BODY_MAX_LENGTH - 1));
+}
+
// static
void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
@@ -504,18 +509,9 @@
}
void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) {
- cec_logical_address_t addr = msg.destination;
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jbyteArray res = (jbyteArray) env->CallObjectMethod(mCallbacksObj,
- gHdmiCecServiceClassInfo.getOsdName,
- getDeviceType(addr));
- jbyte *name = env->GetByteArrayElements(res, NULL);
- if (name != NULL) {
- sendSetOsdName(addr, msg.initiator, reinterpret_cast<const char *>(name),
- env->GetArrayLength(res));
- env->ReleaseByteArrayElements(res, name, JNI_ABORT);
+ if (!mOsdName.empty()) {
+ sendSetOsdName(msg.destination, msg.initiator, mOsdName.c_str(), mOsdName.length());
}
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
@@ -562,8 +558,6 @@
"handleHotplug", "(Z)V");
GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
"getActiveSource", "()I");
- GET_METHOD_ID(gHdmiCecServiceClassInfo.getOsdName, clazz,
- "getOsdName", "(I)[B");
GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
"getLanguage", "(I)Ljava/lang/String;");
@@ -603,6 +597,15 @@
return handler->getPhysicalAddress();
}
+static void nativeSetOsdName(JNIEnv* env, jclass clazz, jlong handlerPtr, jbyteArray name) {
+ HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+ jsize len = env->GetArrayLength(name);
+ if (len > 0) {
+ ScopedByteArrayRO namePtr(env, name);
+ handler->setOsdName(reinterpret_cast<const char *>(namePtr.get()), len);
+ }
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
@@ -615,6 +618,8 @@
(void *)nativeRemoveLogicalAddress },
{ "nativeGetPhysicalAddress", "(J)I",
(void *)nativeGetPhysicalAddress },
+ { "nativeSetOsdName", "(J[B)V",
+ (void *)nativeSetOsdName },
};
#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"