Writing BREW Extensions
One of the key differences between QUALCOMM BREW and other smart phone operating systems is its component-oriented approach to software development. Nearly every API you use when writing a BREW application is actually part of one or more software components, individually packaged by QUALCOMM or the handset OEM as part of the BREW runtime. Like most component-oriented environments such as the Component Object Model (COM), it's possible for you to create your own component, called an extension, that looks and behaves just like any other BREW interface, such as IShell or IDisplay. The extension you create can contain one or more BREW classes, and can be private to your application, or public for other applications (either yours or the world at large) to use.
Why use the extension model, when things like C modules or classes provide ample opportunities for modularity? BREW extensions provide several advantages over classes or modules for major functional areas of your application such as data storage, protocols, or codecs, including:
- You can make an extension that other companies can license and from which you can make money using the same model as with other BREW applications, giving you additional opportunities for revenue.
- Extensions are shared code outside your application; the handset downloads only one copy of an extension's code regardless of how many applications use the extension, letting you save valuable space on the handset's file system.
- Because extensions live outside your application, they're an ideal way for multiple teams at your firm to isolate development to specific functional areas.
- Extensions follow the same paradigm as other BREW interfaces, making it easier for new team members (or customers using your public extensions) to learn how to use your libraries.
- Unlike modules, classes, or even libraries, BREW extensions are served by the BREW Delivery System (BDS) when needed by an application. Thus, if your application relies on an extension, when a customer purchases your application, the extension is downloaded automatically. More importantly, if a customer purchases a second application that uses the same extension, it's not downloaded again. Network and storage use is minimized by the fact that the extension is a shared component between multiple applications, in the same way that dynamically linked libraries save memory and disk space under Linux, Windows, and other desktop and server operating systems.
Crafting a BREW Extension
What makes a good BREW extension? Almost anything you identify as a candidate for reuse between applications that doesn't have a user interface, including:
- Codecs for image, audio, or video presentation. Ideally, these implement the BREW IMedia interface (or IImage for static images), so that your customers can treat your codec as if it were bundled with BREW.
- Data storage for data shared between applications, such as personal information managers.
- Protocol handlers for Internet- or SMS-based protocols.
- New user interface components that extend the BREW UI Toolkit's widgets, or even the older BREW IControl based hierarchy of components.
Some successful BREW developers have gone as far as breaking the bulk of their applications up into shared extensions, so the actual application you see in fact simply leverages a handful of extensions shared among their products. Other companies, such as M7 Networks and Truvideo, provide extensions to application developers, bringing high score management, video playback, and other features to other companies' applications. Finally, many Original Equipment Manufacturers (OEMs) use BREW extensions under the hood when building the applications that make up a cell phone's user interface.
A BREW extension has at least three components: its Module Information File (MIF), a header declaring its public interface, and its private implementation. Users of your extension include only its public interface header, whereas when you provide your interface to other applications through the BDS, you're making both the MIF and the private implementation (compiled as a conventional BREW module, a .MOD file) available.
Clients of your extension create new instances of the classes it contains using ISHELL_ClassCreateInstance, just as they would any other BREW class. To ensure this linkage works correctly, it's important to define your extension's class IDs correctly in your extension's MIF (see Figure 1). To do this:
- Launch the BREW MIF Editor.
- Select the "Extensions" tab.
- Click the "New..." button.
- Select the BID file for your extension's class, or enter the class ID.
- Continue steps 3-4 for each class in your extension.
- Save your MIF file.
Figure 1: Setting an extension's class IDs in the MIF.
The other thing you need to do is create the public interface for your extension. To do this, you need to create a header file that defines the virtual table (also called simply a vtable) for the classes in your extension and the macros to dispatch against the vtable, along with any other structures or macros required by your extension's classes. Creating the vtable is easy to do, even working in C, thanks to the AEEINTERFACE_DEFINE and AEEGETPVTABLE macros provided by Qualcomm in AEEInterface.h.
The AEEINTERFACE_DEFINE lets you define the vtable for a class. For example, consider an extension to manage a user's to do list, presumably as part of a simple personal information manager (PIM); the interface to the task database might resemble that of the IDatabase interface already provided by BREW. To define the vtable for the task database, named ITaskDatabase, I write:
#define INHERIT_ITaskDatabase( iname ) \ INHERIT_IQueryInterface( iname ); \ void (*Reset)( iname *); \ ITaskRecord * (*GetRecordByID)( iname *, uint32 ); \ ITaskRecord * (*GetNextRecord)( iname * ); \ uint32 (*GetRecordCount)( iname * ); \ ITaskRecord * (*CreateRecord)( iname * ) AEEINTERFACE_DEFINE( ITaskDatabase );
The first preprocessor instruction defines a new vtable INHERIT_ITaskDatabase for a class descended from the IQueryInterface class. This vtable has five methods, along with the three defined by IQueryInterface itself. In turn, other classes can declare themselves dependent on this vtable using this macro. The second directive, itself a preprocessor macro, creates a new BREW class named ITaskDatabase using the INHERIT_ITaskDatabase macro itself—in essence, defining ITaskDatabase as the interface that inherits from ITaskDatabase, which would a circular definition except for the first macro declaration.