Low-level Activity API: Difference between revisions

From OLPC
Jump to navigation Jump to search
mNo edit summary
 
(113 intermediate revisions by 23 users not shown)
Line 1: Line 1:
{{Developers}}
Most activities will use the [[Sugar Architecture/API/sugar.activity|Python API]] to implement activities. This page will document the underlying mechanism that non-Python activities need to conform to.


Sugar activities are usually written in Python using the [[API_Reference|Python Activity API]]. This page documents the underlying mechanism that all activities need to conform to. Activities can be written in any language, as long as it can connect to D-Bus and provide an X11 interface.
''This documentation effort was started by [[User:Bert|Bert]] while implementing the [[Squeak]]-based [[Etoys]] activity. Please fill in missing pieces and correct mistakes!''


'''This page has been moved to the [http://wiki.sugarlabs.org/go/Development_Team/Low-level_Activity_API Sugar Labs Wiki].'''
=Overview=
An Activity is basically a regular X11 program which communicates with the special Sugar services via DBus.

The [[Activity Bundle]] specifies an executable. For each [[#Activity Instance|activity instance]], that executable is run with arguments specifying the bundle id (taken from the bundle) and activity id (generated by Sugar). The instance opens an X window, putting these ids into window [[#X Properties|properties]]. It also needs to provide a DBus [[#DBus Methods|service]] to receive messages from Sugar. An activity must retrieve and store its state in the [[#Datastore|datastore]], implement [[#Presence|sharing]] on the mesh network, and be [[#Security|security]] compliant.

=Activity Instance=

When the activity instance is executed, the current working directory will be set to the bundle directory (e.g., ~/Activities/MyActivity.activity) so resource files can be accessed using relative pathes. Also, its "bin" subdirectory is added to the PATH. The following arguments are passed to the executable:

; -b, --bundle-id : Identifier of the activity bundle
; -a, --activity-id : Unique identifier of the activity instance.
; -o, --object-id : (optional) Identity of the journal object associated with the activity instance. When you resume an activity from the journal the object id will be passed in (see [[#Datastore|datastore]]).
; -u, --uri : (optional) URI associated with the activity. Used when opening an external file or resource in the activity, rather than a journal object (downloads stored on the file system for example or web pages).

==Environment Variables==

Some environment variables are setup before the activity is launched:

SUGAR_ACTIVITY_ROOT

Writable space for the activity, see [[#File_Writing|security]].

==X Properties==
The activity instance needs to set some properties on its top-level window:

_SUGAR_BUNDLE_ID

The bundle id (e.g., <tt>my.organization.MyActivity</tt>) of type <tt>STRING</tt>.

_SUGAR_ACTIVITY_ID

The activity id (e.g., <tt>6f7f3acacca87886332f50bdd522d805f0abbf1f</tt>) of type <tt>STRING</tt>.

This properties must to set before the window is showed on the screen, using the gtk realize event, for example.

Also, some Window Manager hints need to be set:

_NET_WM_NAME

should be set to the current activity title. It usually corresponds to the title which is displayed in the journal and advertised on the network for shared activities. See [http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#id2511086 Freedesktop specification].

_NET_WM_PID

must be set to the activity's process id so the shell can associate memory usage with an activity. See [http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#id2511925 Freedesktop specification].

==DBus Methods==
An activity instance needs to create a DBus service:

Service name: org.laptop.Activity6f7f3acacca87886332f50bdd522d805f0abbf1f
Object path: /org/laptop/Activity/6f7f3acacca87886332f50bdd522d805f0abbf1f
Interface: org.laptop.Activity

(where <tt>6f7f3acacca87886332f50bdd522d805f0abbf1f</tt> is the activity id as passed on the cmd line)

It must support the following methods:

org.laptop.Activity.SetActive(active)

Activate or passivate an activity. Passive activities must release resources like sound, camera etc.

org.laptop.Activity.TakeScreenshot()

Hint to the activity that it might be a good idea to take a preview screenshot now, because its window is going to be obscured as soon as the method returns (e.g., by the frame). Should not take more than 0.1 seconds.

''There used to be more methods but the API evolved so only these are remaining for now.''

=Datastore=

An Activity instance must store its complete state in the central datastore so it appears in the Journal and can be resumed later. It needs to connect to the datastore service:

Service name: org.laptop.sugar.DataStore
Object path: /org/laptop/sugar/DataStore
Interface: org.laptop.sugar.DataStore

==Keeping and Resuming==

An item in the datastore is referenced by an object_id, it has a dictionary of properties, and possibly a file. The properties have String keys but Variant values. Here are a few properties:

'activity': 'my.organization.MyActivity'
'activity_id': '6f7f3acacca87886332f50bdd522d805f0abbf1f'
'title': 'My new project'
'title_set_by_user': '0'
'keep': '0'
'ctime': '1972-05-12T18:41:08' #created
'mtime': '2007-06-16T03:42:33' #modified
'timestamp': 1192715145 #modified in seconds since the UNIX epoch
'buddies': array of buddies #not spec'ed yet
'preview': ByteArray(png file data, 300x225 px)
'icon-color': '#ff0000,#ffff00'
'mime_type': 'application/x-my-activity'
'summary:text': 'text I want to be indexed' #properties with key ending in ":text" will be searched in fulltext search

To create an item in the datastore, call create():

object_id = datastore.create(properties, filename)

If filename is not empty, the file will be copied to the datastore. The activity should delete the file once the call completes. The returned id will be a string like '4543af91-7be9-404e-b2f1-3e27cb15a15d'.

To update an item use update():

datastore.update(object_id, properties, filename)

Again, if a filename was given, it should be deleted when the call returns.

To retrieve an object's properties and file:

properties = datastore.get_properties(object_id)
filename = datastore.get_filename(object_id)

The returned temp file should be deleted by the activity as soon as possible, latest when the activity quits.

==Querying==

Activities may query the datastore:

(results,count) = datastore.find(query)

It returns the results as array of properties and a count of matching items (the array may have less items if the query was limited). In addition to the usual metadata items, the properties will include the object id at key 'uid', the mountpoint of the item at key 'mountpoint', and possibly a 'filename' if requested.

The query can be a:

: <b>string</b>: fulltext search
:: the given string is searched in all text properties
: <b>dictionary</b>: structured query
:: the key-value pairs in the dictionary specify the value (or array of values, or dictionary specifying range) for a specific property, e.g.:
::: 'title' = 'First Project'
::: 'mime_type' = ['image/png', 'image/jpeg']
::: 'mtime' = {'start' = '2007-07-01T00:00:00', 'end' = '2007-08-01T00:00:00'}
:: also, there are a few specific keys to adjust the query:
::: 'query': fulltext search term
::: 'order_by': key (or array of keys) to order results by, to reverse order use '-key'
::: 'limit', 'offset': return only limit results starting at offset
::: 'mountpoints': array of mountpoint ids to search (or all if not specified)
::: 'include_files': if true, generate files as if get_filename() had been called for each item. In results, a property 'filename' will be added.

You can also retrieve an array of unique values for a field:

values = datastore.get_uniquevaluesfor(property, query)

Note that currently (2007-07-25) the query is ignored in this call, it looks for all values in all entries.

==Mount Points==
Devices are represented as mount points in the datastore. If no mountpoint is explicitely specified, the main datastore (Journal) is used.

mounts = datastore.mounts()

Returns an array of mount point descriptiors where each descriptor is a dictionary containing at least the following keys:
:'id': the id used to refer explicitly to the mount point
:'title': Human readable identifier for the mountpoint
:'uri': The uri which triggered the mount

Mount points can be specified when creating an object (using a 'mountpoint' key and id value in the properties), and when querying the datastore (by adding a 'mountpoints' query option).

=Presence=

Activities must support sharing using the [[Presence Service DBus API|Presence Service]] (PS). It is accessible on the DBus:

Service: org.laptop.Sugar.Presence
Interface: org.laptop.Sugar.Presence
Object Path: /org/laptop/Sugar/Presence

==Sharing==

If the activity was not yet shared but the user clicked the Share button, sharing is initiated by calling ShareActivity():

activity = PS.ShareActivity(activity_id, bundle_id, name, properties)

The bundle id is used for the icon and to launch the same activity when someone joins it. The name will be shown in the mesh view and should generally be the same as the title of the datastore object (see above). Properties is a dictionary of arbitrary meta data (string-value pairs). It should be empty, the default values are set automatically:
'private': true # if False, advertised to all
'name': 'My new project' # shown in mesh view
'tags': 'tag1,tag2'
'color': '#ff0000,#ffff00'
'type': 'my.organization.MyActivity' # bundle id
'id': '6f7f3acacca87886332f50bdd522d805f0abbf1f' # activity id

Not that sharing will be private (invitation-only) by default, that is, the icon will not be visible in the mesh. To share publicly, set the 'private' property to False.

''Currently, the properties dict is ignored in ShareActivity(), see bug [http://dev.laptop.org/ticket/4660 #4660]. You have to set the properties explicitly after sharing. For example, to enable the shared activity be visible to everyone:''

activity.SetProperties({'private': False})

==Joining==

When launching, the PS must be consulted to see if this instance was shared by someone else, meaning it was launched by the user is trying to join it:

activity = PS.GetActivityById(activity_id)

This yields an error if this instance (identified by its activity id) was not shared, in which case a regular non-shared startup should be performed. Otherwise, the activity object held by the PS is returned, and this activity instance needs to join:

activity.Join()

It should continue by establishing a communication channel with the originating instance (see below)

== Leaving ==

To leave a shared activity (e.g. because it is closing) you need to inform the PS:

activity.Leave()

==Communication==

The activity object created by either sharing the current activity or joining an existing activity is used to establish means of communication between these instances. For example, the meta data can be accessed in either instance:

props = activity.GetProperties()

''to be continued. For more details, see [[Presence Service DBus API]].''

=Security=

Activities will be isolated and will not have the same permissions as you would expect in a non-restricted Linux environment (see [[Bitfrost]] and [[Rainbow]]).

== File Writing ==

All writing to the file system is restricted to subdirectories of the path given in the SUGAR_ACTIVITY_ROOT environment variable. This directory will have three (virtual) subdirectories with different policies:

$SUGAR_ACTIVITY_ROOT/data # in Flash, persistent and shared between instances
$SUGAR_ACTIVITY_ROOT/instance # in Flash, volatile and unique per instance
$SUGAR_ACTIVITY_ROOT/tmp # in RAM, small, volatile and unique per instance

''to be detailed''

== Signing ==

An activity will have to be cryptographically signed to allow secure activity upgrades once they are on the machines. Tools for this will be provided soon.

''to be detailed''

== Permissions Declarations ==

Permission declarations will enumerate which special permissions (camera access? microphone access? non-Tubes network access? etc.)
your activity may need for its normal operation. See [[Bitfrost]].

''to be detailed''

=Example=

[Activity]
name = My Activity
activity_version = 1
host_version = 1
service_name = my.organization.MyActivity
icon = activity-my
exec = myactivity
show_launcher = yes

launch new activity instance by running the myactivity command

create X window with _SUGAR_BUNDLE_ID='my.organization.MyActivity' and _SUGAR_ACTIVITY_ID='6f7f3acacca87886332f50bdd522d805f0abbf1f' and then show it

create instance dbus service at org.laptop.Activity6f7f3acacca87886332f50bdd522d805f0abbf1f

Signal /org/freedesktop/DBus org.freedesktop.DBus.NameAcquired(':1.24')

Signal /org/freedesktop/DBus org.freedesktop.DBus.NameAcquired('org.laptop.Activity6f7f3acacca87886332f50bdd522d805f0abbf1f')


[[Category:Sugar]]
[[Category:Sugar]]
[[Category:Developers]]
[[Category:API]]
[[Category:API]]
[[Category:Telepathy]]
[[Category:Collaboration]]

Latest revision as of 19:05, 25 September 2010

Sugar activities are usually written in Python using the Python Activity API. This page documents the underlying mechanism that all activities need to conform to. Activities can be written in any language, as long as it can connect to D-Bus and provide an X11 interface.

This page has been moved to the Sugar Labs Wiki.