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()
Version 1 functions: |
Send(TInt aFunction,TAny* aPtr) SendReceive(TInt aFunction,TAny* aPtr,TRequestStatus& aStatus) SendReceive(TInt aFunction,TAny* aPtr) |
Version 2 functions: |
Send(TInt aFunction,const TIpcArgs& aArgs) SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus& aStatus) SendReceive(TInt aFunction, const TIpcArgs& aArgs) |
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()
Version 1 functions: |
Send(TInt aFunction,const TAny* aPtr) SendReceive(TInt aFunction,const TAny* aPtr,TRequestStatus&) SendReceive(TInt aFunction,const TAny* aPtr) |
Version 2 functions: |
Send(TInt aFunction,const TIpcArgs& aArgs) SendReceive(TInt aFunction,const TIpcArgs& aArgs, TRequestStatus&) SendReceive(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().
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
.
Version 1 function: |
CSharableSession* CServer::NewSessionL(const TVersion& aVersion) const |
Version 2 function: |
CSession2* CServer2::NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const |
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
.
Version 1 function: |
const RThread& RMessage::Client() const |
Version 2 function: |
TInt RMessage2::Client(RThread& aClient, TOwnerType aOwnerType=EOwnerProcess) const (Note that the function is implemented by the |
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 |
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...) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note: use of |
|
Note: use of |
|
Note: use of |
|
Note: use of |
|
Note: the only way of signalling the client thread is by completing a message which the client sent to the session. |
|
|
|
|
|
|
or or |
|
|
Not required in Version 2. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|