×
Namespaces

Variants
Actions

Symbian C++ Notification Services

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Code ExampleArticle
Created: hamishwillee (11 May 2011)
Last edited: hamishwillee (03 May 2013)

Original Author: hamishwillee

This paper describes mechanisms that you can use to display user notifications and prompts when your code does not include a full GUI environment; for example if you’re writing a server or engine code.

Comes with Code: File:NotifierExample.zip

Contents

Overview

The paper is divided into two very distinct sections:

  • The first part discusses the native Symbian C++ notifier methods that developers can employ to interact with the user "out of the box". The paper also briefly covers the manufacturer-specific notification APIs.
  • The second part describes how to extend the notifier framework to support custom notifiers. Third parties should avoid creating custom notifiers where possible, for the reasons discussed in the section #Who can write Extended Notifiers?.

Example code

The example demonstrates how you launch the default notifiers, and also how you write and launch a simple custom notifier. It provides custom notifier plug-in and a test console EXE which shows how to use both the default notifiers and the custom notifiers plug-in.

\NotifierExample \Notifier\ (Notifier Plug-in)
\NotifierTestConsole\ (Test Console)
\Launchers \ExeLauncher_S60\ (Launcher for S60)
\ExeLauncher_UIQ3\ (Launcher for UIQ)

The code is built using the normal Symbian building procedure from the command line, i.e. from the \Notifier\group\ directory, do bldmake bldfiles and abld build. The test console is built from the root of \NotifierTestConsole\. The \Notifier component must be built first, in order to export the header file containing the notifier UID.

Once the console is started it displays the "default notifiers". Firstly it launches an InfoPrint; four seconds later (one second after the InfoPrint disappears) the console launches a default RNotifier which can be canceled by selecting either button. The example code for launching the default notifiers is discussed in section #"Out of the box" Notification Services.

Lastly, the console launches the custom notifier. After 3 seconds it changes the text displayed by the notifier, and after a further 4 seconds it cancels the notifier altogether. The custom plug-in example is discussed in more detail in #The Extended Notifier Plug-in Example.

The example code runs on both S60 3rd Edition and UIQ 3. As there is no mechanism to easily launch console exes on these UIs, GUI applications are provided that can be used to launch the console on production phones. It would have been possible to have displayed the notifiers directly from the GUI applications, but that wouldn’t have been a true verification of Notifiers running in a non-GUI environment! To use a launcher, simply select its icon in the respective UI. The launcher starts the console EXE, from its AppUi::ConstructL() method, and then exits immediately. Note that the code in the launcher itself is of no great interest.

"Out of the box" Notification Services

Figure 1: Infoprint on UIQ
Figure 2: UIQ 3.0 Notifier
Figure 3: S60 3rd Edition Notifier
Figure 4: UIQ3.0 “All text” notifier
Figure 5: S60 3rd Edition “All text” notifier

The "out of the box" notification services enable both simple event notification and also prompting for and returning responses from the user. They are preferred to implementing your own extended notifier where possible.

The services are provided both as part of the Symbian offering and by manufacturers. Not all these notification services work on all manufacturer UIs. The table below shows which services work on which platforms, and the following sections provide detail for each method.

Method UIQ S60
RNotifier::Notify() Y Y
User::InfoPrint() Y N
CAknGlobalNote N Y

User::InfoPrint

An "InfoPrint" (information print) is a short information-only message which is displayed to the user for approximately 4 seconds. The message appears over the top of the current application and ignores user input.

InfoPrints are supported on UIQ. InfoPrints are not supported on S60; they display on the S60 emulator, but they don’t appear when the API is called on any current production phones.

Figure 1 shows an InfoPrint on the UIQ 3 emulator (it would appear similarly on a real phone).

Code fragment

The code to launch the above InfoPrint is very simple:

_LIT(KTxtMsg,"A message");
User::InfoPrint(KTxtMsg);

Limitations

The main limitations to using InfoPrint are:

  • Suitable for event notification only, not taking responses
  • Not appropriate for multi-platform code (only work on UIQ)
  • Messages must be kept very short


RNotifier::Notify() – default notifier

RNotifier works on both S60 3rd Edition and UIQ 3, and provides a 2 line, 2 button notifier for which you can specify both the line and button text.

void Notify(const TDesC &aLine1, const TDesC &aLine2, const TDesC &aBut1, const TDesC &aBut2, TInt &aButtonVal, TRequestStatus &aStatus);

The default Notifier is implemented similarly on both UIQ and S60 (figures 2, 3):

  • The first line is taken to be a “title”; if you specify more than 25 characters this line will be truncated
  • The second line is the notifier dialog “body text”. The body text can be up to about 420 characters – the notifier wraps the text, grows the dialog and adds scrollbars as necessary
  • Button 1 is on left, button 2 is on right. Button text is truncated if the text is too long. You can specify null descriptors to the buttons
  • The result of which button is pressed is returned to the caller. There is no way to programmatically cancel the dialog (RNotifier::CancelNotifier() and NotifyCancel() are not supported for the default notifier)

Figures 4 and 5 show the appearance of the notifier when the buttons and dialog lines have been seeded with large amounts of text. The resulting dialog has truncated button and title text, with body text wrapped and scrolled over a number of pages.

Code fragment

_LIT(KTxtLine1,"Title (first) line.");
_LIT(KTxtLine2,"Second (message) line. Text wraps and scrolls");
_LIT(KButt1,"Button1");
_LIT(KButt2,"Button2");
TInt whichbutton(-1);
TRequestStatus stat;
RNotifier notifier;
User::LeaveIfError(notifier.Connect());
notifier.Notify(KTxtLine1,KTxtLine2,KButt1,KButt2,whichbutton,stat);
User::WaitForRequest(stat);
if (whichbutton==0)
{
// first button selected
}
 
if (whichbutton==1)
{
// Second button selected
}
notifier.Close();

Note that, for simplicity, this example code blocks using User::WaitForRequest(). In production code the notifier would be wrapped in an active object, the result being checked in the active object RunL().

Limitations

The main limitations of using RNotifier are:

  • Programmatic cancellation of the dialog is not supported; the user must acknowledge the prompt. Therefore this notifier is not suitable for progress notification or other event notification where the user shouldn’t need to cancel the dialog
  • The text cannot be formatted – i.e. you can’t specify newline or any other paragraph or text formatting on either line
  • You cannot have a one line notifier

Note also that if you have previously specified body text, and then try to specify "no body text" by passing a null descriptor, the original body text for the notifier will be displayed. This does not happen for buttons – if you specify a null descriptor the button text will be set to empty.

S60 Global notes

Figure 6: Confirmation Note
Figure 7: Information Note

S60 provides its own global note class (CAknGlobalNote), which is a wrapper around an RNotifier extended notifier. Like the default RNotifier, this allows a global dialog to be launched from code which doesn’t have a UI environment; however it additionally allows an icon, tone, and priority to be specified. It also allows the notifier to be programmatically canceled. Unless multi-platform notifier support is required, CAknGlobalNote should be used on S60 in preference to RNotifier::Notify().

CAknGlobalNote has two APIs to launch the dialogs. The first takes a TRequestStatus that can be used for dialogs where the user needs to provide a response. For example, the screenshot below shows a Confirmation note (EAknGlobalConfirmationNote). The programmer specifies the note text and buttons, and handles the event when the dialog returns after a button is pressed.

The second API can be used for notifications where feedback need not be passed back to the caller. For example, the screenshot below shows an Information dialog (EAknGlobalInformationNote) which the user would acknowledge, but the result will not be processed (and hence no buttons are specified).

Both APIs take as an argument a TAknGlobalNoteType which defines the “type” of dialog to be displayed. Each default type has a different icon and tone; a few are automatically canceled after a set time, with or without use interaction. The icon is replaceable. See the following screenshots of the notification types from the S60 3rd Edition MR Emulator.

Example code

CAknGlobalNote usage is well documented on Nokia Developer at:

http://www.developer.nokia.com/document/Cpp_Developers_Library/GUID-96C272CA-2BED-4352-AE7C-E692B193EC06/html/Notifiers_API4.html

Limitations

The main limitations to using CAknGlobalNote are:

  • Not suitable for multi-platform code (only works on S60)
  • Doesn’t provide a "progress" note (a “wait” note is available)
  • No text formatting supported

CAknGlobalNote does however enable much better control over the notifier behaviour than RNotifier::Notify(); most importantly, it allows programmatic dialog cancellation, support for icons and sounds.

How to write Extended Notifiers

Overview

The notifier framework consists of a client interface, a server and a server-side mechanism for loading notifier plug-ins. Clients issue requests to the server to use these notifiers. The notifier server provides a full GUI environment that the notifiers can use, so that the clients themselves don’t need a GUI environment to interact with the user.

The framework also defines a queue and priority system to handle the case when there are simultaneous events that need to be notified to the user (see #Channels and priorities).

The client side API for the notifier server is provided by the RNotifier class. Developers who wish to use the notifier functionality should use the RNotifier API to interact with the server and implement a plug-in that will handle the interaction with the user.

Symbian OS v9 adds support for notifier plug-ins as ECom DLLs containing one or more notifiers. Note the terminology: a notifier is an object that implements the MEikSrvNotifierBase2 interface. A notifier plug-in is an ECom DLL that contains the code related to one or more notifiers. In practice the terms are quite ambiguous especially as many notifier plug-ins contain only a single notifier.

The following sections describe how you go about developing a Symbian OS v9 ECOM-based notifier, with reference to a simple example. They also specifically cover issues related to the framework’s limitations, and consequent care that you need to take in developing notifiers.

Who can write Extended Notifiers?

All notifiers run in a single thread, and the notifier framework is not robust. Therefore it is easy for a badly written notifier (or even a well written notifier where the author does not fully understand the interaction with other notifiers in the system) to break the prioritisation mechanism (section #Channels and priorities) or to deadlock the framework altogether.

For example, if a high-priority notifier monopolises the channel then the lower priority notifiers will never be activated. If a client is not implemented properly (e.g. it doesn’t always cancel a notifier it activated) it can leave a notifier active and prevent other notifiers being activated.

Therefore, even though the API is public, Symbian recommend that only Device Creation engineers who know exactly what notifiers are present, should use this mechanism.

Third party developers should consider other alternatives; certainly a third party notifier should be thoroughly tested against other notifiers launched through the system – i.e. Bluetooth connection notifier, certificate check notifiers etc.

Channels and priorities

Overview

Warning.pngWarning: It is very important to understand how channels and priorities work. Notifiers share common resources and as a result deadlock situations can occur if they are not written and used properly.

On registration, a notifier declares its channel UID and its priority.

The "channel" represents a physical media that needs to be shared by a number of clients e.g. a screen, LED, sound player etc. Each channel is treated independently by the notifier framework; a single channel can have at most one active notifier at a time but there can be an active notifier on each of them.

The priority determines the order in which notifiers are run on a particular channel; higher priority notifiers are always run immediately, putting active and subsequent lower priority notifiers into a queue to run when the higher priority notifier is completed.

It is up to the notifiers to use the appropriate channels and priorities so that all the notifiers in the system coexist successfully. As for the clients they must make sure they use the notifiers correctly (see #Using a notifier)

Examples

Here are a few examples to illustrate how channels and priorities work.

Consider 2 channels: CA and CB and 3 notifiers PA1, PA2 and PB1. PA1 uses channel CA and has low priority (ENotifierPriorityLow), PA2 uses the same channel with a higher priority (ENotifierPriorityHigh). PB1 uses channel CB and has priority ENotifierPriorityLow. To simplify the discussion we’ll suppose there are no other notifiers in the system.

  1. As PB1 is on different channel than PA1 and PA2, it can be displayed at the same time as PA1 or PA2. The priority of PB1 doesn’t matter as it is the only notifier using channel CB; additionally the priority of PB1 relative to PA1 and PA2 is completely irrelevant since they are on different channels and channels are independent.
  2. If PA2 is currently active and a client requests PA1 to be activated, the server will add the (lower priority) request to the queue. When PA2 is de-activated the server will activate PA1 (unless PA1 was de-activated in the meantime).
  3. If PA1 is currently active and a client requests PA2 to be activated, the server will de-activate PA1, queue it and activate PA2. PA1 will then be re-activated when PA2 is de-activated.
  4. If PA2 is always active, PA1 will never have a chance to be active and this is usually a situation that needs to be avoided. It would be up to the client and/or the notifier plug-in to make sure this doesn’t happen. The framework itself provides no protection against this.

What channel(s) should I use?

A GUI notifier for a single screen device must specify a channel UID of 0x10009D48.

This is a convention only; the notifier server doesn’t care what UID you use. However if you use any other channel value, the notifier server will no longer be prioritizing the notifier against all the other notifiers in the system, and if multiple GUI notifiers must be displayed at the same time the result is undefined (but unlikely to be satisfactory!).

It is possible that in future other channels will be declared – for example to support multi-screen devices. The UIDs to use for these will be published by the manufacturer.

A notifier does not need to display to the screen alone – any other resource might be mediated. However in practice the notifier server is not usually the best way to mediate other resources – for example a sound notification is better mediated by the priority mechanisms in the audio APIs. Therefore there is little reason to use the notifier framework for anything other than screen access.

What priority should I set?

It is extremely important that the priorities are defined correctly and that each notifier behaves correctly. For instance, if a high-priority notifier monopolises the channel then the lower priority notifiers will never be activated.

There is little general advice that can be given, except to say that you need to have an understanding of all the notifiers in the system in order to understand their true relative priority. As a third party you can use low priorities for notifiers that really don’t need urgent attention; you shouldn’t use the highest priority notifiers without testing against common notifier use cases.

The Extended Notifier Plug-in Example

Figure 19: Custom Notifier Launched
Figure 20: Custom Notifier Updated

This section provides a brief overview of what the example notifier does, and how it works. The relevant source code is referenced in fragment form below; the full source code is in the accompanying source-code package.

The notifier is a full-screen CEikDialog-based dialog containing a text editor control. The console test code launches the dialog and sets its initial text. After 3 seconds it updates the text and displays for a further 4 seconds before canceling the dialog. The notifier is an information-only message, and there is no mechanism for canceling it early or passing information from it back to the launching code.

The notifier dialog is shown in figures 19 and 20.

Each notifier in the system is identified by a UID. The notifier for this example has UID 0x102826DC; this is defined in an exported header file (Notifier1Uids.h) so it can be referenced in both the notifier and client code.

const TUid KNotifier1Uid = { 0x102826DC };

The notifier client uses this UID to start and stop the notifier. The code snippet below, taken from NotifierClient1.cpp, is straightforward. First we connect to the notifier server using the RNotifier class. Then we start the notifier, passing in the UID of the notifier we want and the text to be displayed, wait and then update the text. Then we wait a bit more and finally cancel the notifier.

RNotifier notifierSession;
User::LeaveIfError(notifierSession.Connect());
CleanupClosePushL(notifierSession);
_LIT8(KNotifier1StartText,"Notifier1 Launched");
TInt err = notifierSession.StartNotifier(KNotifier1Uid, KNotifier1StartText);
User::LeaveIfError(err);
User::After(3000000);
TBuf8<1> dummy;
_LIT8(KNotifier1UpdateText,"Notifier1 Updated");
notifierSession.UpdateNotifier(KNotifier1Uid, KNotifier1UpdateText, dummy);
User::After(4000000);
notifierSession.CancelNotifier(KNotifier1Uid);
CleanupStack::PopAndDestroy(&notifierSession);

Now look at how the notifier itself is defined (from Notifier1.h). Some of the code has been omitted to simplify the key parts of the description.

class CNotifier1 : public CEikDialog, public MEikSrvNotifierBase2
{
...
public: // from MEikSrvNotifierBase2
...
TPtrC8 StartL(const TDesC8& aBuffer);
void Cancel();
TPtrC8 UpdateL(const TDesC8& aBuffer);
TInt NotifierCapabilites();
...
};

Notifiers must implement the MEikSrvNotifierBase2 interface. As we needed an example notifier with dialog-like behaviour that will work on both S60 and UIQ, we derived from the "largely UI agnostic" CEikDialog. Later on you’ll see that the dialog’s text editor is also present in both UIQ and S60 3rd Edition, and has been used for the same reason. Note that the server has a full GUI environment, so we could alternatively derive (or own) any other UI artifact available on our target UI, e.g. a control, view etc.

Here is the code for the main functions:

TPtrC8 CNotifier1::StartL(const TDesC8& aBuffer)
{
if (!IsVisible())
{
CEikEdwin* editor1 = (CEikEdwin*) Control(ENotifier1ControlEditor);
TInt length=aBuffer.Length();
HBufC* theBuffer = HBufC::NewLC(length);
TPtr theBufferPtr = theBuffer->Des();
theBufferPtr.Copy(aBuffer);
editor1->SetTextL(theBuffer);
CleanupStack::PopAndDestroy(theBuffer);
 
RouseSleepingDialog();
}
 
return KNullDesC8();
}
 
TPtrC8 CNotifier1::UpdateL(const TDesC8& aBuffer)
{
CEikEdwin* editor1 = (CEikEdwin *) Control(ENotifier1ControlEditor);
TInt length=aBuffer.Length();
HBufC* theBuffer = HBufC::NewLC(length);
TPtr theBufferPtr = theBuffer->Des();
theBufferPtr.Copy(aBuffer);
editor1->SetTextL(theBuffer);
CleanupStack::PopAndDestroy(theBuffer);
DrawNow();
return KNullDesC8();
 
void CNotifier1::Cancel()
{
ExitSleepingDialog();
}

The code is fairly simple. StartL() is called by the framework when the client calls StartNotifier(), passing in a buffer from the client-side. The method sets the dialog text from the buffer and then displays the dialog on the screen using RouseSleepingDialog(). The dialog has already been constructed before that (see CNotifier1::ConstructL() in Notifier1.cpp). Pre-creation of the dialog is a common pattern for critical notifiers, as it ensures that there will always be enough memory to display a notifier. You’ll note that by allocating theBuffer here we’ve abused that pattern, so don’t do this in production code!

UpdateL() is run by the framework when the client calls UpdateNotifier(); all it does is set new text for the dialog. Cancel() is called when the client calls CancelNotifier() to discard the dialog.

Also of interest is RegisterL(), which registers the notifier with the notifier server. This is where the notifier indicates what its UID is, the channel and the priority it uses (see #Channels and priorities).

CNotifier1::TNotifierInfo CNotifier1::RegisterL()
{
iInfo.iUid=KNotifier1Uid;
iInfo.iChannel=KScreenOutput;
iInfo.iPriority=ENotifierPriorityHigh;
return iInfo;
}

Lastly, consider NotifierCapabilites(), which is called by the framework in order to determine whether the notifier has the ability to support multiple screen modes/orientations.

TInt CNotifier1::NotifierCapabilites()
{
return EScreenDeviceChangeSupported;
}

If this method doesn’t return EScreenDeviceChangeSupported your notifier will return KErrNotSupported instead of launching, on any screen mode other than mode 0. I’ve included this here because the manufacturer emulators do not default to screen mode 0, and it took some time to debug why my "perfectly good" notifier would not start!

This was only a brief overview. The remaining methods are discussed further in the following sections.

Using a notifier

Overview

The notifier framework provides a synchronous API for pure notification, and an asynchronous API which allows data to be returned from the notifier. A particular notifier plug-in isn’t required to support all methods, and there is no way to check which methods are supported at runtime (unsupported methods should return KErrNotSupported, but this is an un-enforced convention). Furthermore, the meaning of the buffers passed between the client and the notifier is notifier dependent.

Therefore the underlying assumption is that people writing notifier plug-ins are also the ones using them. If a notifier is to be used by anyone else, it may make sense to provide a wrapper class around the notifier client API - thereby abstracting any ambiguity in the use of the notifier.

Connecting to the server

Clients use notifiers by means of the RNotifier class defined in e32std.h. This class is the client interface of the notifier server.

As with any Symbian servers, you first need to connect to the server. This is done in the usual way:

#include <e32std.h>
 
RNotifier notifSession;
TInt err = notifSession.Connect();
...
notifSession.Close();

Starting and stopping a notifier

The following APIs should be used to start and stop a notifier.

TInt StartNotifier(TUid aNotifierUid, const TDesC8& aBuffer);
 
void StartNotifierAndGetResponse(TRequestStatus& aRs, TUid aNotifierUid, const
TDesC8& aBuffer, TDes8& aResponse);
 
TInt CancelNotifier(TUid aNotifierUid);

There is no requirement for a notifier to support both the synchronous and asynchronous version of StartXXX so the caller needs to check which API should be used with the particular notifier it wants to use. This can only done at compile time, there is no run-time API to check this. Similarly the interpretation of the buffer passed by the client is notifier-dependent.

Every notifier that has been started must also be stopped in order to avoid the problems discussed in section 4.3. The usual way to stop a notifier is to call the CancelNotifier() function. It is however possible to write a notifier that cancels itself after it has finished its task (see #Self cancelling notifiers). Again the caller needs to check the appropriate behaviour for the notifier it is using.

There can be a delay between the call to StartNotifier() or StartNotifierAndGetResponse() and the actual start of the notifier. This happens when a notifier with a higher priority is currently active on the same channel. In this case the notifier server will put the new notifier in a queue and start it when the channel becomes available. In the asynchronous case, the request will only be completed when the notifier actually starts, not when it is put in the queue (since only the notifier can provide the right response).

Updating a notifier

Once a notifier has been started the client may want to update it. The following two functions are used for this task.

TInt UpdateNotifier(TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse);
void UpdateNotifierAndGetResponse(TRequestStatus& aRs, TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse);

The exact behaviour of these functions depends on the specific notifier implementation and the caller should use them as required by the notifier it wants to use.

Notifier writers and users need to be aware that a notifier that needs to be updated may not have been started at the moment of the update call. This is because, although a call to StartNotifier() has been made, the notifier may be held in a queue because a higher priority notifier is currently active on the same channel. However the updates are not queued so if the notifier hasn’t been started at the time of the call the update won’t succeed. In the asynchronous case it is possible for the client to know if the notifier has been started but not in the synchronous case.

Similarly the notifier may not be the one currently active so take this into account if the update is actually a request for user input.

Deprecated/unused APIs

The following RNotifier functions should not be used, and are mentioned here for information only:

  • TInt StartNotifier(TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse)

If you need a response from the notifier use StartNotifierAndGetResponse(). If you don’t need a response use the other overload of StartNotifier(). This function should not be used because it can cause unexpected behaviour.

  • TInt StartNotifier(TUid aNotifierDllUid, TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse)

There is no reason to use this function as the first argument is meaningless.

  • void StartNotifierAndGetResponse(TRequestStatus& aRs, TUid aNotifierDllUid, TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse)

There is no reason to use this function as the second argument is meaningless.

  • TInt UnloadNotifiers(TUid aNotifierUid) and TInt LoadNotifiers(TUid aNotifierUid)

Loading and unloading of notifiers is not supported.

Writing a notifier

Overview

To write a notifier you implement a class derived from MEikSrvNotifierBase2. The code for the new notifier is put into an ECom plug-in (in this document we call this ECom plug-in the notifier plug-in). The notifier server loads all the notifiers when it boots (see section 4.10.2.2). Several notifiers can be packaged together into one single plug-in.

Writing the entry point code

The goal of the entry point code is to allow the notifier server to get the notifiers provided by your DLL. The entry point code is largely boilerplate. We briefly go over it in this section, and it is also the example code package

Notifier plug-ins are ECom plug-ins so they use the usual ECom entry point code. This consists of the following elements:

1) The MMP file must follow the usual ECom plug-in conventions. In addition it must have the capabilities TrustedUI and ProtServ in order to allow the notifiers to be loaded into the eiksrvs.exe process. For an example, see Notifier1.mmp in the code package that accompanies this document.

2) There must be an associated resource file to describe the plug-in (as for all ECom plug-ins). For a notifier plug-in the interface_uid must be KUikonUidPluginInterfaceNotifiers. This UID is used by the notifier server to identify a notifier plug-in (see section 4.10.2.2). For an example, see Notifier1.rss in the code package that accompanies this document.

3) The last bit of code needed is the ImplementationGroupProxy() function. For a notifier plug-in the REComSession::CreateImplementationL() function must return a CArrayPtr<MEikSrvNotifierBase2>* i.e. the list of plug-ins. The notifier server will use REComSession::CreateImplementationL() to get the list of notifiers contained in the DLL.

Here is a typical implementation of an entry point.

void CreateNotifiersL(CArrayPtrFlat<MEikSrvNotifierBase2>& aNotifiers)
{
CNotifier1* notifier1 = CNotifier1::NewLC();
aNotifiers.AppendL(notifier1);
CleanupStack::Pop(notifier1);
}
 
CArrayPtr<MEikSrvNotifierBase2>* NotifierArray()
{
CArrayPtrFlat<MEikSrvNotifierBase2>* notifiers =
new CArrayPtrFlat<MEikSrvNotifierBase2>(5);
if (notifiers)
{
TRAPD(err, CreateNotifiersL(*notifiers));
if (err)
{
TInt count = notifiers->Count();
while (count--)
{
(*notifiers)[count]->Release();
}
delete notifiers;
notifiers = NULL;
}
}
return notifiers;
}
 
// ECom plugin entry point
const TImplementationProxy ImplementationTable[] =
{
IMPLEMENTATION_PROXY_ENTRY(0x102826DB, NotifierArray)
};
 
EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
{
aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
return ImplementationTable;
}

The NotifierArray() function is a common source of potential memory leaks as it is often forgotten that ownership of the notifier array or its contents are not transferred to the framework until this function returns. So it’s necessary to ensure that all resources are freed if anything goes wrong. Calling "Release" on a notifier should call "delete this" and returning null from this function causes the framework to leave with KErrNoMemory.

A function similar to that shown above could be used to instantiate the notifiers and add them to the array. It’s tempting to call aNotifiers->AppendL(CMyNotifier::NewL()) – thereby instantiating and appending to the array on one line, but this can lead to memory leaks if there is not enough memory available to extend an array. Either reserve the required space on the array beforehand or use the cleanup stack as shown above.

Now that we have the entry point in place it is time to write the actual notifier implementation(s) i.e. the classes that are derived from MEikSrvNotifierBase2. This is the topic of the next section.

Implementing the MEikSrvNotifierBase2 interface

General considerations

The notifier server is single-threaded so all notifiers run in the same thread. The implementation of the MEikSrvNotifierBase2 methods must not do lengthy tasks as this would stall the notifier server. For instance, don’t put a waiting dialog in the StartL() function.

Notifiers typically use sleeping dialogs in order to ensure that the dialog is displayed even under very low memory conditions. "Sleeping" is the term used to describe a dialog which has allocated all its resources on construction, displaying the dialog simply involves making it visible or "rousing it" (which cannot leave).

Creation and destruction

virtual void Release() = 0;

This function is called by the notifier framework to free all the resources owned by the notifier. At its simplest this function should at least call "delete this".

It’s a common mistake to do nothing in this function; this can lead to significant memory leaks.

virtual TNotifierInfo RegisterL() = 0;

This function should perform all necessary notifier initialisation, and then return a TNotifierInfo object that describes the notifiers parameters.

These notifier parameters include the UID that client code uses to refer to the notifier, the notifier priority (see MEikSrvNotifierBase2::NotifierPriority enumeration), and the channel UID in which the notifier should operate. It may make sense to store this information in a member variable, as it also needs to returned from the Info() function.

A typical implementation of this functions looks like:

CMyNotifier::TNotifierInfo CMyNotifier::RegisterL()
{
//No specific initialisation
 
//Set the notifier parameters
iInfo.iUid = KUidMyNotifier;
iInfo.iChannel = KScreenOutputChannel;
iInfo.iPriority = ENotifierPriorityMedium;
return iInfo;
}

As explained in section 4.3, the choice of channel and priority are significant and need to be considered in context with the rest of the notifiers in the system. Priority is only enforced for notifiers on the same channel, so in general notifiers that need to share the same resource (such as the screen) should be on the same channel. Their relative priority needs to be considered so the notifiers operate correctly if by chance more than one requested to be activated at the same time.

This function is only called once by the framework after construction, and it’s safe to leave from it so it’s possible (although not really necessary) to allocate any objects required by the notifier as you’d do normally in ConstructL() when using the two-phase construction pattern.

Note also that the UID of the notifier should normally be declared in an exported header file so clients can use this to refer to the notifier.

virtual TNotifierInfo Info() const=0;

This function must return the same information as RegisterL().

Starting and stopping a notifier

virtual TPtrC8 StartL(const TDesC8& aBuffer)=0;

This function must be implemented in order for your notifier to support an information-only style notifier which does not return information to the client. It is called by the framework when a client calls:

TInt RNotifier::StartNotifier(TUid aNotifierUid, const TDesC8& aBuffer).

There is no way for the client to retrieve the return value (the TPtrC8) so this value is effectively deprecated. If the client needs a response, the RNotifier::StartNotifierAndGetResponse() should be used. A notifier may choose to implement either or both variants of StartL().

Care needs to be taken when implementing this function. Although it’s a synchronous function you must return from it as soon as possible so that the notifier framework can enforce its priority mechanism as required. You can’t wait for a notifier to complete before returning from this function unless the notifier is likely to finish straight away.

A common (and safe) implementation is to create non-waiting dialogs that can be woken in the StartL() using only a call to RouseSleepingDialog().

Note that it is very important that dialogs are NOT implemented as waiting dialogs (i.e. the EEikDialogFlagWait should not be set for dialog notifiers). Initiating a waiting dialog in StartL() would mean that this function would not return until the user dismisses the dialog.

virtual void StartL(const TDesC8& aBuffer, TInt aReplySlot, const RMessagePtr2& aMessage)=0;

This function must be implemented by notifiers that need to return a result to the client. It is called by the framework to start the notifier when a client calls:

void StartNotifierAndGetResponse(TRequestStatus& aRs, TUid aNotifierUid, const TDesC8& aBuffer, TDes8& aResponse)

The client waits (asynchronously) for your notifier to tell it that it’s completed.

It is essential to return from this function as soon as possible so that the notifier framework can enforce its priority mechanism as required; Copies of the return value and RMessage may need to be taken. You must also remember to call Complete() on aMessage when your notifier is finished, otherwise the client that initiated the notifier will be waiting for a response forever.

Note again that a notifier may choose to implement either or both variants of StartL(). There is no obligation to provide both.

virtual void Cancel()=0;

This function must be implemented to stop the active notifier. It is called by the framework when the client calls RNotifier::CancelNotifier().

Note that if the asynchronous version of StartL() was used to launch the dialog, then you must also complete the message (from StartL()) in this function.

A Cancel() function for an "asynchronous notifier" that uses a sleeping dialog might therefore look like this:

void CNotifierTest::Cancel()
{
ExitSleepingDialog();
if (iMessage.MessagePtr() != RMessagePtr())
{
iMessage.Complete(KErrCancel);
}
}

Self cancelling notifiers

Many notifiers are self-cancelling - after either a fixed period or upon user acknowledgement of the notifier. While you must cancel the notifier in order to avoid the problems described in section 4.3 you can’t do so using RNotifier::CancelNotifier() from within your notifier, as this will result in deadlock (for the reasons given in #Issuing requests to the notifier server from a notifier).

Therefore your notifier must cancel itself using the protected member: MEikSrvNotifierBase2::iManager.

For example:

iManager->CancelNotifier(KNotifier1Uid);

This cancellation might be triggered off a timer that is started in the notifier’s StartL() function, or when the dialog is acknowledged.

Issuing requests to the notifier server from a notifier

Since notifiers run in the notifier server thread they can’t use the RNotifier class to issue requests as this would cause a deadlock. If a notifier wants to make such requests it should use the MEikSrvNotifierManager interface. For instance this interface can be used if the notifier wants to cancel itself rather than relying on the caller to call RNotifier::CancelNotifier().

Every notifier has access to such an interface via the MEikSrvNotifierBase2::iManager member which is protected and initialised when the framework loads the notifier.

File locations

The notifier will typically consist of the ECOM DLL, its registration resource file, and possibly another resource for the dialog definition.

For Symbian OS v9 and later the DLL and its registration file are installed in the same location as other ECOM files i.e. the DLL needs to install to \sys\bin\ and the resource file needs to install to \resource\plugins\. The dialog definition resource file is typically stored to \resource\apps\.

In the example notifier that accompanies this document, the file locations are:

  • \sys\bin\Notifier1.dll
  • \resource\plugins\Notifier1.rsc
  • \resource\apps\Notifier1Dialog.rsc

Debugging Help

Overview

This section is useful if you have access to the notifier server source code.

Notifier server implementation

Server thread

The notifier server has its own thread, which runs in the (existing) eiksrvs.exe process.

If you have access to the source code, the main file for the notifier server implementation is in /app-framework/uikon/srvsrc/eiknfysv.cpp.

All requests to a notifier from a client are handled by the function:

void CEikServNotifySession::ServiceL(const RMessage2 &aMessage)

It is useful to have a breakpoint in this function during debugging.

Plug-in loading

The notifier server loads all plug-ins it can find on boot and unloads them when it shuts down. The server also detects and loads new plug-ins installed from a SIS file, and unloads plug-ins on SIS file removal. There is no API to explicitly dynamically load or unload notifiers.

Loading is done by the following code:

  1. CEikSrvNotifierManager::RegisterL() is called on server construction. It uses REComSession::ListImplementationsL(KUidPluginInterfaceNotifiers,plugInArray) to find all the available plug-ins.
  2. CEikSrvNotifierManager::RegisterL() calls CEikSrvNotifierManager::DoAddPlugInL() for each plug-in that was found. This will result in the plug-in’s implementation of MEikSrvNotifierBase2::RegisterL() being called.

Frequently Asked Questions

Why won’t my notifier uninstall?

Notifiers are loaded by the notifier server when they are detected by the framework. The installer will detect this and won’t allow you to uninstall them.

There is no way to unload a notifier, therefore the only workaround is to install the notifier to removable media, and physically uninstall it.

Other useful notifier documentation

The Symbian platform reference provides the following documentation:

The http://www.NewLC.com website provides a tutorial called “Using Extended Notifiers” that covers Symbian OS notifiers prior to Symbian OS v9. This tutorial is useful because it shows the implementation of an asynchronous notifier:

Further Information

Acknowledgements

The author would like to acknowledge the support of engineers within Symbian Software Engineering, particularly Xavier LeClercq.

Glossary

Term Definition
Notifier A class or instance of class that implements the MEikSrvNotifierBase2 interface.
Notifier plug-in An ECom plug-in that contains one or more notifiers.



Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 3 May 2013, at 10:00.
81 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×