Tải bản đầy đủ (.pdf) (73 trang)

Symbian OS C++ for Mobile Phones VOL 1 PHẦN 9 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (828.06 KB, 73 trang )

The data formats are as follows:
 GDP PDU. This is the chunk of data that's passed down to our GDP implementation
through the CGdpSession interface, along with protocol-specific addressing
information.
 GDP-SMS PDU. Here, we have put a unique pattern on the front so that we can spot
our messages at the far end. In this case, "//BATTLESHIP". Note, alternatively, SMS
Port numbers can be used.
The destination address passed down through the GDP interface is encoded directly into the
SMS PDU, along with the SCA if present.
20.2.3 The GDP-SMS Implementation
The structure of the SMS code is illustrated below. The main component from the client's
point of view is the CGdpSmsComms class shown in the center of the diagram. This
implements CGdpSession, through which all client operations are performed (save for
initially creating the object). This center class actually provides a simple, thin interface, or
facade,into the various components used for SMS communications.
The three main subcomponents, CGdpSmsSender, CGdpSmsReceiver, and
CGdpSmsResourceManager actually carry out the three tasks required for the GDP
session (Figure 20.5).

Figure 20.5

Note
It is interesting to note that the session class, CGdpSmsComms, has no state
itself. This is because GDP-SMS, along with all GDP implementations, is
actually stateless. This may come as a surprise after all the talk about state
machines, but it does make sense when you consider that GDP is a
connectionless, best-efforts datagram protocol. All the state is concerned
with is attempting to get a specific packet in or out of the machine – no
significant state is held over between packet transmissions.
Through the following sections, you'll see an in depth example of how to use the Symbian
OS sockets API to communicate over SMS.


The CGdpSmsSender class handles the task of creating an SMS message and sending it
over via the socket server. It comes to life whenever the client code calls the SendL()
member of the GDP-SMS session object, and continues asynchronous activity until the
packet is successfully sent or has completed a bound number of unsuccessful retries.
Sender
As Figure 20.6 shows, it is a very simple process to send GDP packets via SMS:

Figure 20.6
 Send : Open a socket, stream the SMS message to the socket, and call an
RSocket::Ioctl() to start the send in action.
It is possible that the send operation may fail. I deal with this by using the handler
mechanism of the generic state machine to do a limited number of retries before giving up.
The Ioctl() call on the socket is asynchronous in nature, so the CGdpSmsSender task
class is derived from CGdpSmsStateMachine as follows (from gdpsms.h):
class CGdpSmsSender : public CGdpSmsStateMachine
//
{
public:
class TSenderState : public CGdpSmsStateMachine::TState
{
public:
TSenderState(CGdpSmsSender& aSmsSender);
protected:
CGdpSmsSender& iSender;
};

class TSendMsgState : public TSenderState
{
public:
TSendMsgState(CGdpSmsSender& aSmsSender);

void EnterL();
TState* CompleteL();
TState* ErrorL(TInt aCode);
void Cancel();
private:
};

friend class TSendMsgState;

public:
CGdpSmsSender(CGdpSmsResourceManager& aResMan);
~CGdpSmsSender();
void OpenL(MGdpPacketHandler& aHandler);
void Close();
void SendL(const TDesC8& aAddress, const TDesC8& aData);

protected:
void Reset();
TState* ErrorOnStateEntry(TInt aError);
TState* ErrorOnStateExit(TInt aError);

private:

RSocket iSocket;
CSmsMessage* iSmsMsg;
TPckgBuf<TUint> iOctlResult;
TBool iGotScAddress;
MGdpPacketHandler* iHandler;
TInt iRetries; // Remaining


// States
TSendMsgState iSendMsgState;

};
As you can see, this class actually inherits from CGdpSmsStateMachine, which is a very
slightly specialized version of the abstract CGdpStateMachine class. I will introduce the
facilities it adds when we discuss the resource manager later on.
The sender state machine defines a concrete state class – TSendMsgState along with one
instance variable. This is the state object that the current state pointer in the abstract state
machine will point to. The state class has a reference back to the sender object that it
belongs to – this information is not automatically provided by the nesting construct, because
it only affects the class relationship, not any specific object (or instance) relationship. I
provide this reference variable in a generalized TSenderState class that all the concrete
states derive from.
We'll now trace through the operation of sending a GDP-SMS message. The OpenL()
function is called when the client code calls the corresponding function in CGdpSmsComms.
This puts it into an initial state, by calling Reset(), ready to accept requests to send
packets. It also stores a pointer to the GDP packet handler. This will be used to inform the
handler of a completed send operation.
The state machine gets kicked into life every time the CGdpSmsComms::SendL() is called;
this is the only point of contact with the GDP client in the whole process of sending.
void CGdpSmsComms::SendL(const TDesC8& aAddress, const TDesC8&
aData)
{
RDebug::Print(_L("CGdpSmsComms::SendL() Called."));
__ASSERT_ALWAYS(iSender != NULL, GdpUtil::Fault
(GdpUtil::EProtocolNotOpen));
iSender->SendL(aAddress, aData);
}
This invokes the SendL() function within the sender class, which looks like this:

void CGdpSmsSender::SendL(const TDesC8& aAddress, const TDesC8&
aData)
{
if (IsActive())
return; // Don't leave just quietly drop the
overflow packet

// Create the SMS message
CSmsBuffer* smsBuffer = CSmsBuffer::NewL();
iSmsMsg = CSmsMessage::NewL(iResMan.iFs, CSmsPDU::ESmsSubmit,
smsBuffer);

TSmsUserDataSettings smsSettings;

smsSettings.SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet7Bit);
smsSettings.SetTextCompressed(EFalse);

// Convert address to unicode string required by CSmsMessage
__ASSERT_ALWAYS(aAddress.Length() <= KGdpMaxAddressLen,
GdpUtil::Fault(GdpUtil::EBadSendAddress));

TBuf<KGdpMaxAddressLen> bufAddress;
bufAddress.Copy(aAddress);

iSmsMsg->SetUserDataSettingsL(smsSettings);
iSmsMsg->SetToFromAddressL(bufAddress);
iSmsMsg-
>SmsPDU().SetServiceCenterAddressL(KServiceCenterNumber);

// Convert data into unicode as CSmsBuffer will convert to

appropriate
// format for us.
TBuf<KGdpSmsSduMaxSize> buf;
buf.Copy(aData);

// Insert our SMS pattern header so that our receiver is able
to detect
// the incoming message, and then append the data.
smsBuffer->InsertL(0, KGdpSmsHeaderTag);
smsBuffer->InsertL(KGdpSmsPatternLen, buf);

iRetries = KGdpSmsSendRetries;
Reset();
ReEnterCurrentState(); // Kick us off again.
}
If we're already busy sending an SMS message, we simply give up on this new request and
return – it is up to the client to ensure we're not given more packets than we can cope with.
We create a CSmsBuffer object to contain the text contents of our message. CSmsBuffer
maintains the text content of the message in an array of TText objects, and provides
functions to modify the content.
We then create a CSmsMessage object. This object helps to hide the underlying complexity
of ensuring that the message is structured in the correct network format of an SMS
message. TSmsUserDataSettings allows us to specify that the message is stored in 7-bit
format. SMS supports both 7-bit and 8-bit data transport, but only 7-bit is universally
implemented, so that's what I've chosen to use here. SMS supports a maximum message
length of 160 7-bit characters.
aAddress contains the telephone number of the receiver. The only test we do with this
number is to see that it does not exceed a certain size.
Interestingly, CSmsMessage accepts a Unicode string for the address to which the SMS will
be sent, so we must convert the address from narrow text. We can do this by copying it into

a Unicode descriptor. We then set the service center number.
Before inserting aData into the CSmsBuffer object, we have to ensure that we first insert
the pattern that will enable the receiver to recognize the message as a GDP SMS message.
We then set up how many times we're going to retry sending if the first attempt fails and then
set the state machine going.
The first time a packet is sent, the current state (iState) will already be set to
TSendMsgState, so ReEnterCurrentState() will cause us to enter that state:
void CGdpSmsSender::TSendMsgState::EnterL()
{
// Close the socket as it may already be open.
iSender.iSocket.Close();

// Open a socket
TInt ret = iSender.iSocket.Open(iSender.iResMan.iSocketServer,
KSMSAddrFamily, KSockDatagram, KSMSDatagramProtocol);
User::LeaveIfError(ret);

// Bind to SMS port
TSmsAddr smsAddr;
smsAddr.SetSmsAddrFamily(ESmsAddrSendOnly);
iSender.iSocket.Bind(smsAddr);

// Open a write stream on the socket and stream our message.
RSmsSocketWriteStream writeStream(iSender.iSocket);
TRAP(ret, writeStream << *(iSender.iSmsMsg));
User::LeaveIfError(ret);

// message has not been sent at this point
TRAP(ret, writeStream.CommitL());
User::LeaveIfError(ret);


// Send the message
iSender.iSocket.Ioctl(KIoctlSendSmsMessage, iSender.iStatus,
&iSender.iPkgBuf, KSolSmsProv);
iSender.SetActive();
}
To initiate a send, I first close various resources that might already be open, reopen them,
and then open a socket using the SMS protocol. The socket server is already opened by the
resource manager. We specify that we are only sending an SMS when binding to the socket.
Next we open an RSmsSocketWriteStream object on the open socket, output the
CSmsMessage, and commit the write.
At this point however the message has not been sent. We need to make an asynchronous
call on the socket to complete the send.
If this fails and leaves, the appropriate sender state machine error handler is invoked:
CGdpStateMachine::TState* CGdpSmsSender::ErrorOnStateEntry
(TInt /*aError*/)
{
return NULL; // Give up!
}
If the state entry completes successfully, however, the following code will be invoked by the
RunL():
CGdpStateMachine::TState* CGdpSmsSender::TSendMsgState::CompleteL()
{
RDebug::Print(_L("Message sent!!"));
delete iSender.iSmsMsg;
iSender.iSocket.Close();

// Let handler know message sent successfully
iSender.iHandler->SendComplete(KErrNone);
return NULL; // Last state so return NULL

}
This simply releases the resources we have used to send the message and notifies the
handler that we have successfully sent the message. Notice how we specify the next state to
enter as NULL as we have completed the process.
If the send fails, the ErrorL() function is invoked:
CGdpStateMachine::TState* CGdpSmsSender::TSendMsgState::ErrorL(TInt
aCode)
{
// Let handler know the reason for sent failure
iSender.iHandler->SendComplete(aCode);
User::Leave(aCode);
return NULL; // Never actually hit
}
The sender state machine's error handler performs the following:
CGdpStateMachine::TState* CGdpSmsSender::ErrorOnStateExit(TInt
/*aError*/)
{
Reset();
if ( iRetries < 0)
return NULL;
return CurrentState(); // Force re-entry to initial state
}
As asynchronous errors often indicate a communications error, we attempt to reset the state
machine and do a limited number of retries.
If, for some reason, the operation is cancelled while the send is taking place (generally
because the user chose to quit, and hence Close() has been called), the following will be
called:
void CGdpSmsSender::TSendMsgState::Cancel()
{
if (iSender.iSocket.SubSessionHandle())

iSender.iSocket.CancelIoctl();
}
Receiver
Receiving an SMS message is a bit more complex than sending an SMS message. Once
the client issues a ReceiveAllL(), we need to enter a wait state, and wait for SMS
messages containing our GDP SMS specific pattern. In fact, this is all that we can do as all
other SMS messages will be consumed by the messaging application. Once received, we
can read the SMS message and then go back into the wait state and wait for the next GDP
specific SMS message (Figure 20.7).

Figure 20.7
Note: If the SMS message is received before the CGdpSmsReceiver is Active, the
message will be collected by the messaging app and will no longer be available to the GDP
application.
This requires the following asynchronous requests:
 Issue an RSocket::Ioctl() call on an SMS socket to find a matching pattern on
incoming SMS messages.
 Issue an RSocket::Ioctl() call on an SMS socket to read an SMS message.
This directly tells us the states that are required, as shown in the state diagram above.
Issuing the wait for a GDP SMS message works like this:
void CGdpSmsReceiver::TWaitState::EnterL()
{
// Close the socket, as it may already be open.
iReceiver.iSocket.Close();

// Open a socket
User::LeaveIfError(iReceiver.iSocket.Open(
iReceiver.iResMan.iSocketServer, KSMSAddrFamily, KSockDatagram,
KSMSDatagramProtocol));


// Set the pattern to search for in incoming SMS messages.
Messages,
which do
// not have our signature will be consumed by the Messaging
Application.

TSmsAddr smsAddr;
smsAddr.SetSmsAddrFamily(ESmsAddrMatchText);
smsAddr.SetTextMatch(KGdpSmsHeaderTag8());

User::LeaveIfError(iReceiver.iSocket.Bind(smsAddr));

// Wait for incoming messages
iReceiver.iOctlResult()= KSockSelectRead;
iReceiver.iSocket.Ioctl(KIOctlSelect, iReceiver.iStatus,
&(iReceiver.iOctlResult), KSOLSocket);
iReceiver.SetActive();
}
First of all, as with TSendMsgState::EnterL() I close various resources that might
already be open, reopen them, and then open a socket using the SMS. We then set the
pattern match so that we receive only those messages matching the pattern and call an
asynchronous control command on the socket to accept incoming SMS messages.
Successful completion invokes the following:
CGdpStateMachine::TState* CGdpSmsReceiver::TWaitState::CompleteL()
{
// Received a message so move to read state.
return static_cast<TState*> (&iReceiver.iReadMsgState);
}
As we have a message matching our pattern, we simply move on to the TReadMsgState.
void CGdpSmsReceiver::TReadMsgState::EnterL()

{
// Create an empty message and buffer for our incoming message.
CSmsBuffer* buffer=NULL;
buffer=CSmsBuffer::NewL();
iReceiver.iSmsMsg = CSmsMessage::NewL(iReceiver.iResMan.iFs,
CSmsPDU::ESmsSubmit, buffer);

// Read the message.
RSmsSocketReadStream readstream(iReceiver.iSocket);
TRAPD(ret, readstream >> *(iReceiver.iSmsMsg));
User::LeaveIfError(ret);

// Let the socket know that we have read the message and it
can be removed
// from the message store.
iReceiver.iSocket.Ioctl(KIoctlReadMessageSucceeded,
iReceiver.iStatus, NULL, KSolSmsProv);
iReceiver.SetActive();
}
Here I create a CSmsBuffer and CSmsMessage to store our extracted SMS message; then
I open an RSmsSocketReadStream object on the open socket and stream the SMS from
the socket into the CSmsMessage. As we have collected and dealt with the message, we
send an Acknowledge to the SMS Service Center so it doesn't attempt to resend it.
On successful completion, we need to extract the contents of the message and pass this to
the handler as follows:
CGdpStateMachine::TState*
CGdpSmsReceiver::TReadMsgState::CompleteL()
{
// Extract the message contents
CSmsBuffer& smsbuffer = (CSmsBuffer&)iReceiver.iSmsMsg-

>Buffer();
TInt len = smsbuffer.Length();
HBufC* hbuf = HBufC::NewLC(len);
TPtr ptr = hbuf->Des();

// We are only interested in the message contents, not our
pattern.
smsbuffer.Extract(ptr, KGdpSmsPatternLen, len-
KGdpSmsPatternLen);

// Convert from unicode data and send to handler
TBuf8<KGdpSmsSduMaxSize> buf;
buf.Copy(*hbuf);
iReceiver.iHandler->GdpHandleL(KNullDesC8, buf);

// Cleanup
CleanupStack::PopAndDestroy(); // hbuf;
delete iReceiver.iSmsMsg;
iReceiver.iSocket.Close();

// Return to wait state for next message.
return static_cast<TState*> (&iReceiver.iWaitState);
}
We remove the GDP SMS pattern from the message and convert the data into narrow
format before passing the data to the handler. Finally, we set the next state to TWaitState
to receive further GDP SMS messages.
As with CGdpSmsSender, if we discover an error, we attempt to reset the state machine and
wait for the message to arrive again.
Resource manager
So far, I've just been using the resource manager without actually explaining what it is.

Basically, it's a convenient place to hold onto any resources that are shared between the
sender and the receiver. Specifically, these are connections to the socket server and the file
server. It also implements a bit of functionality in the form of OpenL(), Close() and
ResetL()functions to set up and maintain these resources. Keeping this stuff out of the
central CGdpSmsComms class keeps the fa¸cade interface clean from any implementation
detail, minimizing the dependency of the GDP client on the implementation's internals.
CGdpSmsResourceManager is a simple class, as it does not have any of the active
management functionality and does not carry out any asynchronous task by itself, but simply
acts as a single point of contact (or proxy) for resource access.
class CGdpSmsResourceManager : public CBase
//
{
public:
~CGdpSmsResourceManager();
void OpenL();
void Close();
public:
RSocketServ iSocketServer;
RFs iFs;
};
As you can see, the resource manager is not CActive derived – it just holds the shared
resources used by both the sender and the receiver.

20.3 Bluetooth Implementation
Bluetooth is a short-range wireless communications technology, standardized by the
Bluetooth Special Interest Group (SIG) in 1998. With an operating range of approximately 10
m, it is an ideal technology for sharing information between devices. It also does not suffer
the shortcomings of infrared, which requires direct line of sight.
Bluetooth is composed of a hierarchy of components, referred to as the Bluetooth stack. This
consists of both physical hardware components and software components. Figure 20.8

shows this relationship.

Figure 20.8
The hardware components that implement the lower level of the stack are typically not
directly accessible by software applications. The interface to the hardware is provided by the
Host Controller Interface (HCI) driver.
Applications typically interact with Bluetooth using either of the following:
 RFCOMM: which provides a serial like interface.
 L2CAP: which provides finer control over the bluetoothBT connection.
For our GDP-BT implementation, we will be using the second approach (L2CAP) which is
the typical approach used by applications. RFCOMM is used more often for legacy code
requiring a serial interface.
Here's an overview of how the BT implementation is intended to work. First, the scenario for
sending a message:
 sender makes a move,
 sender selects a BT device in range to send move to,
 a connection is made to the receiver and the packet is transferred.
And then for receiving a message:
 receiving player tells their GDP game to receive incoming messages,
 receiver listens for incoming GDP connections,
 receiver accepts GDP connection and packet is accepted.
20.3.1 Symbian OS Support for Bluetooth
Access to either RFCOMM or L2CAP from Symbian OS is via the socket server client APIs.
The socket server has a plug-in framework for which BT.PRT implements the generic
interface specified by ESOCK.
Figure 20.9 shows that the Socket Server can provide access to both RFCOMM and L2CAP.
We can also see that there is no direct access to the HCI or Link Layer components.
However, it is possible to indirectly access the HCI and Link Layer components through
Ioctl() commands via RFCOMM and L2CAP sockets.


Figure 20.9
However, we will not need access to the Link Layer or HCI for our application and will
concentrate on the L2CAP layer.
20.3.2 The GDP-BT Protocol Mapping
Unlike GDP-SMS, we do not need to modify the GDP-PDU that is passed down to our GDP-
BT implementation from the CGdpSession interface.
We will simply store the data as an 8-bit descriptor array. In this format, the data can be sent
to the receiver using standard Socket APIs.
The destination address passed down through the GDP interface is used to create a
connection to a specific BT device and on a specific port.
20.3.3 The GDP-BT Implementation
The structure of GDP-BT is intentionally very similar to that of GDP-SMS, so there is no
need to explain a whole new class structure. In particular, the BT communications
component has two CGdpStateMachine-derived classes called CGdpBTSender and
CGdpBTReceiver in much the same way that the SMS communications component did for
carrying out the basic sending and receiving tasks.
Sender
The CGdpBTSender class handles sending a data packet over Bluetooth. The most difficult
part of the operation is the initial connection.
Figure 20.10 shows two sequential operations are required to send GDP packets via
Bluetooth:
 Connect : make an L2CAP connection.
 Write: send the packet to the connected receiving device.

Figure 20.10
At any point in the sequence, an operation may fail. As with SMS, I deal with this – the
generic state machine handler mechanism. Both the operations are asynchronous, so
CGdpBTSender is derived from CGdpBtStateMachine as follows (from gdpbt.h):
class CGdpBTSender : public CGdpBTStateMachine
{

// State value declarations
class TSenderState : public CGdpStateMachine::TState
{
public:
TSenderState(CGdpBTSender& aSender);
virtual TState* ErrorL(TInt aCode);
protected:
CGdpBTSender& iSender;
};

class TConnectState : public TSenderState
{
public:
TConnectState(CGdpBTSender& aSender);
void EnterL();
TState* CompleteL();
void Cancel();
};

class TWriteState : public TSenderState
{
public:
TWriteState(CGdpBTSender& aSender);
void EnterL();
TState* CompleteL();
void Cancel();
};

friend class TConnectState;
friend class TWriteState;


public:
CGdpBTSender(CGdpBTResourceManager& aResMan);
void SendL(const TDesC8& aAddress, const TDesC8& aData);
~CGdpBTSender();
void OpenL();
void Close();

protected:
void Reset();

TState* ErrorOnStateEntry(TInt aError); ///< Override from
CGdpStateMachine
TState* ErrorOnStateExit(TInt aError); ///< Override from
CGdpStateMachine

private:
RSocket iWriteSocket; ///< Socket to use for sending
data out
TBuf8<KGdpBTMaxPacketLength> iPacket; ///< Place to store the
data
to go out
TBTSockAddr iAddr; ///< BT address of device to send packet
to
TInt iRetries; ///< No. of (potential) remaining
retires.

// States
TConnectState iConnectState;
TWriteState iWriteState;

};
The sender state machine defines two concrete state classes – TConnectState and
TWriteState along with two instance variables.
As with GDP-SMS, the SendL() function is called when the client code calls the
corresponding function in CGdpBTComms.
void CGdpBTSender::SendL(const TDesC8& aAddress, const TDesC8&
aData)
{
TLex8 lex(aAddress);
lex.Mark();
if (!(lex.Get() == "0" && lex.Get() == "x"))
lex.UnGetToMark();

TInt64 addr;
User::LeaveIfError(lex.Val(addr, EHex));

iAddr.SetBTAddr(TBTDevAddr(addr));

// Check the size of the data we're trying to send:
// - if it's bigger than the maximum we can store in our
member
// variable then call GdpUtil::Fault()
__ASSERT_ALWAYS(aData.Size() <= iPacket.MaxSize(),
GdpUtil::Fault(GdpUtil::EBadSendDescriptor));
// If the current state is NULL then panic as we're not ready
__ASSERT_DEBUG(CurrentState() != NULL,
GdpUtil::Panic(GdpUtil::ESenderNotReady));
// If we're already waiting for another packet to be sent, we
can't
// send another one but we don't leave, we just quietly drop

the
// overflow packet and quit this send
if (IsActive())
return;
// We're still here so it's OK to send the packet.
iPacket.Copy(aData); // Buffer packet until needed
// Set the number of retries to the maximum allowed
iRetries = KGdpBTSendRetries;
ReEnterCurrentState(); // Kick us off again but remain
connected
}
As well as checking the content and size of the aData, SendL()also checks for a valid BT
device address, and stores this for when a connection is required. The data we wish to send
is also stored at this point. We then enter the TConnectState.
void CGdpBTSender::TConnectState::EnterL()
{
DEBUG_PRINT(_L("Entering
CGdpBTSender::TConnectState::EnterL"));

// What are we going to attempt to connect to?
TProtocolDesc& desc = iSender.iResMan.ProtocolDesc();

// Close the socket, just in case it's already open
iSender.iWriteSocket.Close();

// Now open the socket according to what the Resource Manager
told us
User::LeaveIfError(
iSender.iWriteSocket.Open
(iSender.iResMan.SocketServer(),

desc.iAddrFamily,
desc.iSockType,
desc.iProtocol)
);
// Use this hard-coded port num for GDP
// GDP will be port 11, 1 = SDP, 3 = RFCOMM, etc
iSender.iAddr.SetPort(0x000b);

// Send the connect request out of the socket!
iSender.iWriteSocket.Connect(iSender.iAddr, iSender.iStatus);
// and wait until we're connected
iSender.SetActive();
DEBUG_PRINT(_L("Leaving CGdpBTSender::TConnectState::EnterL"));
}
The protocol we will be connecting to is the Bluetooth L2CAP protocol. Our resource
manager is responsible for ensuring that such a protocol exists. We specify an unused port
that we will send data through, in this case port 11, and of course we must ensure that the
receiver is listening to port 11.
Using the BT device address we received in CGdpBTSender::SendL()we try to connect to
that device, and if successful we move to the write state.
CGdpStateMachine::TState* CGdpBTSender::TConnectState::CompleteL()
{
// The connect worked, so now we want to write same data
// - go into the write state
return &(iSender.iWriteState);
}
Once connected, the sockets interface makes sending the packet straightforward and we
can simply call RSocket::Write().
void CGdpBTSender::TWriteState::EnterL()
{

// Initiate a write
iSender.iWriteSocket.Write(iSender.iPacket, iSender.iStatus);
// and wait
iSender.SetActive();
}
If the write is successful, we can let the handler know. We have also finished the send
operation, have no further states to go onto, and can return NULL.
CGdpStateMachine::TState* CGdpBTSender::TWriteState::CompleteL()
{
// Hey! We're done! Stop going through the state machine
iSender.iHandler->SendComplete(KErrNone);
return NULL;
}
Receiver
Receiving a GDP packet over Bluetooth differs very little from how you would receive data
from any other socket. We must listen for a connection, accept the connection, and then
read our data from the socket.
Figure 20.11 shows, two sequential operations are required to receive GDP packets via
Bluetooth:

Figure 20.11
 Accept: the incoming connection from sender.
 Read: read the packet from the connected device and while we have an active
connection we can wait for additional packets.
Before a connection can be accepted, we must give some consideration to device security.
From the earliest Bluetooth specification, device security has always featured heavily.
Typically connections cannot be made between devices without the consent of the user. For
this reason, we will need to create a session with the Bluetooth security manager (RBtMan)
to modify security settings for incoming GDP connections, to remove the requirement for
user interaction, as well as for disabling additional security features including data

encryption.
As we can see in CGdpBTReceiver::OpenL(), we follow a standard approach in setting
up our socket to listen for a connection:
void CGdpBTReceiver::OpenL(MGdpPacketHandler& aHandler)
{
DEBUG_PRINT(_L("Entering CGdpBTReceiver::OpenL"));

// Make sure the current iHandler is NULL - if it isn't then
// we've already got a handler so Panic
__ASSERT_DEBUG(iHandler == NULL, GdpUtil::Panic
(GdpUtil::EReceiverInUse));

iHandler = &aHandler;

// Get the descriptor for protocol we are using. Better be
Bluetooth
TProtocolDesc& desc = iResMan.ProtocolDesc();

// Open the Listen Socket
User::LeaveIfError(iListenSocket.Open(iResMan.SocketServer(),
desc.iAddrFamily,
desc.iSockType,
desc.iProtocol));

// Set up the port to listen on
TInt port = 0x0b; // Port 11 for GDP

iAddr.SetPort(port);

// Now set the socket to listen on the right port

TInt ret = iListenSocket.Bind(iAddr);

// If the Bind() didn't succeed we're outta here!

if (ret != KErrNone)
User::Leave(KErrInUse); // Port is in use

// Listen with a queue size of 4 (random) to allow more than
// one connection
err = iListenSocket.Listen(4);

User::LeaveIfError(err);
DoSecurityParams();

// Now start the state machine off to wait for an incoming
connection
ChangeState(&iAcceptState);
}
The main additional step was to make a call to DoSecurity-Params(), which will create
our session with the security manager to make our modifications:
void CGdpBTReceiver::DoSecurityParams()
{
RBTSecuritySettings secset;
RBTMan btman;
TBTServiceSecurity service;
TUid uid;
uid.iUid = 0x1234;
service.SetUid(uid);
service.SetChannelID(11);
service.SetProtocolID(KSolBtL2CAP);


service.SetAuthentication(EFalse);
service.SetAuthorisation(EFalse);
service.SetEncryption(EFalse);
User::LeaveIfError(btman.Connect());
User::LeaveIfError(secset.Open(btman));
TRequestStatus requestStatus;
secset.RegisterService(service, requestStatus);
User::WaitForRequest(requestStatus);
User::LeaveIfError(requestStatus.Int());
}
We have informed the security manager that L2CAP connections on port 11 will not require
user authorization, that the device will not need to be authenticated, and that communication
will not be encrypted.
void CGdpBTReceiver::TAcceptState::EnterL()
{
RSocket& readSock = iReceiver.iReadSocket;

readSock.Close();
User::LeaveIfError(
readSock.Open(iReceiver.iResMan.SocketServer()));

// Tell socket we'll accept an incoming connection
iReceiver.iListenSocket.Accept(readSock, iReceiver.iStatus);
// and wait
iReceiver.SetActive();
}
In the sockets API, Listen() simply queues incoming connections. In
TAcceptState::EnterL(), we create a new socket that will accept one of the queued
incoming connections. When this happens, we are ready to read our GDP data packet.

CGdpStateMachine::TState* CGdpBTReceiver::TAcceptState::CompleteL()
{
// Got an incoming connection!

// Where'd it come from?
TL2CAPSockAddr sockAddr;
iReceiver.iReadSocket.RemoteName(sockAddr);

// Convert and store as hex in the remote address descriptor
TBTDevAddr remAddr = sockAddr.BTAddr();
iReceiver.iRemAddr.Zero(); // Set length to zero
for (TInt i=0; i<KBTMaxDevAddrSize; ++i)
{
// 00 d0 b7 03 0e c5
TUint16 num = remAddr[i];
// Hack to pre-pend with a 0 if single digit hex
if(num < 0x10)
iReceiver.iRemAddr.AppendNum(0, EHex);

iReceiver.iRemAddr.AppendNum(remAddr[i], EHex);
}

// Now read some data
return &(iReceiver.iReadState);
}
Before reading the data we have the perfect opportunity to extract the address of the BT
Device that sent the GDP packet. This address will be passed back to the handler.
void CGdpBTReceiver::TReadState::EnterL()
{
iReceiver.iReadSocket.Read(iReceiver.iPacket,

iReceiver.iStatus);
iReceiver.SetActive();
}
We read the GDP packet with a standard socket Read().
CGdpStateMachine::TState* CGdpBTReceiver::TReadState::CompleteL()
{
// _LIT(KNullAddress,");
// Complete read by telling handler where the packet came from
// and what was in it
iReceiver.iHandler->GdpHandleL(iReceiver.iRemAddr,
iReceiver.iPacket);
return this; // Queue another read, while the channel is
open
}
We finally pass the received address and data to the handler.
Resource manager
As with GDP-SMS, there is a resource manager in GDP-BT that holds the shared resources
used by both the sender and the receiver.
class CGdpBTResourceManager : public CBase
{
public:
CGdpBTResourceManager();
~CGdpBTResourceManager();
void OpenL();
void Close();
// Resource access
inline RSocketServ& SocketServer();
inline TProtocolDesc& ProtocolDesc();

private:

RSocket iSocket;
// Shared resources
RSocketServ iSocketServer;
TProtocolDesc iProtocolDesc;
};
Inline functions are provided to access these resources:
RSocketServ& CGdpBTResourceManager::SocketServer()
{ return iSocketServer; }
TProtocolDesc& CGdpBTResourceManager::ProtocolDesc()
{ return iProtocolDesc; }
The resource manager is responsible for finding the appropriate protocol we will use to
connect our sender and receiver. As I mentioned before, we will be using the L2CAP
protocol.
void CGdpBTResourceManager::OpenL()
//
// Allocate shared resources
//
{
// Connect to the Socket Server
User::LeaveIfError(iSocketServer.Connect());
// Find something in there that will give us a protocol called
// "L2CAP" (hopefully, Bluetooth :) )
// This fills in the Protocol Descriptor, iProtocolDesc
User::LeaveIfError(iSocketServer.FindProtocol(_L("L2CAP"),
iProtocolDesc));
}
void CGdpBTResourceManager::Close()
//
// Release shared resources
//

{
iSocketServer.Close();
}

20.4 Summary
In this chapter we have presented two GDP Protocol implementations – GDP SMS, and
GDP BT. We have seen how both protocol implementations while based upon very different
underlying technologies, can be accessed in the same way via the generic ESOCK plug-in
interface.
In the SMS section, we have seen how the Symbian OS ESOCK sockets API is used to
send and receive SMS messages via a GSM network. We have seen some utility classes
including CSmsBuffer and CSmsMessage, which encapsulate and hide unnecessary
network implementation details.
In the Bluetooth implementation, we have seen how it is possible to connect a sender and
receiver and send a packet over Bluetooth. We have seen how we use the socket API's to
connect devices and transfer data, and how we can change Bluetooth security settings using
the Bluetooth Security Manager (RBtMan).


Appendix 1: Example Projects
Overview
This appendix details the projects described throughout the book.
Source code is available from
You can
download the examples to any location you choose, although the text of this book assumes
that they are located in subdirectories of a \scmp top-level directory of the drive into which
the UIQ SDK is installed.
The example subdirectories usually contain one project each. About half are independent
example projects, which are covered in association with various topics in the book. The
remaining projects all build up to Battleships.

If there are any specific instructions, or additional information about a project, they will be
found in a readme.html file in that project's top-level directory.
Programs have been tested under the emulator on Windows NT and on a Sony Ericsson
P800 phone.
The Independent Projects
Example Purpose Chapter
active
Basic active objects example. 17
Bossfile
Contains dfbosswritesubproject. For completeness,
includes other subprojects that contain example code
related to the Boss Puzzle.
13
buffers
Dynamic buffers. 8
drawing
Device-independent drawing, with a reusable view and
support for zooming.
15
exeloader
An application that loads and runs text-based applications,
such as hellotext.
1
hellogui
Hello World, GUI version. 4
helloguifull
Hello World, GUI version, with finishing touches. 14
hellotext
Hello World, text version. The code framework is used as
a basis for the buffers and string examples.

1
memorymagic
How to allocate memory and clean it up again – and how
not to.
6
Streams
Using files and stream APIs to save and load data. 13
Strings
Symbian OS strings, using descriptors. 5

The Battleships Projects
Example Purpose Chapter
Example Purpose Chapter
battleships
The full, communicating, two-player Battleships game. 16
soloships
Solo Ships, a single-player game. 9
tp-ships
A simple two-player version. 9
tp-viewtest
A program to test the Battleships application's views. 9

The TOGS Projects
These projects are related to the TOGS reference material in Appendix 3. They are all
needed for battleships, but may also be used for other purposes:
Example Purpose
gdp
Game Datagram Protocol (GDP) – the basic comms interface,
plus three implementations (loopback, Bluetooth and SMS).
gdpchat

Test chat program using GDP.
gsdp
Game Session Datagram Protocol (GSDP) – links packets into
sessions, and distinguishes session types so that different
games can be played.
gsdpchat
Test chat program using GSDP.
rgcp
Reliable Game Conversation Protocol (RGCP) – adds
acknowledgements, resending, and piggy-backing to the
GSDP session, so that an RGCP client can rely on the packet
data it handles.
rgcpchat
Test Converse program using RGCP.

Appendix 2: Developer resources
Symbian Developer Network
Symbian Developer Network is at the hub of the Symbian OS development. With partners
that include network operators, tool providers and mobile phone manufacturers, you can find
the resources you need in just one place. This appendix is only a snapshot of the resources
available to you and more are being released all the time. Check the Symbian DevNet
website regularly for announcements and updates, and subscribe to the Symbian
Community Newsletter for updates.

Symbian OS developer tools
For the latest see
Symbian DevNet and its partners offer various tools:
AppForge

×