|
||
This 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 securable interface for client/server communications, and form an essential component of Platform Security in Symbian OS.
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 OS 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 OS 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.
RSessionBase::Share()
Version 1 functions: |
|
Version 2 function: |
|
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()
.
RSessionBase::Attach()
Version 1 function: |
|
Version 2 function: |
NONE |
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: |
|
Version 2 functions: |
|
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: |
|
Version 2 function: |
|
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: |
|
Version 2 functions: |
|
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
.
CServer
CServer::NewSessionL()
Version 1 function: |
|
Version 2 function: |
|
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.
CSharableSession
Version 1 class: |
|
Version 2 class: |
|
Simply replace CSharableSession
with
CSession2
.
CSharableSession::CreateL()
Version 1 function: |
|
Version 2 function: |
|
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: |
|
Version 2 function: |
|
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.
CSharableSession::RMessage()
Version 1 function: |
|
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.
CSession
Version 1 class: |
|
Version 2 class: |
|
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;
CSession::CSession()
Version 1 function: |
|
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.
RMessage
Version 1 class: |
|
Version 2 class: |
|
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
.
RMessage::Client()
Version 1 function: |
|
Version 2 function: |
(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.
RMessage::MessagePtr()
Version 1 function: |
|
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: |
|
Version 2 functions: |
These functions are replaced by functions that take a message
argument index instead of a |
RMessagePtr
Version 1 class: |
|
Version 2 class: |
|
Replace RMessagePtr
with RMessagePtr2
.
See Using RMessagePtr2.
RThread
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)
RThread::RequestComplete()
Version 1 function: |
|
Version 2 function: |
|
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.
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);
}
}
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 OS 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.
RMessagePtr2::Complete()
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.
RMessagePtr2::Kill()
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.
RMessagePtr2::Terminate()
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.
RMessagePtr2::Panic()
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. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|