Archive for the ‘BREW’ Category

What platform should I write my app for?

Monday, April 28th, 2008

When we set up Airsource, we set it up as a BREW consultancy. We rapidly sold a number of BREW projects, and built on the expertise we had acquired while at QUALCOMM. In the process, however, we inevitably found ourselves working on other software platforms, particularly on Series 60, which now accounts for about half of Airsource’s work. Series 60 and BREW are often held up as competitors, though in practice I would argue quite strongly that they target very different markets.

A BREW phone, such as the Motorola V3M has a primary display is 176×220, it has 23MB of memory, and a processor clocked at perhaps 40MHz. A Nokia E65 costs nearly twice as much, has twice as many pixels, can store five times as much, and runs fives times faster. Pundits will immediately point out that the V3M is a pretty slow example of a BREW phone. That’s certainly true, but the V3 series accounts for a very significant fraction of the US market for BREW phones. The Nokia E65 is similarly chosen for comparison as an example of a popular Series 60 v3 phone.

Comparing the two is pointless. The BREW phone is clearly a much lighter-weight platform, targetted primarily at games. The Symbian phone has considerably more memory, more storage, faster CPU, and generally faster network data transfer. It is also significantly harder to program for - in the same way that coding for a Windows XP PC requires rather more knowledge than writing a program in BBC BASIC did. A Symbian phone is simply more capable than a BREW one, and consequently the APIs are richer, and the learning curve steeper.

That’s not to say that that Airsource’s clients do not want to target their application at both BREW and at Symbian. They absolutely do. But the Symbian application will always have more functionality and tighter device integration than the BREW application, in the same way that a dedicated BREW application will have more functionality and tighter device integration than a web-based application. Clients come to us because they want to get the most out of the phone, and to do things that are non-trivial, whether that be linking into the messaging menu of a Symbian phone, porting a multi-process application to BREW, or simply writing an application that Just Works.

What this means is that while the mobile market is certainly split into markets that may not directly compete with every other platform, they form an overlapping whole that represents the mobile space. Companies want to get their application “on mobile”, and by that, they mean on the maximum number of phones, with users who will spend money, for as little cost as possible. Choice of platform, like Symbian, or BREW, is therefore just a business decision, not a technical one.

Using ILogger

Friday, November 2nd, 2007

It’s strange how many BREW specialists you can talk to who have never used ILogger. It’s not hard to see why, though: the API reference only sometimes tells you the details of how to use it, and while the ILogger overview notionally tells you what it does, you have to read it very carefully to figure out how to use it.

And when you find out what it does, it doesn’t seem like the most appropriate thing to use.

Because you don’t want to be bored with the details (otherwise you’d be reading the API reference, hoping to find out what you’re missing), I’ll give you a code example:

#include "AEELogger.h"
void log_something(AEEApplet* pMe)
{
    int bucket = 0;

    ILogger *pILogger;

    // Error checking has been omitted in this example for clarity
    ISHELL_CreateInstance(pMe->m_pIShell,
                                    AEECLSID_LOGGER_WIN,
                                    (void**)&pILogger);
    ILOGGER_SetParam(pILogger,
                              AEE_LOG_PARAM_FILTER_ONE,
                              bucket,
                              (void*)TRUE);

    // Do the actual logging
    LOG_TEXT(pILogger, bucket, "Hello world");

    // Cleanup
    ILOGGER_Release( pILogger );
    pILogger = NULL;
}

As mentioned in the comments, error handling has been omitted: if ISHELL_CreateInstance() fails, your app will crash (ISHELL_CreateInstance(), ILOGGER_SetParam(), and LOG_TEXT() return error codes; ILOGGER_Release() returns the ramining reference count). In English, what it does is pretty simple:

  1. Create an ILogger instance, which logs to the BREW output window.
  2. Enable log bucket 0.
  3. Log a test message.
  4. Release the logger, and set the pointer to NULL (generally good practice).

The log output appears a bit cryptic at first: the log message is prefixed with the log bucket, the record type, your app’s class ID, and the ILogger’s instance ID.

There are three classes implementing ILogger mentioned in the API references:

  • AEECLSID_LOGGER_FILE logs to a file
  • AEECLSID_LOGGER_SERIAL logs to the serial port, and
  • AEECLSID_LOGGER_WIN logs to the BREW Simulator output window.

Already, you’ve probably realised something: It doesn’t log to the serial port or BREW output window as appropriate, unlike DBGPRINTF(). Of course, you can probably do something with an #ifdef. [1]

You set parameters with a call to ILOGGER_SetParam() (parameters are defined in AEELogParamType). While param is usually a uint32, pParam is sometimes a uint32pretending to be a void*, and the documentation isn’t that clear on this.

There are 256 log buckets, which can be filtered by the app (call ILOGGER_SetParam and set AEE_LOG_PARAM_FILTER_ONE with the bucket in param and (void*)TRUE or (void*)FALSE in pParam), and by the log parser, so it’s good for tagging different sections of code so you can switch logging for, say, your network code on at runtime. There are also 256 instance IDs, and the default is 0; you set it by setting AEE_LOG_PARAM_INSTANCE_ID. If you do it at runtime with some sort of thread ID, you can distinguish between threads.

There are 65536 record types, defined in AEELogItemType:

  • 0 is text,
  • 1 is a “binary message”,
  • 2 is a “binary block” (a BLOB), and
  • anything 0×100 or over is in a “user-defined format”.

A binary message is typically produced from ILOGGER_PutMsg(). The format is defined in AEELogBinMsgType, but to summarise, they’re fixed at 112 bytes (supposedly this makes logging faster), and store four uint32s and two strings totalling 80 bytes (including null terminators); the first uint32 is meant to be __LINE__ and the first string is meant to be __FILE__ (and standard log parsers probably assume this). The current implementation (3.1.5 simulator) also outputs some contents of your stack if you don’t fill 80 bytes with the two strings, this is a security risk (it ought to zero them anyway to make log entries cleaner).

A binary block is just a byte array, and you’ll have to find a way of distinguishing between them (like user-defined formats, or a prefix string, or XML).

If you’re logging to a file, you’ll have to call ILOGGER_SetParam() with AEE_LOG_PARAM_FILE_NEW and a file name (drive letters don’t work; for testing, just use something like “LogFile”). You need to #include "AEEFile.h" for the file modes.

You’ll notice that logs are actually in a binary format. It’s defined in the API (AEELogRecord, AEELogRcdHdrType), but I haven’t found something that will parse the log files, let alone let you filter for specific buckets and class IDs, let you have pluggable pretty printing for your user-defined log format, or save a BLOB to a file. ILogger could prove to be a useful general logging tool (all apps spam the serial port, and the receiver filters), but for most purposes, it’s a bit overkill.

[1] The BREW Tools Suite’s BREW Logger is meant to be able to connect to the BREW Simulator, but I haven’t managed this. Anyone?

All jobs filled…

Monday, October 8th, 2007

One pile of CVs later, after 53 days and a whole bunch of interviewing, we have finally filled our roles. The Airsource team is about to double! This means we’re not currently recruiting. I would say that speculative applications from good candidates are always welcome, but until we get our new office, that’s just not true. Unless you are a bat or a sloth, and can code whilst hanging upside-down from the ceiling.

Looking for a new office is another task that’s been occupying our time. We’ve found one, over four times bigger than our current pad, in the centre of town. We’re just going through all the legals, which apparently could take quite a while. In the meantime, the next couple of months are going to be pretty cosy.

Airsource are recruiting

Wednesday, August 15th, 2007

If you’ve been following our blog, you’ll notice things have been a bit quiet recently. Business is good - and we need another software engineer. If you’re good, and willing to work in Cambridge (that would be Cambridge UK, for our US readers), then we’d love to hear from you. Experience counts, but so does passion and talent.

Degrees? We’ve met PhDs who can’t, and 15 year-olds who can. If you’re looking for your first full-time software job, then you will learn more in a month with us than in a year at a big corporate. If you’ve been watching your company fail to learn from history for, well, what seems like long enough to *be* history, then come talk to us. If you want to be in early on a successful startup, whether in a junior or senior role, send your CV to jobs@airsource.co.uk. More details at http://www.airsource.co.uk/jobs.

Agency emails will go straight on our spam filter. If we want an agency, we’ll call you.

DUMA Release

Tuesday, July 17th, 2007

As promised, here’s the release of DUMA for BREW, announced at BREW 2007. It’s a library that helps debug memory problems, and Airsource have ported it over to to BREW. Download it here - it includes a test program, and full documentation of how to use it.

DUMA Release Date

Friday, July 13th, 2007

I know that some of you out there have been waiting for our release of DUMA for BREW, which we announced in our talk at BREW 2007. We’re still finalising the documentation for this - but it will be out on Tuesday 17th July. Check back here then, and in the meantime, sorry for the delay!

BREW Conference 2007

Monday, July 2nd, 2007

Setting up the Airsource stand

The Airsource team are now back in the UK after BREW 2007. We’ve had a good post-conference debrief, which was particularly interesting for me, the CTO of Airsource, as it was my first BREW conference. I have a few thoughts I’d like to share.

We found the conference invaluable for meeting new contacts, both leads and possible partners, and our stand definitely enabled us to meet leads who we wouldn’t otherwise have spoken to. As a relative newcomer, we met a lot of people who I suspect we would not have encountered had we not had a stand - and if we can convert any of these into sales, then it will definitely have been worth it.

What I’m less certain about is the technical talk we gave. We’re still eagerly awaiting the speaker evaluations, though the initial feedback we’ve had is good, and we’re hopeful that we gave a good impression of Airsource. However, there are two doubts in my mind about the talk.

Firstly, I’m far from convinced that a single 50 minute talk is the best way to educate an audience of people on a technical level. Presumably our audience were primarily embedded software engineers, and my experience of that community is that they like to read, and are perhaps not so adept at listening. I read (and write) a lot of technical articles, and in retrospect I think that the time we spent constructing, practising, and delivery our talk, might have been better spent on writing a set of good articles to be digested at leisure by the technical community, to be followed up by an in-person Q&A session. I had some quality one-to-one discussions with QUALCOMM engineers while at the conference, with output which was far more valuable and interesting than any talk could possible be.

Secondly, and more seriously from a business perspective, the talk we gave took at least two man-weeks to prepare (I’ll have to check our timesheets for precise figures when I get back to the office), and that’s a considerable investment for a small company. We need to be certain, if we are to repeat this exercise next year, that it will reap benefits. I’m not convinced. It may improve our standing in the developer community. It may, just possibly, improve our Google ranking. It certainly helped my personal development in a number of ways. But will it lead to cash on the table? I’m dubious. If we can help the BREW community while growing our business, then we will certainly do so, but to spend two man-weeks a year on essentially pro-bono activity is simply not going to be sustainable for us.

One approach might be for us to give a more business-related, less technical talk next year. However, feedback has been that there isn’t really much truly advanced programming matter in the technical tracks. The BREW community seems to want more developer information, not less. We certainly do… Part of the problem, perhaps, is that the compensation for speaking is a free conference ticket - which is not exactly compensation for the time and investment in creating a worthwhile talk.

I’m aware that a number of BREW developers who weren’t previously aware of our blog will now be reading it, and I’d very much appreciate any feedback about what you felt about the talks this year. Do you feel that the talks need to be more technical? Did you speak, and if so, do you think it will help your business? Either email us, or post a comment here on the blog. We look forward to hearing from you!

The backdrop

Friday, June 22nd, 2007

The full backdrop from the Airsource stand at BREW 2007. We want visitors to know that if they need BREW, they need to talk to Airsource!

Posted by ShoZu

RVCT 3.0 Released

Wednesday, June 20th, 2007

If you’ve been paying close attention to the grapevine, you will have noticed that today ARM has announced the release of an upgrade to the RVCT toolchain for BREW. For those who aren’t aware, RVCT gives BREW developers access to a version of their ARM compiler, linker and associated tools which can be used for creating applications optimised for running on device.

If you looked closely at the announcement you would have seen that we got a mention. Airsource has had an early opportunity to test out RVCT 3.0 and evaluate the performance and features being offered. It’s fair to say we were pretty impressed - it’s a much-needed upgrade and ARM has definitely got the edge over GCC when it comes to performance and size.

We’ll be offering more details on some of the results we discovered and some analysis of the new features in RVCT 3.0 in our talk at BREW 2007 on Friday in Randle A at 13:45. After our presentation we’ll try to get some more details up here.

BREW interfaces - implementing a new interface

Tuesday, June 5th, 2007

BREW makes heavy use of an interface framework derived from COM. It’s very useful to understand the internals of how it works, especially if you want to extend any of the built in classes. In this series of articles, I will discuss

Part 2. How to implement a simple interface.

As with any task, the simplest way to do something is to copy an existing solution and adapt it to your own needs. If you want to get up and running quickly, then a very good approach is to download the uiOneSDK, which includes Widgets, and checkout widgets/src/BitmapWidget.c, which includes a simple class exposing the IImage interface.

However, in this article, we’ll go through the steps of implementing an interface all the way from scratch. We’re going to implement a very simple interface, that extends IAStream, treats the stream data as ASCII, and capitalizes it. Obviously this is pretty useless as it stands, but it provides a good basis for more useful streams, such as encryption classes. You should not that we take some shortcuts which mean that the implementation as it stands is not suitable for using in an extension.

First, decide on the interface

We’ll call it IUCStream, for Upper Class Stream. It looks exactly like an IAStream, with one additional interface - IUCSTREAM_EnableTranslation which enables or disables the upper type translation.

Write the public header (AEEUCStream.h)

The public header needs to declare the layout of the interface, so that other pieces of code can use it. It does not have to - and should not - say anything about the private implementation of the class.

    // Include our base interface definition. AStream is defined in AEE.h
    #include 

    // Provide a cast method
    #define IUCSTREAM_TO_IASTREAM(po) (IAStream*)(po)

    // Define a struct that lets people inherit from the new interface
    #define INHERIT_VTBL(IUCStream) \
        INHERIT_VTBL(IAStream) \
        void (*EnableTranslation)(iname * pIUCStream, boolean enable)

    // Define the new interface
    DEFINE_VTBL(IUCStream)
    {
        INHERIT_VTBL(IUCStream);
    };_

    // Set up standard macros
    // We only define one extra function. We do not define the base
    // functions. Since this class is expected to be used like an
    // IAStream, we will override a couple of functions by hooking into
    // the IAStream implementations. See later
    #define IUCSTREAM_EnableTranslation        \
              AEEGETPVTBL(IUCStream, po)->EnableTranslation()

Write the private header (UCStream.hpp)

The private header declares the general implementation of the class, storage of the vtables, and declares functions that will implement interface methods,

    #include 

    // This class MUST NOT use any C++ virtual functions, as we are reliant
    // on the first 4 bytes being the pointer to the interface vtable. If
    // we use virtuals, then the first four bytes will instead be a pointer
    // to a C++ vtable. This is useful if you want to model BREW interfaces
    // in C++ - but only if you are not using the ADS or RVCT 1.2
    // compilers, as these have a non-compliant ABI
    class UCStream
    {
    public:

        // Factory method to create an IUCStream wrapped around an IAStream
        static IUCStream*    create(IAStream*);

    private:
        // The VTable
        const AEEVTBL(IUCStream)*   pvt;

        // Store a reference to the stream we are wrapping
        IAStream*                   stream;

        // Is translation on?
        boolean                      translationEnabled;

        // Static member implemnetations of interface functions
        static uint32                Release(IAStream* pStream);
        static int32                 Read(IAStream*    pStream,
                                          void*        pBuffer,
                                          uint32       dwCount);
        static void                  EnableTranslation(IAStream* pStream,
                                                       boolean   enable);

        // Other interface implementations hook onto the IAStream
        // implementation
                                    UCStream(IAStream* pStream);
                                    ~UCStream(void) {}
    };

Write the factory method implementation

The first thing we need to do is construct one of these new classes. Since we are not writing an extension, but merely implementing a new interface to be used within our own code, we do not need to hook into CreateInstance, and we can set up the IAStream private member in construction, which is normally impossible. We use a single heap allocation for both the class memory space and the vtable. In order to do this in a slightly safer manner, and save ourselves from arithmetic, we use MALLOCREC_EX from AEEStdLib.h.

    #include "AEEStdLib.h" // For MALLOCREC_EX_
    #include  // For placement new
    IUCStream* UCStream::create(IAStream* pStream)
    {
        // Allocate space for both the vtable and the class itself
        UCStream* ucs = MALLOCREC_EX(UCStream, sizeof(AEEVTBL(IUCStream));
        // Placement new
        new (ucs) UCStream(pStream);
        return (IUCStream*)ucs;
    }    

    UCStream::UCStream(IAStream* pStream)
    {
        // Initialise the vtable
        AEEVTBL(IUCStream*) vt = (AEEVTBL(IUCStream)*)(this + 1);
        pvt = vt;

Now we come to initialising the vtable. As we mentioned, we’ll be using IASTREAM default methods in several cases. We could write a UCStream::AddRef method that simply called through to IASTREAM_AddRef - but it’s much easier to use our knowledge of the vtable layout, and point directly at the correct method. We would need to be a bit more rigorous in our implementation if writing an extension with its own class id - this will be discussed in Part 3.

        // Use the base AStream implemnetations for some functions
        AEEVTBL(IAStream)* astreamVT = AEEGETPVTBL(pStream, IAStream);

        vt->AddRef             = astreamVT->AddRef;
        vt->Readable           = astreamVT->Readable;
        vt->Cancel             = astreamVT->Cancel;

        // Point the rest at our own implementations
        vt->Release            = UCStream::Release;
        vt->Read            = UCStream::Read;
        vt->EnableTranslation = UCStream::EnableTranslation;
    }

Implement the interface

This is the easiest bit. We want to take data read from the IAStream, and do some post-processing on it.

    #define ME_FROM_IUCSTREAM(pStream) UCStream* me = (UCStream*)pStream;

    int32 UCStream::Read(IUCStream* pStream, void* pBuffer, uint32 dwCount)
    {
        ME_FROM_IUCSTREAM(pStream);
        int32 bytes_read = IASTREAM_Read(me->stream, pBuffer, dwCount);
        if(bytes_read > 0 && me->translationEnabled)
        {
            char* buf = (char*)pBuffer;
            char* end = buf + bytes_read;
            while(buf < end)
            {
                if(*buf >= 'a' && *buf <= 'z')
                {
                    *buf += 'A' - 'a';
                }
                buf++;
            }
        }
        return bytes_read;
    }
 

Our Release method uses the reference counting of IAStream to avoid the need to store a reference count ourselves. When the ref count hits 0, we’ll cleanup this object as well.

    uint32 UCStream::Release(IUCStream* pStream)
    {
        ME_FROM_IUCSTREAM(pStream);
        int32 nRefs;

        nRefs = IASTREAM_Release(me->stream);
        if(0 == nRefs)
        {
            delete me;
        }
        return nRefs;
    }

Finally, and simplest of all, we implement the new method

    void UCStream::EnableTranslation(IUCStream* pStream, boolean enable)
    {
        ME_FROM_IUCSTREAM(pStream);
        me->translationEnabled = enable;
    }

Use the interface in your code

You can use an IUCStream anywhere that you’d use an IAStream. For example:

    IAStream* pAStream;
    // Get an ASTREAM from somewhere - simplest way is to construct a
    // MEMSTREAM and use that as an ASTREAM, or build an IPEEK from
    // an ISOURCEUTIL
    ...
    IUCSStream pUCStream = UCSStream::create(pAStream);
    IUCSTREAM_EnableTranslation(pUCStream, TRUE);

    // Now use it like an IAStream in any method that requires an
    // IAStream*. You can directly cast - or better use the defined
    // cast macro
    int32 bytes_read = IASTREAM_Read(
	                       IUCSTREAM_TO_IASTREAM(pUCStream),
                           pBuffer,
                           dwCount);