diff -r 578be2adaf3e -r 307f4279f433 Adaptation/GUID-EBFD653D-6E6A-5F6F-88D7-8CCE07B4002D.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adaptation/GUID-EBFD653D-6E6A-5F6F-88D7-8CCE07B4002D.dita Fri Oct 15 14:32:18 2010 +0100 @@ -0,0 +1,606 @@ + + + + + +Publish +and SubscribePublish and Subscribe allows global variables to be set and retrieved, +and allows subscribers to be notified that variables have changed. +

The general pattern for using on the kernel side is almost the same as +for the user side. However, different classes are used on the kernel side. +It may be useful to compare kernel side usage with user side usage as described +in the Publish and +Subscribe guide for the user-side API.

+ +
Properties

A +property has the two attributes: identity and type.

Identity

A +property is identified by a 64-bit integer made up of two 32-bit parts: the +category and the key.

A property belongs to a category, and a category +is identified by a UID.

A key is a 32-bit value that identifies a +specific property within a category. The meaning applied to the key depends +on the kind of enumeration scheme set up for the category. At its simplest, +a key can be an index value. It can also be another UID, if the category is +designed to be generally extensible.

Type

A property +can be:

    +
  • a single 32-bit value

  • +
  • a contiguous set of +bytes, referred to as a byte-array, whose length can vary from 0 to 512 bytes

  • +

Once defined, a property value can change, but the property type cannot. +Byte-array type properties can also change length provided the length does +not exceed the value RProperty::KMaxPropertySize. The limit +on size of property ensures some limit on RAM usage.

The API allows +a byte-array text type property to be pre-allocated when it is defined. This +means that the time taken to set the values is bounded. However, if the length +of this property type subsequently increases, then memory allocation may take +place, and no guarantees can be made on the time taken to set them.

Note +that the RProperty::ELargeByteArray property type can never +provide a real-time guarantee.

For code running kernel side, properties +and their values are defined, set and retrieved using a RPropertyRef object.

+
Creating and +closing a reference to a property

On the kernel side, all accesses +to a property must be done through a property reference, an instance +of a RPropertyRef.

You must create a reference +to a property, before doing any operation on that property. By operation, +we mean defining a property, subscribing to a property, publishing and retrieving +a property value. The kernel will fault if you have not first created a reference.

Only +one property, as uniquely identified by its category and key, can be accessed +by an instance of RPropertyRef; however a property can +be referenced by more than one instance of RPropertyRef.

Internally, +properties are represented by TProperty objects, and these +are reference counted. The act of creating a reference to a property results +either in the creation of a TProperty object or an increase +in the reference count of an existing object. The property itself has no attributes +or "structure" until it is defined.

Please note that the structure +and management of TProperty objects are part of Symbian platform's +internal implementation and will not be discussed further.

+Objects internal to Symbian platform + +

To establish a reference to a property, create an RPropertyRef object, +and then use one of the following functions:

    +
  • RPropertyRef::Attach() - +tries to open the property identified by the category and key, if it exists, +but creates that property if it does not exist. Creation is simply the act +of creating the internal TProperty object. The object has +no type or "structure" associated with it other than the use of the category +and key as identification markers.

  • +
  • RPropertyRef::Open() - +tries to open the property identified by the category and key, and assumes +that the property already exists; this fails if the property does not exist.

  • +

You can call these functions from a user thread running in supervisor +mode, from a kernel thread or from a DFC. If calling from a user thread running +in supervisor mode, then your thread must be running in a critical section. +In debug mode, if a user thread is not in a critical section, then the kernel +will fault.

On successful return from these functions, the RPropertyRef object +owns a resource, the property, and this can then be defined and accessed through +the RPropertyRef object.

It is difficult to make +firm rules as to which one your code should use, but generally, you use Open() if +you have no responsibility or interest in ensuring that the property exists. +You would use Attach() if you were to have single or joint responsibility +for ensuring that the property exists. It depends on the intent of the property +and the role of your driver code in the system.

Note that responsibility +for creating the property does not necessarily align with who can define, +delete, publish (write) or retrieve (read) a property value. This is governed +by the intent of the property and, for retrieving and publishing, by the security +policies in place.

When the property is no longer needed, you can +release it by calling RPropertyRef::Close(). Closing the +reference does not cause the property to disappear. This only happens when +the final reference to the property is closed.

Note that it +is quite legitimate to attach to, or to open, a property that has not been +defined, and in this case no error will be returned either. This enables the +lazy definition of properties as used in some of the usage patterns.

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +. . . + +// Attach to the ‘counter’ property. This creates the property +// if it does not already exist. +RPropertyRef counter; +TInt r; +r=counter.Attach(KMyPropertyCat,EMyPropertyCounter); + +// r should be KErrNone if sucessful or KErrNoMemory if there +// is an out of memory failure. +if (r != KErrNone) + { + // Handle the bad return value + } + +// use the counter object... + +// when finished, release the property +counter.Close(); +
+
Defining a +property

Defining a property gives it "structure" i.e. attributes +such as the property type, and the security policies that define the capabilities +that a process must have to publish and retrieve the property value.

A +property is defined using the RPropertyRef::Define() function. +You can call this function from a user thread running in supervisor mode, +from a kernel thread or from a DFC. If calling from a user thread running +in supervisor mode, then your thread must be running in a critical section. +In debug mode, if a user thread is not in a critical section, then the kernel +will fault.

You can call this function from a user thread running +in supervisor mode, from a kernel thread or from a DFC. If calling from a +user thread running in supervisor mode, then your thread must be running in +a critical section. In debug mode, if a user thread is not in a critical section, +then the kernel will fault.

The information needed to define the property +is passed to Define(). Note that a reference to the property +must already have been established using RPropertyRef::Attach() or RPropertyRef::Open().

A +property does not need to be defined before it can be accessed. This supports +programming patterns where both publishers and subscribers may define the +property.

Once defined, a property persists until the system reboots, +or the property is explicitly deleted. Its lifetime is not tied to that of +the thread or process that originally defined it. This means that, when defining +a property, it is important to check the return code from the call to RPropertyRef::Define() to +deal with the possibility that the property has previously been defined.

The +following code shows the definition of two properties, which we call: 'name' +and 'counter':

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +static _LIT_SECURITY_POLICY_PASS(KAllowAllPolicy); +static _LIT_SECURITY_POLICY_C1(KPowerMgmtPolicy,ECapabilityPowerMgmt); + +TInt r; + +// Attaches to the ‘counter’ property. +// If the property already exists, a new reference to it is created. +// If the property does not exist, it is created. +RPropertyRef counter; +r=counter.Attach(KMyPropertyCat,EMyPropertyCounter); +if (r != KErrNone) + { + // Handle the bad return value + } + +// Attaches to the ‘name’ property. +// If the property already exists, a new reference to it is created. +// If the property does not exist, it is created. +RPropertyRef name; +r=name.Attach(KMyPropertyCat,EMyPropertyName); +if (r != KErrNone) + { + // Handle the bad return value + } + +// Now define the 'counter' property: +// 1. Integer type +// 2. Pre-allocated size has no meaning, and must be zero. +// 3. Allow all processes to retrieve (read) the property. +// 4. Only processes with power managament capability can write. +// 5. Capability checks to be done against client thread's process. + +r=counter.Define(RProperty::EInt,KAllowAllPolicy,KPowerMgmtPolicy,0,iClientProcess); + +// You will probably need to check the return value. +// It may legitimately by non-KErrNone. + +... + +// Now define the 'name' property: +// 1. Byte array +// 2. Pre-allocate 100 bytes. +// 3. Allow all processes to retrieve (read) the property. +// 4. Only processes with power managament capability can write. +// 5. Capability checks to be done against client thread's process. + +r=name.Define(RProperty::EByteArray,KAllowAllPolicy,KPowerMgmtPolicy,100,iClientProcess); + +// You will probably need to check the return value. +// It may legitimately be non-KErrNone. +...

Once defined, a property value can change, but the property +type cannot. Byte-array type properties can also change length provided the +length does not exceed the 512 bytes, for RProperty::EByteArray types +or 65535 bytes or RProperty::ELargeByteArray types.

The +API allows byte-array type properties to be pre-allocated when they are defined. +This means that the time taken to set the values is bounded. However, if the +length of these property types subsequently increases, then memory allocation +may take place, and no guarantees can then be made on the time taken to set +them.

Security notes:

    +
  • Symbian platform defines +a property category known as the system category that is reserved for system +services, and is identified by the KUidSystemCategoryValue UID. +To define a property within this category, then the process on whose behalf +your code is doing the define operation must have the writeDeviceData capability, ECapabilityWriteDeviceData.

    To +ensure that this security check is made, you must pass a pointer to the appropriate DProcess object +as the second parameter to Define(). If you omit this parameter, +a null pointer is assumed by default, and the security check is bypassed. +This may be legitimate if you are doing this on behalf of the kernel or on +behalf of the driver itself.

  • +
  • Whether you pass a DProcess pointer +or not, the owner of the property is deemed to be the process that is current when +the code runs. It is this, the current process, that you will need to pass +to RPropertyRef::Delete() at a later time.

  • +
  • You also need to define +two security policies: one to define the capabilities that will be required +to publish (write to) the property, and the other to define the capabilities +that will be required to retrieve (read) the property. Security policies are TSecurityPolicy objects +or their static equivalents.

    In the above code fragment, we specify +that all processes in the system will be able to read the defined property +but only those with power management capability will be able to write to the +property - this is an arbitrary choice and is for illustration only.

  • +

In the above code fragments, iClientProcess is a +pointer to the client thread's owning process object, and assumes that the +driver code is making the request on behalf of a client, although this may +not necessarily be so. Typically, if you need to keep this information, you +could set this up in the logical channel constructor using the following code:

iClientProcess=&Kern::CurrentProcess();

The +constructor code runs in the context of the client user thread. Note that DProcess is +internal to Symbian.

+
Deleting a +property

Deleting a property is the opposite of defining it. It +removes type and security information. It does not remove a reference +to the property.

A property is deleted using the RPropertyRef::Delete() function. +You can call this function from a user thread running in supervisor mode, +from a kernel thread or from a DFC. If calling from a user thread running +in supervisor mode, then your thread must be running in a critical section. +In debug mode, if a user thread is not in a critical section, then the kernel +will fault.

Any outstanding subscriptions for this property complete +with KErrNotFound.

Security notes:

    +
  • Only the owning process +is allowed to delete the property. This is deemed to be the process that was +current when the property was defined. However, to enforce this, you must pass +into RPropertyref::Delete() a pointer to the DProcess object +that represents the owning process. If you omit to pass this parameter to Delete(), +a null pointer is assumed by default, and the security check is bypassed.. +This may be legitimate if you are doing this on behalf of the kernel or on +behalf of the driver itself.

  • +

For example, extending the code fragment introduced in defining a +property above:

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +static _LIT_SECURITY_POLICY_PASS(KAllowAllPolicy); +static _LIT_SECURITY_POLICY_C1(KPowerMgmtPolicy,ECapabilityPowerMgmt); + +TInt r; + +// Attaches to the ‘counter’ property. +// If the property already exists, a new reference to it is created. +// If the property does not exist, it is created. +RPropertyRef counter; +r=counter.Attach(KMyPropertyCat,EMyPropertyCounter); +if (r != KErrNone) + { + // Handle the bad return value + } + +// Attaches to the ‘name’ property. +// If the property already exists, a new reference to it is created. +// If the property does not exist, it is created. +RPropertyRef name; +r=name.Attach(KMyPropertyCat,EMyPropertyName); +if (r != KErrNone) + { + // Handle the bad return value + } + +// Now define the 'counter' property: +// 1. Integer type +// 2. Pre-allocated size has no meaning, and must be zero. +// 3. Allow all processes to retrieve (read) the property. +// 4. Only processes with power managament capability can write. +// 5. Capability checks to be done against client thread's process. + +r=counter.Define(RProperty::EInt,KAllowAllPolicy,KPowerMgmtPolicy,0,iClientProcess); + +// You will probably need to check the return value. +// It may legitimately by non-KErrNone. + +... + +// Now define the 'name' property: +// 1. Byte array +// 2. Pre-allocate 100 bytes. +// 3. Allow all processes to retrieve (read) the property. +// 4. Only processes with power managament capability can write. +// 5. Capability checks to be done against client thread's process. + +r=name.Define(RProperty::EByteArray,KAllowAllPolicy,KPowerMgmtPolicy,100,iClientProcess); + +// You will probably need to check the return value. +// It may legitimately by non-KErrNone. + +... + +// Delete the ‘name’ property. +// Assumes that the owning process is iClientProcess. This will be checked +// as being the valid owner of the property. +r=name.Delete(iClientProcess); +if (r != KErrNone) + { + // deal with a non-KErrNone return value. + } + +// Delete the ‘counter’ property. +// Assumes that the owning process is iClientProcess. This will be checked +// as being the valid owner of the property. +r=name.Delete(iClientProcess); +if (r != KErrNone) + { + // deal with a non-KErrNone return value. + }
+
Publishing +a property value

A property is published (written), using the RPropertyRef::Set() family +of functions.

This is guaranteed to have bounded execution time, suitable +for high-priority, real-time tasks, except when publishing a byte-array property +that requires the allocation of a larger space for the new value, or when +publishing a large byte-array property type, as identified by ELargeByteArray.

Property +values are written atomically. This means that it is not possible for threads +reading a property to get a garbled value.

All outstanding subscriptions +for a property are completed when the value is published, even if it is exactly +the same as the existing value. This means that a property can be used as +a simple broadcast notification service.

Publishing a property that +is not defined is not necessarily a programming error. The Set() functions +just return an error. If this is not expected for any particular usage, then +the error must be checked and processed by the caller.

Security +notes:

    +
  • If you pass a pointer +to a DProcess object, then the capabilities of that process +will be checked against those contained in the write security policy that +was created and passed to RPropertyRef::Define(). If you +omit this DProcess parameter, a null pointer is assumed by +default, and the security check is bypassed. This may be legitimate +if you are doing this on behalf of the kernel or on behalf of the driver itself.

  • +

See the code fragment in the section Retrieving +a property value

+
Retrieving +a property value

The current value of a property is retrieved +(read) using the RPropertyRef::Get() family of functions.

This +is guaranteed to have bounded execution time, suitable for high-priority, +real-time tasks, except when retrieving a large byte-array property type, +as identified by ELargeByteArray.

Property values +are read atomically. This means that it is not possible for threads reading +a property to get a garbled value.

Retrieving a property that is not +defined is not necessarily a programming error. The Get() functions +just return an error. If this is not expected for any particular usage, then +the error must be checked and processed by the caller.

Integer properties +must be accessed using the overload that takes an integer reference, whereas +a byte-array property is accessed using the overload that takes a descriptor +reference.

The following code fragment shows publication and retrieval +of a property. Note that it contains a race condition, especially if another +thread is executing the same sequence to increment the ‘counter’ value.

Security +notes:

    +
  • If you pass a pointer +to a DProcess object, then the capabilities of that process +will be checked against those contained in the read security policy that was +created and passed to RPropertyRef::Define(). If you omit +this DProcess parameter, a null pointer is assumed by default, +and the security check is bypassed. This may be legitimate if you are +doing this on behalf of the kernel or on behalf of the driver itself.

  • +
const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; + +TInt r; + +RPropertyRef counter; +RPropertyRef name; + + +// Assume that the 'name' and 'counter' property references have +// already been created. They may have been defined. +// +// Assume that the process to be used for security checking is iClientProcess. + +... + +// publish a new name value. +_LIT8(KSomeExampleName,"My example name"); +r=name.Set(KSomeExampleName, iClientProcess); +if (r != KErrNone) + { + // Check the return value. KErrNotFound means that the property has not yet been + // defined which may be legitimate. + // KErrArgument is a serious problem at this stage. + // KErrPermissionDenied is a security violation; the process iClientProcess has + // insufficient capability to do this operation. + } + + +// Retrieve the first 10 characters of the name value. +// We are not doing any security checking for this operation, so no DProcess pointer +// is passed to Get(). +TBuf<10> n; +r=name.Get(n); + +if ((r!= KErrNone) && (r != KErrOverflow)) + { + // Handle error value. + } + +// retrieve and publish a new value using the attached ‘counter’ property +TInt count; +r=counter.Get(count); +if (r==KErrNone) + { + r=counter.Set(++count); + } +else + { + // Handle bad return value + } +... + +// When finised, release the property references. +counter.Close(); +name.Close();
+
Subscribing +to, and unsubscribing from, a property

Subscribing to a property +is the act of making an asynchronous request to be notified of a change to +that property.

You make a request for notification of a change to +a property by calling the RPropertyRef::Subscribe() member +function on a property reference object. Only one subscription request can +be outstanding at any time for a given RPropertyRef instance.

You +can cancel an outstanding subscription request by calling RPropertyRef::Cancel(). +This is unsubscribing from the property.

Subscribing to a property +is a single request to be notified when the property is next updated, it does +not generate an ongoing sequence of notifications for every change to that +property's value. Neither does it provide the caller with the new value. In +essence, the act of notification should be interpreted as “Property X has +changed” rather than “Property X has changed to Y”. This means that the new +value must be explicitly retrieved, if required. As a result, multiple updates +may be collapsed into one notification, and subscribers may not have visibility +of all intermediate values.

This might appear to introduce a window +of opportunity for a subscriber to be out of synchronisation with the property +value – in particular, if the property is updated again before the subscriber +thread has had the chance to process the original notification. However, a +simple programming pattern, outlined in the second example below ensures this +does not happen. The principle is that, before dealing with a subscription +completion event, you should re-issue the subscription request.

Note +that if the property has not been defined, then a subscription request does +not complete until the property is subsequently defined and published. Note +that the request will complete with KErrPermissionDenied if +the subscribing process does not have sufficient capability as defined by +the TSecurityPolicy object supplied by the process defining +the property.

If the property is already defined, then the request +completes immediately with KErrPermissionDenied if the +subscribing process does not have sufficient capability.

The essence +of subscribing to a property is that you pass a function into RPropertyRef::Subscribe() and +that this function is called when the property value is published. You pass +the function by wrapping it in TPropertySubsRequest object +and then pass this into Subscribe(). What the function does +depends on the implementation, but you may want to re-subscribe to the property +and then retrieve the property value, or you may want to set some flag. It +all depends on the intent of the property and the driver code.

The +following code fragments show the general idea.

const TUid KMyPropertyCat={0x10012345}; +enum TMyPropertyKeys={EMyPropertyCounter,EMyPropertyName}; class DMyLogicalChannel : public DLogicalChannel + { + public : + DMyLogicalChannel(); + void OpenReference(); + void SetUpSubscription(); + ... + private : + static void HandleSubsComplete(TAny* aPtr, TInt aReason); + ... + private: + RCounterRef iName; + TBuf<10> iNameValue; + DProcess iClientProcess; + TPropertySubsRequest iSubsRequest; + TInt iReason; + } DMyLogicalChannel::DMyLogicalChannel() : iSubsRequest(&HandleSubsComplete, this) + { + iClientProcess = &Kern::CurrentProcess(); + // Other code omitted + } void DMyLogicalChannel::OpenReference() + { + // Open a reference to the ‘name’ property, and assume that + // the property has already been created and defined. + + TInt r + ... + r=iName.Open(KMyPropertyCat,EMyPropertyName); + if (r != KErrNone) + { + // Handle bad return value. + } + ... + } void DMyLogicalChannel::SetUpSubscription() + { + // Now ask to be notified when the 'name' property is updated. + // This will eventually result in a call to the function HandleSubsComplete() + // at some later time (asynchronously). + // When eventually called, the pointer to this DMyLogicalChannel object will + // be passed to HandleSubsComplete(). + // + ... + iReason = KRequestPending; + TInt r = iName.Subscribe(iSubsRequest); // ignoring security issues here. + if (r != KErrNone) + { + // handle bad return code. + } + return; + } void DMyLogicalChannel::CancelSubscription() + { + if (iReason == KRequestPending) + { + iName.Cancel(iSubsRequest); + } + } void DMyLogicaChannel::SubsCompleteFn(TAny* aPtr, TInt aReason) + { + // A static function called when a change to the property occurs. + // aPtr will point to the DMyLogicalChannel object + // (see the DMyLogicalChannel constructor) + // aReason is the reason for the subscription completing. This may be: + // KErrNone - for a normal completion. + // KErrPermissionDenied - if the security check has failed. + // KErrCancel - if the request was cancelled. + // For a normal completion, setup another notification request before + // getting the current value of the property. + // + DMyLogicaChannel* self = (DMyLogicaChannel*) aPtr; + self->iReason = aReason; + if (iReason == KErrNone) + { + self->SetUpSubscription(); + self->Get(iNameValue,iClientProcess); + return; + } + // Investigate the non-zero reason code. + }
+
Usage patterns

There +are three usage patterns that can easily be identified, labelled as: standard +state, pure event distribution, and speculative publishing.

Standard state

This +pattern is used for events and state that are known to be used widely in the +system. Examples of this might be battery level and signal strength, which +are important in every phone.

The publisher calls RPropertyRef::Define() to +create the appropriate property. For byte array or text properties, a size +sufficient for all possible values should be reserved. An error of KErrAlreadyExists should +be ignored. The publisher then publishes the property values as, and when, +appropriate. If the RPropertyRef::Set() call fails, this +should be treated as a serious error, since it indicates that important system +state is not getting through. Appropriate action might include panicking or +rebooting the system. Subscribers will use RPropertyRef::Subscribe() to +request notification, and RPropertyRef::Get() to retrieve +the property value on notification.

The memory to store the property +value will be permanently allocated, even if it turns out that no-one in the +system needs that value. This does ensure that the value can always be published, +even if the system is in an out of memory situation. For this reason, this +approach should be limited to widely used and important state. The Speculative publishing pattern offers an approach for dealing with +less important state.

Pure event distribution

This +pattern is used when events need to be distributed, not values.

The +publisher of the event simply uses an integer property, and calls RPropertyRef::Set() with +any value. Even if the value of the property is not changed by this operation, +all subscribers will be notified that a Set() has occurred, +and by implication that the related event has occurred.

Subscribers +will be able to detect that an event has occurred, but will get no other information. +The minimum possible memory is wasted on storage for the dummy value.

Speculative publishing

This +pattern is used when it is not known whether a value will be of interest to +others or not. Unlike the standard +state pattern, the publisher of the event does not call RPropertyRef::Define() to +create the property. Instead, it simply calls RPropertyRef::Set() as +appropriate, and ignores any KErrNotFound error.

When +other code in the system, i.e. a potential subscriber, is interested in the +state, it calls RPropertyRef::Define() to create the property +and allocate the memory for the value. An error of KErrAlreadyExists should +be ignored, as this only indicates that some other code in the system is also +interested in the value and has already created the property.

The +subscriber then calls RPropertyRef::Subscribe() and RPropertyRef::Get() as +usual to interact with the property. On the first Get(), +the subscriber may retrieve the property default value (zero, or a zero length +descriptor). This must be substituted with a sensible default value for the +property in question.

Using this pattern, no memory is wasted on properties +that have no subscribers, while the publisher code is simpler as there is +no need for configuration as to which properties to publish.

The publisher, +however, wastes some time attempting to publish unneeded values, but this +should not be an issue unless the value is very frequently updated.

Where +events are published very infrequently, the subscriber could have a dummy +value for a long time, until the next publish event updates the value. Often +this is not a problem as a default value can be substituted. For example a +full/empty indicator for a battery level, none for signal strength etc. This +pattern is unlikely to be useful if there is no suitable default value.

+
\ No newline at end of file