The conventional model for Java applications doesn’t work very well in a digital TV environment. The basic Java application model assumes that only one application is executing in a given virtual machine, and that the application itself is in complete control of its lifecycle (which can include killing the Java VM that it’s running in). This is less than ideal for a digital TV receiver, where several applications may be running at the same time, and where there is a need to enforce some separation between the applications. Luckily, we already have a good starting point that gives us something pretty close to what we need: applets. Unlike a normal application, these can be started by something other than a command line (in the case of applets, this is the web browser), and several of them can execute in the same web page at the same time.
Of course, the digital TV world is not the same as the Web, and so some changes had to be made in order for this kind of concept to work well in a digital TV receiver. The result is known as Xlets. These are a concept similar to applets, that have been introduced by Sun in the JavaTV specification and adopted as the Java application format for MHP and related digital TV standards. Like applets, the Xlet interface allows an external source (the application manager in the case of a digital TV receiver) to start and stop an application, as well as controlling it in other ways. The Xlet interface is found in the javax.tv.xlet
package:
public interface Xlet { public void initXlet(XletContext ctx) throws XletStateChangeException; public void startXlet() throws XletStateChangeException; public void pauseXlet(); public void destroyXlet(boolean unconditional) throws XletStateChangeException; }
If you compare this to the java.applet.Applet
class, you’ll notice some similarities. Like the applet class, the Xlet has methods that allow the application manager to initialize it, start, and stop it. There are some major differences, however, between Xlets and applets.
The biggest of these is that an Xlet can also be paused and resumed. The reason for this is very simple – in an environment like a digital TV receiver several applications may be running at the same time, hardware restrictions mean that only one of those applications may be visible at any time. In this scenario, non-visible applications must be paused in order to keep resources free for the application which is visible.
An Xlet is also much simpler than an applet – broadcasters and box manufacturers are paranoid, and so Xlets are in some way more limited in what they can do when interacting with their environment. Some of the other tasks that can be carried out via the Applet
class are supported in standards like MHP or JavaTV, but other APIs must be used to do them.
So, an Xlet has four main states – Loaded, Paused, Started, and Destroyed. If we examine the lifecycle of an Xlet, we can see where these states fit into the overall picture:
- The application manager loads the Xlet’s main class file (as signalled by the broadcaster) and creates an instance of the Xlet by calling the default constructor. This can happen at any point after the application is signalled. Once this has happened, the Xlet is in the Loaded state.
- When the user chooses to start the Xlet (or the AIT or other application signalling indicates that the Xlet should start automatically), the application manager in the receiver calls the
initXlet()
method, passing in a newXletContext
object for the Xlet.The Xlet may use this
XletContext
to initialize itself, and to preload any large assets such as images that may require some time to load from the object carousel. When the initialization is complete, the Xlet is in the Paused state and is ready to start immediately. - Once the
initXlet()
method returns, the application manager calls thestartXlet()
method. This will move the Xlet from the Paused state into the Started state, and the Xlet will be able to interact with the user. - During the execution of the Xlet, the application manager may call the
pauseXlet()
method. This will cause the application to move from the Started state back to the Paused state. The application will later be moved back to the Started state by calling thestartXlet()
method again. This may happen several times during the Xlet’s life. - At the end of the Xlet’s life, the application manager will call the
destroyXlet()
method, which will cause the Xlet to move into the Destroyed state and free all its resources. After this point, this instance of the Xlet cannot be started again.
It’s important to remember that an Xlet is not a standard Java application. There are many important differences between the two. An Xlet is conceptually much closer to an applet. Like an applet, there may be more than one Xlet running at any one time, which means that Xlets should not take certain actions that will globally affect the Java virtual machine. For instance, an Xlet should never, ever, EVER call the System.exit()
method because it may not be the only application running in the VM at the time. Some more do’s and don’ts are listed below.
Xlet Contexts
As we have already mentioned, each Xlet has a context associated with it – an instance of the javax.tv.xlet.XletContext
class. This is similar to the AppletContext
class that is associated with an applet. In both cases the context is used to provide a way for the application to get more information about its environment and to communicate any changes in its state to its environment.
public interface XletContext { public static final String ARGS = "javax.tv.xlet.args" public void notifyDestroyed(); public void notifyPaused(); public void resumeRequest(); public Object getXletProperty(String key); }
The notifyDestroyed()
and notifyPaused()
methods allow an Xlet to notify the receiver that it is about to terminate or pause itself. The Xlet can use these to make sure that the receiver knows the state of every application and can take appropriate action. These methods should be called immediately before the Xlet enters the Paused or Destroyed states, because the receiver may take action that the application is otherwise unprepared for.
An application can request that it be moved from the Paused state back to the Started state using the resumeRequest()
. This may happen when a given event has occurred, for instance a certain time is reached or a certain event is detected in the MPEG stream. This effectively allows an application to ‘sleep’ for a while. Having said that, this method does only request that an application is started again – the receiver software may choose to ignore this request due to resource limitations, display issues or simply because it’s feeling cruel.
The diagrams below show exactly what happens when an Xlet requests a change of state via its Xlet context. In this case, the Xlet will pause itself and then requests to resume.
- First, the Xlet notifies its Xlet context that it has paused, by calling the
XletContext.notifyPaused()
method. - The Xlet context then passes this information on to the application manager in the middleware.
- The application manager then updates its internal state to reflect that the application has paused, and the Xlet goes to sleep.
- When the application wants to resume operation (e.g. because a certain time has been reached, or because the user has pressed a key), it calls the
XletContext.requestResume()
method. - As before, the Xlet context passes this request on to the application manager.
- The application manager will then decide whether to restart the application or not. If it does, then it will update its internal state to reflect this change and then call the
startXlet()
method on the Xlet. Like all other operations that control the lifecycle of the Xlet, this method is called directly from the application manager and does not go via the xlet context. - The Xlet will then resume operation.
The getXletProperty()
method allows the Xlet to access properties that are defined for it in the information signalled by the broadcaster. At present, only one property is defined by JavaTV and MHP. The property name given by XletContext.ARGS
enables an application to access any arguments that are given to it in the application signalling (the AIT), since the Xlet model does not directly allow for command-line arguments to be passed to it. MHP also defines the following Xlet properties, which are also used by OCAP:
dvb.app.id
– the application ID of the application, as indicated in the application signallingdvb.org.id
– the organization ID of the application, as indicated in the application signallingdvb.caller.parameters
– the parameters passed to this application if it was started by a mechanism other than application signalling.
The main difference between the XletContext.ARGS
property and the dvb.caller.parameters
property is that the former refers to parameters passed in via application signalling while the latter refers to parameters passed in via the MHP application listing and launching API. In the latter case, the XletContext.ARGS
property will still carry the arguments as they are signalled. For applications that have not been started via the MHP application listing and launching API, dvb.caller.parameters
will contain an empty string.
As well as these Xlet properties, a number of system properties are available to the application. These can be read using the standard System.getProperty()
method. The table below shows which properties are available to JavaTV, OCAP and MHP applications.
System Property | JavaTV | OCAP | MHP |
---|---|---|---|
path.separator
Path separator in filenames (will always be ‘/’ in MHP) |
|||
dvb.persistent.root
Root directory for persistent storage |
|||
dvb.returnchannel.timeout
Timeout period (in seconds) for return channel connections |
|||
mhp.profile.enhanced_broadcast
Indicates whether the enhanced broadcast profile is supported (will always be "YES" since this is the minimum profile) |
|||
mhp.profile.interactive_broadcast
Indicates whether the interactive broadcast profile is supported ("YES" if it is, "NO" or |
|||
mhp.profile.internet_access
Indicates whether the Internet access profile is supported ("YES" if it is, "NO" or |
|||
mhp.eb.version.major
Major version number of the supported enhanced broadcast profile, or |
|||
mhp.eb.version.minor
Minor version number of the supported enhanced broadcast profile, or |
|||
mhp.eb.version.micro
Micro version number of the supported enhanced broadcast profile, or |
|||
mhp.ib.version.major
Major version number of the supported interactive broadcast profile, or |
|||
mhp.ib.version.minor
Minor version number of the supported interactive broadcast profile, or |
|||
mhp.ib.version.micro
Micro version number of the supported interactive broadcast profile, or |
|||
mhp.ia.version.major
Major version number of the supported Internet access profile, or |
|||
mhp.ia.version.minor
Minor version number of the supported Internet access profile, or |
|||
mhp.ia.version.micro
Micro version number of the supported Internet access profile, or |
|||
mhp.option.ip.multicast
Has a value of "SUPPORTED" if the system supports IP multicast in transport streams |
|||
mhp.option.dsmcc.uu
Has a value of "SUPPORTED" if the system supports DSM-CC User-To-User protocol over the return channel |
|||
mhp.option.dvb.html
Has a value of "SUPPORTED" if the system supports DVB-HTML applications |
|||
mhp.stored.services
Flag showing how much memory is reserved for stored services and applications (if any memory is reserved). This does not reflect how much of that memory is actually free. (MHP 1.1. only) |
|||
mhp.smartcard.reader
Has a value of "SUPPORTED" if the system has a smart card reader accessible using the smart card API (MHP 1.1. only) |
|||
mhp.option.memorycard
Has a value of "SUPPORTED" if the system supports memory card devices |
|||
mhp.option.opentype
Has a value of "SUPPORTED" if the system supports OpenType font technology |
|||
mhp.option.highdef
Has a value of "SUPPORTED" if the system supports high-definition TV |
|||
given by org.havi.ui.HVersion. HAVI_SPECIFICATION_VENDOR
Vendor name of the HAVi specification. Will always be "DVB" for MHP systems |
|||
given by org.havi.ui.HVersion. HAVI_SPECIFICATION_NAME
Name of the HAVI specification that is implemented. Will always be "MHP" for MHP systems |
|||
given by org.havi.ui.HVersion. HAVI_SPECIFICATION_VERSION
Version of the HAVi specification that is implemented. This will be the same as the version number of the MHP specification that is implemented by the receiver |
|||
given by org.havi.ui.HVersion. HAVI_IMPLEMENTATION_VENDOR
Name of the vendor for the HAVi implementation (e.g. the middleware vendor) |
|||
given by org.havi.ui.HVersion. HAVI_IMPLEMENTATION_VERSION
Version of the HAVi implementation |
|||
given by org.havi.ui.HVersion. HAVI_IMPLEMENTATION__NAME
Name of the HAVi implementation |
Do’s and don’ts for application developers
We’ve already seen that applications should not call the System.exit()
method. But there are a few other things that Xlets should do, depending on their state:
- The
destroyXlet()
method should remember to kill all application threads and cancel any existing asynchronous requests that are currently outstanding in the service information and (in the case of MHP applications) section filtering APIs. - The
destroyXlet()
(and ideally thepauseXlet()
) methods should free any graphics contexts that the application has created. The middleware will maintain references to these unless they are disposed of properly with a call tojava.awt.Graphics.dispose()
- The application should remember that it may be paused or destroyed at any time, and should make sure that it can always clean up after itself.
- The application should respect the restrictions that may be imposed on the core Java APIs. Both MHP and OCAP define a number of restrictions on classes in
java.lang
and other packages that application developers should be aware of. - Resource issues are especially important in an digital TV environment, as we will see later. An application should cooperate with other Xlets where possible on resource issues, and especially should not keep scarce resources longer than it has to.
- Remember that your application should be as reliable as possible. If a method throws an exception, catch it. Exceptions get thrown for a reason.