Sunteți pe pagina 1din 28

Using Symbian OS

DatasharingandpersistEnce
Data Sharing and Persistence
part of the Using Symbian OS series

1st edition, 01/09

Published by:
Symbian Software Limited
2-6 Boundary Row
Southwark
London SE1 8HP
UK
www.symbian.com

Trademarks, copyright, disclaimer


‘Symbian’, ‘Symbian OS’ and other associated Symbian marks are all trademarks of Symbian
Software Ltd. Symbian acknowledges the trademark rights of all third parties referred to in this
material. Copyright © Symbian Software Ltd 2009. All rights reserved. No part of this material
may be reproduced without the express written permission of Symbian Software Ltd. Symbian
Software Ltd makes no warranty or guarantee about the suitability or the accuracy of the
information contained in this document. The information contained in this document is for
general information purposes only and should not be used or relied upon for any other
purpose whatsoever.

Compiled by: Reviewed by:


Anna Fisher Henry Boothroyd Brooks
Philip Cheung
Managing Editor: Richard Coles
Ashlee Godwin Husien Hong
Ivan Litovski
Richard Maynard
Mark Shackman
Jo Stichbury

Symbian Developer Network


developer.symbian.com
Contents
Introduction .................................................................................................................................1
Symbian SQL ...............................................................................................................................1
Structured Query Language (SQL) ............................................................................................2
Symbian SQL API ......................................................................................................................3
Database access types .............................................................................................................6
Client-server issues...................................................................................................................6
Backing up databases ..............................................................................................................6
POSIX support ..........................................................................................................................7
Publish and Subscribe ................................................................................................................7
Properties .................................................................................................................................8
Publish and Subscribe API........................................................................................................8
Message queues .........................................................................................................................9
Basic message queue operations.............................................................................................9
Message queue API.................................................................................................................10
The Central Repository...............................................................................................................11
Key masks ...............................................................................................................................14
Central Repository API ............................................................................................................15
Backing up your repository.....................................................................................................16
The CentRepConv conversion tool ..........................................................................................17
Memory chunks .........................................................................................................................17
Conclusion .................................................................................................................................18
1

Introduction
This booklet introduces four important mechanisms provided by Symbian OS for sharing data
securely across threads and processes:

• Symbian SQL
• Publish and Subscribe
• Message queues
• Central Repository.

Two of these mechanisms – Symbian SQL and the Central Repository – provide mechanisms for
persisting data. The other two – Publish and Subscribe and message queues – provide
mechanisms for transient communications (that is, the data that does not persist after the
phone reboots).

This booklet ends with a brief mention of using shared memory regions (which are known as
chunks). However, their use is beyond the scope of this booklet and is not covered in detail.
We do not cover the Symbian OS client-server architecture, which is another mechanism for
sharing data on Symbian OS. Similarly, we do not cover persisting data to files. For an
introduction to these topics, refer to the Symbian Press book, Developing Software for
Symbian OS, Second Edition by Steve Babin, at
developer.symbian.com/main/documentation/books/books_files/dasos/index.jsp.

Symbian SQL
Symbian OS includes an SQL database service to applications and other Symbian OS software
components. As Figure 1 shows, it is implemented using the Symbian OS client-server model
and is based on the SQLite database engine. SQLite is a widely deployed open source
database engine. You can find out more about SQLite at www.sqlite.org.

Figure 1: Symbian SQL


2

Structured Query Language (SQL)


SQL is a standard language for querying and modifying data and managing databases.

Here is an example of the data definition part of the language (DDL) that shows using the
CREATE keyword to create a database table:

CREATE TABLE booking (


booking_no INTEGER PRIMARY KEY,
event_no INTEGER NOT NULL,
student_name TEXT NOT NULL
);

Notice that we are using the PRIMARY KEY keyword which causes a unique index to be
created automatically.

We can then use the data manipulation language (DML) keywords INSERT INTO to add a
record to the table:

INSERT INTO booking


(event_no, student_name)
VALUES
(1003, "Bridget Jones");

Other common DML keywords are UPDATE to modify data and DELETE FROM to delete
records.

Finally, here is an example of a query, which is the most common type of SQL operation:

SELECT student_name, event_no


FROM booking
ORDER BY student_name;

This returns a record set containing a row for each record in the booking table, sorted in
descending alphabetical order of student name.

Queries start with the SELECT keyword. You can use the WHERE clause to restrict the results
to certain criteria and you can use ORDER BY to sort the results. The supported query syntax
is extensive and includes using JOIN to combine the results from two or more tables (unlike
the older Symbian OS DBMS database service, which does not support the JOIN keyword).

There is a full list of the SQL language supported by SQLite at www.sqlite.org/lang.html.


With one or two minor exceptions, Symbian SQL supports the full range of SQL syntax that is
supported by SQLite. Symbian SQL does not support the PRAGMA keyword, and ATTACH and
DETACH are deprecated in Symbian SQL in favor of functions available in the API and will fail
if applied to a secure database.

Like many programming languages, you can often achieve the same results in more than one
way, but some ways are more efficient than others. Some of the details are specific to how the
underlying components have been implemented. For detailed information about the best way
to write SQL for Symbian OS, see
developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/guide/System-
Libraries-subsystem-guide/SQL/SQlHowToMakeEfficientUseOf.guide.html.
3

Symbian SQL API


Symbian SQL has two fundamental client-side classes: RSqlDatabase and
RSqlStatement.

RSqlDatabase
RSqlDatabase is a handle to the SQL database. This provides functions for creating,
opening, copying and deleting a database, etc. Here is an example of creating a non-secure
database:

RSqlDatabase myDatabase;
_LIT(KDBFileName, "C:\\public\\data\\MyDatabase.db");
TInt err = myDatabase.Create(KDBFileName);

You can create a secure database by passing the security policies that you want to apply to
the database to the Create() method. We’ll look at the database access types in the
‘Database access types’ section later in the booklet.

You can use the Open() method to open an existing database. For example:

TInt err = myDatabase.Open(KDBFileName);

And as you might expect, you use Close() to close the handle to the database to free
allocated resources.

RSqlDatabase also provides an Exec() function for executing a simple one-shot SQL
statement. Here we are executing a simple DML statement to update a record in the event table:

_LIT(KUpdateString, "UPDATE event SET available_spaces=8 WHERE


event_no=1004");

TInt ret = myDatabase.Exec(KUpdateString);

You can use the same approach to insert a row into the table. However, in real code you often
need to insert multiple rows, using different values each time. One way to do this is to build
up a different SQL statement for each row. A more efficient alternative, however, is to use the
RSqlStatement class, which enables you to prepare a single statement and substitute
different parameters for each row, as shown in the code sample on the next page.

RSqlStatement
But first let’s look at an example of using RSqlStatement to execute a SELECT statement.
You must use RSqlStatement to execute SELECT statements because RSqlDatabase
does not provide a mechanism for accessing the results of a query.

Here is an example:

_LIT(KSelectString, "SELECT title, cost FROM course");

RSqlStatement myStatement; // 1
CleanupClosePushL(myStatement);

myStatement.PrepareL(myDatabase, KSelectString); // 2

TInt ret;
4

while ((ret = myStatement.Next()) == KSqlAtRow) // 3


{
TPtrC title = myStatement.ColumnTextL(0); // 4
TInt cost = myStatement.ColumnInt(1);

// ... Use the values

if (ret != KSqlAtEnd)
{
User::LeaveIfError(ret);
}

CleanupStack::PopAndDestroy(&myStatement); // 5

This follows a standard pattern:


1. Create the RSqlStatement object and push it onto the cleanup stack.
2. Prepare the statement.
3. Retrieve a record from the result set.
4. Read the results from specified columns – the argument denotes the column number in the
returned record set. Notice that we use different functions, depending on the data type. (If
necessary, you can use the ColumnType() function to find out the data type.)
5. Pop and destroy the RSqlStatement object from the cleanup stack.

Notice that in this example we are potentially retrieving multiple rows from the table, so we
read the results and retrieve the next record in a loop. We execute the loop while the return
code is KSqlAtRow. This means that we drop out of the loop if an error occurs when we
retrieve a row. If no errors occur, the loop ends when we get to the end of the rows
(KSqlAtEnd). We must also check the return value after the end of the loop in order to
handle any errors.

As mentioned earlier, RSqlStatement is also useful when you need to execute multiple DML
statements (such as INSERT and UPDATE), because you can prepare the statement once and
then substitute parameters. For example, you can use this technique to add multiple rows to a
database table, like this:

RSqlStatement myStatement; // 1
CleanupClosePushL(myStatement);

_LIT(KInsertString, "INSERT INTO booking (event_no, student_name) \


VALUES (:event_no, :student_name)");

myStatement.PrepareL(myDatabase, KInsertString);

_LIT(KEvent, ":event_no");
_LIT(KName, ":student_name");

TInt eventParam = myStatement.ParameterIndex(KEvent); // 2


TInt nameParam = myStatement.ParameterIndex(KName);

for (TInt i = 0; i < numRecords; i++) // 3


{
5

User::LeaveIfError(myStatement.BindInt(eventParam, events[i]));
User::LeaveIfError(myStatement.BindText(nameParam,(*names)[i]));
User::LeaveIfError(myStatement.Exec());
myStatement.Reset();
}

CleanupStack::PopAndDestroy(&myStatement); // 4

These are the steps:


1. As in the previous example, we start by creating the RSqlStatement, pushing it to the
cleanup stack and calling PrepareL() to prepare the statement. Notice that we have
used the :column_name syntax in the SQL statement to represent a parameter, to which
we can bind a value.
2. Then we retrieve the indexes of the two parameters and store them in local variables. This
is an alternative to simply specifying their numeric indexes.
3. For each record that we want to add to the table, we first bind values to the two
parameters (notice that the function we use depends on the type of data we are binding).
Then we call Exec() to execute the statement followed by Reset() to reset the
prepared statement to its original state so that it is now ready to bind new values in the
next iteration of the loop.
4. Finally, we pop and destroy the RSqlStatement object from the cleanup stack.

When you need to do more than one INSERT, UPDATE or DELETE operation, it is best to
execute them within an explicit transaction. You do this by executing the BEGIN statement
prior to the first change and executing COMMIT after all of the changes have finished. This not
only avoids inconsistencies in the data but also speeds performance, because only one time-
consuming COMMIT operation occurs.

For example, we could add the following code before the first line of the previous example to
execute BEGIN:

_LIT(KBeginTransaction, "BEGIN");
User::LeaveIfError(myDatabase.Exec(KBeginTransaction));
CleanupStack::PushL(TCleanupItem(DoRollback, &myDatabase));

Notice that we have pushed a TCleanupItem that points to a rollback function onto the
cleanup stack. This means that we can revert the changes if a leave occurs mid-transaction.
Here is an example of the rollback function:

void DoRollback(TAny* aDbHandle)


{
_LIT(KRollbackTransaction, "ROLLBACK");
if (aDbHandle)
{
RSqlDatabase* myDb = static_cast<RSqlDatabase*>(aDbHandle);
myDb->Exec(KRollbackTransaction);
}
}

Then we would add the following to execute COMMIT. We need to add this after the loop that
executes the INSERT statements:
6

_LIT(KCommitTransaction, "COMMIT");
User::LeaveIfError(myDatabase.Exec(KCommitTransaction));
CleanupStack::Pop();

Database access types


There are three database access types: public shareable, private and secure shareable. The
access type is defined when you create the database.

• To create a public shareable database, do not provide a security policy, then create the
database in a public folder, as shown in the example in the ‘RSqlDatabase’ section.
• To create a private database, use the same syntax but with the location set to the
application’s private data cage.
• To create a secure shareable database, you need to provide one or more security policies
in a container. The database is then automatically created in the SQL server’s private data
cage. For an example of creating a secure shareable database, see
developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/guide/System-
Libraries-subsystem-guide/SQL/UsingSymbianSQLFramework.html.

Client-server issues
Because Symbian SQL is implemented using the Symbian OS client-server model, two or more
applications or processes can share a secure database without the need to wrap it within its
own server. However, sometimes you might need to consider implementing an intermediate
server, for example, if you want to provide notifications when the database is updated
(currently, Symbian SQL does not provide notifications).

Backing up databases
Symbian OS includes software to back up data to, and restore data from, a connected PC. If
you want your application’s data and files to be backed up and restored, you need to create
an XML registration file (called backup_registration.xml) in the application’s private
data cage. This should specify the names of the files and/or directories to be backed up and
restored, including your private and public databases. For a public or private database, you
simply need to include the name or directory of the database in the backup registration file.

In the example below, the passive_backup element specifies that the entire private data
cage directory should be backed up.

<?xml version="1.0" standalone="yes" ?>


<backup_registration version="1.0">
<passive_backup>
<include_directory name = "\" />
</passive_backup>
<proxy_data_manager SID="0x10281e17" />
<restore requires_reboot="no" />
</backup_registration>

For a secure shareable database, use the SID attribute of the proxy_data_manager
element in the backup registration file and set it to the Symbian SQL server’s secure ID (SID)
of 0x10281e17, as highlighted in the example above.
7

POSIX support
Symbian OS v9.5 will provide the native SQLite C API as part of its P.I.P.S. offering. P.I.P.S. is a
recursive abbreviation for ‘P.I.P.S. Is POSIX on Symbian OS’ and provides an API layer, above
the native Symbian C++ APIs, that is more closely aligned with industry standard APIs. This
makes Symbian OS more accessible to developers who program using the Standard C
language. For more information about P.I.P.S., please see
developer.symbian.com/main/documentation/books/books_files/pdf/P.I.P.S..pdf.

Publish and Subscribe


Publish and Subscribe provides a mechanism for defining and publishing global properties, which
can be integer values, descriptors or byte arrays. This mechanism is useful for publishing
properties that are not suited to the one-to-one communication offered by client-server
communication.

Publish and Subscribe is suitable for transient settings only because the properties are not
preserved when the phone shuts down, although they persist beyond the lifetime of the defining
process. Typical scenarios include multicasting the status of battery and communication links.

Figure 2: Publish and Subscribe

As Figure 2 illustrates, a publisher is a thread that defines and sets a property and a
subscriber is a thread that reads a property’s value and subscribes to notifications of changes.
The publisher and subscriber can be different threads within the same process or different
processes. Any program can act as a publisher or subscriber or both. Publishers and
subscribers do not need to know about each other or link to special client APIs.

Publish and Subscribe can be used to notify critical tasks that require real-time guarantees.
When real-time guarantees are not required, Publish and Subscribe can be used to trigger
actions that are based on the state of the property even when the client has missed a
previous state.

For properties that are updated frequently, it is recommended that you do not perform a
substantial amount of processing on each update notification because this can negatively
affect performance.
8

Properties
A Publish and Subscribe property has an identity and a type:

• The identity consists of a 64-bit integer made up of a category and a key. The category is
identified by a UID, which is normally the secure ID (SID) of the process that defines the
property. An error is generated if any other process attempts to delete the property. In
effect, this forms a data cage, preventing a process from defining, or ‘occupying,’ another
process's property. The key is a 32-bit value that identifies the property within the category.
• The type defines the data type that can be stored in the property. It can be a single
32-bit value, a contiguous set of bytes, referred to as a byte array, or Unicode text. Byte
arrays and text properties are both limited to 512 bytes; there are separate types for
larger arrays but these cannot be used in real-time code.

A property’s identity, type and value are the only things that are shared between the
publisher(s) and subscriber(s).

Publish and Subscribe API


For a program to use the Publish and Subscribe service, it must declare an RProperty
object. This provides methods as follows:

• Define() – create a property and define its type and identity, optionally specifying
security policies
• Delete() – remove a property from the system
• Set() – change the value of a property
• Get() – retrieve the value of a property
• Attach() – create a handle to a property
• Subscribe() – register for notification of changes
• Cancel() – unsubscribe to notification of changes.

The write operations are atomic, which means that they either succeed or do nothing. As a
result, when a thread reads a property, the data is guaranteed to be valid.

Setting and getting values is accomplished in bounded time (and are thus suitable for real-
time code) under the following conditions:

• The value being published is either a 32-bit value, or a byte array that has been
preallocated to a sufficient length at Define() time.
• The publishing thread already has an RProperty object that has been attached to the
desired category/property using Attach(), and the one-argument version of
Set()/Get() is used.

Note that the following are not accomplished in bounded time and are therefore not suitable
for real-time code:

• defining and deleting properties


• any operations on the large byte array type.

For examples of their use and further documentation, please see


developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/guide/Base-
subsystem-guide/e32/PublishAndSubscribe/UsingPublishSubscribe.guide.html.

Also see the Publish and Subscribe white paper on the Symbian Developer Network:
developer.symbian.com/main/downloads/papers/publishandsubscribe/PublishAndSubscribe_v1.0.pdf
9

Message queues
Symbian OS message queues also allow the sharing of transient data, by providing one-way,
one-to-one communication between two threads. Threads can send and receive messages from
a message queue, be notified when a message enters the queue and be notified when space
becomes available in the queue.

Message queues lend themselves to the passing of memory buffers from one thread to another
in the same or different processes. This usage can be employed effectively for processing intensive
and/or multimedia applications.

Figure 3: Message queue

As Figure 3 illustrates, a message queue is a block of memory in the kernel. Each message is a
slot in that memory and there are a fixed number of slots, determined at the time of creation.
Messages are therefore of fixed size and the queue has a limited capacity and can potentially run
out of space. This means that message queues are not real-time safe – it is not possible to write
a message to a queue in bounded time because the queue may be full.

Message queues are transient: neither the messages nor the queues are persistent. When the last
handle to the queue is closed, cleanup is performed and the messages and queue disappear.

Message queues can be created ‘locally’ to allow communication between threads in the same
process, or ‘globally’ to allow threads in different processes to communicate. A global message
queue can be named and publicly visible to other threads or it can be anonymous, which means
that it does not have a registered global name. Using a global name is not recommended because
it is insecure. When using an anonymous message queue, you share its handle with a child process
at creation time, or with any process via the client-server architecture. This prevents other
processes from accessing the queue, which makes it a more secure way of sharing messages.

Basic message queue operations


Basic message queue operations include:

• Creating and opening a message queue.


• Sending a message – either using the blocking option, which waits for space if the queue
is full, or the non-blocking option, which does not wait if the queue is full, (that is, the
message is not sent).
• Receiving a message – this removes the message from the queue, hence it is unicast.
There are blocking and non-blocking receive options, just like for sending.
• Requesting to be notified when space becomes available in the queue and/or when data
arrives in the queue.
10

It is the responsibility of the receiving thread to validate the data that is received. There is no
in-built validation of the data passed in the messages other than basic type checking; that is,
the data type is specified as the template argument.

Although it is possible for any number of threads to have the same message queue open, in
practice there are limitations that mean that the main use case for message queues is one-way,
one-to-one communication between two threads. The reasons for this include:

• The communication is unicast in nature and so only one thread can receive each
message.
• Only one thread can do a blocking send at any one time because the kernel object has
only one slot for a thread to wait for space to become available. Similarly, only one
thread can do a blocking receive at any one time because the kernel has only one slot
for a thread to wait for data to arrive. A thread panics if it attempts to do a blocking
send or receive whilst another thread is occupying the corresponding waiting slot.
• To handle multiple threads doing non-blocking sends and/or receives, you would need to
use a polling mechanism. However, polling is not a recommended solution on a Symbian OS
smartphone, because it can quickly run down the battery.

If you require two-way communication between two threads, it is possible to use a single message
queue provided that the communication is strictly alternating – that is, Thread A sends a message
and Thread B reads that message before sending a message back. Thread A then reads this
message before sending another message back, and so on. For any other type of two-way
communication between two threads, you should set up two message queues, one for each
direction.

Message queue API


The main class is RMsgQueue, which is a handle to a message queue. It is a templated class,
where the template parameter defines the message type. This must be a T type. Here is an
example of creating an anonymous global queue with five message slots and sending a message:

RMsgQueue<TInt> myQueue;

// Create an anonymous global message queue.


User::LeaveIfError(myQueue.CreateGlobal(KNullDesC, 5));

TInt message = 6;

// Send a message consisting of a single integer.


User::LeaveIfError(myQueue.Send(message));

Notice that in this example we are using the KNullDesC global constant to set the name to an
empty string. This means that the global message queue is anonymous and we need to pass its
handle to the thread to which we want to send data. This is the recommended way of working
with global queues.

Now let’s launch another process and pass the handle to the message queue:

// Create a child "Receiver" process


RProcess receiverProc;
_LIT(KProcessName, "Receiver");
User::LeaveIfError(receiverProc.Create(KProcessName, KNullDesC));
11

// Pass the message queue handle in the child process's


// environment slot 3.
receiverProc.SetParameter(3, myQueue);

// Close the message queue.


myQueue.Close();

// Start the child process.


receiverProc.Resume();

When we pass the message queue handle in the child process’s environment slot, we can simply
pass the message queue itself, because RMsgQueue is itself derived from RHandleBase. When
we close the message queue in the sending process, the handle in the receiving process keeps
the message queue open.

Here is an example of the receiver process opening the queue and receiving a message:

RMsgQueue<TInt> myQueue;

// Open the message queue using the handle passed in


// environment slot 3.
TInt err = myQueue.Open(3, EOwnerThread);
if (KErrNone == err)
{
TInt message;

// Read the message from the queue.


User::LeaveIfError(myQueue.Receive(message));

// Close the message queue.


myQueue.Close();
}

Clearly this is an unrealistically simple example, but it demonstrates that the message queue API
is essentially straightforward. Further information about message queues can be found at
developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/guide/Base-
subsystem-guide/e32/MessageQueue.

The Central Repository


The Central Repository is the Symbian OS mechanism for persistently storing application and
system settings. By settings we mean configuration details, user preferences, etc. Other
applications can be notified of changes to the settings and the settings are preserved, including
when the phone shuts down. The Central Repository is not designed for storing normal application
data, for which other mechanisms, such as a file (for example, for images or music) or an SQL
database (for example, for contacts), are more suitable.

The Central Repository is implemented using the Symbian OS client-server architecture and it is
fully integrated into platform security. This gives it a major advantage compared to using Symbian
INI files.
12

Figure 4: The Central Repository

Central Repository settings are stored in repositories (also known as key spaces). Each repository
is identified by a UID, which must be allocated through Symbian Signed in the normal way (see
www.symbiansigned.com for further details). The UID is usually the secure ID (SID) of the owning
application. It is possible for an application to own more than one repository, but this is not
common, because each repository can store up to 232 key-value pairs (which are called settings).

Each setting is identified by an integer key and has a value that can be integer, float, text or
binary. You specify security policies that control who can read and write the settings in the
initialization file. You can also specify metadata that controls whether settings are included in
backup and RFS (restore factory settings) operations.

Initialization files
You cannot create a repository through the API; instead, you create one by supplying an
initialization file. Smartphone manufacturers include the initialization files for applications and
other executables in the \private\10202be9\ folder in ROM. Provided that they are Symbian
Signed, after-market applications can also provide initialization files, and do so by providing
them for installation by the software installer. Currently, applications that are not Symbian Signed
cannot use the Central Repository mechanism.

When applications (including those installed in ROM) are upgraded by a SIS file, their Central
Repository initialization files can be upgraded too.

Initialization files are named after the repository UID and a filename extension that depends on
the file format – .txt for plain text format (such as F0009396.txt) and .cre for the binary
format (such as F0009396.cre). When using the plain text format, you must save the file in
UTF-16 format.

The binary format provides faster access. However, unless the file is large, the plain text format
is generally preferred because it is easier to work with. But you can use the CentRepConv tool
to convert a binary file to text for debugging, etc. There’s more on this in the ‘The CentRepConv
conversion tool’ section, later in the booklet.
13

You need to pay particular attention to the format and syntax of the initialization file, because
these are a common source of errors. Also, because you can’t change the security policy and
metadata after initialization, you need to think these through in advance. Try to think what will
be needed in the future and provide for it. A common approach is to specify security policies for
reserved ranges of settings in order to cover future eventualities.

The initialization file has a number of sections. Here is an example initialization file, which is
followed by brief explanations of the various sections under separate subheadings.

# This is a comment. 2000A970.txt


cenrep # This is the first header line
version 1 # This is the second header line

[owner]
0x2000A970 # The SID of app that is responsible for backup/restore

[defaultMeta]
0x03000000 # Enable backup/restore & restore factory settings (RFS)

# Default metadata settings for keys in the range 0x1000 to 0x2000


0x1000 0x2000 0x01000000 # Enable backup/restore, disable RFS

[platsec]
# Default security policy
cap_rd=AlwaysPass cap_wr=WriteDeviceData

# Default security policy for a group of keys identified by key mask


0x00000010 mask=0XFFFFFFF0 cap_rd=ReadDeviceData sid_wr = 0x2000A971

[main]
# Syntax is: Key Type InitialValue <Metadata> <platsec>
1 string "Unknown"
2 int 100
3 real 54.56

Header
The header contains two lines that are always the same. The version number refers to the file
format version; currently this should be version 1.

You can use the hash (#) sign anywhere in the file to add comments.

[Owner]
The [Owner]section is optional and is used to specify the SID of the application that owns the
repository and is responsible for the backup and restore operation, if relevant. There’s more on
backup and restore in the ‘Backing up your repository’ section.

[defaultMeta]
The [defaultMeta]section is also optional. If present, it specifies default metadata values
which may be overridden by individual metadata values assigned in the [main]section. The
metadata is a 32-bit number, of which the most significant 8 bits of the value are reserved for
internal use and the least significant 24 are made available for client metadata. Currently, there
are two reserved metadata bits that are published. These are:

• backup and restore (0x01000000)


• RFS (restore factory settings) (0x02000000).
14

In our example file, there are two values in this section. The first applies to all of the settings to
which we don’t give a different metadata value. It is a bitwise AND of 0x01000000 (which means
enable backup and restore) and 0x02000000 (which means enable restore factory settings). The
second value applies to keys in the range 0x1000 to 0x2000 and it enables these settings to
be backed up and restored but stops them being restored to their factory settings during an RFS
operation. The factory settings are the original values specified in the [main] section.

You can also use a key mask to specify metadata values for a group of settings. There’s more on
this in the ‘Key masks’ section.

[platsec]
The [platsec]section is compulsory and specifies default security policies, in terms of SIDs
(which restrict access to identified applications) and/or capabilities (which enable sharing based
on capabilities). You must have a general default security policy, otherwise none of the settings
will be accessible. You can also specify security policies for ranges of settings, as we did in the
default metadata section, or groups of settings by using a key mask. Like the default metadata
settings, the default security policies can be overridden on individual settings in the [main]
section.

In our example we have two security policies in this section. The first applies to all of the settings
to which we don’t give a different security policy. It specifies that, by default, the settings can be
read by all processes and can only be modified by processes that have the WriteDeviceData
capability. The second security policy applies to a group of keys identified by a key mask/partial
key combination. It specifies that the ReadDeviceData capability is required to read these
settings and that only a process that has a specific SID can modify them. There’s more on key
masks shortly.

Note that when you specify a SID, you can only specify up to three capabilities, rather than the
maximum of seven when you do not specify a SID.

[main]
The [main]section enables you to specify the data type and initial values (and optionally
metadata and security policies) for individual settings, ranges of settings or groups of settings
specified with a key mask and partial key.

It is not necessary to specify all settings in the initialization file; it is possible to create settings
dynamically through the API. In fact, the [main]section can be empty. However, if you do specify
settings in the [main]section, you must define their initial values.

The syntax for the [main]section is as follows:

Key Type InitialValue <Meta> <platsec>

In our example we have simply specified initial values for keys 1, 2 and 3.

Key masks
Sometimes you may want to define more complex data structures, such as a 2D structure of
columns and rows or a 3D structure of tables, columns and rows. You can do this by using some
bits in the 32-bit key to identify the table, some to identify the column and the rest the row.

To illustrate how this works, suppose you want to store four pieces of information about your
application’s most recently used files, such as file name, description, date last used and duration
of use. You could create a 2D data structure within the repository by using two bits in the 32-bit
15

key to represent the fields (file name, description, date last used and duration of use) and the
other bits to represent the row number. So 0x00000011 could be the key for the name of the
most recently used file, 0x00000021 could be the key for its description, 0x00000031 could
be the key for its last use date, 0x00000041 could be the key for its duration of use and
0x00000012 could be the key for the name of the second most recently used file, etc., as
shown in the following table.

File name Description Date Duration


1 0x00000011 0x00000021 0x00000031 0x00000041
2 0x00000012 0x00000022 0x00000032 0x00000042
3 0x00000013 0x00000023 0x00000033 0x00000043
4 0x00000014 0x00000024 0x00000034 0x00000044
5 0x00000015 0x00000025 0x00000035 0x00000045
6 0x00000016 0x00000026 0x00000036 0x00000046

Table 1: Example keys for storing information about most recently used files
On occasions you may want to refer to groups of settings without specifying them individually
by key. We can do this using a key mask and a partial key. The key mask is a 32-bit hexadecimal
number that represents a bit mask, in which binary 1s indicate mandatory values and binary 0s
indicate non-mandatory ones. The partial key represents the pattern to be matched.

For example, continuing our recently used files example, suppose you want to set a platform
security setting on the recently used file name field. Here is the key mask:

0XFFFFFFF0

And here is the partial key:

0x00000010

Taken together, these specify all of the repository keys with the most significant 28 bits equal to
0x0000001, which in this example are the keys representing ‘File name’ of the most recently
used files.

You can use key masks to specify groups of settings in some of the API functions as well as in
the initialization file, as we saw in the example shown earlier in the ‘Initialization files’ section.

Central Repository API


The Central Repository API has one class: CRepository. You open a repository by creating a
CRepository object; there is no Open() function. For example:

// Open the repository.


const TUid KUid = { 0xF0281D7B };
CRepository* myRepository = CRepository::NewLC(KUid);

To open several repositories, you must create one CRepository object for each of them.

The CRepository object provides Get(), Set(), Create() and Delete() functions for
working with settings. Set() creates the setting if it doesn’t exist, so Create() is not generally
used. Note that Create() fails if the setting already exists.
16

Here is an example of using the Get() function to retrieve a file name stored in the Central
Repository:

TBuf<KMaxFileName> filename;
const TUint32 KKey1 = 0x01;

// Retrieve the file name from the Central Repository.


User::LeaveIfError(myRepository->Get(KKey1, filename));

StartTransaction() and CommitTransaction() are available for wrapping the write


operations in a transaction. Symbian recommends that you use this feature when writing multiple
interdependent settings in order to avoid writing inconsistent data if one of the write operations
fails. Here is an example:

// Start a read-write transaction.


User::LeaveIfError(myRepository->StartTransaction(
CRepository::EReadWriteTransaction));

// Causes FailTransaction() to be called if a Leave occurs.


myRepository->CleanupCancelTransactionPushL();

const TUint32 KKey1 = 0x00000011;


User::LeaveIfError(myRepository->Set(KKey1, iRecentFile.iName));

const TUint32 KKey2 = 0x00000012;


User::LeaveIfError(myRepository->Set(KKey2, iRecentFile.iDesc));

// Commit the transaction – this writes the number of keys


// that were successfully updated to keyInfo.
TUint32 keyInfo;
User::LeaveIfError(myRepository->CommitTransaction(keyInfo));

There are functions for finding settings, FindL(), FindNeqL(), FindEqL(), requesting
notification of changes to settings, NotifyRequest(), and restoring the default values,
sometimes called factory settings specified in the initialization file Reset().

Backing up your repository


As we mentioned earlier, Symbian OS includes software to back up data to, and restore data from,
a connected PC. You can also include your application’s repository in the backup. However, there
are some special details about how this is done.
1. In the backup registration file, include the SID of the Central Repository server, like this:

<?xml version="1.0" standalone="yes" ?>


<backup_registration version="1.0">
<proxy_data_manager SID="0x10202be9" />
<restore requires_reboot="no" />
</backup_registration>

2. In the [owner]section in the Central Repository initialization file, specify the SID of the
application that owns the repository and is responsible for the backup and restore.
17

3. Use metadata values in the initialization file to specify the settings to be backed up. A setting
is backed up if bit 25 of its metadata value is set. You can set this bit for the whole repository
and groups of settings in the [defaultMeta] section or for individual settings using the
metadata field in the [main] section.

The CentRepConv conversion tool


CentRepConv is an emulator tool that converts Central Repository initialization files from the
plain text format to the binary format and vice versa.

Run the tool from the eshell prompt in the Windows emulator. Note that file paths are Symbian
OS paths, not paths in the PC’s native file system. The tool can tell whether it is a text to binary
conversion or vice versa from the filename extension.

The syntax for text to binary conversion is:

CentRepConv [-nowait] [-o output_path\rep_uid.cre] [input_path\] rep_uid.txt

The syntax for binary to text conversion is:

CentRepConv [-nowait] [-o output_path\rep_uid.txt] [input_path\] rep_uid.cre

The -nowait option is useful when the tool is run as part of an automated process, such as from
a build script, as it stops the tool from waiting for an acknowledgement.

Memory chunks
Symbian OS provides support for shared memory regions that can be accessed directly across
multiple threads and processes. These shared memory regions are known as memory chunks
(usually shortened to chunks). When a chunk can be shared across processes, it is known as a
global chunk and when shared across threads in the same process, it is known as a local chunk.
You create a chunk and access existing chunks by using the RChunk API.

Global chunks have the advantage that they are a fast and efficient way of sharing data between
processes. However, their use has a number of important caveats:

• The chunk may not be mapped at the same memory address in each process and
therefore storing ordinary pointers inside the chunk cannot be guaranteed to work. In
each process you should express the address of a data item in the chunk as the chunk’s
base address plus an offset. You can use RChunk::Base() to obtain the base address
of a chunk within a given process.
• You must implement your own concurrency mechanism, for example, using a mutex to
prevent more than one thread from updating data at the same time and semaphores to
enforce the order of execution. Concurrency mechanisms can be complex and you need to
design and implement them well in order for them to work correctly, even though the
basic building blocks are provided. The introduction of SMP (symmetric multiprocessing)
enabled versions of Symbian OS will make it even more important that your concurrency
mechanisms are well designed.
• There is no built-in validation or security. For example, you need to check that a pointer
does in fact point to data within the chunk, otherwise the thread will panic. If you are
using the chunk to share data with code that you do not trust, you must not dereference
any pointers without validating that they refer to an expected and reasonable location,
and must never call virtual methods on shared objects. It is much safer to communicate
with untrusted code via the client-server architecture.
18

Nevertheless, global chunks can sometimes be a useful solution, particularly when you need to
share large data structures between different parts of your own code.

The term shared memory chunk generally refers to chunks that are used for sharing data between
a user-side and a kernel-side process and are typically used by device drivers. The details of
using shared memory chunks differ from local and global chunks. However, the caveats, including
the need to be careful to synchronize access to the chunk through mechanisms such as mutexes,
remain the same.

Further information about the use of memory chunks can be found at


developer.symbian.com/main/documentation/sdl/symbian94/sdk/doc_source/guide/Base-
subsystem-guide/e32/MemoryManagement/MemoryAllocationOverview.guide.html.

Conclusion
Symbian OS provides a variety of mechanisms for sharing data between threads and processes
and it provides the SQL service and Central Repository for persisting application data and settings
respectively.

This booklet has introduced four of these mechanisms and touched upon the use of memory
chunks. The amount of space allocated in this booklet to each these mechanisms does not
necessarily reflect their relative importance. Symbian SQL and the Central Repository are both
covered in some detail because the former is new and the latter has some setup details that you
need to understand in order to take advantage of its features. Publish and Subscribe and message
queues are covered more briefly because their usage is relatively straightforward and Publish
and Subscribe is already well documented in the public domain.
New from

Common Design Patterns for Symbian OS:


The Foundations of Smartphone Software
Common Design Patterns for Symbian OS
introduces the common design patterns used
to implement a variety of applications and
services on Symbian OS. The book describes
patterns based on the architectural elements
of Symbian OS and discusses how patterns
suited for desktop software must be adapted
or even avoided on a mobile platform.

Multimedia on Symbian OS: Inside the Convergence Device


Combining the insights and experience of
subject experts within Symbian and the
third-party developer community, this book
will be an invaluable reference for anyone
working with multimedia on Symbian OS.
The authors provide details of the native
C++ APIs for accessing camera, video,
audio, image manipulation and radio tuner
functionality, and discuss best practice,
tips and tricks.

Symbian Press: developer.symbian.com/books


New from

Quick Recipes on Symbian OS


This book aims to make it easier to develop
applications by describing a number of
common programming tasks and providing
clear explanations of how to complete them.
The recipes are divided by technology,
including graphics, multimedia, location-based
services, networking, telephony, connectivity
and messaging.

Full sample code is available for download, so


it can be used as a starting point in your own
projects.

Games on Symbian OS: A Handbook for Mobile Development


This book forms part of the Technology
Series from Symbian Press. It describes
the key aspects of the mobile games
marketplace, with particular emphasis on
creating games for smartphones based on
Symbian OS v9.x.

It also looks at C/C++ Standards support


available for developers porting games to
Symbian OS, how to write for the N-Gage
platform, and coding for runtimes such as
Java ME and Flash Lite.

Symbian Press: developer.symbian.com/books


from

Developing Software for Symbian OS, Second Edition


This second edition of Developing Software
for Symbian OS helps software developers
new to Symbian OS to create smartphone
applications. The original book has been
updated for Symbian OS v9 and now
includes a new chapter on application
signing and platform security, and updates
throughout for Symbian OS v9 and changes
to the development environment.

Symbian OS C++ for Mobile Phones, Volume 3


The latest edition of Richard Harrison’s
existing bestsellers draws on the
experience of Symbian’s own engineers to
help you become an effective Symbian OS
developer.

If you’ve programmed in C++ at any level


and wish to develop software for Symbian
smartphones, this book gives you a
thorough grounding in writing C++
applications for Symbian OS v9.
from

For all Symbian C++ developers:


Symbian OS Communications Programming, 2nd Edition
by Iain Campbell

S60 Programming - A Tutorial Guide


by Coulton & Edwards

Symbian OS Explained
by Jo Stichbury

Symbian OS Internals
by Jane Sales

Symbian OS Platform Security


by Craig Heath

Smartphone Operating System Concepts with Symbian OS


by Mike Jipping

Accredited Symbian Developer Primer


by Jo Stichbury & Mark Jacobs
from

Published Booklets
.NET Development on S60
A Guide to P.I.P.S.
Carbide.c++ v1.3
Coding Standards
Coding Tips
Coverity Prevent on Symbian OS (SDN++ only)
Creating Location-Aware Applications
Essential S60 - Developers’ Guide
Getting Started
Getting to Market
Java ME on Symbian OS
Localization (SDN++ only)
Performance Tips
Platform Security for all
Quick Recipes Taster
Ready for ROM

Translated Booklets
Chinese Russian
Japanese Persian
Korean Italian
Spanish
Using Symbian OS

DatasharingandpersistEnce

Why? What? Where? How?

Symbian OS provides various methods for sharing and


persisting data securely. This booklet provides an
introduction to four of these mechanisms (Symbian SQL,
Publish and Subscribe, message queues and the Central
Repository). The booklet aims to help you get started using
these services and to know when to use each one.

Data Sharing and Persistence is part of the Using


Symbian OS series, designed to provide information in a
handy format to Symbian developers.

Symbian Press
Symbian Press publishes books designed to
communicate authoritative, timely, relevant and
practical information about Symbian OS and related
technologies. Information about the Symbian Press
series can be found at developer.symbian.com/books

S-ar putea să vă placă și