http://www.developer.com/ws/brew/article.php/3385111/Inter-Application-Communication-in-BREW.htm
This time, we will discuss inter-application communication—a rather strange topic taking into account the BREW environment, where small, standalone applications prevail. Once BREW becomes more mainstream and handhelds more powerful, applications tend to be more complex, leaving behind the old monolithic approach and making use of more sophisticated design choices. That's why this article will try to answer questions such as: There are various reasons, usually implying design decisions. The simplest case is when you need to pass/extract data to/from other applications (a suite of applications is a good example). A more complex situation is a service; for example, an XML parser. There is little need for a parser to share its state (if any) with other processes. An extension fills the bill here, no doubt—an extension used by all the interested parties in this service. Extensions in BREW are like "in-process" components in COM, being loaded in the space (context) of the caller. This means that our extension will have the same life span as the caller and every call to should generate a new instance of IExtensionCls. There is no inter-application communication in this case, but rather a shared "execution model." What happens if our service has to share state (a database, for example)? An extension doesn't fill the bill any more because there are obvious limitations, for example: The best design in this case is to encapsulate the data in a process of its own, accessible via a well-known interface that can be modeled as an extension. Shortly, we will present such an example. BREW 2.0 introduced a new kind of state for an application: background, in addition to active and suspended states. Background applications are discussed in detail in a Qualcomm document [2]—what follows is just a short recap: An applicationm once startedm is in the active (running) state—capable of receiving all events, including key events (the active application owns the display). At any time, the application can be interrupted and be put in suspended state. A suspended application can become active again (resume). Background applications are different; they have no way to interact directly with the user but they are in a running state. This makes them extremely attractive to be used as global services processing data in the background. An application can be in the background for a limited period of time too—when receiving an event from another application. Global data repositories can use this feature to avoid some of the inherent limitations of background applications, which are otherwise notable. Other than not being accessible directly by the user, [2] mentions: " ... handsets running background applications will experience increased power drain due to the inability to enter standby mode; consequently, prolonged background execution should be avoided for its deleterious effects on handset battery lifetime. Some OEMs may limit the capabilities available to a background application; for instance, while running in the background, applications may be unable to open sockets or play ringers." INotifier is an interface that might be used for initiating communication between two or more applications. Because INotifier will be discussed in more detail in a future article, we will concentrate our presentation on what IShell provides. We can roughly qualify all the IShell calls of type as inter-application communication. Once you know the clsid of another applet you can start exchanging information—;almost. Let's analyze for a moment the most useful methods (please note that some of these methods were enhanced—carefully read the documentation for the most suitable version and availability). ISHELL_StartApplet() allows you to asynchronously start a different process from inside another process. The running process receives the EVT_APP_SUSPEND event immediately before the new application is started. ISHELL_PostEvent() posts an asynchronous event to a specific applet, ISHELL_SendEvent being its synchronous counterpart. Because BREW is an event-based system, sending and posting events should be seen like the main vehicle for inter-application communication. But, now comes the problem: An event cannot accommodate too much additional data to be passed through. Some developers might use the last parameter (an unsigned long) to pass a raw pointer from a process A to process B: Unfortunately, this technique exhibits all the dangers presented before: "address" lives in A's context. Once A is destroyed, "address" is no longer valid. What we need is a clean way to marshal data from process A to be consumed in process B. One way is to use a file in the SHARED directory as a pipe. PostEvent is simply used to signal the availability of data (it can describe meantime the size of the data chunk). For example, we might have in process A: Of course, a more comprehensive protocol might be used, but this is the general idea. Furthermore, A and B don't access the protocol directly, but via an extension hide all the implementation details. Other things to have in mind: The example consists of two applications (<cppapp> and <cppapp1>) and one extension (<extension>). <cppapp> contains the same code as [3] and is practically a client for <cppapp1>. <extension> is the component used by both application, hosting the pipe. The interface <extension> exposes is minimal: and the implementation trivial: Obvious improvements would be the use of smart pointers to access BREW interfaces (see BrewIPtr in [4]) and refactoring the two methods, but this is beyond the scope of this article. This code is based on a fixed-size pipe, but the variable size can be easily implemented as discussed previously. <cppapp1> has two methods to initiate communication: Commands are decoded in the EVT_USER section of the HandleEvent method of <cppapp>. <cppapp> has only one relevant method: How it works: <cppapp1> asks <cppapp> to execute a command like:
This is a very simple protocol using only one unsigned long paramter. <cppapp> will consequently execute: More complexes cases need more data being transferred: <cppapp> simply reads the data passed to it and responds in a similar manner: Please note that <cppap1> is a minimal example and provides no support for suspend/resume. Download the example files here.
Inter-Application Communication in BREW
July 23, 2004
Why Do We Need Inter-Application Communication?
IExtensionCls* e = 0;
ISHELL_CreateInstance(shell, AEECLSID_EXTENSION_CLS, (void **)&e);
Background Applications
Exchanging Information
ISHELL_X(AEECLSID ....)
char* address = (char*)MALLOC(100);
ISHELL_PostEvent(shell, clsIdB, EVT_USER, command , address);
writeData2Pipe(&size);
ISHELL_PostEvent(shell, clsIdB, EVT_USER, command , size);
Process B consumes data in:
boolean CPPApp::OnEvent(AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
switch (eCode)
{
case EVT_USER:
switch(wParam)
{
case command:
readFromPipe(dwParam ); //dwParam is the size of data
return true;
... ....
}
... ..
}
}
A Short Example
QINTERFACE(IExtensionCls)
{
DECLARE_IBASE(IExtensionCls)
int (*PipeWrite)(IExtensionCls * po, const char* data);
int (*PipeRead)(IExtensionCls * po, char* data);
};
static int ExtensionCls_PipeWrite(IExtensionCls * po, const char* v)
{
IFileMgr* fmgr = 0;
IShell* s = ((AEEApplet*)GETAPPINSTANCE())->m_pIShell;
int err = ISHELL_CreateInstance(s, AEECLSID_FILEMGR, (void **)&fmgr);
if(err != SUCCESS) return err;
const char fn[]=AEE_SHARED_DIR"/pipe";
IFILEMGR_Remove(fmgr, fn);
IFile* fl( IFILEMGR_OpenFile(fmgr, fn , _OFM_CREATE));
err = fl ? SUCCESS : EFAILED;
if (fl)
{
IFILE_Write(fl, v, 32);
IFILE_Release(fl);
}
IFILEMGR_Release(fmgr);
return err;
}
static int ExtensionCls_PipeRead(IExtensionCls * po, char* v)
{
IFileMgr* fmgr = 0;
IShell* s = ((AEEApplet*)GETAPPINSTANCE())->m_pIShell;
int err = ISHELL_CreateInstance(s, AEECLSID_FILEMGR, (void **)&fmgr);
if(err != SUCCESS) return err;
const char fn[]=AEE_SHARED_DIR"/pipe";
IFile* fl( IFILEMGR_OpenFile(fmgr, fn , _OFM_READ));
err = fl ? SUCCESS : EFAILED;
if (fl)
{
IFILE_Read(fl, v, 32);
IFILE_Release(fl);
}
IFILEMGR_Release(fmgr);
return err;
}
static void placeCommand(AEECLSID cls, int command, uint32 params=0)
{
IShell* s = ((AEEApplet*)GETAPPINSTANCE())->m_pIShell;
ISHELL_StartApplet(s, cls);
ISHELL_PostEvent(s, cls, EVT_USER, command , params);
}
static void shareData()
{
IExtensionCls* e = 0;
IShell* s = ((AEEApplet*)GETAPPINSTANCE())->m_pIShell;
if(ISHELL_CreateInstance(s, AEECLSID_EXTENSION_CLS,
(void **)&e) != SUCCESS)
return ;
const char v[] = "TEST";
IEXTCLS_PipeWrite(e, v);
IEXTCLS_Release(e);
}
static void readSharedData()
{
IExtensionCls* e = 0;
IShell* s = ((AEEApplet*)GETAPPINSTANCE())->m_pIShell;
if(ISHELL_CreateInstance(s, AEECLSID_EXTENSION_CLS,
(void **)&e) != SUCCESS)
return ;
char v[32];
IEXTCLS_PipeRead(e, v);
IEXTCLS_Release(e);
}
placeCommand(AEECLSID_CPPAPP, 1, 700);
test1(dwParam);
where dwParam = 700.
placeCommand(AEECLSID_CPPAPP, 2, (uint32)address);
shareData();
readSharedData();
ISHELL_PostEvent(((AEEApplet*)GETAPPINSTANCE())->m_pIShell,
AEECLSID_CPPAPP_1, EVT_USER, 1 , 0);
ISHELL_CloseApplet(((AEEApplet*)GETAPPINSTANCE())->
m_pIShell, false);
Downloads
References