Updated: December 12, 2011
Motorola HDMI Dual Screen API
This document describes how to use the High Definition Multimedia Interface (HDMI) API supported on some Motorola devices. The HDMI port has been configured so that when you plug in a cable connected to an HDMI screen, the contents of the device screen is mirrored to the HDMI port, and hence displayed on the HDMI screen. The mirroring behavior happens automatically and is enough to satisfy the needs of many users.
You use this API when you want to render different content on the HDMI screen from what is on the mobile device screen. Not all devices with a HDMI port support this API. An example of use is displaying a game on the large screen, with the game controller on the mobile device. Because of the two screens, this API is known as the “Dual Screen API”.
If you don’t need to render, but simply want to block part or all of your app’s content from displaying over HDMI (usually for licensing reasons), refer to Motorola HDMI Status API.
The HDMI API interface allows a developer to do these things:
- Make status enquiries about the HDMI port
- Set the HDMI resolution and reset it back to what it was
- Render content to the HDMI port
- Assert Digital Rights Management, using HDCP
The framework automatically handles the entry and exit of mirror mode. User applications are not involved with managing the transition into and out of screen mirroring.
An application should typically inform the user when it supports dual screen HDMI output. This can be as simple as a message suggesting “Plug in an HD screen for best results”. Whenever HDMI output is enabled, audio is also routed to HDMI, (only one of the DVI resolutions supports audio). HDMI always has landscape screen orientation. The application should ensure that the content it renders to HDMI is always in landscape orientation, too. Changing the device orientation has no effect on the HDMI output when in dual screen mode.
This document assumes that the reader has a working knowledge of EGL APIs and Android NDK functionality.
Overview
- An Android service (the External Display Service) provides an interface for applications to:
- get the current HDMI state
- enable & disable cable detection
- enable & disable HDMI output
- override the service-determined HDMI resolution
- route video content to the HDMI port
- read the HDTV’s EDID
- Apps bind to the service using the
IExtDispServiceinterface described below.
When an app starts rendering content using the HDMI dual screen API, mirror mode is turned off automatically. After an application stops rendering using the HDMI dual screen API, screen mirroring is restored automatically. - Broadcast intents are used to respond asynchronously back to the application. Your app will register for the intents corresponding to the events it wants to track, namely
- cable attach and detach events
- HDMI state change events. HDMI has these states: disabled, enabled, enabled-connected, enabled-disconnected, enabled-connected-active, enabled-connected-inactive
- A native HDCP interface is provided, allowing DRM engines to use HDCP when necessary. (It may be required by the content being sent over the HDMI connection). The HDCP interface is a C API. It is accessed from your C code and is not visible to Java.
Screen resolutions supported in the HDMI API
The term “standard” is used in the HDMI reference document to refer to the configured resolution of the HDMI video output. It is a synonym for resolution. The “standards” supported by the ExtDispService are shown in this code fragment:
static final int HDMI_STANDARD_INVALID = -1; static final int HDMI_STANDARD_AUTO = 0; static final int HDMI_STANDARD_2_3_60HZ = 1; // 720x480P @ 60Hz static final int HDMI_STANDARD_4_60HZ = 2; // 1280x720P @ 60Hz static final int HDMI_STANDARD_17_18_50HZ = 3; // 720x576P @ 50Hz static final int HDMI_STANDARD_19_50HZ = 4; // 1280x720P @ 50Hz static final int HDMI_STANDARD_1_60HZ = 5; // 640x480P @ 60Hz static final int HDMI_STANDARD_16_60HZ = 6; // 1920x1080P @ 60Hz static final int HDMI_STANDARD_31_50HZ = 7; // 1920x1080P @ 50Hz static final int HDMI_STANDARD_PC_SVGA_60HZ = 8; // 800x600 @ 60Hz static final int HDMI_STANDARD_PC_XGA_60HZ = 9; // 1024x768 @ 60Hz static final int HDMI_STANDARD_PC_SXGA_60HZ = 10; // 1280x1024 @ 60Hz static final int HDMI_STANDARD_PC_WXGA_60HZ = 11; // 1440x900 @ 60Hz static final int HDMI_STANDARD_PC_WSXGA_60HZ = 12; // 1680x1050 @ 60Hz static final int HDMI_STANDARD_PC_LD_60HZ = 13; // 1366x768 @ 60Hz
As an example, the HDMI resolution or standard of “4” refers to the resolution (1280 x 720) at 50 Hz. There are six PC or DVI resolutions listed. These support compatibility with additional user configurations, such as the use of an HDMI-to-DVI connector and a non-HDMI monitor. Note that standard 13 is the only DVI resolution that supports audio. These constants are available in the include file hdmi.h.
Connecting with the ExtDispService
The application’s manifest file (AndroidManifest.xml) indicates that the application is using the ExternalDisplayService library via the following element, contained after the <application> tag and before the </application> tag. This tag prevents Android Market from offering the app to devices without the library.
<uses-library android:name="com.motorola.android.iextdispservice" />
There is another uses-library element to represent the native code library used on a platform:
<uses-library android:name="com.motorola.android.nativehdmiapis_v1" />
The version number "v1" may change for platforms with different underlying hardware.
The application Java code uses this import statement to access the ExtDispService library:
import com.motorola.android.extdispservice.IExtDispService;
The ExtDispService uses the Android Interface Definition Language (AIDL) feature to handle IPC between your app, and the process running the service. See Designing a Remote Interface Using AIDL for more information.
The application binds to the service as shown in this example:
private static IExtDispService mService = null;
…
// The application will probably implement its own service connection class.
mConnection = new HDMIServiceConnection();
Intent intent = new Intent(IExtDispService.class.getName());
boolean connected =
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if (connected == false ) {
Log.i(LOG_TAG, "Bind Service Failed!");
}
The service connection class that you implement must get the handle to the service (mService in this example) in the service connection callback, as shown here:
class HDMIServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(LOG_TAG, "onServiceConnected");
// save the handle to the service
mService = IExtDispService.Stub.asInterface(service);
You can now use the mService handle to make calls to the ExtDispService. Your calls to the service should have handlers for catching DeadObjectException exceptions, which are thrown when the connection has broken; this is the only exception thrown by remote methods.
Making a request of the ExtDispService API
ExtDispService offers the functionality shown in the interface below:
package com.motorola.android.extdispservice;
interface IExtDispService
{
// The "type" parameter, used with most functions, is defined as:
// - "1" = HDMI
// - all other values reserved
boolean isSupported(int type);
boolean isDetectionEnabled(int type);
boolean isConnected(int type);
boolean isDisplayEnabled(int type);
boolean enableDetection(int type, IBinder deathWatcher);
void disableDetection(int type);
boolean enableDisplay(int type, IBinder deathWatcher);
void disableDisplay(int type);
boolean enableStandardOverride(int type, IBinder deathWatcher, int std);
void disableStandardOverride(int type);
// Used to configure an output routing path
// Currently supported:
// - key of "nextvideoplayback"
// with route of "" (not to hdmi)
// or "hdmi" (to hdmi only)
boolean setRouting(String key, String route);
// Get the currently configured resolution:
// [0] = w; [1] = h; [2] = rate; [3] = resolution enum value;
int[] getResolution(int type);
// Get a list of all of the supported resolutions
// - platform = true: return resolutions supported by the platform HW
// - platform = false: return resolutions available based on the attached HDTV
int[] getResolutions(int type, boolean platform);
// Get the EDID of the attached HDTV
byte[] getEdid(int type);
// Move the Sign-Of-Life display in front of all content or not
boolean enableSolInFront(int type, IBinder deathWatcher);
void disableSolInFront(int type);
}
The enableStandardOverride() method allows the application to explicitly set the HDMI resolution. You might use this if the handset is connected to a 1080p HDTV and the application is about to play a 1080p video, but the handset is defaulted to 720p. The application can query the HDTV native resolution and override the ExternalDisplayService’s current setting to provide an optimal experience.
DisableStandardOverride() reverts the screen resolution to the setting it had before enableStandardOverride() was called.
EDID is an “Extended Display Identification Data” - a data structure provided by a digital display to describe its capabilities to the mobile device.
Receiving a response from the ExtDispService API
ExtDispService supports the broadcast intents whose action strings are shown in the table below:
static final String EXTDISP_STATUS_SUPPORT =
"com.motorola.intent.action.EXTDISP_STATUS_SUPPORT";
static final String EXTDISP_STATUS_DETECTION =
"com.motorola.intent.action.EXTDISP_STATUS_DETECTION";
static final String EXTDISP_STATUS_CONNECTION =
"com.motorola.intent.action.EXTDISP_STATUS_CONNECTION";
static final String EXTDISP_STATUS_DISPLAY =
"com.motorola.intent.action.EXTDISP_STATUS_DISPLAY";
static final String EXTDISP_STATUS_RESOLUTION =
"com.motorola.intent.action.EXTDISP_STATUS_RESOLUTION";
static final String EXTDISP_STATUS_GLESCLIENT =
"com.motorola.intent.action.EXTDISP_STATUS_GLESCLIENT";
Your app should register to receive intents for the various state changes in which it is interested. The value is delivered in data in the Intent Extras bundle, retrieved with the name “hdmi”.
|
Intent action string |
Semantics of the EXTRA_HDMI field in the Intent |
|
EXTDISP_STATUS_SUPPORT |
true if HDMI is supported by the hardware |
|
EXTDISP_STATUS_DETECTION |
true if detection is enabled |
|
EXTDISP_STATUS_CONNECTION |
true if a cable and powered screen is detected |
|
EXTDISP_STATUS_DISPLAY |
true when the external display is active and displaying content. |
|
EXTDISP_STATUS_RESOLUTION |
provides the current external display resolution and any changes/updates to that resolution |
|
EXTDISP_STATUS_GLESCLIENT |
true if a GL client is active |
For example, to register for connection status so the application will be notified when an HDMI cable is inserted and an active connection is obtained, the application would do the following:
Create the receiver and register for the connection intent
private HdmiBroadcastReceiver mExtConnectionReceiver = null; … mHDMIConnectionReceiver = new HdmiBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(EXTDISP_STATUS_CONNECTION); mContext.registerReceiver(mHDMIConnectionReceiver, intentFilter);
The connection receiver in this example could simply be:
class HdmiBroadcastReceiver extends BroadcastReceiver {
private static final String EXTRA_HDMI = "hdmi";
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
Bundle extras = (intent != null) ? intent.getExtras() : null;
if (action.equals(EXTDISP_STATUS_CONNECTION))
{
if (extras != null)
{
boolean conn = extras.getBoolean(EXTRA_HDMI);
if ( conn == true )
{
Log.i(LOG_TAG, "HDMI Connection Detected!");
// Mirror Mode starts automatically
}
}
}
}
};
Here is the sample code for receiving the resolution in the broadcast receiver.
if (action.equals(EXTDISP_STATUS_RESOLUTION) {
int w = 0, h = 0, i;
if (extras != null) {
String res = extras.getString(EXTRA_HDMI);
for ( i = 0; i < res.length(); i++ ) {
if ( res.charAt(i) == 'x' ) {
w = Integer.parseInt( res.substring(0,i) );
h = Integer.parseInt( res.substring(i+1) );
break;
}
}
}
Log.d(TAG, “res = " + String.valueOf(w) + "x" + String.valueOf(h) + "\n");
}
The receiver can add checks for each of the ExtDispService status broadcasts, and the application can process them in a similar manner.
Rendering to HDMI
Before rendering to HDMI, an active HDMI connection must be established and configured using the Java methods described in the External Display Service section above. Specifically, the enableDisplay() method must be called to enable the HDMI display.
The actual rendering is done in native C or C++ code. Your app will create a special GLES window context and manipulate it using three overridden EGL functions. These functions are exposed as part of the “libhdmi.so” library. The Java portion uses the ExternalDisplayService interface, but all dual-screen rendering is in the native C layer. Here are the 3 functions:
EGLSurface HDMI_eglCreateWindowSurface
(EGLDisplay dpy, EGLConfig cfg, const EGLint *attrib_list);
EGLBoolean HDMI_eglDestroySurface(EGLDisplay dpy, EGLSurface surf);
EGLBoolean HDMI_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf);
Instead of calling the normal EGL function to create a window surface (eglCreateWindowSurface()), a special call, HDMI_eglCreateWindowSurface(), must be used in its place. There are similarly overloaded functions for eglDestroyWindowSurface() and eglSwapBuffers(). These three functions must be used together, in place of the standard EGL calls, for the HDMI context. Other EGL calls can be made as usual. These function calls are only available in native “C” space. Therefore all apps that render to HDMI will have an NDK component and should include the API references in the application’s native C++ code by including header file hdmi_egl.h.
extern "C" {
EGLSurface HDMI_eglCreateWindowSurface(EGLDisplay dpy, EGLConfig cfg,
const EGLint *attrib_list);
EGLBoolean HDMI_eglDestroySurface(EGLDisplay dpy, EGLSurface surf);
EGLBoolean HDMI_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf);
}
Teardown after use
When your application ends, it should unregister its broadcast receiver and unbind from the ExternalDisplayService. For example, in the application’s onDestroy() method:
public void onDestroy()
{
if ( mHDMIConnectionReceiver != null )
{
mContext.unregisterReceiver(mHDMIConnectionReceiver);
mExtConnectionReceiver = null;
}
if (mConnection != null)
{
mContext.unbindService(mConnection);
mConnection = null;
}
}
Digital Rights Management using HDCP
High-bandwidth Digital Content Protection (HDCP) encrypts the data sent over the HDMI cable. Before sending data, the handset checks that the attached HDMI device is authorized to receive it. If so, the handset encrypts the data to prevent eavesdropping as it flows to the HDMI panel. The HDCP API was developed as an Android binder interface and is built as part of the libhdmi.so library. This interface contains two parts, the client to service binder API and a client callback binder API.
Client to service binder API
#ifndef __IHDMIIPC_H__
#define __IHDMIIPC_H__
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include "IHdmiIpcCB.h"
#define HDMI_SERVICE_NAME "motorola.hdmi"
namespace android {
// ----------------------------------------------------------------------------
class IHdmiIpc : public IInterface
{
public:
enum
{ REG_CALLBACK = IBinder::FIRST_CALL_TRANSACTION
, UNREG_CALLBACK
, IS_CONNECTED
, IS_ENABLED
, GET_STANDARD
, IS_HDCP_ENABLED
, IS_HDCP_ACTIVE
, TRY_HDCP
, ENABLE_HDCP
, DISABLE_HDCP
};
DECLARE_META_INTERFACE(HdmiIpc);
// If the callback is not set, there will be no notifications
// returns 0 (success) or -1 (failure)
virtual int registerCallback(const sp<IHdmiIpcCB>& callback) = 0;
virtual void unregisterCallback() = 0;
virtual int isConnected() = 0; // returns 1 (connected) or 0 (disconnected)
virtual int isEnabled() = 0; // returns 1 (enabled) or 0 (disabled)
virtual int getStandard() = 0; // returns HDMI_STANDARD_* (from hdmi.h)
virtual int isHdcpEnabled() = 0; // returns 1 (enabled) or 0 (disabled)
virtual int isHdcpActive() = 0; // returns 1 (active) or 0 (inactive)
// Only a client that has a registered callback may try/enable/disable HDCP
// tryHdcp() means enable HDCP but don't block the output when inactive
virtual int tryHdcp() = 0; // returns 0 (success) or -1 (failure)
// enableHdcp() means enable HDCP and block the output when inactive
virtual int enableHdcp() = 0; // returns 0 (success) or -1 (failure)
virtual void disableHdcp() = 0;
};
// ----------------------------------------------------------------------------
class BnHdmiIpc : public BnInterface<IHdmiIpc>
{
public:
virtual status_t onTransact( uint32_t code
, const Parcel &data
, Parcel *reply
, uint32_t flags = 0
);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif //__IHDMIIPC_H__
Client Callback API
#ifndef __IHDMIIPCCB_H__
#define __IHDMIIPCCB_H__
#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
namespace android {
// ----------------------------------------------------------------------------
class IHdmiIpcCB : public IInterface
{
public:
enum { NOTIFYCB = IBinder::FIRST_CALL_TRANSACTION };
DECLARE_META_INTERFACE(HdmiIpcCB);
/* For notifyCallback:
*
* o type = UNREG_CALLBACK, no extras
* - Denotes that the registered callback binder is unregistered
* o type = HDCP_ACTIVE, ext1 = <1 = active; 0 = inactive;>
* - Denotes an HDCP active state while enabled
* o type = STATE_CHANGE, ext1 = <what changed>, ext2 = <new state>
* - Denotes that an interface state has changed
* - Possible changes: CONNECTION, ENABLE, STANDARD, HDCP_ENABLE
*/
enum { UNREG_CALLBACK = 100, HDCP_ACTIVE, STATE_CHANGE };
enum { CONNECTION = 1, ENABLE, STANDARD, HDCP_ENABLE };
virtual void notifyCallback(int32_t type, int32_t ext1, int32_t ext2) = 0;
};
// ----------------------------------------------------------------------------
class BnHdmiIpcCB: public BnInterface<IHdmiIpcCB>
{
public:
virtual status_t onTransact( uint32_t code
, const Parcel &data
, Parcel *reply
, uint32_t flags = 0
);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // __IHDMIIPCCB_H__
HDMI Limitations
- Inserting an HDMI cable does not wake a sleeping device. HDMI cable detection requires the phone to provide a 5V supply to detect a cable insertion. If this were provided all the time, it would quickly deplete the battery. Therefore, the HDMI 5V supply (and thus HDMI cable detection) is enabled only when the platform LCD display is enabled. We do not detect an HDMI cable insertion when the LCD is off, hence inserting an HDMI cable will not wake up the device from sleep.
- There is a minimum one to two second lag detecting a cable insertion (this is the case for most removable peripherals). Some HDMI panels have longer detection/switching times. HDMI may take anywhere from 2-7 seconds to display. Extensive logcat messages have been provided to aid in debugging.
- Some monitors require the source device to render content as soon as the connection is made. To avoid any issues, Motorola devices have a “sign of life” (SOL) feature, which activates the HDMI output and renders a bitmap as soon as a HDMI device is detected. This also reduces the typical multi-second HDMI enable sync lag, as an HDMI signal is already present and synchronized. The SOL feature is part of the Motorola HDMI driver. Applications do not need to implement their own.
Overscan
Overscan is only applicable to Mirror Mode. If a dual-screen application needs to address overscan, you will need to develop a solution independently.
Building your application
So that you can successfully compile code that employs this Motorola-specific API, Motorola has created a JAR file with the necessary stubs that you must include in your project. To get this JAR file, download the SDK add-on for one of the supported devices—for example, DROID X2. In the SDK add-on, the hdmi.jar file is located in the libs folder, and detailed build procedures are provided in docs/README_HDMI_DualScreenAPI.txt. The README also provides instructions for loading the API stub file and building/testing the sample app included in the SDK add-on.
Disclaimer and License
Android does not have any standard HDMI support as of API level 8. This document describes Motorola’s HDMI solution to be used with certain Motorola handsets that include an HDMI port shipping in 1Q 2010 and beyond. Motorola’s solution may change if Google introduces HDMI support in future versions of Android.
Created: January 24, 2011
Updated: December 12, 2011
ECCN 5D992.a: In accordance with United States Export Administration Regulations (EAR), and specifically the Commerce Control List (CCL), this item has been classified 5D992.a. Export or re-export of this commodity and compliance with the U.S. Export Administration Regulations is ultimately the responsibility of the exporter. For more detailed information related to export or re-export of this item, please consult the EAR at http://www.access.gpo.gov/bis/ear/ear_data.html.
Copyright © 2011, Motorola Mobility, Inc. All rights reserved unless otherwise explicitly indicated. Sample source code written by Motorola Mobility, Inc. is provided to you under the conditions of the Motorola Modified BSD License.
Share This Page
Table of Contents
- Supported devices
- Overview
- Screen resolutions supported in the HDMI API
- Connecting with the ExtDispService
- Making a request of the ExtDispService API
- Receiving a response from the ExtDispService API
- Create the receiver and register for the connection intent
- Rendering to HDMI
- Teardown after use
- Digital Rights Management using HDCP
- HDMI Limitations
- Overscan
- Building your application
- Disclaimer and License



