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: Anna Fisher Managing Editor: Ashlee Godwin
Symbian Developer Network developer.symbian.com
Reviewed by: Henry Boothroyd Brooks Philip Cheung Richard Coles Husien Hong Ivan Litovski Richard Maynard Mark Shackman Jo Stichbury
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/SystemLibraries-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; CleanupClosePushL(myStatement);
// 1
myStatement.PrepareL(myDatabase, KSelectString);
// 2
TInt ret;
4 while ((ret = myStatement.Next()) == KSqlAtRow) { TPtrC title = myStatement.ColumnTextL(0); TInt cost = myStatement.ColumnInt(1); // ...
// 3 // 4
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; CleanupClosePushL(myStatement);
// 1
_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); TInt nameParam = myStatement.ParameterIndex(KName);
// 2
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 timeconsuming 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
(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/SystemLibraries-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. <passive_backup> <proxy_data_manager SID="0x10281e17" /> 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 realtime 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/Basesubsystem-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/Basesubsystem-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. cenrep # This is the first header line version 1 # This is the second header line
2000A970.txt
[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> 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> 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.
1
File name 0x00000011
Description 0x00000021
Date 0x00000031
Duration 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 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: <proxy_data_manager SID="0x10202be9" /> 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/Basesubsystem-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 Japanese Korean Spanish
Russian Persian Italian
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