Describes how to modify client-server implementations to use the new Version 2 client-server APIs instead of the deprecated Version 1 APIs.
The new APIs have been introduced as part of the effort to provide a more secure interface for client-server communications, and form an essential component of Platform Security in Symbian platform.
The user library, EUSER.DLL, makes both the Version 1 and Version 2 client-server APIs available. However, it is possible to 'hide' the Version 1 APIs during compilation by defining the C pre-processor macro __HIDE_IPC_V1__.
For example, by adding the following line to a component's MMP file:
macro __HIDE_IPC_V1__
This can also be done globally for all components in Symbian platform by defining the macro in the platform's HRH file:
#define __HIDE_IPC_V1__
In addition, the EUSER header files define the macro __IPC_V2_PRESENT__, which allows source code that still needs to compile with older Symbian platform releases, to detect the presence of the new APIs, and use conditional compilation as appropriate.
The following classes and functions are defined as constituting the Version 1 client-server APIs:
class CSharableSession class CSession class CServer class RMessagePtr class RMessage class RServer RSessionBase::Share(TAttachMode aAttachMode=EExplicitAttach) RSessionBase::Attach() RSessionBase::Send(TInt aFunction,TAny* aPtr) RSessionBase::SendReceive(TInt aFunction,TAny* aPtr,TRequestStatus& aStatus) RSessionBase::SendReceive(TInt aFunction,TAny* aPtr) RSubSessionBase::CreateSubSession(RSessionBase&,TInt aFunction,const TAny* aPtr) RSubSessionBase::Send(TInt aFunction,const TAny* aPtr) RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr,TRequestStatus&) RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr) RThread::GetDesLength(const TAny* aPtr) RThread::GetDesMaxLength(const TAny* aPtr) RThread::ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) RThread::ReadL(const TAny* aPtr,TDes16 &aDes,TInt anOffset) RThread::WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset) RThread::WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset)
The following functions are considered part of the client-server Version 1 APIs, when the target thread is in another process. Note that these APIs are not hidden by the __HIDE_IPC_V1__ macro.
RThread::RequestComplete(TRequestStatus*& aStatus,TInt aReason) RThread::Kill(TInt aReason) RThread::Terminate(TInt aReason) RThread::Panic(const TDesC& aCategory,TInt aReason)
The following classes and functions are defined as constituting the Version 2 client-server APIs.
class CSession2 class CServer2 class RMessagePtr2 class RMessage2 class RServer2 class TIpcArgs RSessionBase::Send(TInt aFunction,const TIpcArgs& aArgs) RSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus&) RSessionBase::SendReceive(TInt aFunction, const TIpcArgs& aArgs) RSessionBase::ShareAuto() RSubSessionBase::CreateSubSession(RSessionBase&,TInt aFunction,const TIpcArgs&) RSubSessionBase::Send(TInt aFunction,const TIpcArgs& aArgs) RSubSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus&) RSubSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs)
The client interface to a server consists of a class derived either from RSessionBase or RSubSessionBase. This section lists the changes required to the use of these classes.
Version 1 functions: |
Share() Share(EExplicitAttach) Share(EAutoAttach) |
Version 2 function: |
ShareAuto() |
In Version 1, the two different share modes existed for historical reasons. All sessions created with new Version 2 servers behave like the Version 1 auto attach sessions; there is no explicit attach sharing mode. The new ShareAuto() function should be used to replace all occurrences of Share().
There is no explicit attach mode in the Version 2 API, (see RSessionBase::Share()). This means that all occurrences of Attach() must be removed from your code when migrating.
RSessionBase::Send() & RSessionBase::SendReceive()
These are functions for sending request messages to a server. Each of these messages can take up to KMaxMessageArguments arbitrary arguments (i.e. 4 arguments), and it is the packaging of these arguments which has been changed in the Version 2 APIs.
The Version 1 APIs take a TAny*, which points to a C array containing a KMaxMessageArguments number of 32-bit quantities, usually TAny * or TInt types. This is an example Version 1 client function:
TInt RMySession::Write(TDes8C& aDes, TInt aLength, TInt aMode) { TAny* args[KMaxMessageArguments]; args[0]=&aDes; args[1]=(TAny*)aLength; args[2]=(TAny*)aMode; return SendReceive(ERequestWrite, &args[0]); }
The Version 2 APIs package all of the arguments into a TIpcArgs object. This has templated constructors that take between 0 and 4 objects. The above example would be implemented like this using the Version 2 APIs:
TInt RMySession::Write(TDes8& aDes, TInt aLength, TInt aMode) { TIpcArgs args(&aDes, aLength, aMode); return SendReceive(ERequestWrite, args); }
This could also be written in a shorter way:
TInt RMySession::Write(TDes8C& aDes, TInt aLength, TInt aMode) { return SendReceive(ERequestWrite, TIpcArgs(&aDes, aLength, aMode) ); }
The TIpcArgs object stores additional type information for each argument, and this is used to validate a server's usage of the arguments with the various RMessagePtr2::Read() and RMessagePtr2::Write() functions.
If you need to explicitly send an empty or unused argument to a server, then use the ENothing enumeration. For example:
TIpcArgs args(arg1, TIpcArgs::ENothing, arg3);
The second argument will have an undefined value when the server receives the message.
RSubSessionBase::CreateSubSession()
Version 1 function: |
CreateSubSession(RSessionBase& aSession,TInt aFunction,const TAny* aPtr) |
Version 2 function: |
CreateSubSession(RSessionBase, TInt aFunction, const TIpcArgs& aArgs) |
The message arguments supplied to this function use a TIpcArgs object in Version 2. This is for the same reason as described in RSessionBase::Send() & RSessionBase::SendReceive().
RSubSessionBase::Send() & RSubSessionBase::SendReceive()
The message arguments supplied to this function use a TIpcArgs object in Version 2. This is for the same reason as described in RSessionBase::Send() & RSessionBase::SendReceive().
This section details the changes required to migrate a server implementation to the Version 2 APIs. These APIs have, in nearly all cases, been implemented using the same class names as in Version 1, but with the addition of the suffix '2'. For example, CServer2 implements the Version 2 APIs corresponding to the Version 1 functionality provided by CServer.
Simply replace CServer with CServer2.
In Version 1, CServer::NewSessionL() is implemented by a class derived from CServer. In Version 2, your derived class uses CServer2 as the base class.
The RMessage2 argument in Version 2 is the 'connect' message from the client. It has been added to allow implementations to check identity and security information about the new client.
This message argument can be ignored when migrating code from the Version 1 APIs.
Simply replace CSharableSession with CSession2.
Version 1 function: |
void CSharableSession::CreateL(const CServer& aServer) |
Version 2 function: |
void CSession2::CreateL() |
In Version 1, CSharableSession::CreateL() can be re-implemented by a class derived from CSharableSession. In Version 2, your derived class uses CSession2 as the base class.
The Version 2 CreateL() function has no arguments. If a derived session object overrides this function, then it will need modifying.
CSharableSession::ResourceCountMarkEnd()
Version 1 function: |
void CSharableSession::ResourceCountMarkEnd() |
Version 2 function: |
void CSession2::ResourceCountMarkEnd(const RMessage2& aMessage) |
In Version 2, the function requires a reference to the client message that requested the resource check. This message is used to panic the client if the check fails.
Version 1 function: |
const RMessage& CSharableSession::Message() const |
Version 2 function: |
No equivalent function. |
In Version 1. this function returns a reference to the last message delivered to the server. It is usually used by a session implementation to mean the current message being processed by the session.
This function is not available in Version 2. When a session needs to manipulate a message delivered to it, it should use the RMessage2 argument passed to ServiceL(), instead. Achieving this may require passing this reference as an argument between function calls, or if that is complex, then storing a reference to the message in the session object.
Replace CSession with CSession2.
The changes applicable to CSharableSession derived classes also apply to CSession derived classes. See:
In addition, if you use the following CSession functions, then you will need to change your code to use the equivalent functions in the RMessage2 and/or RMessagePtr2 classes. See Using RMessagePtr2.
void ReadL(const TAny* aPtr,TDes8& aDes) const; void ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) const; void ReadL(const TAny* aPtr,TDes16& aDes) const; void ReadL(const TAny* aPtr,TDes16& aDes,TInt anOffset) const; void WriteL(const TAny* aPtr,const TDesC8& aDes) const; void WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset) const; void WriteL(const TAny* aPtr,const TDesC16& aDes) const; void WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset) const; void Panic(const TDesC& aCategory,TInt aReason) const; void Kill(TInt aReason) const; void Terminate(TInt aReason) const;
Version 1 function: |
CSession::CSession(RThread aClient) |
Version 2 function: |
No equivalent constructor. |
The Version 1 CSession object stores a handle for the client thread. This should no longer be required after moving over to Version 2, and indeed, a constructor taking a thread handle as an argument is not supplied.
Replace RMessage with RMessage2.
Note that RMessage2 derives from RMessagePtr2 and that most of the functions that were members of the Version 1 class RMessage are now implemented in the Version 2 class RMessagePtr2.
In Version 1, the kernel maintains a handle to the client thread which can be extracted from a message by calling RMessage::Client().
In Version 2, this special handle is not present; instead, a function is provided that explicitly opens a handle on the client thread. This should be treated just like any other thread handle, i.e. Close() should be called on the handle when it is no longer needed.
Here's an example of its usage:
void CMySession::ServiceL(const RMessage2& aMessage) { ... RThread clientThread; // Open a handle on the client thread. Leaving if there is an error. User::LeaveIfError(aMessage.Client(clientThread)); // Do something with the handle. (Print its ID in this example) RDebug::Print(_L("Client Thread ID = %n"),clientThread.Id()); // Close handle clientThread.Close(); ... }
You need to carefully examine all uses of RMessage::Client(), as most servers should not need to use this function after migrating to the Version 2 APIs. This is because the use of thread handles in a Version 1 server is likely to involve those functions that have been removed from the Version 2 APIs. See RThread for more information.
Version 1 function: |
const RMessagePtr RMessage::MessagePtr() const |
Version 2 function: |
No equivalent function. |
RMessage2 derives from RMessagePtr2, therefore an RMessagePtr2 can be simply constructed or assigned directly from an RMessage2. There is no need for an explicit method of construction as was the case in the Version 1 APIs.
RMessage::descriptor access methods
Version 1 functions: |
void RMessage::ReadL(const TAny* aPtr,TDes8& aDes) const; void RMessage::ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) const; void RMessage::ReadL(const TAny* aPtr,TDes16& aDes) const; void RMessage::ReadL(const TAny* aPtr,TDes16& aDes,TInt anOffset) const; void RMessage::WriteL(const TAny* aPtr,const TDesC8& aDes) const; void RMessage::WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset) const; void RMessage::WriteL(const TAny* aPtr,const TDesC16& aDes) const; void RMessage::WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset) const; |
Version 2 functions: |
These functions are replaced by functions that take a message argument index instead of a TAny*. See Using RMessagePtr2. |
Replace RMessagePtr with RMessagePtr2.
See Using RMessagePtr2.
Calls to the following functions need to be changed to use the equivalent functions in a RMessage2 /RMessagePtr2 class. See Using RMessagePtr2.
TInt RThread::GetDesLength(const TAny* aPtr) const TInt RThread::GetDesMaxLength(const TAny* aPtr) void RThread::ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) const void RThread::ReadL(const TAny* aPtr,TDes16 &aDes,TInt anOffset) const void RThread::WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset) const void RThread::WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset) const void RThread::Kill(TInt aReason) void RThread::Terminate(TInt aReason) void RThread::Panic(const TDesC& aCategory,TInt aReason)
Version 1 function: |
void RThread::RequestComplete(TRequestStatus*& aStatus,TInt aReason) const |
Version 2 function: |
void RMessagePtr2::Complete(TInt aReason) const |
With the Version 2 APIs, the only way of signalling a client's request status in a different process is by completing a message sent by the client to the session. This means that a call to RThread::RequestComplete() should be replaced by a call to RMessagePtr2::Complete(). This requires some minor changes to the internal architecture of the server.
An example of a call to RThread::RequestComplete() is where a server implements a kind of notification service, where a client asks to be signalled when some event or change of state occurs.
A version 1 server implementation
In a Version 1 server implementation, the notification scheme is often implemented with code similar to this:
Client function:
TInt RMySession::NotifyChanges(TRequestStatus& aStatus) { TAny* args[KMaxMessageArguments]; args[0]=&aStatus; return SendReceive(ERequestNotifyChanges, &args[0]); }
The server processes the request like this:
void CMySession::ServiceL(const RMessage& aMessage) { ... // Get arguments needed to signal client RThread clientThread = aMessage.Client(); TRequestStatus* clientStatus = (TRequestStatus*)aMessage.Ptr0(); // Make notify object TMyNotifyChanges notifyObject(clientThread,clientStatus); // Add notify object to list of all requests TInt result=AddNotification(notifyObject); // Complete request message aMessage.Complete(result); ... }
When the event happens, clients are notified:
void CMySession::SignalNotifications(TInt aResult) { TMyNotifyChanges* notifyObject; while(notifyObject=RemoveNotifyObject()) { // Complete each NotifyChanges object RThread clientThread = notifyObject->ClientThread(); TRequestStatus* clientStatus = notifyObject->ClientStatus(); clientThread.RequestComplete(clientStatus,aResult); } }
A version 2 server implementation
In a Version 2 server implementation, the notification scheme could be implemented using the Version 2 APIs like this:
Client function:
void RMySession::NotifyChanges(TRequestStatus& aStatus) { TIpcArgs args(); // No arguments // Use asynchronous SendReceive for request SendReceive(ERequestNotifyChanges, args, aStatus); // void return type because errors will be signalled through aStatus }
The server processes the request like this:
void CMySession::ServiceL(const RMessage2& aMessage) { ... RMessagePtr2 messagePtr = aMessage; // Make notify object TMyNotifyChanges notifyObject(messagePtr); // Add notify object to list of all requests TInt result=AddNotification(notifyObject); // Complete request message (only if error) if(result!=KErrNone) { aMessage.Complete(result); } ... }
When the event happens, clients will be notified:
void CMySession::SignalNotifications(TInt aResult) { TMyNotifyChanges* notifyObject; while(notifyObject=RemoveNotifyObject()) { // Complete each NotifyChanges object RMessagePtr2 messagePtr = notifyObject->MessagePtr(); messagePtr.Complete(aResult) } }
In Version 2, a server's interaction with its clients is channelled through RMessagePtr2, from which RMessage2 is derived. An RMessagePtr2 object acts as a handle to the message that the client has sent. The details of the original message are maintained within the kernel so that it can enforce correct use of the RMessagePtr2 functions.
The RThread and RSession functions for accessing descriptors, panicking the client and completing requests are not available in Version 2. Instead, this functionality is provided by RMessagePtr2. Because of this, server implementations may need to have a reference to the message available in many places. This may be done by passing such references as arguments between functions or by storing a reference in the session object processing the request.
TInt RMessagePtr2::GetDesLength(TInt aParam) const; TInt RMessagePtr2::GetDesMaxLength(TInt aParam) const; void RMessagePtr2::ReadL(TInt aParam,TDes8& aDes,TInt aOffset=0) const; void RMessagePtr2::ReadL(TInt aParam,TDes16 &aDes,TInt aOffset=0) const; void RMessagePtr2::WriteL(TInt aParam,const TDesC8& aDes,TInt aOffset=0) const; void RMessagePtr2::WriteL(TInt aParam,const TDesC16& aDes,TInt aOffset=0) const; TInt RMessagePtr2::Read(TInt aParam,TDes8& aDes,TInt aOffset=0) const; TInt RMessagePtr2::Read(TInt aParam,TDes16 &aDes,TInt aOffset=0) const; TInt RMessagePtr2::Write(TInt aParam,const TDesC8& aDes,TInt aOffset=0) const; TInt RMessagePtr2::Write(TInt aParam,const TDesC16& aDes,TInt aOffset=0) const
These are used in the same way as the equivalent RThread functions in the Version 1 APIs, except that instead of referring to the descriptor by an address in the client, the aParam argument is used. This is a value between 0 and 3 and indicates which of the four arguments in the original client message contains the pointer to the descriptor.
For example, if a client sends a message using code like this
TInt RMySession::Write(TDes8C& aDes, TInt aLength) { return SendReceive(ERequestWrite, TIpcArgs(&aDes, aLength) ); }
then the server would access aDes using an aParam value of 0.
void CMySession::ServiceL(const RMessage2& aMessage) { ... TInt length=aMessage.Int1(); // Get length from message param 1 TPtr8 buffer=MyNewBufferL(length); // Make a new buffer for the data aMessage.ReadL(0,buffer); // Read data from client descriptor (param 0) ...
Because TIpcArgs also stores type information about arguments, the kernel knows that argument 0 in the above message is an 8-bit constant descriptor. On Symbian platforms using the EKA2 kernel, this information is used to enforce correct usage of descriptor access methods; in this case, trying to write to aDes, or treating it as a 16-bit descriptor would fail with KErrBadDescriptor. (The latter case would have allowed access to data beyond the length of the 8- bit descriptor.
Note, both leaving and non-leaving versions of the Read() and Write() functions are provided. This allows the removal of some TRAP statements in code after migration to the Version 2 APIs.
void RMessagePtr2::Complete(TInt aReason) const;
This function signals completion of the client request, in the same way that RMessage::Complete() does in Version 1. After completion, the iHandle member is set to zero, and this means that RMessagePtr2::Handle() returns zero, and RMessagePtr2::IsNull() returns True.
It is important to note that once a message has been completed, it cannot be used by the server, and any such attempt results in a KERN-EXEC 44 panic (Bad Message Handle). To avoid this situation, implementations may need to check RMessagePtr2::IsNull(), but be aware that any copies of the message made before completion will not have had their handles nulled. For this reason, it is best to avoid making copies of RMessage2 or RMessagePtr2 objects, this includes passing them as arguments to other functions 'by value'. Use references instead.
void RMessagePtr2::Kill(TInt aReason) const;
This function is used in the same way as the equivalent RMessage or RThread functions in Version 1. Note that this function also performs the action of RMessagePtr2::Complete(), so care must be taken that the message is not used again.
void RMessagePtr2::Terminate(TInt aReason) const;
This function is used in the same way as the equivalent RMessage or RThread functions in Version 1. Note that this function also performs the action of RMessagePtr2::Complete(), so care must be taken that the message is not used again.
void RMessagePtr2::Panic(const TDesC& aCategory,TInt aReason) const;
This function is used in the same way as the equivalent RMessage or RThread functions in Version 1. Note that this function also performs the action of RMessagePtr2::Complete(), so care must be taken that the message is not used again.
This is a quick reference to show you which Version 2 APIs you should use to replace the Version 1 APIs.
Version 1 (where used) |
Version 2 (replace with...) |
CSharableSession |
CSession2 |
CSession |
CSession2 |
CServer |
CServer2 |
RMessagePtr |
RMessagePtr2 |
RMessage |
RMessage2 |
RServer |
RServer2 |
RThread::GetDesLength(const TAny* aPtr) |
RMessagePtr2::GetDesLength(TInt aParam) |
RThread::GetDesMaxLength(const TAny* aPtr) |
RMessagePtr2::GetDesMaxLength(TInt aParam) |
RThread::ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset) |
RMessagePtr2::ReadL(TInt aParam,TDes8& aDes,TInt aOffset=0) Note: use of RThread::ReadL() enclosed in a TRAP statement can be replaced by the non-leaving RMessagePtr2::Read(). |
RThread::ReadL(const TAny* aPtr,TDes16& aDes,TInt anOffset) |
RMessagePtr2::ReadL(TInt aParam,TDes16& aDes,TInt aOffset=0) Note: use of RThread::ReadL() enclosed in a TRAP statement can be replaced by the non-leaving RMessagePtr2::Read(). |
RThread::WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset) |
RMessagePtr2::WriteL(TInt aParam,const TDesC8& aDes,TInt aOffset=0) Note: use of RThread::WriteL() enclosed in a TRAP statement can be replaced by the non-leaving RMessagePtr2::Write(). |
RThread::WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset) |
RMessagePtr2::WriteL(TInt aParam,const TDesC16& aDes,TInt aOffset=0) Note: use of RThread::WriteL() enclosed in a TRAP statement can be replaced by the non-leaving RMessagePtr2::Write(). |
RThread::RequestComplete(TRequestStatus*& aStatus,TInt aReason) |
RMessagePtr2::Complete(TInt aReason) Note: the only way of signalling the client thread is by completing a message which the client sent to the session. |
RThread::Kill(TInt aReason) |
RMessagePtr2::Kill(TInt aReason) |
RThread::Terminate(TInt aReason) |
RMessagePtr2::Terminate(TInt aReason) |
RThread::Panic(const TDesC& aCategory,TInt aReason) |
RMessagePtr2::Panic(const TDesC& aCategory,TInt aReason) |
RSessionBase::Share() or RSessionBase::Share(EExplicitAttach) or RSessionBase::Share(EAutoAttach) |
RSessionBase::ShareAuto() |
RSessionBase::Attach() |
Not required in Version 2. |
RSessionBase::Send(TInt aFunction,TAny* aPtr) |
RSessionBase::Send(TInt aFunction,const TIpcArgs& aArgs) |
RSessionBase::SendReceive(TInt aFunction,TAny* aPtr,TRequestStatus& aStatus) |
RSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus& aStatus) |
RSessionBase::SendReceive(TInt aFunction,TAny* aPtr) |
RSessionBase::SendReceive(TInt aFunction, const TIpcArgs& aArgs) |
RSubSessionBase::CreateSubSession(RSessionBase& aSession,TInt aFunction,const TAny* aPtr) |
RSubSessionBase::CreateSubSession(RSessionBase, TInt aFunction, const TIpcArgs& aArgs); |
RSubSessionBase::Send(TInt aFunction,const TAny* aPtr) |
RSubSessionBase::Send(TInt aFunction,const TIpcArgs& aArgs) |
RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr,TRequestStatus&) |
RSubSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus&) |
RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr) |
RSubSessionBase::SendReceive(TInt aFunction,const TIpcArgs& aArgs) |
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.