Enough theory, let's look past the header at some source code and explore how it works. In order to get at the implementation of an extension, you'll need to start with how Brew knows where to find it: the MIF
The first step in accessing your own custom extension (and the sample extension in this article) is to obtain a new class ID from Qualcomm's Web site. This requires a login to the Brew extranet. Qualcomm will issue you a unique class ID for your extension. This hexadecimal number is unique to your object. Once your string reversal extension is installed on a phone, any Brew application with the correct permissions can call ISHELL_CreateInstance, pass in this unique ID, and your AEEClassCreateInstance function will be called. The MIF file makes this transaction possible by tying the unique class ID to your specific function within your module file. Here is what the creation function looks like:
int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * pMod,void ** ppObj)
*ppObj = NULL;
if(ClsId == AEECLSID_IREVERSAL)
if(AEEClsNew_Reversal(pIShell, pMod, ppObj))
In the above listing, your AEEClsCreateInstance
is called, and you respond by returning the result of your constructor. It's important to return true only when your function is called with the correct class ID. You now know how Brew finds your code to build a new extension, so you can look at how the process of building a new extension takes place (see Listing 1
Let's break down what's happening in Listing 1. Once you've declared your structures and verified what's been passed in, you'll need to start grabbing memory. Your extension will need enough memory for the private structure and the vtable. You'll allocate enough for both at the same time and set the vtbl pointer to the end of the private function. After you know there's enough memory for the object and the vtable, you need to set the vtbl pointer to the appropriate location: the block of memory after your private object. Accomplish this with the following line:
vtbl = (IReversalClsVtbl *) ( (byte *)pMe + sizeof(IReversal));
Now, with memory in hand and the basic pointers in place, it's possible to set up all the individual functions and fill in the vtable
. From this point on, the process is fairly straight-forward. Set all the variables in the vtable
to their corresponding functions and tie the vtable
into the private object using INIT_VTBL
. Then stash aside the module and shell pointers for later use, set up the return pointer, initialize any internal variables, and return Success
| Tip: The Brew heap manager (in Brew 3.x and greater) knows your extension is de-allocated when the reference count of module objects in your application reaches zero. If you don't keep a reference for your module or leak it, you'll find the handset crashing in some very creative ways. Additionally, if you don’t release your module pointer Brew will think your application is still open and won’t validate your heap. If this happens, in the simulator, you will not get memory leak reports. Be very careful with the module pointer count.
Now that you've got a fully functional extension, you can move on to at how it's used. To quickly recap:
- A user will call ISHELL_CreateInstance and pass in your class ID.
- In turn, you will have your AEEClsCreateInstance function called and return an instance of your extension.
- With that object, the user may then use your accessor macros to execute your functions.
- These macros look up the location of your functions in memory, pass in the correct parameters, and you're locked and loaded.
Take a look at a simple function GetString
static uint32 IReversal_GetString(IReversalCls *pIRevCls, char **ppszResult)
IReversal *pMe = (IReversal *)pIRevCls;
*ppszResult = (char *)MALLOC(STRLEN(pMe->szString) + 1);
The first line of any extension function is going to be a pointer cast. It converts the single item structure containing only the vtable
to the internal function containing all your private data. Recall that, in order for this cast to work, the vtable
must be the first element in both structures. Once you have access to your pMe
pointer, this function should be straight forward. You're simply returning the string kept inside the private data.
Recall, from your C programming book gathering dust on the shelf, that functions declared static can only be referenced from within the file in which they're defined. This piece of C lore, in combination with the vtable, is what makes obfuscation with extensions possible. Users cannot reference your functions by name directly, but you can reference them as you set their addresses up in the vtable. This is the secret sauce that allows the user to access the static functions from outside the file in which they're defined.
Your last challenge is to release the module without setting off the memory leak alarm. This is the release function for your example extension:
static uint32 IReversal_Release(IReversalCls *pIRevCls)
IReversal *pMe = (IReversal *)pIRevCls;
//Check our Reference count. If it's not at one we should just
//decrement the count and return
if(pMe->nRefs > 1)
//Release C++ object
The purpose of the function listed above should be clear: if you have more than reference and the user calls release, you'll decrement the reference count and return. Otherwise, you'll clean up the internal data and release the object.
| Tip: Notice that you return the current reference count on release and only de-allocate if the count is at 0. This behavior is consistent with all Brew objects. If you're having trouble tracking down a leak and you think it might be an extension (Brew object, widget, or otherwise) check the number returned from the last time you've called release. If it isn't 0, there's still a reference floating around and you've just found the source of your problem.
As you can see, the process of creating, managing, and releasing a new object is fairly complex, but in reality the task changes very little from extension to extension. When working on your own, I would recommend that you copy this example as a template and fill in your own functions over mine. I've found it to be the easiest way to crank these things out.