Telephony

Series 60 provides APIs that allow you to make use of its telephony features. The telephony services in Series 60 are provided by a server called ETel . ETel is very similar in design to the other servers described in the previous chapter, such as the Socket Server and the Comms Server:

  • It provides a generic, high-level client API that abstracts clients from the differences in implementation detail between specific hardware types.

  • Plug-in modules (here known as TSY s) are used server-side to add support for different hardware types, without affecting the client-side API.

  • ETel provides safe management of multiple, concurrent client connections.

The ETel client-side API consists of a session class RTelServer plus subsession classes to implement three fundamental abstractions: phones, lines and calls .

A phone is represented by the subsession class RPhone . This represents a particular telephony device. Its API provides access to the status and capabilities of the device and allows the client to be notified if these change.

A line is represented by the subsession class RLine . A phone can have one or more lines. RLine presents a single line and can be used to obtain status and capability information about it (for example, its hook status). As with RPhone , it also provides the capability to be notified if changes occur to the line.

A call is represented by the subsession class RCall . A line may have zero or more active calls, and an RCall object represents one such call. The RCall API is used to perform tasks such as dialing a number, waiting for an incoming call, and hanging up a call. As with the other subsessions, it can also be used to notify a client of changes to a call.

Using the ETel API

The code samples in this section come from the AnsPhone example application, which demonstrates many common ETel features. The code is mostly taken from the following classes:

  • CAnsPhoneEngine main engine class.

  • CAnsPhoneCallMaker responsible for making calls.

  • CAnsPhoneCallWatcher responsible for receiving calls.

  • CAnsPhoneCallLog responsible for retrieving information about logged calls.

The AnsPhone example application uses these and other classes to perform the following tasks:

  • Making a call.

  • Receiving a call.

  • Retrieving the last calling number.

These steps are described in detail in the following sections, with reference to the code where appropriate.

Getting Started

Before you can do any real work with ETel, you first need to set up your RTelServer session and a phone and line. This is performed in the CAnsPhoneEngine class, and this class's key member data is shown here:

 RTelServer           iSession; RLine                iLine; RAnsPhonePhone       iPhone; 

Note that RAnsPhonePhone is a thin class, publicly derived directly from RPhone and adding one or two additional capabilities.

CAnsPhoneEngine initializes the session and subsession objects in the function CAnsPhoneEngine::TelStartL() . First, it opens the RTelServer session:

 _LIT(KTSY, "phonetsy"); ... void CAnsPhoneEngine::TelStartL()    {    ...    User::LeaveIfError(iSession.Connect());    // load the appropriate tsy    User::LeaveIfError(iSession.LoadPhoneModule(KTSY)); 

The function then lists the phones supported by the device and selects the first one from the resulting list:

 // in order to get a handle on a line, you must get a // handle on an RPhone object TInt numberPhones = 0; User::LeaveIfError(iSession.EnumeratePhones(numberPhones)); if (!numberPhones)    {    User::Leave(KErrNotFound);    } // use the 1st available phone RTelServer::TPhoneInfo phoneInfo; User::LeaveIfError(iSession.GetPhoneInfo(0, phoneInfo)); User::LeaveIfError(iPhone.Open(iSession, phoneInfo.iName)); 

Once the phone is opened, you need to enumerate the lines on that phone and find one that supports voice calls. Once you find a suitable line, you can open it:

 // we must now find a line that will accept voice calls TInt numberLines = 0; User::LeaveIfError(iPhone.EnumerateLines(numberLines)); RPhone::TLineInfo lineInfo; TBool foundLine = EFalse; for (TInt a = 0; a < numberLines; a++)    {    User::LeaveIfError(iPhone.GetLineInfo(a, lineInfo));    if (lineInfo.iLineCapsFlags & RLine::KCapsVoice)       {       foundLine = ETrue;       break;       }    } if (!foundLine)    {    User::Leave(KErrNotFound);    } User::LeaveIfError(iLine.Open(iPhone, lineInfo.iName)); } 

Once successfully opened, this RLine handle is passed into classes like CAnsPhoneCallMaker and CAnsPhoneCallWatcher which use it to open an RCall object.

Making a Call

This step, as you may expect, is performed by the CAnsPhoneCallMaker class. This is an active object with the following key members :

 private:    // the state of this object governs what happens when the    // StartL()/Stop()/RunL()/DoCancel() functions are called    enum TState { ENoState, EDialling, EWatching }; private:    MAnsPhoneCallMakerObserver&   iObserver;    RLine&                        iLine;    RCall                         iCall;    RCall::TStatus                iCallStatus;    TState                        iState;    TPtrC                         iNumber; 

iLine is a reference to the RLine object which was opened in CAnsPhoneEngine::TelStartL() . MAnsPhoneCallMakerObserver is an observer class defined along with this class to allow CAnsPhoneCallMaker to pass events elsewhere in the application. Its detail is peripheral to this chapter, but you can follow it easily in the AnsPhone example code.

A number of asynchronous calls are involved in making a call with ETel. These are all handled by CAnsPhoneCallMaker , which means that its RunL() must be able to determine which asynchronous call has completed at any given time. In order to facilitate this, CAnsPhoneCallMaker uses the iState member to implement a simple state machine. Before each asynchronous call, the state is set appropriately, and it can then be checked at the start of the RunL() method.

Here is a high-level outline of the steps involved in making a call:

1.
Open an RCall object.

2.
Commence dialing on this RCall object. This is an asynchronous request.

3.
When the call to commence dialing completes, check whether the caller has already hung up.

4.
If not, issue a second asynchronous request to watch for the end of the call.

5.
When this request completes, take appropriate steps to handle the end of the call.

These steps are covered in more detail below.

Steps 1 & 2: Open RCall and Dial

The process begins by opening the RCall object, then commencing dialing. This is performed by the CAnsPhoneCallMaker::MakeCallL() function, which looks like this:

 void CAnsPhoneCallMaker::MakeCallL(const TDesC& aNumber)    {    iState = EDialling;    iNumber.Set(aNumber);    StartL();    } 

StartL() switches on iState . For the state EDialling , it opens its RCall object and commences dialing:

 User::LeaveIfError(iCall.OpenNewCall(iLine)); iCall.Dial(iStatus, iNumber); 

Finally, it sets itself active. Now it has to wait until its RunL() is invoked to see whether dialing was successful.

Step 3: Check If the Caller Has Hung Up

When CAnsPhoneCallMaker::RunL() is called, it first checks to see whether the caller has hung up before connecting:

 // if the caller hangs up, then we will get // KErrGeneral error here if (iState == EDialling && iStatus.Int() == KErrGeneral)    {    Stop();    iObserver.HandleCallHungUpL();    return;    } 

Step 4: Watch for the End of the Call

Having ascertained that the asynchronous call to RCall::Dial() was successful, CAnsPhoneCallMaker now has to watch for the end of the call. It does this by advancing iState to EWaiting , then calling StartL() . This in turn calls the appropriate ETel API:

 iCall.NotifyStatusChange(iStatus, iCallStatus); ... SetActive(); 

Step 5: Handle the End of the Call

Now all that remains is to handle the end of the call. This is detected when RunL() is called with the following conditions:

  • iState is equal to EWatching .

  • iCallStatus is equal to RCall::EStatusHangingUp or RCall::EStatusIdle .

When this occurs, you need to cancel your request for status information on iCall and close the handle. This is performed in CAnsPhoneCallMaker :: Stop() :

 iCall.NotifyStatusChangeCancel(); iCall.Close(); 

Receiving a Call

Receiving a call is very similar to making a call: it, too, consists of a basic state machine built around a number of asynchronous requests to ETel. In the AnsPhone example, this state machine is encapsulated in the CAnsPhoneCallWatcher class. Here is its relevant member data:

 private:    // the state of this object governs what happens when    // the StartL() / Stop() / RunL() / DoCancel()    // functions are called.    enum TState { EWaiting, EAnswering, EWatching }; private:    MAnsPhoneCallWatcherObserver&   iObserver;    RLine&                          iLine;    RCall                           iCall;    TName                           iCallName;    RCall::TStatus                  iCallStatus;    TState                          iState; 

By now most of this should be familiar from CAnsPhoneCallMaker , above. The only new addition is iCallName , a descriptor which will be used to store the name that ETel assigns to an incoming call.

Here the state machine is obviously slightly different from that required for making a call. Here are its basic steps:

1.
Wait on iLine for a call to come in. This is an asynchronous request.

2.
When a call comes in, attempt to answer it. (Another asynchronous request.)

3.
If answering completes successfully, wait for the end of the call. (A third asynchronous request.)

4.
Finally, take appropriate steps to handle the end of the call.

These steps are covered in detail below.

Step 1: Wait for an Incoming Call

This is performed as follows in CAnsPhoneCallWatcher::StartL() :

 // sets iCallName when it receives an incoming call iLine.NotifyIncomingCall(iStatus, iCallName); 

This is all that is required. Remember that iLine has already been opened by CAnsPhoneEngine .

Step 2: Attempt to Answer a Call

When NotifyIncomingCall() completes successfully, indicating that an incoming call has been received, two steps are required to handle it: open a call handle, and use it to answer the call. This is performed in CAnsPhoneCallWatcher::StartL() :

 // a call has come in, so start watching the call User::LeaveIfError(iCall.OpenExistingCall(iLine, iCallName)); iCall.AnswerIncomingCall(iStatus); ... SetActive(); 

Note that iCallName , which was earlier passed to RLine::NotifyIncomingCall() , has now been initialized by ETel and is used to identify the call you wish to answer.

Step 3: Wait for the Call to End

If the call to RCall::AnswerIncomingCall() completes successfully, then you just need to wait for the call to end. Again, this is performed in CAnsPhoneCallWatcher::StartL() , and in exactly the same way that CAnsPhoneCallMaker watches for the end of an outgoing call:

 iCall.NotifyStatusChange(iStatus, iCallStatus); ... SetActive(); 

Step 4: Handle the End of the Call

At the end of a call, you should perform the same shutdown steps as in CAnsPhoneCallMaker and also return to the waiting state if you intend to listen for further incoming calls. This is carried out in CAnsPhoneCallWatcher::RunL() :

 User::LeaveIfError(iCall.GetStatus(iCallStatus)); ... if (iCallStatus == RCall::EStatusHangingUp)    {    ...    iCall.Close();    iState = EWaiting;    } StartL(); 

Setting iState to EWaiting and calling StartL() restarts the state machine.

Retrieving the Last Calling Number

This is the only part of this section not to use an ETel API, as ETel does not provide facilities for retrieving the last calling number. Instead, you need to use the Log Engine API. The Log Engine is responsible for storing details of various system events (including incoming and outgoing phone calls) in a database.

The class which encapsulates this functionality in the AnsPhone example is CAnsPhoneCallLog . This is an active object, with the following key data members:

 MAnsPhoneCallLogObserver&   iObserver; CLogClient*                 iSession; CLogViewRecent*             iRecentView; 

MAnsPhoneCallLogObserver is a locally defined observer interface to allow other objects in AnsPhone to receive information from CAnsPhoneCallLog .

CLogClient is a high-level session class to the Log Engine it encapsulates an R -class session and adds further functionality.

CLogViewRecent is a view on the Log Engine database, not a view in the UI sense. This is used to retrieve data from the Log Engine .

Both of the Log Engine classes are active objects.

Getting the Number

Components in AnsPhone obtain the last calling number by calling CAnsPhoneCallLog::GetNumberL() . This asks iRecentView for a list of recent events as follows:

 if (iRecentView->IsActive())    iRecentView->Cancel(); if (iRecentView->SetRecentListL(KLogNullRecentList, iStatus))    SetActive(); 

CLogViewRecent potentially has access to a number of event liststhe value KLogNullRecentList passed as the first parameter indicates that the view is to include events from all lists.

You should also note that SetRecentListL() returns TBool (rather than the void return type you would normally expect from an asynchronous function). This is used to indicate whether the log contains any eventsif the function is called before any call information has been logged (that is before a call has been made/received and hung up ), it returns EFalse and does not issue an asynchronous request . So it is important that you only call the handling active object's SetActive() function if SetRecentListL() returns ETRue .

Once the SetRecentListL() request completes, obtaining the last calling number is very straightforward. This is how it is implemented in CAnsPhoneCallLog::RunL() :

 const CLogEvent& event = iRecentView->Event(); iObserver.HandlePhoneNumberL(event.Number()); 



Developing Series 60 Applications. A Guide for Symbian OS C++ Developers
Developing Series 60 Applications: A Guide for Symbian OS C++ Developers: A Guide for Symbian OS C++ Developers
ISBN: 0321227220
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net