Memory-Mapped Files for Qualcomm Brew

Although today’s wireless handsets have far more memory than just a few short years ago, memory remains one of those things where more is simply better. A frequent pattern in application development is to load something into main memory, manipulate it (such as parse or display) it in some way, and then dispose of the memory you used in the first place. Not only is this a hassle, but it can fragment the heap, leading to the inability to allocate large blocks later on. Fortunately, in some cases there’s a better way—simply map the desired file to memory using Qualcomm Brew’s IFILE_Map API.

Before I show you how to do this, I should tell you the limitations of this API (as of this writing, anyway). First, the interface was introduced in BREW 3.1, so it doesn’t exist on older handsets. Secondly, the file must be read-only, and you can read only from the memory mapped region, not write to the region to update the file. Finally, it only works for certain kinds of files: specifically, constant files placed on the handset by the OEM. These restrictions mean that the API is best-suited for developers of embedded Brew applications, such as device manufacturers and their partners.

Mapping a File to Memory with IFILE_Map

When you map a file to memory using IFILE_Map, you’re creating a pointer in memory that can be used to read the contents of the file as if it were loaded on the heap. The idea is the same as with the BSD mmap and munmap functions, but given the limitations of the BREW platform, it’s understandably less flexible.

The actual process to use IFILE_Map is quite simple:

  1. Create your desired data file and embed it in the handset’s production environment as a constant (read-only) file.
  2. In your application, open the file using an instance of IFILEMGR_OpenFile, passing the _OFM_READ flag to indicate read-only access to the file.
  3. Determine the part (perhaps all) of the file you’d like to map into a memory region, obtaining the size of the file as a guideline using IFILE_GetInfo.
  4. Invoke IFILE_Map to obtain a pointer to the file’s contents.
  5. Read from the pointer in the normal way (say, pass it to a parser to parse what’s needed from the file).
  6. When you’re done, close the file using IFILE_Release.

How you do Step 1 is documented in the documentation that Qualcomm provides OEMs, so take a closer look at Steps 2–6. Here’s some pseudocode that illustrates what you need to do:

{
   IFileMgr *pifm;
   ISHELL_CreateInstance( pMe->m_pIShell, AEECLSID_FILEMGR,
      (void **)&pifm );
   if ( pifm )
   {
      IFile *pif = IFILEMGR_OpenFile(
         pifm, THE_FILE_NAME, _OFM_READ );
      if ( pif )
      {
         FileInfo fi = { 0 };
            int err;
            char *p;
            IFILE_GetInfo( pif, &fi );
            p = IFILE_Map( pif,
               NULL,
               fi.dwSize,
               AEE_FMAP_PROT_READ,
               AEE_FMAP_SHARED,
               0 );

         if ( p )
         {
            // Do stuff with the contents.
            Parse( pMe, p );
         }
         else
         {
            // Handle the error
         }
         IFILE_Release( pif );
      }
      IFILEMGR_Release( pifm );
   }
}

Opening the file with IFILEMGR is straightforward; so is mapping the file, once you know the arguments. IFILE_Map returns a pointer to the mapped region in memory, given:

  1. A pointer to an open file.
  2. The starting point in memory at which to map the file, which should be NULL.
  3. The number of bytes to map from the file starting at the current seek position for the file.
  4. How the file should be mapped (read-only, writable, or executable). At present, only AEE_FMAP_PROT_READ is supported.
  5. Indicates how shared memory should be handled. The region can be shared between callers using the AEE_FMAP_SHARED flag.
  6. The offset from the start of the file.

Why Memory Map Files?

There are two reasons to map files into memory. I’ve already hinted at the first: Mapping files into memory can reduce the amount of memory your application consumes, and potentially reduce heap fragmentation as well. It can also lead to clearer code, by eliminating the need to write record-handling and record-parsing code that reads and parses chunks of your data file. Although the first reason may be less important in today’s world of handsets with multiple megabytes of heap, the second reason remains important no matter how much memory the device has.

Areas of your application where mapping files to memory may help include:

  • Dynamic user interfaces, in which the look and feel of your application (including widget placement) is specified indirectly through configuration files at run-time.
  • Parsing of large files in XML or other formats (potentially for the aforementioned purpose), eliminating the need to construct a parser that can handle tags split across read buffer boundaries.
  • Managing read-only data in non-serialized forms (in other words, direct memory dumps) from disk files.

Note that I don’t claim that memory mapping files really helps for managing application resources. That’s because Brew has a good resource manager through the shell, and the tools in the SDK lets you efficiently manage your application resources using an XML file to describe image resources and specify text resources. Using separate resource files for different languages and the existing ISHELL_LoadResImage and ISHELL_LoadResString interfaces leads to an application that can be easily localized for a particular region, whereas mapping images to save the RAM they might occupy would result in littering the filesystem with images or the need to write a facility similar to Brew’s resource management tools in the first place.

One other area of dubious applicability I should mention is media: The Brew IMedia interface can read from both memory and files on the file system, so there’s little need to map a media file on the file system to a memory region and invoke the IMedia interfaces using the resulting mapped region.

Conclusion

Mapping files to memory is a valuable trick to reduce memory use, prevent heap fragmentation, and simplify some kinds of large-file handling code. You can use IFILE_Map to do this for read-only files compiled on to the BREW file system at handset manufacturing time, a valuable trick for handset manufacturers.

About the Author

Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today’s wireless devices. Ray is the author of several books on software development, including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active mmateur radio operator. Contact Ray at kf6gpe@lothlorien.com.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read