Software">
[go: up one dir, main page]

0% fanden dieses Dokument nützlich (0 Abstimmungen)
132 Ansichten88 Seiten

Diplomarbeit

Als pdf oder txt herunterladen
Als pdf oder txt herunterladen
Als pdf oder txt herunterladen
Sie sind auf Seite 1/ 88

AUTOMATISIERUNGSTECHNIK

B E T R I E B S I N F O R M AT I K
N E U F E L D E N
HÖHERE TECHNISCHE BUNDESLEHRANSTALT 4120 NEUFELDEN HÖFERWEG 47

Diplomarbeit 2010/11

Software Suite
für Smartphones
Lukas Hetzenecker

ausgeführt

an der Höheren Technischen Lehranstalt


für Wirtschaftsingenieurwesen,
Ausbildungsschwerpunkt Betriebsinformatik
in Neufelden

von

Lukas Hetzenecker (Matr.Nr.: 20062079)

Betreuer
Dipl.-Ing. Günther Oberaigner

Neufelden, Mai 2011


Eidesstattliche Erklärung

Ich erkläre an Eides statt, dass ich die vorliegende Diplomarbeit selbstständig
und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht
benutzt und die den benutzten Quellen wörtlich oder inhaltlich entnommenen Stellen als
solche kenntlich gemacht habe.

Neufelden, am 15. Mai 2011

…………………………………………………………………………………………………...........
Lukas Hetzenecker

Seite 2
Danksagung

An dieser Stelle möchte ich mich bei all jenen bedanken, die durch ihre fachliche und
persönliche Unterstützung zur Entstehung dieser Diplomarbeit beigetragen haben.
Besonderer Dank gebührt meinem Betreuer Dipl.-Ing. Günther Oberaigner, der mir stets
mit Rat zur Seite gestanden ist.
Dank den freiwilligen Übersetzern Chris Van Bael, Marián Kyral, Piotr Kalina, Mirko
Allegretti, Franco Funes, Romuald Menant und Nikolai Stoilov kann die Anwendung bereits
in 9 Sprachen verwendet werden.
Weiters möchte ich mich bei den Firmen Trolltech und Nokia bedanken. Durch deren
Qt-Toolkit wurde die Programmierung erheblich erleichtert.
Vielen Dank!

Seite 3
Abstract
The application „Open Mobile Suite“ provides a way to connect to a mobile phone using
the Bluetooth technology.
It allows you to synchronize your contacts, calendar entries and messages. Afterwards it is
possible to edit your contacts or write text messages. You can also access the file system
of your mobile phone.
Thanks to the platform-independent programming language Python and the framework Qt,
the application is available for Windows, Linux and Mac OS X without a single modification
of the source code.
Currently the mobile phone operating systems Android and S60 / Symbian^3 are
supported.
The simpleness of the application was really important. The installation and configuration
was made as easy as possible. There are binary packages available for the most
important operating systems and Linux distributions. When the application is started for the
first time a dialog is opened that allows you to configure the application easily.
In addition to the application a website was also created, which contains a support forum
for users.

Seite 4
Zusammenfassung
Unter dem Projekt-Namen „Open Mobile Suite“ wurde eine Anwendung entwickelt, welche
die Kommunikation über Bluetooth zwischem einem Mobiltelefon und einem Computer
ermöglicht.
Zum Funktionsumfang zählt der Abgleich der Kontakte, Kalender-Einträge und
Mitteilungen des Mobiltelefons mit dem PC. Nach dieser Synchronisierung können vom
PC aus die Kontakte bearbeitet oder Mitteilungen geschrieben werden. Auch ein Zugriff
auf das Dateisystem des Telefons ist möglich.
Da nur plattform-unabhängige Programmiersprachen und Frameworks verwendet wurden,
ist die Anwendung ohne Änderungen des Quellcodes unter den Betriebssystemen
Windows, Linux und Mac OS X ausführbar.
Auf Mobiltelefon-Seite werden derzeit die Plattformen Android, S60 und dessen
Nachfolger Symbian^3 unterstützt.
Es wurde großer Wert auf Einfachheit gelegt. Die Installation und Einrichtung wurde so
unkompliziert wie möglich gestaltet. Dazu wurden Binärpakete für die wichtigsten
Betriebssysteme erstellt. Beim ersten Start der Anwendung wird ein Dialog aufgerufen,
welcher dem Benutzer die Einrichtung erleichtert.
Neben der Anwendung selbst wurde auch noch eine Webseite erstellt, welche den
Anwendern auch als Support-Forum dienen kann.

Seite 5
Inhaltsverzeichnis

1. Projektbeschreibung.....................................................................................................9

2. Grundlagen...................................................................................................................10
2.1. Python.............................................................................................................................. 10
2.1.1. Datentypen............................................................................................................................... 10
2.1.2. Lambda-Operator...................................................................................................................... 11
2.1.3. Ausnahmebehandlung.............................................................................................................. 11
2.1.4. Generatoren.............................................................................................................................. 11
2.1.5. Global Interpreter Lock............................................................................................................. 12
2.2. Python-Module................................................................................................................ 13
2.2.1. PyS60....................................................................................................................................... 13
2.2.2. PyQt4........................................................................................................................................ 13
2.2.3. PyBluez..................................................................................................................................... 13
2.2.4. PyOBEX.................................................................................................................................... 14
2.2.5. Obexftp..................................................................................................................................... 14
2.2.6. Matplotlib.................................................................................................................................. 14
2.2.7. vobject und ldif.......................................................................................................................... 14
2.3. Qt Framework.................................................................................................................. 15
2.3.1. Signale & Slots......................................................................................................................... 16
2.3.2. Model/View-Architektur............................................................................................................. 16
2.4. Bluetooth......................................................................................................................... 17
2.4.1. Einführung................................................................................................................................ 17
2.4.2. Protokollstapel.......................................................................................................................... 18
2.4.3. RFCOMM / L2CAP................................................................................................................... 19
2.4.4. Service Discovery Protocol....................................................................................................... 20
2.5. Interprozesskommunikation..........................................................................................21
2.5.1. D-Bus........................................................................................................................................ 21
2.5.1.1. Mitteilungen.......................................................................................................21
2.5.1.2. Service-Name....................................................................................................22
2.5.1.3. Objekt-Pfade......................................................................................................22
2.5.1.4. Interfaces........................................................................................................... 22
2.6. Versionsverwaltungssystem..........................................................................................23
2.6.1. Apache Subversion (SVN)........................................................................................................ 23
2.6.1.1. Tags und Branches............................................................................................23

3. Entwickler-Dokumentation.........................................................................................24
3.1. Anfänge des Projekts.....................................................................................................24
3.2. Aktuelle Entwicklung......................................................................................................25
3.3. Grundlegende Objekte....................................................................................................28
3.3.1. Serialisierung............................................................................................................................ 30
3.4. Wizard beim ersten Start................................................................................................31
3.5. Logging............................................................................................................................ 34
3.6. Verbindungsaufbau........................................................................................................ 40
3.6.1. Beispiel: PyS60......................................................................................................................... 44
3.6.2. Beispiel: Android....................................................................................................................... 49
3.7. Geräteverwaltung............................................................................................................ 52
3.7.1. Connection Manager................................................................................................................ 52
3.7.2. GUI........................................................................................................................................... 52

Seite 6
3.8. Kontaktverwaltung.......................................................................................................... 55
3.8.1. Connection Manager................................................................................................................ 55
3.8.2. Kontaktliste der grafischen Oberfläche.....................................................................................56
3.8.3. Anlegen, Bearbeiten und Löschen von Kontakten....................................................................58
3.9. Kalenderverwaltung........................................................................................................59
3.10. Dateiverwaltung............................................................................................................ 61
3.11. Mitteilungsverwaltung..................................................................................................63
3.11.1. Senden von Mitteilungen......................................................................................................... 63
3.11.2. Empfangen von Mitteilungen................................................................................................... 67
3.11.3. Mitteilungs-Status.................................................................................................................... 69
3.11.4. Verlauf..................................................................................................................................... 69
3.11.5. Statistiken............................................................................................................................... 70
3.12. Datenbank..................................................................................................................... 71
3.13. Plattformunabhängigkeit..............................................................................................72
3.14. Übersetzungen.............................................................................................................. 73

4. Webseite.......................................................................................................................75

5. Mailing List...................................................................................................................77

6. Bug Tracker..................................................................................................................78

7. Quellen..........................................................................................................................79

8. Anhang..........................................................................................................................80
8.1. Veröffentlichte News-Beiträge der Webseite...............................................................80
8.1.1. New architecture of Open Mobile Suite.....................................................................................80
8.2. Wichtige Mails der Mailing list series60-remote-devel.................................................81
8.2.1. 27.01.2011: Status update [Re: Split series60-remote in two projects].....................................81
8.3. Patches............................................................................................................................ 82
8.3.1. PyBluez..................................................................................................................................... 82
8.3.1.1. 31.10.2009 – Patch für Compiler VC++08.........................................................82
8.3.1.2. 03.08.2010 – Geräte-Klasse beim Bluetooth-Scan zurückgeben......................83
8.3.1.3. 03.08.2010 – Verhindern einer Blockierung der GUI .........................................86
8.3.1.4. 08.04.2011 – Behebung eines Fehlers beim Geräte-Scan.................................86

Seite 7
Abbildungsverzeichnis
Abbildung 2-1: Model/View-Architektur...........................................................................................16
Abbildung 2-2: Bluetooth-Protokollstapel........................................................................................18
Abbildung 3-1: Neue Architektur der "Open Mobile Suite"..............................................................25
Abbildung 3-2: Entwicklung der "Open Mobile Suite".....................................................................27
Abbildung 3-3: Willkommens-Seite.................................................................................................31
Abbildung 3-4: Geräte-Auswahl-Dialog..........................................................................................31
Abbildung 3-5: Plattform-Auswahl..................................................................................................32
Abbildung 3-6: Installation für Android............................................................................................32
Abbildung 3-7: Installation für S60..................................................................................................32
Abbildung 3-8: Weitere Hinweise für S60.......................................................................................33
Abbildung 3-9: Weitere Hinweise für S60.......................................................................................33
Abbildung 3-10: Datenbank-Einrichtung.........................................................................................33
Abbildung 3-11: Logging-Dialog......................................................................................................34
Abbildung 3-12: Verbindungsaufbau...............................................................................................40
Abbildung 3-13: Geräteübersicht....................................................................................................42
Abbildung 3-14: Diagramm des Verbindungsaufbaus.....................................................................43
Abbildung 3-15: Model/View-Klassen der Geräteverwaltung..........................................................52
Abbildung 3-16: DeviceScanWidget mit Gerät................................................................................53
Abbildung 3-17: DeviceScanWidget mit Fehlermeldung.................................................................53
Abbildung 3-18: Einstellungsdialog.................................................................................................54
Abbildung 3-19: Kontaktliste........................................................................................................... 56
Abbildung 3-20: Suche in der Kontaktansicht.................................................................................57
Abbildung 3-21: Bearbeitung eines Kontakts..................................................................................58
Abbildung 3-22: Ansicht der Kalender-Einträge..............................................................................59
Abbildung 3-23: Model/View-Klassen der Kalenderverwaltung.......................................................59
Abbildung 3-24: Bearbeitung von Kalender-Einträgen....................................................................60
Abbildung 3-25: Dateiverwaltung....................................................................................................62
Abbildung 3-26: Senden einer Kurzmitteilung.................................................................................63
Abbildung 3-27: Theme "Fresh" des Chat-Dialogs.........................................................................64
Abbildung 3-28: Theme "Perfect Pushpin"......................................................................................64
Abbildung 3-29: Theme "Stock"......................................................................................................64
Abbildung 3-30: Theme "Clear"......................................................................................................64
Abbildung 3-31: Theme "Glossyk"..................................................................................................64
Abbildung 3-32: Theme-Auswahl im Einstellungsdialog.................................................................65
Abbildung 3-33: Mitteilungs-Status.................................................................................................69
Abbildung 3-34: Verlaufs-Ansicht....................................................................................................69
Abbildung 3-35: Statistiken............................................................................................................. 70
Abbildung 3-36: Datenbank-Layout................................................................................................71
Abbildung 3-37: Bearbeiten eines Kontaktes unter Mac OS X........................................................72
Abbildung 3-38: Kontakliste und Bearbeitung unter Windows XP...................................................72
Abbildung 3-39: Screenshot beim Bearbeiten der deutschen Übersetzung....................................73
Abbildung 3-40: Polnische Übersetzung.........................................................................................74
Abbildung 3-41: Niederländische und tschechische Übersetzungen..............................................74
Abbildung 4-1: Screenshot der Projekt-Webseite...........................................................................75
Abbildung 4-2: Downloadbereich der Webseite..............................................................................76
Abbildung 5-1: Screenshot des Archivs der Mailing-Liste "series60-remote-devel"........................77
Abbildung 6-1: Screenshot des Bug-Tracking-Systems..................................................................78

Seite 8
1. Projektbeschreibung
Derzeit wird von den großen Handyherstellern kein Softwarepaket zur Verwaltung von
Mobiltelefonen für alternative Betriebssysteme wie Linux oder Mac OS X angeboten.
Ziel war die Erstellung einer „Software Suite“, die sich vom PC über Bluetooth mit einem
Mobiltelefon verbindet und danach eine Verwaltung des Telefons am Computer ermöglicht.
Dazu zählen speziell die folgenden Funktionen:
• Abgleich der Kontakt-Datenbank
◦ Anlegen, Bearbeiten und Löschen der Kontakte des Mobiltelefons
• Abgleich der Kalender-Einträge
◦ Anlegen, Bearbeiten und Löschen der Kalender-Einträge des Mobiltelefons
• Anzeigen einiger Systeminformationen des Mobiltelefons (wie Signalstärke,
Akkuzustand,...)
• Versenden von SMS-Mitteilungen
• Benachrichtigungen beim Eintreffen neuer SMS-Mitteilungen

Daneben gibt es noch einige weitere Funktionen, die dieses Produkt von Konkurrenz-
produkten unterscheidet:
• Versenden und Empfangen von SMS-Mitteilungen in einem „Chat-Fenster“ (ähnlich
wie der Chat-Dialog gängiger Instant-Messaging-Programme)
• Anzeigen von Statistiken über das Mitteilungsverhalten
• Speichern des Mitteilungs-Verlaufes in einer Datenbank

Zusätzlich wird auch noch der Export und Import von Kontakten und Kalendern unterstützt.

Bei der Programmierung wurde besonderer Wert auf die Erweiterbarkeit gelegt. Derzeit
werden Smartphones mit den Betriebssystemen S60 und Android unterstützt, durch den
modularen Aufbau der Anwendung könnte dies jedoch einfach erweitert werden.

Weiters wurde auch auf eine einfache Installation Wert gelegt. Dazu werden Binärpakete
für Windows, einige Linux-Distributionen (wie Fedora oder Ubuntu) und Mac OS X
bereitgestellt.

Im Rahmen des Projekts wurde auch eine Webseite erstellt, die unter
http://series60-remote.sourceforge.net zu finden ist.

Seite 9
2. Grundlagen
2.1. Python
Durch die höhere Programmiersprache Python werden mehrere Programmierparadigmen
unterstützt. Dazu zählen objektorientierte, aspektorientierte und funktionale
Programmierung.
Die Sprache steht unter der Python License und wird als Open Source anerkannt.
Sie wurde mit dem Ziel entwickelt, möglichst einfach und übersichtlich zu sein. Daher
werden Blöcke durch Einrückungen markiert. Dies steht im Gegensatz zu anderen
Programmiersprachen, wie C, bei denen Klammern ( { } ) zur Abgrenzung von
Codeblöcken verwendet werden.
Als Beispiel dazu dient eine Fakultätsfunktion:

def fakultät(x):
if x > 1:
return x * fakultät(x - 1)
else:
return 1

Außerdem verfügt Python über eine mächtige Standardbibliothek für alle denkbaren
Aufgabenstellungen. Sie ist auch auf allen gängigen Betriebssystemen und Plattformen
lauffähig.
Die folgenden Kapitel liefern eine kurze Übersicht über die wichtigsten Eigenschaften der
Sprache, die auch in meiner Diplomarbeit verwendet worden sind.

2.1.1. Datentypen
Die Datentypen werden in Python dynamisch verwaltet, eine statische Typprüfung (wie
etwa in C++) existiert nicht. Um die Freigabe nicht mehr benutzter Speicherbereiche
kümmert sich ein Garbage Collector.
Neben den grundlegenden Datentypen (wie Integer) werden auch beliebig große
Ganzzahlen (long) sowie komplexe Zahlen unterstützt.
Zeichenketten sind (wie in Java) unveränderliche Objekte (immutable). Operationen, die
eine Zeichenkette verändern, führen dazu, dass eine neue Zeichenkette zurückgegeben
wird.
Es gibt 3 verschiedene Gruppen von Datentypen:
• Skalare
zum Beispiel Ganzzahlen (int), Ganzzahlen mit beliebig vielen Stellen (long),
Fließkommazahlen (float), komplexe Zahlen (complex) und Zeichenketten (str /
unicode)
• Sequenzen
nehmen eine Folge anderer Objekte auf. Sequenzen werden unterteilt in Listen (list)
und Tupel (tuple).

Seite 10
• Assoziative Listen
Dictionaries (dict) erlauben den Zugriff auf Werte über einen Schlüssel (key), der
auch nicht-numerisch sein kann.

2.1.2. Lambda-Operator
Lambda-Funktionen sind anonyme Funktionen mit einer beliebigen Anzahl an Parametern,
die einen Ausdruck ausführen und dessen Ergebnis zurückliefern:
>>> f = lambda x, y : x + y
>>> f(1,1)
2

Dies ist vor allem in Kombination mit map-, filter- und reduce-Funktionen vorteilhaft, wie
folgende Beispiele aus meiner Diplomarbeit zeigen:

possibleDates = filter(lambda item: daysDictToDay(item, first.date())>currentDay, days)

In der Liste possibleDates werden alle Tage gespeichert, die größer als der aktuelle sind.
Dazu muss vorher das Dictionary item noch in einen Integer-Wert umgewandelt werden,
der für den aktuellen Tag steht. Dazu wird eine lambda-Funktion verwendet.

self.connect(self.thread, SIGNAL("finished()"), lambda:


self.emit(SIGNAL("importComplete")))

Hier wird das Signal finished ausgelöst, nachdem ein Thread seine Arbeit beendet hat.

2.1.3. Ausnahmebehandlung
Durch einen try ... except – Block können während der Laufzeit Fehler abgefangen
werden.
while True:
try:
num = raw_input("Eine Zahl eingeben: ")
num = int(num)
break
except ValueError:
print("Eine _Zahl_, bitte!")

In diesem Beispiel wird so lange nach einer Zahl gefragt, bis die Eingabe in einen Integer
umgewandelt werden kann.

2.1.4. Generatoren
Ein Generator ist eine Funktion, die bei jedem Aufruf das nächste Element einer virtuellen
Sequenz zurückgibt. Sie ist dabei einer normalen Funktion sehr ähnlich.
Hier ist ein Beispiel einer solchen:
def range_generator(max):
i = 0
while i < max:
yield i
i += 1

Seite 11
Mit Hilfe des yield-Statements können die einzelnen Werte einer virtuellen Sequenz
zurückgegeben werden.
Durch ein return-Statement wird der Programmablauf einer normalen Funktion
unterbrochen und der Kontrollfluss an die nächsthöhere Ebene zurückgegeben.
Beim Erreichen einer yield-Anweisung wird jedoch die aktuelle Position innerhalb der
Generatorfunktion gespeichert. Es erfolgt ein Rücksprung in das aufrufende Programm mit
dem hinter yield angegebenen Wert. Beim nächsten Iteratoraufruf macht Python dann
hinter dem zuletzt ausgeführten yield weiter und kann wieder auf die alten lokalen
Variablen, in dem Fall i, zugreifen. Erst wenn das Ende der Funktion erreicht wird,
beginnen die endgültigen Aufräumarbeiten.

2.1.5. Global Interpreter Lock


In interpretierten Programmiersprachen – wie es etwa Python oder Ruby sind – gibt es
einen Lock, der sicherstellt, dass gleichzeitig nur ein Thread in der virtuellen Maschine der
Python-Implementierung CPython ausgeführt wird. Das geht jedoch auf Kosten der
Parallelität von Multi-Prozessor-Maschinen.
Anwendungen, die in einer Sprache geschrieben sind, die Global Interpreter Locks
verwenden, müssen in separate Prozesse aufgeteilt werden, um vollständige
Nebenläufigkeit zu erreichen, da jeder Prozess seinen eigenen Interpreter und daher
seinen eigenen Global Interpreter Lock hat.
In Erweiterungs-Modulen für Python, welche in C geschrieben sind, kann durch die
Makros Py_BEGIN_ALLOW_THREADS bzw. Py_END_ALLOW_THREADS ein Block
erzeugt werden, in denen I/O-Funktionen des Systems oder länger laufende
Berechnungen, die keinen Zugriff auf Python-Objekte benötigen, durchgeführt werden.

Seite 12
2.2. Python-Module
Neben der umfangreichen Standardbibliothek von Python machte ich auch noch von
einigen anderen Modulen Gebrauch, die von Drittanbietern entwickelt worden sind.

2.2.1. PyS60
Python for S60 (oder kurz PyS60) ist Nokias Portierung von Python auf das
Betriebssystem S60. Neben den Features von Python werden noch Module angeboten,
die speziell auf diese Plattform abgestimmt sind. Dazu zählen zum Beispiel die
Möglichkeiten, auf die SMS-Mitteilungen zuzugreifen (inbox-Modul), SMS- oder MMS-
Mitteilungen zu schreiben (messaging), die Kontakt-Datenbank zu lesen und zu bearbeiten
(contacts) oder Informationen zum Gerät anfordern zu können (sysinfo).
Ferner kann auch beispielsweise auf die Kamera zugegriffen werden und es besteht die
Möglichkeit Anrufe zu tätigen. Von diesen Funktionen mache ich aber derzeit noch keinen
Gebrauch.

2.2.2. PyQt4
PyQt ist die Anbindung des GUI-Frameworks Qt an Python. Das ist eine Alternative für
Tkinter1, welches direkt mit Python ausgeliefert wird.
Qt wird im Kapitel 15 genauer beschrieben.

2.2.3. PyBluez
PyBluez ist ein Wrapper der System-Bibliotheken für Bluetooth, damit Entwickler schnell
und einfach Bluetooth-Anwendungen unter Python erstellen können.
Dazu können folgende Bluetooth-Stacks (Implementierungen des Bluetooth Protokoll-
Stacks) genutzt werden:
• Windows
◦ Widcomm: Dieser Bluetooth-Stack war der erste für Windows. Es wurde
anfangs von Widcomm Inc. Entwickelt. Diese Firma wurde aber später von der
Broadcom Corporation übernommen.
◦ Microsoft Windows Stack: Von diesem Stack werden nur externe und interne
USB-Dongles unterstützt, jedoch keine mit PCI- oder seriellen Verbindungen.
Seit Windows XP SP 2 ist dieser direkt in Windows inkludiert.
• Linux
◦ BlueZ: Das Ziel dieses Stack ist es, eine Implementierung der Bluetooth
wireless standards specifications unter Linux zu schaffen. Er unterstützt dabei
alle grundliegenden Bluetooth-Protokolle und ist für alle Linux-Kernel ab Version
2.4.6 verügbar.

Unter Mac OS X wird das Modul lightblue verwendet, das auf den unter OS X integrierten
Bluetooth-Stack zugreift.

1 Tkinter (Tk interface) ist ein Wrapper für das Tk-Toolkit für die Programmiersprache Python

Seite 13
Für PyBluez habe ich auch einige Patches entwickelt (siehe Anhang „Patches für
PyBluez“, Seite 82). Einer davon war für die Unterstützung von Windows Vista und
Windows 7, da dieses Modul unter diesen Betriebssystemen nicht kompiliert werden
konnte und daher auch nicht funktionierte. Ein weiterer Patch machte es möglich, durch
einen Geräte-Scan die Klasse des Gerätes bestimmen zu können. Der bisher letzte
änderte zwei Zeilen, um während der Initialisierung des Microsoft-Stacks Threads zu
erlauben und beschleunigte daher den Verbindungsvorgang.

2.2.4. PyOBEX
Dieses von David Boddie entwickelte Modul ist eine Implementierung des OBEX-
Protokolls für Python, das auf der frei verfügbaren Bluetooth specification basiert.
OBEX
OBEX (OBject EXchange) ist ein Kommunikations-Protokoll zum Austausch binärer Daten
zwischen Geräten. Es wurde ursprünglich für Infrarot entwickelt, es gibt aber auch
Portierungen für Bluetooth, USB und WAP.
Es ist die Basis für einige Higher-Level-Profile, unter Bluetooth beispielsweise das Object
Push Profile, File Transfer Profile und Synchronization Profile.
In meiner Anwendung verwende ich den BrowserClient von PyOBEX, um im Datei-
Browser auf die Verzeichnisse und Dateien zugreifen zu können. Es wird auch verwendet
um die SIS-Dateien beim ersten Start an das Telefon zu senden.

2.2.5. Obexftp
Python-Obexftp ist ein Wrapper für Obexftp und dient als Alternative für PyOBEX. Es wird
unterstützt, da die meisten Linux-Distribution Pakete für diese Bibliothek anbieten, für
PyOBEX allerdings nicht. Obexftp ist jedoch nicht für Windows und Mac OS X verfügbar.

2.2.6. Matplotlib
Matplotlib ist eine Bibliothek zum Erzeugen und grafischem Darstellen von
mathematischen Funktionen und Statistiken. Diese Plots können auch in eine Vielzahl von
Datei-Formaten wie SVG oder PNG exportiert werden.
Es wird zum Anzeigen der Mitteilungs- und Kontaktstatistiken verwendet. Diese werden
auch als PNG- oder SVG- Bilder im HTML-Export angezeigt.

2.2.7. vobject und ldif


Die Bibliothek vobject (in Fedora als Paket python-vcobject) dient zum Parsen und
Erzeugen von vCard- und vCalendar-Dateien, ldif (unter Fedora python-ldap) macht das
gleiche für LDIF, könnte aber auf LDAP-Directory-Server zugreifen.
Diese Module werden zum Exportieren und Importieren der Kontakte verwendet.

Seite 14
2.3. Qt Framework
Qt ist eine Bibliothek für die plattformübergreifende Programmierung grafischer
Benutzeroberflächen.
Das Framework wurde für die Programmiersprache C++ geschrieben, es gibt jedoch auch
Anbindungen für viele andere Sprachen. Dazu zählen PyQt und PySide für Python.
Zu den prominenten Programmen, die dieses Toolkit verwenden, gehören die
Desktopumgebung KDE, Google Earth, der Webbrowser Opera, die VoIP-Software Skype
und der Media-Player VLC.
Die Firma Trolltech war ursprünglich für die Entwicklung von Qt zuständig, diese wurde
aber Anfang 2008 von Nokia aufgekauft. Die Entwicklung wird seit dem in der Sparte Qt
Developement Frameworks fortgeführt. Da dieses Framework als Open Source-Software
veröffentlicht wurde, tragen auch viele andere Entwickler zu diesem Projekt bei.
Es wird für Linux, Windows und Mac OS X ein vollständiges SDK angeboten, das auch
eine umfangreiche Dokumentation enthält. Die Plattformen Embedded Linux, Windows
CE, Symbian und Maemo werden auch offiziell unterstützt, externe Entwickler konnten
bereits einen Großteil des Frameworks auf das iPhone und Android portieren.
Die gesamte Bibliothek ist in Module gegliedert. Dadurch können die erstellten Programme
kleiner und schneller werden, da nur die verwendeten Module eingebunden werden
müssen.
Diese Module habe ich in meiner Diplomarbeit verwendet:
• QtCore: Kern-Klassen, die von allen anderen Modulen genutzt werden
• QtGui: Dieses Modul dient zur Erstellung von grafischen Benutzeroberflächen
◦ verwendet für: die gesamte Benutzeroberfläche, Model/View-Programmierung
• QtNetwork: Durch dieses Modul wird Netzwerk-Programmierung einfach und
portabel
◦ verwendet für: Update-Checker
• QtSql: Datenbankintegration in Qt, die eine Vielzahl von
Datenbankmanagementsystemen unterstützt
◦ verwendet für: MySQL- und SQLite-Datenbank zum Speichern der Kontakte,
Mitteilungen und Kalendereinträge
• QtXml: Stream-Reader und -Writer für XML-Dokumente
◦ verwendet für: Parsen der Ordnerliste, die man über das OBEX-Protokoll
bekommt
• QtSvg: Klassen zur Verwendung von skalierbaren Vektorgrafiken
◦ verwendet für: Animation beim Scannen nach Bluetooth-Geräten
• Webkit: Darstellung von Webseiten
◦ verwendet für: Verlaufs-Browser im Chat-Fenster

Seite 15
2.3.1. Signale & Slots
Signale sind Meldungen, die beim Eintreten eines bestimmten Ereignisses abgegeben
(emittiert) werden. Ein Slot ist im Prinzip eine normale Funktion, welche mit einem oder
mehreren Signalen verbunden ist.
Beim Emittieren eines Signals werden dann die ihm zugehörigen Slots aufgerufen.
Dieses Konzept hat den Vorteil, das es einfacher als Rückruffunktionen (Callbacks) zu
verwenden ist.

2.3.2. Model/View-Architektur
Die Model/View-Architektur wurde in Qt4 eingeführt. Diese soll vor allem die Trennung der
Form (Benutzeroberfläche) und den Inhalt (Daten) vereinfachen.
Daher gibt es eine Modell-Klasse, welche das Interface für die Daten bereitstellt, und eine
View-Klasse, die auf diese Daten zugreift und auf der grafischen Benutzeroberfläche
anzeigt.
Die Modell-Klasse stellt jedoch nur Methoden zum Zugriff auf die Daten bereit. Die Daten
selber können jedoch auch wo anders liegen, zum Beispiel in einer Datenbank oder in
einer Datei.
Durch die Aufteilung der Programmlogik in Modell- und View-Klassen wird das Programm
einfacher und besser strukturiert.
Im Qt-Framework gibt es einige Basisklassen, die das Anzeigen der Daten ermöglichen.
Dazu zählt beispielsweise die Klasse QListView, welche die Daten in einer Liste darstellt.
Weiters gibt es noch QTreeView, die eine Baum-Struktur ermöglicht und QTableView,
welche die Daten in einer Tabelle darstellt.
Daneben gibt es auch noch Delegate-Klassen, welche die einzelnen Einträge –
beispielsweise in einer QListView – zeichnen.

Abbildung 2-1: Model/View-Architektur

Seite 16
2.4. Bluetooth
2.4.1. Einführung
Bluetooth ist eine Technologie um Peripherie-Geräte wie Mäuse oder Mobiltelefone
drahtlos verbinden zu können. Zu den Vorteilen zählt, dass Bluetooth kostengünstig und
stromsparend ist. Da es nur auf kurze Distanz funktioniert wird es nicht für Drahtlos-
Netzwerke verwendet. Es dient eher als Ersatz für IrDA2. Im Gegensatz zu IrDA muss bei
Bluetooth keine direkte Sichtverbindung bestehen und es kann eine weitere Distanz als 1
Meter überbrückt werden.
Zur Kommunikation wird von Bluetooth – wie auch von Geräten nach dem 802.11b- oder
802.11g- Standard - das 2,4 Ghz-ISM-Band verwendet. Dafür ist keine Lizensierung
erforderlich. Es basiert auf Ad-Hoc Piconetzen, das sind lokale Netzwerke mit einer
kleinen Ausdehnung, die üblicherweise keine besondere Infrastruktur benötigen. Jedes
Bluetooth-Gerät kann gleichzeitig mit bis zu 7 Teilnehmern in diesem Netzwerk
kommunizieren.
Um Robustheit gegenüber Störungen zu erreichen, wird ein Frequenzsprungverfahren
(Frequency Hopping) eingesetzt, bei dem das Frequenzband in 79 verschiedene
Frequenzstufen im 1-MHz-Abstand eingeteilt wird, die normalerweise bis zu 1.600-mal in
der Sekunde gewechselt werden. Das bildet auch eine Grundlage für die Sicherheit, da es
schwierig vorherzusagen ist welche Frequenz als nächstes benutzt wird.
Jeder jemals erzeugt Bluetooth-Chip ist mit einer global eindeutigen 48 Bit langen Adresse
versehen. Diese wird als Bluetooth-Adresse bezeichnet und kann mit der MAC-Adresse
von Netzwerkadaptern verglichen werden (es werden sogar beide von der gleichen
Organisation – der IEEE Registration Authority – verwaltet). Sie wird zur Zeit der
Herstellung des Chips zugewiesen und ist weltweit einzigartig und unveränderbar.
Um mit anderen Geräten kommunizieren zu können, muss es Möglichkeiten geben, um
diese Adresse in Erfahrung zu bringen.

2 Die Infrared Data Association (IrDA) spezifiziert das Infratot-Protokoll

Seite 17
2.4.2. Protokollstapel
Die grundlegenden Protokolle können nach dem
in der rechten Abbildung erkennbaren Schema
unterteilt werden:
Funkschicht (Radio): In dieser Schicht wird die
Funkübertragung, die genutzten Frequenzen
und die Sendeleistung geregelt.
Basisband (Baseband): Es werden die
Mechanisamen zum Verbindungsaufbau, die
Rahmenstruktur und das Zeitverhalten geregelt.
Sie entspricht in etwa der Sicherungsschicht des
OSI-Modells.
Link Manager: Es steuert den Verbindungs-
aufbau und die Verbindungsverwaltung zwischen
zwei Geräten. Er beinhaltet auch Sicherheits-
und Authentifizierungsfunktionen.
L2CAP (Logical Link Control and Adaption
Protocol): Zusammen mit dem LMP stellt es die
Sicherungsschicht des Bluetooth-Protokoll-
Stacks dar. Es dient zum Aufbau und zur
Konfiguration von logischen Kanälen. Abbildung 2-2: Bluetooth-Protokollstapel
RFCOMM (Radio Frequency Communication): Dieses Protokoll stellt eine
Befehlssteuerung dar, die es erlaubt bis zu 60 serielle Schnittstellen zu emulieren. Daher
wird es auch als Serial Port Emulation bezeichnet.
SDP (Service Discovery Protocol): Das SDP dient zur Erkennung und Suche nach
Diensten mit bestimmten Eigenschaften und der Beschreibung von Diensten innerhalb der
Funkreichweite eines Bluetooth-Gerätes.
OBEX (Object Exchange): Dieses vom IrDA-Standard bekannte Protokoll kann zum
Austausch von Dateien, Kalenderinformationen oder Visitenkarten verwendet werden. Es
arbeitet im klassischen Client-Server-Modell und enthält „PUT“ und „GET“-Methoden zur
Datenübertragung.
Audio: Audio-Anwendungen können direkt die Basisbandschicht nutzen, nachdem die
Audio-Signale entsprechend kodiert wurden.
Darüber befinden sich Anwendungen und Profile. Die Spezifikation von Bluetooth umfasst
einige Anwendungen, die „Standardlösungen“ für bestimmte Nutzungsszenarien für
bestimmte Anwendungsfälle umfassen. Diese werden als Profile bezeichnet. Dazu zählen
beispielsweise das BIP (Basic Imaging Profile) zum Übertragen von Bilddaten, DUN
(Dial- up Networking Profile) als Internet-Einwahlverbindung, SAP (SIM Access Profile)
zum Zugriff auf die SIM-Karte und SYNC (Synchronisation Profile) zum Datenabgleich.

Seite 18
2.4.3. RFCOMM / L2CAP
Das RFCOMM-Protokoll bietet eine ähnliche Zuverlässigkeit wie TCP (Transmission
Control Protocol) im TCP/IP-Protokoll-Stack. Es dient zum Aufbau einer Punkt-zu-Punkt-
Verbindung von 2 Geräten, über die zuverlässig Datenströme aufgebaut werden können.
Wenn Daten nicht transportiert werden können, wird die Verbindung abgebrochen und ein
Fehler zurückgegeben. Der größte Unterschied zu TCP ist die Wahl der Port-Nummer.
Während TCP 65535 verschiedene Ports auf einer Maschine zulässt, hat RFCOMM nur 30
Ports.
UDP (User Datagram Protocol) wird in Situationen verwendet, wo die Zuverlässigkeit nicht
wichtig ist und man den Overhead von TCP vermeiden möchte. Diese Kriterien treffen
auch auf L2CAP zu. L2CAP ist sowohl ein verbindungsorientiertes als auch
verbindungsloses Protokoll zum Austausch von Datagrammen einer fixen Länge.
Die Zuverlässigkeit von L2CAP kann in 3 Verfahren eingeteilt werden:
• Verlorene Pakete niemals neu übertragen
• Verlorene Pakete bis zu einem Verbindungsfehler neu übertragen
• Paket verwerfen, wenn es innerhalb einer bestimmten Zeit (0-1279 ms) nicht
bestätigt wird. Dies ist für zeitkritische Anwendungen nützlich.
Die folgende Tabelle von „The Use of Bluetooth in Linux and Location Aware Computing “
von Albert Huang bietet einen Überblick über die Situationen, in welchen RFCOMM oder
L2CAP gewählt werden sollte:
Requirement Internet Bluetooth
Reliable, streams-based TCP RFCOMM
Reliable, datagram TCP RFCOMM or L2CAP with infinite retransmit
Best-effort, datagram UDP L2CAP (0-1279 ms retransmit)
Table 2.1: A comparison of the requirements that would lead us to choose certain protocols. Best-e ffort
streams communication is not shown because it reduces to best-e ffort datagram communication.

Diese Tabelle bietet eine Übersicht über die Port-Nummern:


protocol terminology reserved/well-known port dynamically assigned ports
TCP port 1-1024 1025-65535
UDP port 1-1024 1025-65535
RFCOMM channel none 1-30
L2CAP PSM odd numbered 1-4095 odd numbered 4097 - 32765
Table 2.2: Port numbers and their terminology for various protocols

Seite 19
2.4.4. Service Discovery Protocol
Das Service Disvocery Protocol (SDP) definiert das Aufspüren von Services und
Eigenschaften eines Bluetooth-Gerätes.
Die Kommunikation erfolgt dabei über einen SDP-Server und einen SDP-Client. Der
Server verwaltet dabei eine Liste von Service Records, welche die auf dem Gerät
laufenden Services beschreiben. Jeder Service Record beinhaltet Informationen über ein
einzelnes Service. Ein Client kann diese Informationen durch einen SDP request abfragen.
Auf jedem Bluetooth-Gerät soll nur ein SDP-Server laufen. Falls das Gerät nur als Client
betrieben wird kann auf den SDP-Server verzichtet werden.
Für die genauen Details des Protokolls wird auf das Kapitel „SERVICE DISCOVERY
PROTOCOL (SDP) SPECIFICATION“ der „BLUETOOTH SPECIFICATION“ verwiesen.

Seite 20
2.5. Interprozesskommunikation
Mit dem Begriff Interprozesskommunikation (IPC) bezeichnet man den den
Informationsaustausch zwischen nebenläufigen Prozessen (auch zwischen Threads).

2.5.1. D-Bus
D-Bus ist ein freies IPC-Framework, welches speziell für die Bedürfnisse von
Desktopumgebungen geschaffen wurde. Es sollte auch die damals verfügbaren IPC-
Frameworks (DCOP3 für KDE und CORBA4 für GNOME) vereinigen und ein einheitliches
Framework schaffen.
Es wird im Rahmen eines freedesktop.org-Projekts entwickelt und derzeit von fast jeder
modernen Linux-Distribution eingesetzt. Daneben existieren auch noch Portierungen für
andere Betriebssysteme, darunter auch einige Unix-Derivate (wie Mac OS X) und
Windows.
D-Bus wird verwendet um die Kommunikation von Programmen untereinander zu
ermöglichen. Dazu dient vor allem die Bibliothek libdbus, die es ermöglicht Nachrichten
auszutauschen.
Diese Programme können auch bei Bedarf gestartet werden, also wenn die Services von
einem Daemon-Programm benötigt werden.
D-Bus unterstützt dabei einen systemweiten Dienst (system daemon) und einen
benutzerspezifischen (per-user-login-session daemon). Dadurch wird die Kommunikation
zwischen Prozessen auf dem System-Level (wie Drucker oder andere Hardware-Services)
und normalen Benutzer-Anwendungen möglich.
Die miteinander kommunizierenden Anwendungen können dabei entweder auf dem
gleichen Rechner laufen, oder dank TCP/IP auch auf verschiedenen Rechnern im
Netzwerk.
Die libdbus-Bibliothek hat außer einem XML-Parser (libxml oder expat) keine weiteren
Abhängigkeiten, die Bindings für weitere Frameworks (also Qt, GLib, Java, C#, Python
u.s.w.) sind einfacher zu verwenden, bringen aber auch mehr Abhängigkeiten zu weiteren
Bibliotheken.
Die low-level-Bibliothek libdbus sollte daher nicht direkt von den Entwicklern verwendet
werden, sondern dient nur als Basis für die verschiedenen darauf aufbauenden Bindings.

2.5.1.1. Mitteilungen
Auf dem unteren Level kommunizieren Anwendungen über Mitteilungen. Dabei wird ein
schnelles, binäres Protokoll verwendet, um diese weiterzuleiten. Hier wurde auf geringe
Latenz und geringen Overhead Wert gelegt.
Mitteilungen leiten die Remote Procedure Calls (RPC) weiter und geben danach die
Antworten oder Fehler davon zurück.
Es gibt noch eine spezielle Art der Mitteilung: die Signal-Nachricht. Diese hat kein klar
definiertes Ziel, sondern wird an alle daran interessierten Teilnehmer gesendet
(Opt-In- Mechanismus).
3 Desktop Communication Protocol, ein einfaches IPC-Framwork
4 Common Object Request Broker Architecture

Seite 21
Durch das QtDBus-Modul muss sich der Programmierer über das Konzept der
Mitteilungen keine Gedanken machen, da dadurch ein einfacher, objekt-orientierter Ansatz
zur Verfügung steht.

2.5.1.2. Service-Name
Anwendungen registrieren sich einen Service-Namen um über D-Bus mit anderen
Anwendungen kommunizieren zu können. Dieser Name wird verwendet um eine Nachricht
von einer Anwendung zu einer anderen weiterzuleiten.
Das Format dieses Namens ist dem eines Hostnamen in einem Netzwerk sehr ähnlich, da
er auch durch Punkte getrennt wird.
Üblicherweise wird dieser Name nach dem Domain-Namen der Organisation gewählt, zum
Beispiel net.sourceforge.OpenMobileSuite.ConnectionManager.

2.5.1.3. Objekt-Pfade
Die Services exportieren nun einzelne Objekte. Die Namen dieser Objekte sind
hierarchisch aufgebaut und werden mit Schrägstrichen (/) getrennt.
Im Vergleich mit Web-Adressen ist dieser Teil der Pfad der Adresse.
So kann das Service net.sourceforge.OpenMobileSuite.ConnectionManager nun zum
Beispiel die Objekte /Devices, /Contacts und /Database exportieren.

2.5.1.4. Interfaces
Interfaces sind ähnlich abstrakter Klassen aus C++ bzw. dem interface-Keyword aus Java.
Diese bestimmen den „Vertrag“ zwischen dem aufrufenden und dem aufgerufenem Objekt.
Das Interface stellt daher einen Namensraum für Methoden und Signale bereit und wird
durch einen Namen eindeutig identifiziert. Dieser startet wieder mit dem umgekehrten
Domain-Namen.

Seite 22
2.6. Versionsverwaltungssystem
2.6.1. Apache Subversion (SVN)
Das Versionsverwaltungssystem Apache Subversion (oft wegen des Kommandos als svn
abgekürzt) ist freie Software. Es wird unter der Apache-Lizenz veröffentlicht.
Es dient zur Versionsverwaltung von Dateien und Verzeichnissen in Form einer einfachen
Revisionszählung.
Dabei erfolgt die Versionierung in einem zentralen Projektarchiv (repository). Dies
unterscheidet es von dezentralen Systemen, wie etwa git.
Subversion ist eine Weiterentwicklung von CVS (Concurrent Versions System) und hat
diesem gegenüber den Vorteil, dass Dateien und Verzeichnisse umbenannt und
verschoben werden können, ohne die Versionsgeschichte zu verlieren.

Die Entwicklung dieses Versionsverwaltungssystems wurde 2000 begonnen und erreichte


2004 die erste stabile Version. Die aktuell verfügbare Version ist 1.6 (März 2011).

2.6.1.1. Tags und Branches


Ein Repository wird normalerweise in drei Bereiche aufgeteilt: branches, trunk und tags.
Im branches-Pfad (branches = Verzweigungen) wird die grundlegende Entwicklung eines
Projekts durchgeführt. Zu den grundlegenden Entwicklungen zählen weitgreifende
Überarbeitungen und komplett neu entwickelte Bereiche.
Im trunk-Pfad (trunk = Stamm) wird die Entwicklung der aktuellen Version durchgeführt.
Diese sollte immer möglichst stabil sein und keine großen, inkompatiblen Neuerungen
enthalten.
Im tags-Pfad (tags = Markierungen) befinden sich Kopien von veröffentlichten Versionen.
Diese stellen eine Kopie aus dem trunk- oder einem branch-Verzeichnis dar. Dieser Pfad
ist nicht für Entwicklungen vorgesehen, sondern stellt eher einen Schnappschuss dar.

Seite 23
3. Entwickler-Dokumentation
3.1. Anfänge des Projekts
Im Jahr 2006 hörte ich das erste Mal von der Portierung der Programmiersprache Python
auf die Smartphone-Plattform S60, auf der mein damaliges Mobiltelefon basierte.
Als im Juli 2007 die erste stabile Version davon freigegeben wurde 5, dachte ich erstmals
an die Implementierung einer Weiterleitung der am PC geschriebenen SMS-Nachrichten
zu meinem Mobiltelefon.

Nach langer Recherche wurde ich auf das Projekt „btsms6“ aufmerksam, das genau
meinen Vorstellungen entsprach. Es war sehr einfach geschrieben (der ganze
Programmcode mitsamt der Oberfläche bestand aus weniger als 400 Zeilen) und hatte
außer der SMS-Weiterleitung keine weiteren Funktionen. Da es bei mir Probleme mit dem
Senden von Umlauten in SMS-Mitteilungen gab machte ich mich mit dem Quellcode
vertraut und besserte kleine Fehler aus. Mit der Zeit erweiterte ich das Programm um
einige weitere Funktionen (wie das Speichern der Mitteilungen in einer Datenbank). Da ich
die auf Tkinter7 basierende Oberfläche nicht mochte, schaute ich mich nach Alternativen
um. Ich entschied mich für das Toolkit Qt, das nativ wirkende Oberflächen für die
Betriebssysteme Windows, Linux und Mac OS X versprach 8. Dieses Toolkit wird auch von
der Linux-Desktopumgebung KDE benutzt, welche ich verwende.
Am 29. August 2007 begann ich mit der Entwicklung unter diesem Toolkit und
veröffentlichte diese Arbeit unter dem Namen „Series60-Remote“ in einem
Versionsverwaltungssystem auf Sourceforge 9. Als ich die für mich wichtige Mitteilung- und
Kontaktverwaltung in einer Datenbank fertiggestellt hatte machte ich einige Monate Pause.
Erst im Februar 2009 beschäftigte ich mich wieder mit diesem Projekt und veröffentlichte
neben einer Version für das Betriebssystem Linux auch erstmals eine für Windows.
Im Rahmen der Diplomarbeit habe ich dieses Projekt nun vorangetrieben. Alle
Änderungen seit dem Start der Diplomarbeit am 10. Juni 2010 sind Teil davon.

5 http://wiki.opensource.nokia.com/projects/PyS60_history (toter Link, archiviert unter


http://replay.waybackmachine.org/20090404090521/http://wiki.opensource.nokia.com/projects/PyS60_history )
6 http://tovganesh.blogspot.com/2006/06/btsms-v01-forward-your-sms-over.html
7 Tk interface, ein Toolkit für grafische Benutzeroberflächen für die Programmiersprache Python
8 http://qt.nokia.com/products - „Qt uses the native graphics APIs of each platform it supports, taking full advantage of
system resources and ensuring that applications have native look and feel.“
9 http://series60-remote.svn.sf.net/viewvc/series60-remote?view=revision&revision=1

Seite 24
3.2. Aktuelle Entwicklung
Ein wichtiger Punkt in der aktuellen Entwicklung ist die Unterstützung weiterer Plattformen
für Smartphones. Neben S60 und Symbian^3 soll in Zukunft auch Android unterstützt
werden. Dazu wird eine Server-Anwendung in der Programmiersprache Java geschrieben.
Das Projekt wird von „Series60-Remote“ in „Open Mobile Suite“ umbenannt.

Abbildung 3-1: Neue Architektur der "Open Mobile Suite"

Ein weiterer wichtiger Punkt ist die Änderung der Architektur der Anwendung: Früher
bestand sie aus einem Prozess, der für alles – Oberfläche, Datenbank und Bluetooth-
Verbindung – zuständig war. Diese drei Komponenten sollen nun in eigene Anwendungen
ausgegliedert werden. Das dient vor allem zur Umgehung der Global Interpreter Lock von
Python. Diese wird genauer im Kapitel 2.1.5 Global Interpreter Lock (Seite 12)
beschrieben. Weitere Details dazu findet man in der Ankündigung auf der Webseite. Diese
findet man im Anhang im Kapitel 8.1.1. New architecture of Open Mobile Suite (Seite 80).
Der zentrale Prozess ist nach diesem Aufbau nun der „Connection Manager“, welcher über
das DBus-Interface net.sourceforge.OpenMobileSuite.ConnectionManager erreichbar ist.
Er kümmert sich um die Verwaltung der (SQLite- oder MySQL-) Datenbank und stellt eine
einfache API zur Verfügung, mit der die verschiedenen Verbindungen einfach verwaltet
werden können. Er benachrichtigt weiters auch die verschiedenen Clients – in etwa
Benutzeroberflächen – bei Veränderungen mittels Signalen.

Seite 25
Die „Open Mobile Suite Connection“ stellt nun eine Verbindung zu dem Mobiltelefon her –
dabei werden sowohl die Plattformen S60/Symbian^3 als auch Android unterstützt. Durch
dieses Konzept gestaltet sich die Portierung auf weitere Plattformen einfach. Diese sind
über das Interface net.sourceforge.OpenMobileSuite.Connections erreichbar, die
Benutzeroberflächen sollten jedoch indirekt über den Connection Manager darauf
zugreifen.
Eine weitere Bluetooth-Verbindung wird durch den „OBEX Connection“ Prozess
ermöglicht, der einen Datei-Manager implementiert.
Zu den Vorteilen dieser Architektur zählt auch die Möglichkeit, verschiedene
Benutzeroberflächen verwenden zu können, ohne sich um die Logik dahinter Gedanken
machen zu müssen. Neben der derzeit implementierten Oberfläche könnte man
beispielsweise durch wenig Programmieraufwand ein Frontend erzeugen, das bei
bestimmten kritischen Systemereignissen den Administrator per SMS benachrichtigt. Eine
weitere Möglichkeit besteht in der Integration der Kontaktliste und des SMS-Versandes in
bestehende Instant-Messaging-Applikationen, wie etwa Telepathy. Weiters kann auch der
Chat-Verlauf in andere Anwendungen importiert werden. Dies ist vor allem für die KDE-
Projekte Nepomuk und Akonadi interessant, da so eine Verknüpfung zu anderen
semantischen Daten möglich ist.
Auf der Seite des Mobiltelefons läuft eine Server-Anwendung, die für S60 in Python und
für Android in Java geschrieben ist.
Anmerkung: Durch den Python-Profiler „cProfile“ konnte die Laufzeit-Performance der
alten Architektur (als alles noch aus einem Prozess bestand) analysiert werden.
Dadurch konnten einige aussagekräftige Statistiken erzeugt werden. Es konnte
beispielsweise festgestellt werden, dass die Methode zur Berechnung des Contact-
Hashs sehr lange dauert (bei einer großen Datenbank mit mehr als 400 Kontakten
kann diese Funktion bis zu 30 Sekunden in Anspruch nehmen). Da in dieser Zeit die
Benutzeroberfläche nicht bedienbar ist und es für den Benutzer den Anschein hat,
dass das Programm hängt, wurde diese Funktion in einen anderen Prozess verlegt.

Seite 26
Abbildung 3-2: Entwicklung der "Open Mobile Suite"

Die Entwicklung fand dabei in mehreren Zweigen des SVN-Archives statt.


Nachdem einige Änderungen an der GUI gemacht wurden, konzentrierte sich die
Entwicklung auf die Portierung zur Android-Plattform. Erst danach wurde die Anwendung
in drei einzelne Prozesse aufgeteilt.
Daneben wird auch noch der aktuell stabile Zweig (mit den Versionen 0.4.x) betreut.

Seite 27
3.3. Grundlegende Objekte
In dem Python-Module openmobilesuite.base sind einige grundlegende Objekte definiert,
die in anderen Modulen (wie openmobilesuite.gui oder
openmobilesuite.connectionmanager) verwendet werden.
Dazu zählen beispielsweise die folgenden Klassen:
• Connection (openmobilesuite.base.element.connection)
stellt eine aktive Verbindung zu einem Telefon dar

Attribute:
id: Eine für diese Verbindung eindeutige ID
plugin: Das verwendete Verbindungs-Plugin
device: Das Gerät (Instanz der Klasse Device), zu dem die Verbindung gehört

• ConnectionPlugin (openmobilesuite.base.element.connection_plugin)
enthält Informationen, die zur Verbindung des Telefons notwendig sind

Attribute:
name: Der Name des Plugins
executable: Das auszuführende Programm, das bei einer Verbindungsanforderung
gestartet wird (z.B. openmobilesuite-connection-oms)
bluetooth_servicename: Der Name des damit verbundenen Bluetooth-Services
devices: Die durch dieses Plugin unterstützten Gerät-Typen
fix_bluetooth_ports: Bluetooth-Ports, die einem gewissen Gerät-Typ fix zugeordnet
sind (damit ist kein Service-Scan notwendig)

• Device (openmobilesuite.base.element.device)
stellt ein (in der Datenbank gespeichertes oder verbundenes) Mobiltelefon dar

Attribute:
id: Die in der Datenbank gespeicherte ID des Gerätes
name: Der sichtbare Name des Gerätes (durch den Bluetooth-Scan bekannt)
bluetooth_address: Adresse des Gerätes in der Form xx:xx:xx:xx:xx:xx
bluetooth_port: Der Bluetooth-Port, zu dem eine Verbindung besteht
bluetooth_device_class: Die Geräte-Klasse des Gerätes
device_type: Der Typ des Gerätes (Instanz einer von device.general abgeleiteten
Klasse)
values: In diesem Dictionary werden die Systeminformationen gespeichert

• SysinfoValue (openmobilesuite.base.element.device_sysinfo_value)
ist ein Eintrag der Systeminformationen

Attribute:
key: Der Typ des Eintrags
value: Der normale Wert des Eintrags
alternative_value: Ein alternativer (lesbarer) Wert
max_value: Der maximal mögliche Wert bei einem Integer-Schlüssel

Seite 28
• Contact (openmobilesuite.base.element.contact)
ein Kontakt des Adressbuches

Attribute:
id: Die in der Datenbank gespeicherte ID des Kontakts
id_on_phone: Die auf dem Mobiltelefon verwendete ID des Kontakts
device: Die ID des dazugehörigen Gerätes
name: Der Name des Kontakts
ignore: (bool) Wird auf True gesetzt wenn dieser Kontakt ignoriert wird
favorite: (bool) Wird auf True gesetzt wenn dieser Kontakt bevorzugt wird

values: Eine Liste der Eigenschaften (Instanzen von ContactField) des Kontakts
internal_values: Eigenschaften, die nicht in der Datenbank bzw. am Telefon
gespeichert werden

• CalendarEntry (openmobilesuite.base.element.calendar_entry)
ist ein Eintrag in der Kalender-Datenbank

Attribute:
id: Die in der Datenbank gespeicherte ID des Kalender-Eintrags
id_on_phone: Die auf dem Mobiltelefon verwendete ID des Kalender-Eintrags
device: Die ID des dazugehörigen Gerätes
type: Der Typ („Überschrift“) des Eintrags
content: Der längere Inhalt des Eintrags
location: Der zu diesem Eintrag definierte Ort
real_start_time: Der für diesen Eintrag definierte Startzeitpunkt
real_end_time: Der für diesen Eintrag definierte Endzeitpunkt
start_time: Der Startzeitpunkt (bei Wiederholungen der Startzeitpunkt der
Wiederholung)
end_time: Der Endzeitpunkt (bei Wiederholungen der Endzeitpunkt der
Wiederholung)
last_modified: Das Datum der letzten Änderung
replication: Zugriffsberechtigung, kann 'open', 'private' oder 'restricted' sein
alarm: Zeitpunkt des Alarms oder None
priority: Priorität des Eintrags (Integer von 0 bis 255)

Falls der Eintrag wiederholt wird gibt es noch folgende Attribute:


repeat_type: Typ der Wiederholung
repeat_days: Tage der Wiederholung
repeat_exceptions: Ausnahmen (Als Sekunden seit der UNIX-Epoche)
repeat_start: Start der Wiederholung (Als Sekunden seit der UNIX-Epoche)
repeat_end: Ende der Wiederholung (Als Sekunden seit der UNIX-Epoche)
repeat_interval: Intervall der Wiederholung

Seite 29
• Message (openmobilesuite.base.element.message)
id: Die in der Datenbank gespeicherte ID der Mitteilung
id_on_phone: Die auf dem Mobiltelefon verwendete ID der Mitteilung
device: Die ID des dazugehörigen Gerätes
contact: Die ID des Empfängers oder Senders
address: Die Telefonnummer, an die die SMS gesendet oder von der sie
empfangen wurde
type: Gesendet (MessageType.Sent) oder empfangen (MessageType.Inbox)
priority: Priorität der Mitteilung (normalerweise MessagePriority.Medium)
date_time: Der Zeitpunkt des Sendens oder Empfangens
message: Der Inhalt der Mitteilung
encoding: Kodierung der Mitteilung (normalerweise '7bit')
Daneben gibt es noch die Klassen StatisticsRequest und StatisticsResponse, welche eine
Anfrage bzw. Antwort der Statistik-Abfrage darstellen.

3.3.1. Serialisierung
Da diese Objekte von der Klasse DbusSerializeable abstammen verfügen sie über die
Methoden serialize und deserialize. Dadurch kann eine Instanz einer Klasse über D-Bus
an einen anderen Prozess geschickt und von diesem wieder in das ursprüngliche Objekt
umgewandelt werden.
Für jede Klasse muss durch das Attribut _dbus_properties ein Dictionary definiert werden,
welches als Schlüssel die über D-Bus übermittelbaren Attribute und als Wert den Datentyp
davon festhält.

Seite 30
3.4. Wizard beim ersten Start
Beim ersten Start der Anwendung wird ein Einrichtungs-Assistent gestartet, der beim
Installieren und Konfigurieren der Anwendung hilfreich ist. Wenn die Konfiguration
abgeschlossen ist wird der Eintrag firstStart in der Kategorie general in der
Konfigurationsdatei auf true gesetzt und dadurch der Dialog beim nächsten Start nicht
mehr angezeigt.
Jede Seite basiert auf einer QWizardPage, welchen den für einen Einrichtungsdialog
typischen Stil auf jedem unterstützten Betriebssystem verwendet. Für jede Seite wird ein
Titel, Untertitel und Banner-Logo angegeben.
Für die QWizardPage können die virtuellen
Methoden initializePage und validatePage
überschrieben werden. initializePage wird
zum Vorbereiten der Seite verwendet,
nachdem der Benutzer auf der
vorhergehenden Seite den „Weiter“-Button
klickt. validatePage wird nach dem Klick auf
„Weiter“ aufgerufen und bestimmt ob die
nächste Seite angezeigt werden soll.

Auf der Willkommens-Seite findet man eine


kurze Information zum Programm. Hier
werden die wichtigsten Features aufgelistet.
Sollte eine nicht stabile Version installiert
worden sein, erscheint auch ein Hinweis, Abbildung 3-3: Willkommens-Seite
dass Fehler auftreten können und man diese
im Bug-Tracking-System melden soll.

Auf der Geräteauswahl-Seite wird ein Blue-


tooth-Scan gestartet und man bekommt eine
Übersicht über die gefundenen Geräte. Das
Widget für den Scan basiert auf dem
DeviceScanWidget, welches im Kapitel
3.7 Geräteverwaltung (Seite 52) beschrieben
wird.
Wenn kein Gerät ausgewählt wird, erscheint
eine Meldung, dass man sein Gerät später
über den Einstellungs-Dialog auswählen
kann. Die folgenden Seiten werden dadurch
übersprungen.
Das ausgewählte Gerät wird in einer Variable
gespeichert und auf den nachfolgenden Abbildung 3-4: Geräte-Auswahl-Dialog
Seiten für die Installation und den Eintrag in
die Datenbank gespeichert.

Seite 31
Als nächstes kommt die Seite zur Auswahl
der Mobiltelefon-Plattform. Hier kann
derzeit zwischen „Android“ und „S60 und
Symbian^3“ gewählt werden.
Diese Auswahl wird gespeichert und dem
zuvor ausgewählten Gerät zugeordnet.

Die nächsten Seiten sind von der


ausgewählten Plattform abhängig.

Wenn Android ausgewählt wurde erscheint


eine Seite, die bei der Übertragung der
Android-App hilft.

Für die Plattform S60 bzw. Symbian^3 Abbildung 3-5: Plattform-Auswahl


muss vor der Anwendung die Bibliothek
„Python for S60“, oder kurz PyS60 auf das
Gerät übertragen und dort installiert werden.
Die Übertragung zum Smartphone
übernimmt der Connection Manager durch
das ObexConnections-Objekt.

Kommt es zu einem Fehler bei der Über-


tragung, wird ein Dialog ausgegeben.
Falls die Server-Anwendung für das Telefon
nicht gefunden werden konnte (weil
beispielsweise die SVN-Version herunter-
geladen wurde) erscheint ebenfalls eine
Meldung mit Hinweisen zum Erzeugen der Abbildung 3-6: Installation für Android
Anwendung.
Für die Übertragung wird das Python-Modul
obexftp (siehe Kapitel 2.2.5 Obexftp, Seite
14) oder PyOBEX verwendet (siehe Kapitel
2.2.4 PyOBEX, Seite 14).
Da diese optional sind, werden, falls diese
nicht verfügbar sind, die Schaltflächen
ausgegraut.

Abbildung 3-7: Installation für S60

Seite 32
Für die Plattform S60 bzw. Symbian^3
werden auf den folgenden zwei Seiten noch
Anweisungen für das Ausführen der
Anwendung nach der Installation angezeigt.
Auf der ersten (siehe auf Abbildung
Abbildung 3-8: Weitere Hinweise für S60)
findet man Hinweise zum Start der
Anwendung. Bei der Installation wird eine
Verknüpfung im Anwendungs-Menü erstellt,
welche das Programm startet. Das ist bei
Symbian (im Gegensatz zu Android)
notwendig, da sonst das Bluetooth-Service
nicht gestartet wird.
Auf der nächsten Seite (siehe auf Abbildung
3-9: Weitere Hinweise für S60) wird eine
Möglichkeit gezeigt, wie das Programm in Abbildung 3-8: Weitere Hinweise für S60
den Hintergrund verschoben werden kann
ohne es zu beenden. In zukünftigen
Versionen soll dies vereinfacht werden, da
derzeit durch einen Klick auf die rechte
Navigations-Taste das Programm beendet
wird.
Das wird – durch ein 3rd-Party-Modul – in
kommenden Versionen durch einen
„Verstecken“-Button ersetzt.

Die Einrichtung der Datenbank ist die


letzte Seite des Wizards. Hier kann zwischen
SQLite und MySQL gewählt werden.
Bei MySQL erscheint zusätzlich noch eine
Groupbox, in der Host, Port, Benutzername, Abbildung 3-9: Weitere Hinweise für S60
Passwort und Name der Datenbank
eingestellt werden können.
Da die Qt-Module für SQLite (QSQLITE) und
MySQL (QMYSQL) optional sind, erscheint
eine rote, fett gedruckte Meldung unter der
Combobox falls das aktuell ausgewählte
Modul nicht installiert und so der Datenbank-
Typ nicht verfügbar ist.

Abbildung 3-10: Datenbank-Einrichtung

Seite 33
3.5. Logging
Auf der Oberfläche kann durch den Menüpunkt „Datei – Log-Meldungen“ ein Dialog
aufgerufen werden, in dem sämtliche Log-Meldungen angezeigt werden, die zum
Debuggen der Anwendung dienen. Im Wizard beim ersten Start kann dieser Dialog durch
die Tastenkombination Strg+D aufgerufen werden.
Dieser Dialog fasst sämtliche Debug-Meldungen der verschiedenen Module
(unterscheidbar durch unterschiedliche Farben) zusammen. Weiters werden in das
Textfeld im Tab „Output“ auch die Meldungen der Standard-Ausgabe und der Standard-
Fehlerausgabe kopiert.

Abbildung 3-11: Logging-Dialog

Ein weiterer Handler der Meldungen ist die Standard-Ausgabe. Wenn beim Start der
Anwendung der Parameter -v (oder --verbose) angegeben wird, werden auch Info-
Meldungen angezeigt. Der Paramter -d (oder --debug) zeigt auch Meldungen auf dem
Debug-Level und der Parameter -l (--long) schaltet auf eine ausführliche Ansicht um.
Beispiel:
lukas@laptop ~ $ openmobilesuite-gui --verbose --long
[29.04.2011 09:57:14] - GUI - INFO - Show wizard-dialog (gui: __init__ - line 212,
process 11345)
[29.04.2011 09:57:14] - CM - INFO - Opening SQLite database in file
/home/lukas/.config/OpenMobileSuite/data.db (database: __openSqliteDatabase - line 60,
process 11351)
[29.04.2011 09:57:14] - CM - INFO - Bluetooth scan started
(bluetoothscanner_object: ScanStarted - line 87, process 11351)
[29.04.2011 09:57:25] - CM - INFO - Bluetooth scan found device Desire HD
(bluetoothscanner_object: ScanFoundDevice - line 91, process 11351)
[...]

Seite 34
Als Logging-Framework wird das Python-Modul logging verwendet. Dieses ist sehr
umfangreich und flexibel.
Die Grundfunktionen dieses Moduls werden in die folgende Bereichen gegliedert:
• Logging-Objekte stellen ihre Interfaces der Anwendung zur Verfügung
• Handler senden die von den Loggern erzeugten Log-Datensätze zum bestimmen
Ziel
• Durch Filter kann festgelegt werden, welche Datensätze angezeigt werden.
• Formatter bestimmen das Layout der Log-Datensätze in der endgültigen Ausgabe.
Es kann auch ein Level für Handler und Logger, ab welchem die Datensätze behandelt
werden sollen, festgelegt werden (liegt ein Datensatz unter dem festgelegten Level wird er
einfach verworfen): Die Level sind unterteilt in DEBUG, INFO, WARNING, ERROR und
CRITICAL (falls diese vordefinierten Level nicht ausreichen, können selber auch noch
welche definiert werden).
Dieses Logging-Framework wird in der Oberfläche durch die Methode createLogger
initialisiert:
def createlogger(self):
# Log Window
self.log_window = LogWindow(None, self)

# Output forwarding
sys.stdout = QtOutput(self.log_window.outputEdit, sys.__stdout__)
sys.stderr = QtOutput(self.log_window.outputEdit, sys.__stderr__, QColor(Qt.red))

# Create logger
self.log_general = logging.getLogger("openmobilesuite")

self.log_gui = logging.LoggerAdapter(self.log_general, {"origin": "GUI"})


self.log_cm = logging.LoggerAdapter(self.log_general, {"origin": "CM"})
self.log_sql = logging.LoggerAdapter(self.log_general, {"origin": "SQL"})
self.log_con = logging.LoggerAdapter(self.log_general, {"origin": "CON"})

# Create standard output handler


self.stdout = logging.StreamHandler(sys.__stderr__)
if self.debug:
self.stdout.setLevel(logging.DEBUG)
elif self.verbose:
self.stdout.setLevel(logging.INFO)
else:
self.stdout.setLevel(logging.WARNING)
self.logging_object.Set(self.logging_level_interface, "log_level", self.stdout.level)

if self.long:
format = logging.Formatter("[%(asctime)s] - %(origin)-4s - %(levelname)-7s - %
(message)s (%(module)s: %(funcName)s - line %(lineno)s, process %(process)s)",
"%d.%m.%Y %H:%M:%S")

self.stdout.setFormatter(format)

# Create log window handler


self.dialogHandler = QtStreamHandler(self.log_window.logEdit, self)

if self.settings.setting("log/long"):
format = logging.Formatter("[%(asctime)s] - %(origin)-4s - %(levelname)-7s - %
(message)s (%(module)s: %(funcName)s - line %(lineno)s, process %(process)s)",
"%d.%m.%Y %H:%M:%S")

else:
format = logging.Formatter("%(origin)-4s - %(message)s")

Seite 35
self.dialogHandler.setFormatter(format)

# Add handlers to logger


self.log_general.addHandler(self.stdout)
self.log_general.addHandler(self.dialogHandler)

self.dialog_filter = LoggingFilter(
self.settings.setting("log/showGuiOrigin"),
self.settings.setting("log/showCmOrigin"),
self.settings.setting("log/showConOrigin"),
self.settings.setting("log/showSqlOrigin"))

self.dialogHandler.addFilter(self.dialog_filter)
self.setLoggingLevel(self.settings.setting("log/level"))
self.log = self.log_gui # Alias...

[aus pc/gui/src/gui.py]

Das Log Window wird vom Designer erzeugt und um Funktionen zum Ändern des Levels
erweitert.
Die Standard-Ausgabe und -Fehlerausgabe wird durch die Klasse QtOutput in das
textWidget des Dialogs geschrieben:
class QtOutput(object):
def __init__(self, parent, out=None, color=None):
self.textWidget = parent
self.out = out
self.color = color

def write(self, m):


self.textWidget.moveCursor(QtGui.QTextCursor.End)

if self.color:
tc = self.textWidget.textColor()
self.textWidget.setTextColor(self.color)

self.textWidget.insertPlainText( m )

if self.color:
self.textWidget.setTextColor(tc)

if self.out:
if isinstance(m, unicode):
self.out.write(m.encode("utf8"))
else:
elf.out.write(m)

[aus pc/gui/src/lib/log.py]

Im nächsten Schritt wird der Logger der Anwendung erzeugt. Die LoggingAdapter werden
benutzt um Kontext-Informationen (in diesem Fall den Ursprung der Informationen)
festzulegen, so dass diese nicht für jede Log-Meldung explizit spezifiziert werden müssen.
Danach wird der Handler für die Ausgabe in die Kommandozeile erstellt und das Level und
das Format dieses Handlers festgelegt (wie es über die Kommandozeilen-Parameter
festgelegt wurde).

Seite 36
Der Handler für die Ausgabe im Dialog ist nicht mehr über einen StreamHandler
realisierbar, sondern es wurde eine eigene Klasse dafür erstellt (von logging.Handler
abgeleitet):
class QtStreamHandler(logging.Handler, QtCore.QObject):
colors = {
"GUI" : QtGui.QColor("#CE5C00"), # Orange
"CM" : QtGui.QColor("#4E9A06"), # Chameleon
"SQL" : QtGui.QColor("#204A87"), # Sky Blue
"CON" : QtGui.QColor("#5C3566"), # Plum
}

def __init__(self, parent, main):


logging.Handler.__init__(self)
QtCore.QObject.__init__(self)
self.parent = parent
self.main = main

self.textWidget = parent

def setFormatter(self, format):


self.formater = format

def createLock(self):
self.mutex = QtCore.QMutex()

def acquire(self):
self.mutex.lock()

def release(self):
self.mutex.unlock()

def emit(self, record):


self.textWidget.moveCursor(QtGui.QTextCursor.End)

tc = self.textWidget.textColor()
self.textWidget.setTextColor(self.colors[record.origin])
self.textWidget.insertPlainText(self.formater.format(record) + '\n')
self.textWidget.setTextColor(tc)
self.textWidget.moveCursor(QtGui.QTextCursor.StartOfLine)
self.textWidget.ensureCursorVisible()

[aus pc/gui/src/lib/log.py]

Abschließend wird noch das Format für den Dialog-Handler festgelegt und ein Filter
erzeugt, der je nach Einstellung nur Meldungen von gewissen Ursprüngen anzeigt.

Seite 37
Die Log-Meldungen des Connection Managers (und daher auch die der Bluetooth-
Verbindungen) werden vom DBus-Objekt Logging gesendet.
Dieses verfügt über die folgenden Methoden und Signale:
net.sourceforge.OpenMobileSuite.ConnectionManager
/Logging
Methoden:
GetAll (Interface: org.freedesktop.DBus.Properties)
in: string interface_name
out: a{sv} properties
Get (Interface: org.freedesktop.DBus.Properties)
in: string interface_name, string property_name
out: variant property
Set (Interface: org.freedesktop.DBus.Properties)
in: string interface_name, string property_name, variant new_value

Signale:
PropertiesChanged (Interface: org.freedesktop.DBus.Properties)
out: a{sv} changed_properties
LogMessage
out: string name, int levelno, string pathname, double created, string origin,
string levelname, string message, string module, string funcName,
int lineNo, int process

Durch die Eigenschaft log_level des Logging-Level-Interface


(net.sourceforge.OpenMobileSuite.ConnectionManager.DebugLevel) kann das Level
festgelegt werden, ab welchem die Meldungen gesendet werden sollen.
Die Log-Meldungen selber werden durch das Signal LogMessage gesendet. Dazu wurde
wieder ein eigener Log-Handler implementiert:
class DbusLogHandler(logging.Handler):
def __init__(self, main):
super(DbusLogHandler, self).__init__()
self.main = main

def emit(self, record):


self.main.logging_service.LogMessage(
record.name,
record.levelno,
record.pathname,
record.created,
record.origin,
record.levelname,
unicode(record.msg),
record.module,
record.funcName,
record.lineno,
record.process
)

[aus pc/connection-manager/src/lib/dbus_log_handler.py]

Seite 38
Dieser ruft die Funktion Log-Message des DBus-Service mit den geforderten Parametern
auf.
Damit die Meldungen nun auch auf der Oberfläche sichtbar sind wird ein Listener
gestartet, der eben auf dieses Signal reagiert und daraus wieder einen Log-Datensatz
erzeugt. Die per DBus übermittelten Argumente werden nun diesem Datensatz zugeordnet
und so wird auch wieder die richtige Zeilennummer bzw. Prozess-ID des Eintrags
verwendet:

class LoggingListener(QtCore.QObject):
def __init__(self, main):
super(LoggingListener, self).__init__(main)

[...]
self.logging_object.connect_to_signal("PropertiesChanged", self.propertiesChanged)
[...]

def log(self, name, levelno, pathname, created, origin, levelname, msg, module,
funcName, lineno, process):
record = logging.makeLogRecord(
{
"name" : unicode(name),
"levelno" : int(levelno),
"pathname" : unicode(pathname),
"created" : float(created),
"origin" : str(origin),
"levelname" : str(levelname),
"msg" : unicode(msg),
"module" : unicode(module),
"funcName" : unicode(funcName),
"lineno" : int(lineno),
"process" : int(process)
}
)

self.main.log_general.handle(record)

[aus pc/gui/src/service/logging_listener.py]

Somit erscheinen auch die Log-Ausgabe des Connection Managers mit dem richtigen
Ursprung in den Meldungen.

Seite 39
3.6. Verbindungsaufbau
Der Prozess des Verbindungsaufbaus wird in den folgenden Seiten für die S60-Plattform
genauer erläutert. Der Java-Prozess für Android verhält sich ähnlich.
Die Benutzeroberfläche (openmobilesuite-gui) ruft dazu die Methode RequestConnection
beim Verbindungsmanager (openmobilesuite-connectionmanager) auf, die in einem
eigenen Prozess (openmobilesuite-connection-oms) eine Verbindung zum Telefon aufbaut.
In der GUI wird der aktuelle Status des Verbindungsaufbaus angezeigt:

Abbildung 3-12: Verbindungsaufbau

Nach der Synchronisation der Systeminformationen werden diese auf der Oberfläche
unter dem Tab „Devices“ (Geräte) angezeigt.
Die angezeigten Informationen bestimmt der Typ des Geräts: Für Android gibt es einige
Felder (wie Netzwerk-Betreiber und SIM-Nummer), die für S60 nicht existieren.

Seite 40
Welche Informationen zur Verfügung stehen wird in den Objekten zu den Geräte-Typen
(unter pc/base/src/device) gespeichert.
Als Beispiel dazu ist hier die Definition der S60-Klasse:
from PyQt4 import QtCore
from .generic import Generic
from ..element.device_sysinfo_key import StringKey, IntegerKey, SizeKey,
TelephoneCategory, MemoryCategory, SystemCategory

class Symbian(Generic):
name = "symbian"
has_been_configured = True

def sysinfo(self):
return Generic.sysinfo(self) + [
StringKey(False, True, TelephoneCategory, "model", "Model type"),
IntegerKey(False, True, TelephoneCategory, "battery", "Battery level"), "", False,
"%", 100),
IntegerKey(False, True, TelephoneCategory, "signal_strength", "Signal strength", "",
True, "dbM", 7),
StringKey(False, True, TelephoneCategory, "active_profile", "Active profile"),
SizeKey(False, False, MemoryCategory, "free_ram", "Free RAM"),
SizeKey(False, False, MemoryCategory, "total_rom", "Total ROM"),
StringKey(True, False, SystemCategory, "display", "Display pixels", "", False,
"pixels"),
StringKey(True, False, SystemCategory, "imei", "IMEI"),
StringKey(False, False, SystemCategory, "program_version", "Application version"),
StringKey(False, False, SystemCategory, "pys60_version", "PyS60 version"),
StringKey(False, False, SystemCategory, "s60_version", "S60 SDK version"),
]

[aus pc/base/src/symbian.py]

Seite 41
Aus diesen Informationen wird dynamisch das SysinfoWidget erzeugt, welches im Geräte-
Tab der Oberfläche angezeigt wird (siehe Abbildung 3-13: Geräteübersicht).

Abbildung 3-13: Geräteübersicht

Die Kontakte und Kalender-Einträge werden asynchron in die jeweiligen Modelle


eingefügt.
Der genaue Prozess des Verbindungsaufbau wird in dem Diagramm auf der folgenden
Seite veranschaulicht.

Seite 42
Abbildung 3-14: Diagramm des Verbindungsaufbaus

Seite 43
3.6.1. Beispiel: PyS60
Beim Start der Server-Anwendung auf dem Mobiltelefon wird ein Bluetooth-Socket
geöffnet, welches auf den Port 18 lauscht und Anfragen von allen Geräten annimmt. Die
Flags AUTH (authentication) und AUTHOR (authorization) werden gesetzt, damit ein
Verbindungsaufbau bestätigt werden muss. Es wird auch das Advertising des Services
eingeschaltet, damit es über SDP (Service Disvocery Procovol) auffindbar ist.
Danach wird auf eine eingehende Verbindung gewartet.
PORT = 18
[...]
self.sock = socket.socket(socket.AF_BT, socket.SOCK_STREAM)
self.sock.bind(('', PORT))
self.sock.listen(1)

socket.set_security(self.sock, socket.AUTH | socket.AUTHOR)


socket.bt_advertise_service(u"pys60_remote", self.sock, True, socket.RFCOMM)

[...]

while self.service:
self.client = self.sock.accept()

[aus mobile/s60/mobile.py]

Auf der PC-Seite versucht nun die Client-Anwendung, eine Verbindung zu dem
Mobiltelefon herzustellen. Für die Kommunikation wird das RFCOMM (Radio Frequency
Communication, ein Bluetooth-Protokoll, das serielle Schnittstellen emuliert)-Protokoll
verwendet.
Es muss eine Verbindung zur Bluetooth-Adresse des Telefons auf Port 18 hergestellt
werden.

try:
self.sock = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
self.sock.connect((self.parent.device().bluetoothAddress(),
self.parent.device().port()))
[...]
while True:
try:
recv = unicode(self.sock.recv(1000), "utf8")
[...]

[aus pc/connection-oms/src/plugin/OpenMobileSuite.py]

Seite 44
Nachdem das Mobiltelefon den Verbindungsaufbau akzeptiert hat, werden File-Objekte
erzeugt, die ein einfaches Lesen und Schreiben auf den Socket ermöglichen. Dem PC
wird eine Meldung über den erfolgreichen Verbindungsaufbau gesendet, die auch die
aktuelle Protokoll-Version der Anwendung enthält.
self.client = self.sock.accept()
[...]
self.fos = self.client[0].makefile("w")
self.fis = self.client[0].makefile("r")

self.send(NUM_CONNECTED, PROTOCOL_VERSION)
[...]
while(True):
try:
data = self.fis.readline()
except:
[...]

[aus mobile/s60/mobile.py]

Der PC vergleicht nun diese Version mit der eigenen und fährt mit dem
Verbindungsaufbau nur fort, wenn beide Nummern übereinstimmen.
elif header == NUM_CONNECTED:
if not float(message) == PROTOCOL_VERSION:
self.emit(SIGNAL("connectionVersionMismatchError"), float(message),
PROTOCOL_VERSION)
return

[aus pc/connection-oms/src/plugin/OpenMobileSuite.py]

Der eigentliche Verbindungsaufbau ist nun abgeschlossen und es können Daten


übermittelt werden. Begonnen wird mit der Synchronisation der Geräte.

Geräteinformationen
Dazu fragt der PC zuerst das Mobiltelefon nach Systeminformationen, wie etwa den Akku-
Ladezustand oder die Signalstärke. Es wird jeweils ein Wert pro Nachricht gesendet , der
Header dafür ist NUM_SYSINFO_REPLY_LINE. Sind keine weiteren Informationen
verfügbar wird abschließend eine Nachricht mit dem Header
NUM_SYSINFO_REPLY_END gesendet.

Seite 45
Kontakte
Auf die Kontakt-Datenbank kann durch das PyS60-Modul contacts zugegriffen werden.
Zum Öffnen der Standard-Datenbank reicht es, die Methode open ohne Parameter
aufzurufen. Es wird ein ContactDb-Objekt zurückgegeben, in welchem die einzelnen
Kontakte durch einen Generator ausgelesen werden können. Durch die Eigenschaften id
und title können eine eindeutige ID und der Name des Contact-Objektes ausgelesen
werden. Auf die einzelnen Felder (ContactField) kann wieder durch einen Generator
zugegriffen werden. In diesen Feldern ist der Typ (z.B. last_name für den Nachnamen,
phone_number für die Telefonnummer oder email_address für eine E-Mail-Adresse), der
Wert dieses Feldes und ein Ort (home, work oder none) gespeichert.
import contacts
[...]
self.contactDb = contacts.open()
[...]
keys = self.contactDb.keys()

contactDict = dict()
for key in keys:
contact = self.contactDb[key]
[...]
contactDict[contact.id] = list()
for field in contact:
_type = field.type
value = field.value
value = unicode(value)
value = value.replace(u'\u2029', u'\n') # PARAGRAPH SEPARATOR (\u2029) replaced by
LINE FEED (\u000a)
location = field.location

if _type == "unknown":
continue
elif _type == "thumbnail_image":
value = self.getContactThumbnail(contact)
if not value:
continue
elif _type == "date":
value = self.getContactBirthday(contact)

if isinstance(value, type(None)):
# Ignore this field
continue

contactDict[contact.id].append((_type, location, value))


contactDict[contact.id].sort()
[...]

[aus mobile/s60/mobile.py]

Seite 46
Auf das Vorschaubild des Kontakts kann nicht direkt zugegriffen werden, da der Wert
dieses Feldes immer leer ist. Auch beim Zugriff auf den Geburtstag gibt es Probleme, falls
dieser vor Beginn der UNIX-Epoche (1. Januar 1970 00:00 Uhr UTC) liegt. In diesen
beiden Fällen ist ein Workaround notwendig, der das Contact-Field-Objekt als VCard
exportiert und so auf den Wert zugreift.

def getDetailFromVcard(self, contact, detail, delimiter='\r\n'):


# This is an ugly hack, needed for some fields that cannot be handled using the
contact object
try:
value = unicode(contact.as_vcard(), 'utf8')
value = value.split(detail + ":")[1].split(delimiter)[0]
return value
except:
return
[...]
def getContactThumbnail(self, contact):
# Ugly workaround!
# HACK: The value of type "thumbnail_image" is empty, it is only shown when we export
# the contact to a vCard
image = self.getDetailFromVcard(contact, "PHOTO;TYPE=JPEG;ENCODING=BASE64",
"\r\n\r\n")
if image:
image = image.split("\r\n\r\n")[0]
image = image.replace("\r", "").replace("\n", "").replace(" ", "")
return image
return
[...]
def getContactBirthday(self, contact):
return self.getDetailFromVcard(contact, "BDAY")

[aus mobile/s60/mobile.py]

Das Vorschaubild wird in der Funktion getContactThumbnail aus der vCard geholt. Es ist
im JPEG-Format und durch base64 (Kodierung in eine Zeichenfolge, die nur aus
ASCII-Zeichen besteht) kodiert. Es müssen auch noch alle Whitespaces (\r, \n und
Leerzeichen) entfernt werden.
Nachdem alle Kontakte mit sämtlichen Feldern in einem Dictionary gespeichert wurden
wird erstmal ein MD5-Hash aller Felder gebildet und an den PC gesendet. Dort wird auch
ein Hash über alle Einträge gebildet, die sich zur Zeit in dessen Datenbank befinden.
Stimmt dieser Wert überein, ist die Synchronisation der Kontakte abgeschlossen.
Gibt es einen Unterschied, wird vom Mobiltelefon eine Liste sämtlicher Kontakt-IDs mit
ihren zugehörigen Hash-Werten angefordert. Die PC-Anwendung kann nun überprüfen, ob
am Mobiltelefon ein Kontakt hinzugefügt (ID ist in der PC-Datenbank noch nicht
vorhanden), geändert (Hash stimmt nicht überein) oder gelöscht (ID ist in der PC-
Datenbank, jedoch nicht in der Liste, die das Mobiltelefon gesendet hat) wurde. Falls es
nur gelöschte Kontakte gibt, werden diese aus der Datenbank entfernt und die
Synchronisation ist abgeschlossen.
Hinzugefügte oder geänderte Kontakte werden vom Mobiltelefon angefordert (siehe
Kapitel Kontakte).
Die Grafik auf der nächsten Seite sollte diesen Prozess verdeutlichen.

Seite 47
User interface

<Contact #258: "Normalverbraucher Otto">


<field #0: type=phone_number value=+4372321234 location=home>
<field #1>: type=last_name value=Normalverbraucher location=none>
<field #2>: type=email_address value=otto@example.com location=none>
<field #3>: type=first_name value=Otto location=none> Contact
<field #4>: type=thumbnail_image value= location=none>
<field #5>: type=mobile_number value=+436641234567 location=none>
<Contact #259: "Mustermann Martha">
objects
<field #0>: type=last_name value=Mustermann location=none>
<field #1>: type=email_address value=martha@example.com location=none>
<field #2>: type=first_name value=Martha location=none>
<field #3>: type=mobile_number value=+436767654321 location=none>
<field #4: type=phone_number value=+4370324321 location=work>

{258: [('email_address', 'none', u'otto@example.com'),


('first_name', 'none', u'Otto'),
('last_name', 'none', u'Normalverbraucher'),
('mobile_number', 'none', u'+436641234567'),
Python
('phone_number', 'home', u'+4372321234'),
('thumbnail_image', 'none', u'/9j/4AAQSkZJR...ikM//Z')], contact
259: [('email_address', 'none', u'martha@example.com'),
('first_name', 'none', u'Martha'),
('last_name', 'none', u'Mustermann'), dictionary
('mobile_number', 'none', u'+436767654321'),
('phone_number', 'work', u'+4370324321')]}

u'email_address\x1dnone\x1dotto@example.com\x1efirst_name\x1dnone\x1dOtto{…
}\x1e\x1femail_address\x1dnone\x1dmartha@example.com{…}phone_number\x1d MD5 hash of all
work\x1d+4370324321\x1e\x1f'
= c0b0364f7ca275603dd7f0b3a4f7026a contacts

u'email_address\x1dnone\x1dotto@example.com\x1efirst_name\x1dnone\x1dOtto{…
}\x1e' = d8fdc493884f03dcd1d6d7db9f44e417
MD5 hash for each
u'email_address\x1dnone\x1dmartha@example.com{…}phone_number\x1dwork\x1d
+4370324321\x1e\x1f' contact
= e3695bf258d70f6b0318793e9f01d1e2

Seite 48
Kalender
Die Synchronisation der Kalendereinträge erfolgt ähnlich wie die Kontakt-Synchronisation.
Es wird wieder erstmals ein MD5-Hash über die Einträge gebildet und dieser zum PC
gesendet. Sollte dieser nicht übereinstimmen werden alle Kalender-Einträge gesendet um
die PC-Datenbank aktualisieren zu können.

3.6.2. Beispiel: Android


Zum Verständnis dieses Kapitels sollte der Verbindungsaufbau mit einem S60-Telefon
bereits gelesen worden sein, da hier nur der Teil der Android-Anwendung, jedoch nicht
mehr der des Connection Managers beschrieben wird.
Hier folgt die Beschreibung der wichtigsten Funktionen der Implementierung in Java:
Die Server-Anwendung ist hier als Service realisiert, da dieses nicht – im Gegensatz zu
normalen Programmen – jederzeit beendet werden kann.
public class BluetoothService extends Service {
[...]
public void onStart(Intent intent, int startId) {
serverWorker = new BluetoothServiceThread(getApplicationContext());
serverWorker.start();

super.onStart(intent, startId);
}
[...]

[aus mobile/android/src/com/hetzenecker/openmobilesuite/service/
BluetoothServiceThread.java]

Dieses Service startet einen neuen Thread, in dem das Bluetooth-Socket initialisiert wird.
Danach wird auf eine Verbindungsanfrage gewartet.

Seite 49
Sollte sich eine Anwendung verbinden, wird diese wieder in einem eigenen Thread – dem
Bluetooth-Client-Thread – bearbeitet.

public class BluetoothServiceThread extends Thread {


private final BluetoothServerSocket mBluetoothServerSocket;
private final BluetoothAdapter mBluetoothAdapter =
BluetoothAdapter.getDefaultAdapter();
private final UUID uuid = UUID.fromString("A060C43C-913B-E4EB-CB81-34486401B52F");

public BluetoothServiceThread(Context context) {


mContext = context;
BluetoothServerSocket serverSocket = null;
serverSocket =
mBluetoothAdapter.listenUsingRfcommWithServiceRecord("pys60_remote", uuid);
mBluetoothServerSocket = serverSocket;
}

@Override
public void run() {
BluetoothSocket bluetoothSocket = null;

try {
while (true) {
bluetoothSocket = mBluetoothServerSocket.accept();
if (bluetoothSocket != null) {
new BluetoothClientThread(bluetoothSocket, mContext).start();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

[aus mobile/android/src/com/hetzenecker/openmobilesuite/service/
BluetoothService.java]

Seite 50
Im Client-Thread erfolgt die Behandlung der Kommandos des PC (in der Methode
handleCommand).

class BluetoothClientThread extends Thread {


private final Context mContext;
private SysinfoManager mSysinfoManager;
private ContactManager mContactManager;
private MessageManager mMessageManager;

[...]

@Override
public void run() {
mSysinfoManager = new SysinfoManager(this, mContext);
mContactManager = new ContactManager(this, mContext);
mMessageManager = new MessageManager(this, mContext);

byte[] buffer = new byte[1024];


int bytes;

send(Statuscodes.NUM_CONNECTED, String.valueOf(Statuscodes.PROTOCOL_VERSION));

String data = "";


String recv;
while (true) {
try {
// Read from the InputStream
bytes = inputStream.read(buffer);
recv = new String(buffer, 0, bytes);
[...]

} catch (IOException e) {
connectionLost();
break;
}
}
}
[...]
}

[aus mobile/android/src/com/hetzenecker/openmobilesuite/service/
BluetoothClientThread.java]

Im Gegensatz zur S60-Implementierung werden für die Teilbereiche einzelne Klassen (wie
SysinfoManager, ContactManager und MessageManager) verwendet. Dies hat den Grund,
das die Implementierung für Android um einiges aufwendiger ist. Für den Zugriff auf die
Kontakt-Datenbank gibt es keine einfache API, sondern man muss mit einem Content
Resolver arbeiten.

Seite 51
3.7. Geräteverwaltung
3.7.1. Connection Manager
Das DBus-Objekt Devices exportiert die Methoden DeviceCount, die die Anzahl der in der
Datenbank gespeicherten Geräte zurückgibt und Devices, die die Geräte selbst als Array
liefert.
Es gibt außerdem noch das Objekt BluetoothScanner, welches asynchron einen Geräte-
Scan durchführt. Dieses hat die Funktionen StartScan und StopScan zum Initialisieren und
Stoppen eines Bluetooth-Scans, die Funktion FoundDevices, welche alle gefundenen
Geräte zurückgibt und die Signale ScanStarted, ScanCompleted, ScanFailed und
ScanFoundDevice, welche über den aktuellen Stand des Scans Bescheid geben.

3.7.2. GUI
In der grafischen Oberfläche werden beim Start der Anwendung alle in der Datenbank
verfügbaren Geräte geladen. Diese werden in der devices-Variable der Main-Klasse
gespeichert und stehen so der gesamten Anwendung zur Verfügung.

Abbildung 3-15: Model/View-Klassen der Geräteverwaltung

Seite 52
Zur Anzeige der Geräte wurden Widgets erstellt, die
eine einfache Darstellung in einer Liste oder einer
Combobox ermöglichen.
Das Widget DeviceScanWidget weist den
Connection Manager an, einen Scan zu starten und
fügt die gefundenen Geräte asynchron zu seinem
Modell hinzu.
Beim Auftreten eines Fehlers scheint das auch in
einem Element der Liste auf (siehe Abbildung 3-17:
DeviceScanWidget mit Fehlermeldung).
Durch Rechtsklick auf ein Gerät erscheint ein
Menü, das Aktionen zum Senden der
Installationsdatei der Server-Anwendung für
Android und S60 enthält.
Das DeviceViewWidget verfügt über die Methoden
addDevice, addDevices und removeDevice zum
Anzeigen bereits verfügbarer Geräte (z.B. die in der
Main-Klasse gespeicherten Geräte der Datenbank).
Abbildung 3-16: DeviceScanWidget
Durch die Klasse DeviceDatabaseViewCombobox
mit Gerät
ist die Anzeige aller in der Datenbank ge-
speicherten Geräte in einer Combobox möglich.
Das abstrakte Modell der Daten, das später in der
Liste dargestellt wird, wird in allen Widgets in einem
_DeviceModel (bzw. den davon abgeleiteten
DeviceScanModel und DeviceViewModel) ge-
speichert.
In diesem wird auch der passende Geräte-Typ und
das Icon zur Bluetooth-Geräteklasse festgelegt
(Methode: classToTuple).
Das Icon wird durch die Rolle Qt.DecorationRole
bereitgestellt und wird darum auch in der
Combobox angezeigt.
Das Zeichnen der Elemente wird von einem
DeviceDelegate übernommen. (siehe Kapitel 2.3.2
Model/View-Architektur, Seite 16).

Abbildung 3-17: DeviceScanWidget


mit Fehlermeldung

Seite 53
Diese Widgets werden im First Run – Wizard und im Einstellungsdialog verwendet:

Abbildung 3-18: Einstellungsdialog

Seite 54
3.8. Kontaktverwaltung
3.8.1. Connection Manager
Die Kontakte werden in einer Datenbank des Connection Managers verwaltet. Dieser stellt
Methoden zum Abrufen der Kontaktliste bereit, aktualisiert sie beim Verbindungsaufbau
und sendet Signale bei Änderungen (beim Hinzufügen neuer Kontakte und beim Ändern
oder Löschen bestehender Kontakte).
Das Dbus-Objekt Contacts hat dabei folgende Methoden und Signale:
net.sourceforge.OpenMobileSuite.ConnectionManager
/Contacts
Methoden:
Contact
in: int id
out: a{sv} contact
Contacts
in: bool only_contacts_from_phone
out: aa{sv} contacts
ContactCount
in: bool count_only_contacts_from_phone
out: int count
ContactHash
out: string hash
ContactHashSingle
out: a{is} hashes

Signale:
ContactAdded
out: a{sv} contact
ContactChanged
out: a{sv} contact
ContactDeleted
out: int contact_id

Seite 55
3.8.2. Kontaktliste der grafischen Oberfläche
Beim Start der grafischen Oberfläche
werden alle in der Datenbank
gespeicherten Mobiltelefone geladen.
Diese Objekte vom Typ Device haben
in der GUI eine weitere Eigenschaft
contacts_model. Das ist eine Referenz
zu einem ContactModel, das auf der
Qt-Klasse QAbstractListModel (siehe
Kapitel 2.3.2 Model/View-Architektur,
Seite 16) basiert. Diese Eigenschaft ist
standardmäßig auf None gesetzt. Erst
wenn das Gerät ausgewählt wird,
werden die Kontakte dazu angefordert.
Das Widget ContactListView (basierend
auf einer QListView) dient zum Dar-
stellen der Kontaktliste. Das Zeichnen
der einzelnen Elemente in dieser Liste
Abbildung 3-19: Kontaktliste wird jedoch einem Delegate über-
lassen. Das Erscheinungsbild kann
dabei über die Methoden setThumbnailSize (Ändern der Größe der Thumbnail-Icons der
Kontakte), setRoundedRectThumbnail bzw. setCircleThumbnail (Thumbnail in
rechteckigen oder runden Rahmen) und showMobile, showPhone und showMail
(Anzeigen bzw. Ausblenden gewisser Eigenschaften) angepasst werden.
Möchte man nun die Kontakte eines Telefons in der ContactListView anzeigen lassen,
muss man der Klasse nur das Quell-Modell übergeben.
Weiters gibt es noch eine Klasse zum Filtern der Kontakte (ContactProxy, basierend auf
einem QSortFilterProxyModel). Damit lassen sich beispielsweise – wie im unteren Beispiel
aus dem Mitteilungs-Fenster der Oberfläche – nur Kontakte anzeigen, die über eine
Mobiltelefon-Nummer verfügen.
[...]
device = self.parent.device_widget.device_box.currentDevice()
if device.contacts_model is not None:
self.source_model = device.contacts_model
else:
self.source_model = ContactModel(device)
self.proxy_model = ContactProxy(self)
self.proxy_model.setSourceModel(self.source_model)
self.proxy_model.setDynamicSortFilter(True)
self.proxy_model.setFilter("mobile_number")
self.proxy_model.setSearchTypes([SearchType.Name, "mobile_number"])
self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.proxy_model.sort(0)
self.contacts_view.setModel(self.proxy_model)

self.contacts_view.delegate.setSmallThumbnail()
self.contacts_view.delegate.showMobile(True)
self.contacts_view.delegate.showPhone(False)
self.contacts_view.delegate.showMail(False)
[...]

[aus pc/gui/src/window/mainwindow/messages.py]

Seite 56
Hier wird ein neues ContactModel für ein Gerät erstellt, falls dieses noch nicht existiert.
Dieses Modell sendet automatisch eine Anfrage an den Connection Manager, der die, in
der Datenbank gespeicherten Kontakte liefert. Es überwacht auch selber Änderungen,
indem es auf Signale des Connection Managers horcht.
Es wird auch ein ContactProxy erstellt, der dafür sorgt, dass nur Kontakte mit
Mobiltelefonnummern angezeigt werden. Die Suche wird auf den Namen und die
Mobiltelefonnummer beschränkt.
Für das Delegate werden auch Anweisungen zum Zeichnen der Elemente gegeben, die
jedoch über das Options-Menü überschrieben werden können.

Die Suche auf der Oberfläche könnte beispielsweise so aussehen:

Abbildung 3-20: Suche in der Kontaktansicht

Seite 57
3.8.3. Anlegen, Bearbeiten und Löschen von Kontakten
Wie man im vorherigen Screenshot (Abbildung 3-20: Suche in der Kontaktansicht)
erkennen kann werden unter den Kontakt-Details Buttons zum Anzeigen und Ändern von
Kontakten angezeigt.
Das Löschen eines Kontakts ist durch Rechtsklick möglich.
Der Dialog zum Anlegen und Bearbeiten von Kontakten basiert auf der gleichen Klasse, es
wird nur der Text in der Titelleiste und das Icon geändert.
Gründsätzlich werden die Kontakt-Details in 3 Gruppen (Locations) eingeteilt: Allgemein,
Privat und Geschäftlich. Für Kontakte können auch noch Notizen angelegt werden.

Abbildung 3-21: Bearbeitung eines Kontakts

Seite 58
3.9. Kalenderverwaltung
Neben den Kontakten werden beim Verbindungsaufbau auch die Kalender-Einträge
synchronisiert.
Diese werden auf der Oberfläche unter dem Punkt „Kalender“ angezeigt.

Abbildung 3-22: Ansicht der Kalender-Einträge

Als Vorbild des Erscheinungsbildes wurde das Programm „Kontact“ aus der KDE PIM-
Suite verwendet. Es wurden daraus einige Teile entnommen und von C++ nach Python
übersetzt.
Dafür wurde das „Graphics
View Framework“ verwendet.
Dieses wird einem QPainter
beim Darstellen vieler be-
nutzerdefinierter Widgets und
Elemente bevorzugt.
Die Architektur besteht aus
einer Szene (die von der
Klasse QGraphicsScene
dargestellt wird), und den
darauf platzierten Elementen
Abbildung 3-23: Model/View-Klassen der Kalender-
(die von einem QGraphicsItem
verwaltung
abgeleitet sind). Dies wird dem
Benutzer in einer Ansicht (einer QGraphicsView) sichtbar gemacht.

Seite 59
In meinem Beispiel kümmert sich die CalendarGraphicsView um das Zeichnen des
Hintergrunds (Tabellenstruktur, Überschriften,.. ). Die einzelnen Einträge des Kalenders
werden stattdessen durch CalendarGraphicItems dargestellt. Pro Eintrag kann es davon
mehrere geben, da pro Zeile (Woche) im Kalender ein eigenes Item angelegt werden
muss.

Im nächsten Screenshot (Abbildung 3-24: Bearbeitung von Kalender-Einträgen) ist der


Dialog zum Anlegen und Bearbeiten von Einträgen ersichtlich. Dieser kann durch
Doppelklick oder Rechtsklick auf einen bereits bestehenden Eintrag oder eine freie Fläche
aufgerufen werden.
Der Titel ist das einzige notwendige Feld, optional kann man dazu noch einen Ort
eingeben. Der Start- und Endzeitpunkt ist bei Ereignissen und Geburtstagen wichtig, bei
Aufgaben kann er weggelassen werden.
Danach kann auch noch eine Wiederholung des Ereignisses und ein Alarm definiert
werden.
Für die einzelnen Einträge kann man die Priorität anpassen und den Zugriff ändern.

Abbildung 3-24: Bearbeitung von Kalender-Einträgen

Seite 60
3.10. Dateiverwaltung
Im Connection Manager gibt es eine spezielle Art einer Bluetooth-Verbindung: Durch das
Objekt ObexConnections kann eine Verbindung zu dem OBEX-Service eines Bluetooth-
Gerätes hergestellt werden. Dadurch kann auf dessen Dateien zugegriffen werden und
diese – ähnlich wie bei FTP (File Transfer Protocol) – heruntergeladen und hochgeladen
werden.
Das ist vom Typ des Gerätes unabhängig (es kann sich sowohl um ein Mobiltelefon als
auch um einen Computer handeln).
Im Hintergrund wird dafür die Bibliothek PyOBEX, oder falls diese nicht installiert ist,
Obexftp verwendet. Diese bieten eine sehr einfache API zur Verbindung. Daher wird das
dem zu Grunde liegende OBEX-Protokoll hier nicht genauer beschrieben.
Anmerkung: Im Rahmen der Diplomarbeit wurde in Zusammenarbeit mit David Boddie,
dem Entwickler von PyOBEX, ein Patch entwickelt, der einen Fehler in der
Implementierung des Moduls behob. Dieser lag in der Behandlung der Connection ID,
die von PyOBEX ignoriert wurde und daher zu Fehlern beim Anzeigen des
Ordnerinhalts führt.
Dadurch konnte ich umfassendere Erfahrung mit dem OBEX-Protokoll sammeln. Zum
Mitlesen des Bluetooth-Traffics wurde das Tool hcidump verwendet.
Da die Antworten der Auflistung des Ordnerinhaltes im XML-Format vorliegen, wurde ein
XML-Parser des Moduls QtXml verwendet.

Seite 61
Die Dateiverwaltung basiert auch auf auf dem Model/View-Ansatz. Dadurch ist es möglich,
die Daten von einem Modell in mehreren Views darzustellen. Wie im Beispiel unten
ersichtlich, ist ein Teil davon (die linke Ordneransicht) in einer QListView, während rechts
daneben der Ordnerinhalt in einer QTableView angezeigt wird.

Abbildung 3-25: Dateiverwaltung

Seite 62
3.11. Mitteilungsverwaltung
3.11.1. Senden von Mitteilungen
Ein zentraler Punkt der Anwendung stellt das Senden von Kurzmitteilungen dar.
In der GUI ist dies durch mehrere Wege möglich. Einerseits gibt es im Hauptfenster durch
den Tab „Mitteilungen“ eine Möglichkeit, um direkt eine SMS – auch an mehrere
Kontakte – verschicken zu können:

Abbildung 3-26: Senden einer Kurzmitteilung

Andererseits kann man durch Rechtsklick auf einen Kontakt ein Chat-Fenster öffnen.
Dieses zeigt einen Teil des Verlaufs und man kann mit dem Kontakt wie in einer üblichen
Instant Messaging-Anwendung schreiben:

Seite 63
Abbildung 3-27: Theme "Fresh" des Chat-Dialogs
Dieser Dialog kann dabei durch eine Vielzahl von Themes – die zu dem Instant-
Messaging-Programm Kopete und daher auch zu Adium kompatibel sind - angepasst
werden.
Eine kurze Übersicht ist hier abgebildet:

Abbildung 3-28: Theme "Perfect Pushpin" Abbildung 3-29: Theme "Stock"

Abbildung 3-30: Theme "Clear" Abbildung 3-31: Theme "Glossyk"

Seite 64
Das gewünschte Design kann dabei im Einstellungs-Dialog ausgewählt werden:

Abbildung 3-32: Theme-Auswahl im Einstellungsdialog

Für das Senden der Mitteilung wird ein neues Message-Objekt angelegt, welches an den
Connection Manager weitergeleitet wird. Dieser speichert die Mitteilung in seiner
Datenbank und schickt zu danach zum OMS-Programm weiter. Diese sendet über
Bluetooth eine Anfrage zum Senden (NUM_MESSAGE_SEND_REQUEST) an das
Mobiltelefon. Dieses versucht die SMS an den Empfänger zu senden und meldet die dabei
auftretenden Statusmeldungen (siehe Kapitel 3.11.3 Mitteilungs-Status, Seite 69) dem PC
zurück. Wurde die Mitteilung erfolgreich versandt, wird die Meldung
NUM_MESSAGE_SEND_REPLY_OK mit der ID der Mitteilung an den PC gesendet. Falls
beim Sendevorgang ein Fehler auftritt, wird stattdessen die Meldung
NUM_MESSAGE_SEND_REPLY_FAILURE mit ID und Fehlertext geschickt.
def sendMessage(self, name, phone, encoding, msg):
try:
messaging.sms_send(phone, msg, encoding, self.sentMessage, name)
except RuntimeError, detail:
if str(detail) == "Already sending":
# Workaround for the "Already sending" bug:
# http://discussion.forum.nokia.com/forum/showthread.php?t=141083
messaging._sending = False
self.send(NUM_MESSAGE_SEND_REPLY_RETRY, str(detail) + "; tried workaround")
else:
self.send(NUM_MESSAGE_SEND_REPLY_RETRY, detail)

def sentMessage(self, status):


if status == messaging.ECreated:
self.send(NUM_MESSAGE_SEND_REPLY_STATUS, "Message created.")
[...]
elif status == messaging.EDeleted:
self.send(NUM_MESSAGE_SEND_REPLY_OK,
"The SMS message has been deleted from device's outbox queue.")
elif status == messaging.EScheduleFailed:
self.send(NUM_MESSAGE_SEND_REPLY_FAILURE, "Schedule failed.")
[...]

[aus mobile/s60/mobile.py]

Seite 65
Das Senden wird durch die Methode sms_send des Moduls messaging ermöglicht. Im
einfachsten Fall reicht als Parameter die Telefonnummer und die Mitteilung. Im oberen
Beispiel wird noch die Koderierung der Mitteilung (normalerweise 7bit), ein Callback,
welcher bei Statusänderungen aufgerufen wird und der Name des Kontaktes angeben.
Im Callback (sentMessage) wird der aktuelle Status (Erfolg, Fehler oder normale
Statusmeldung) an den PC geschickt.
Bei einem Android-Telefon funktioniert dieser Vorgang ähnlich:
public void sendMessage(String address, String message) {
final String SMS_ADDRESS_PARAM = "SMS_ADDRESS_PARAM";
final String SMS_DELIVERY_MSG_PARAM = "SMS_DELIVERY_MSG_PARAM";
final String SMS_SENT_ACTION =
"com.hetzenecker.openmobilesuite.service.Messagemanager.SMS_SENT";

ContentValues values = new ContentValues();


values.put("address", address);
values.put("date", System.currentTimeMillis());
values.put("read", 1);
values.put("status", -1);
values.put("type", 2); // sent
values.put("body", message);
mContext.getContentResolver().insert(Uri.parse("content://sms"), values);

ArrayList<String> messages = mSmsManager.divideMessage(message);

ArrayList<PendingIntent> listOfIntents = new ArrayList<PendingIntent>();


for (int i = 0; i < messages.size(); i++) {
Intent sentIntent = new Intent(SMS_SENT_ACTION);
sentIntent.putExtra(SMS_ADDRESS_PARAM, address);
sentIntent.putExtra(SMS_DELIVERY_MSG_PARAM, (messages.size() > 1) ?
"Part " + i + " of Message " : "Message ");
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, sentIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
listOfIntents.add(pi);
}

// Method onReceive is called when SMS has been sent


mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
switch (getResultCode()) {
case Activity.RESULT_OK:
Bundle b = intent.getExtras();
mClientThread.send(Statuscodes.NUM_MESSAGE_SEND_REPLY_OK,
b.getString(SMS_DELIVERY_MSG_PARAM) + "sent");
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
mClientThread.send(Statuscodes.NUM_MESSAGE_SEND_REPLY_FAILURE, "Generic failure");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
mClientThread.send(Statuscodes.NUM_MESSAGE_SEND_REPLY_FAILURE, "No service");
break;
[...]
}
}
}, new IntentFilter(SMS_SENT_ACTION));

mSmsManager.sendMultipartTextMessage(address, null, messages, listOfIntents, null);


}

[aus
mobile/android/src/com/hetzenecker/openmobilesuite/service/MessageManager.java]

Seite 66
Hier muss die Mitteilung auch (anders als in der Implementierung für S60) in die
Datenbank geschrieben werden. Dazu wird über den ContentResolver ein Insert in der
Datenbank ausgeführt.
Danach wird die SMS in mehrere Fragmente aufgeteilt, von denen keines größer als die
maximale SMS-Größe ist. Wenn ein Fragment davon erfolgreich versandt wurde wird ein
Signal ausgelöst, welches im nächsten Programm-Block behandelt wird. Hier wird der
aktuelle Status – Erfolg oder Fehler – an den PC gemeldet.

3.11.2. Empfangen von Mitteilungen


Zum Empfang der Mitteilungen wird ein Callback registriert, welches aufgerufen wird wenn
eine SMS eintrifft:
Hier wird das am Beispiel S60 demonstriert:
def __init__(self):
[...]
self.inbox.bind(self.newMessage)
def newMessage(self, sms):
if not self.connected:
return

id = sms
time = self.inbox.time(sms)
address = self.inbox.address(sms)
content = self.inbox.content(sms)

self.send(NUM_MESSAGE_NEW, id, time, address, content)

[aus mobile/s60/mobile.py]

Seite 67
Die gleiche Funktionalität wird unter Android durch diesen Code erfüllt:
public class MessageManager {

public SmsManager mSmsManager;


private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null)
return;

Object[] pdus = (Object[]) extras.get("pdus");

for (int i = 0; i < pdus.length; i++) {


SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = message.getOriginatingAddress();
String body = message.getMessageBody().toString();

mClientThread.send(Statuscodes.NUM_MESSAGE_NEW, "0",
String.valueOf(message.getTimestampMillis()), sender, body);
}
}
};

public MessageManager(BluetoothClientThread clientThread, Context context) {


[...]
mSmsManager = SmsManager.getDefault();
mContext.registerReceiver(mMessageReceiver,
new
IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
}
}

[aus
mobile/android/src/com/hetzenecker/openmobilesuite/service/MessageManager.java]

In beiden Beispielen wird im Callback die Telefonnummer des Senders, der Text der
Nachricht und die Uhrzeit des Empfangs geholt. Diese Informationen werden durch den
Header NUM_MESSAGE_NEW an den PC geschickt.
Der PC zeigt – wie im unteren Screenshot (Abbildung 3-33: Mitteilungs-Status)
ersichtlich – ein Popup-Fenster an, falls für den Kontakt der Mitteilung noch kein Chat-
Fenster offen ist.

Seite 68
3.11.3. Mitteilungs-Status

Abbildung 3-33: Mitteilungs-Status

Im oberen Fenster kann man auch die Status-Meldungen der Mitteilungen erkennen. Falls
eine SMS erfolgreich versendet werden konnte, wird dies durch ein grünes Häkchen
deutlich gemacht.

3.11.4. Verlauf
Vom Connection Manager kann
auch der gesamte Verlauf
abgefragt werden.
Dabei ist eine Filterung nach
Kontakt und Mitteilungs-Typ
möglich.
Nach einem Klick auf „Mitteilungen
suchen“ erscheint am unteren
Rand eine Suchleiste. Die
Suchergebnisse werden farblich
hervorgehoben und in der
Datumsanzeige sind nur noch
diejenigen Tage vorhanden, an
dem eine Mitteilung gefunden
wurde und die den Filterkriterien
entsprechen.

Abbildung 3-34: Verlaufs-Ansicht

Seite 69
3.11.5. Statistiken
Ein weiteres Feature ist die Anzeige des Mitteilungsverhalten. Um die Daten abzufragen,
wird an das Objekt Database des Connection Managers eine Anfrage gestellt. Die Anfrage
ist dabei vom Objekt StatisticRequest, als Antwort kommt eine StatisticResponse.
Der Graph im Statistikfenster wird durch die Python-Bibliothek matplotlib erzeugt. Wenn
diese nicht installiert ist, erscheint ein Hinweis, dass der Graph nicht angezeigt werden
kann.
Neben der periodenbezogenen Anzeige kann man sich auch noch die Kontakte in einem
Tortendiagramm und allgemeine Statistiken anzeigen lassen.

Abbildung 3-35: Statistiken

Seite 70
3.12. Datenbank
Die Daten – Geräte, Kontakte, Kalender-Einträge und Mitteilungen - werden vom
Connection Manager in einer Datenbank gespeichert. Dabei kann man zwischen einer
SQLite- und MySQL-Datenbank wählen.
Die Verbindung zu dieser Datenbank wird dann über das QtSql-Model hergestellt. In
Abbildung 3-36 sieht man das Layout der Datenbank.

Abbildung 3-36: Datenbank-Layout

Hier kann man erkennen, dass die Device-Tabelle im Mittelpunkt steht und alle anderen
Objekte einem Gerät zugeordnet werden können. Eine Ausnahme bildet die config-Tabelle
– in dieser wird etwa die verwendete Version der Datenbank gespeichert, um später
Upgrades durchführen zu können.
Über die Klasse QSqlQuery können SQL-Abfragen durchgeführt werden.
In diesem Beispiel wird die durchschnittliche Länge einer eingehenden SMS-Mitteilung
ermittelt:
query = "SELECT ROUND(AVG(LENGTH(message)),2) FROM messages WHERE type='" +
str(MessageType.Incoming) + "'"
query = self.__queryReturn(query)

if query.last():
ret["incoming_avglength"] = str(query.value(0).toDouble()[0])

[aus
mobile/android/src/com/hetzenecker/openmobilesuite/service/MessageManager.java]

Seite 71
3.13. Plattformunabhängigkeit
Durch die Verwendung der Programmiersprache Python und des Toolkits Qt war es
möglich, die Anwendung nahezu unverändert unter den Betriebssystemen Windows, Linux
und Mac OS X auszuführen. Seit Qt4 werden dazu die nativen Style-APIs der jeweiligen
Betriebssysteme verwendet und dadurch passen sich die Qt-Widgets perfekt an das
jeweilige Betriebssystem an.

Abbildung 3-37: Bearbeiten eines Kontaktes unter Mac OS X

Abbildung 3-38: Kontakliste und Bearbeitung unter Windows XP


Seite 72
3.14. Übersetzungen
Damit diese Anwendung von so vielen Menschen wie möglich benutzt werden kann, wurde
von Anfang an eine einfache Übersetzbarkeit angestrebt. Dazu müssen die zu
übersetzenden Zeichenketten im Programm gekennzeichnet werden (in etwa durch die
Methode QApplication.translate) und danach durch PyQt extrahiert werden. Die
Übersetzer können dann das einfach zu benutzende Programm Qt Linguist zum
Übersetzen in die eigene Sprache benutzen.

Abbildung 3-39: Screenshot beim Bearbeiten der deutschen Übersetzung

Diese Übersetzung kann dann zur Laufzeit geladen werden:


# Languages
localename = self.settings.setting("lookAndFeel/language")
if localename:
self.locale = QLocale(localename)
QLocale.setDefault(self.locale) # for QDate.toString(x, Qt.DefaultLocaleLongDate)
else:
self.locale = QLocale.system()

locale = self.locale.name()

tmpTranslator = QTranslator()
appTranslator = QTranslator()

if tmpTranslator.load("app_" + locale, ":/lang"):


appTranslator.load("app_" + locale, ":/lang")

self.app.installTranslator(appTranslator)

[aus
mobile/android/src/com/hetzenecker/openmobilesuite/service/MessageManager.java]

Seite 73
Dadurch wurde es für freiwillige Helfer sehr einfach, das Programm in die eigene Sprache
zu übersetzen. Daher existieren derzeit neben der Hauptsprache Englisch Übersetzungen
für Deutsch, Französisch, Italienisch, Polnisch, Spanisch, Tschechisch, Bulgarisch und
Niederländisch.

Abbildung 3-40: Polnische Übersetzung

Abbildung 3-41: Niederländische und tschechische Übersetzungen

Seite 74
4. Webseite
Unter der Adresse http://series60-remote.sourceforge.net wurde eine Webseite für dieses
Projekt erstellt. Diese basiert auf dem Content Management System (CMS) Zikula.
Ein wichtiger Grund für die Wahl dieses CMS war die einfache Unterstützung mehrerer
Sprachen der Webseite. Sie ist derzeit auf Deutsch und Englisch verfügbar, die optimale
Sprache wird durch den Browser erkannt.
Auf der Homepage werden laufend News-Einträge veröffentlicht, es sind Screenshots der
aktuellen Releases vorhanden und es gibt eine Übersicht über getestete Mobiltelefone.
In einem Support-Forum können aufgetretene Fehler und Verbesserungsvorschläge zum
Programm gepostet werden.

Abbildung 4-1: Screenshot der Projekt-Webseite

Seite 75
Nach Klick auf die Kategorie „Downloads“ bekommt der Besucher eine übersichtliche
Seite mit Downloads für alle verfügbaren Betriebssysteme:

Abbildung 4-2: Downloadbereich der Webseite

Seite 76
5. Mailing List
Neben der Webseite werden auch noch drei Mailing-Listen betreut, welche auch auf
Sourceforge gehostet werden.
Die Mailing List series60-remote-devel dient zum Austausch unter den Entwicklern. Hier
werden auch Themen wie Übersetzungen diskutiert.
Daneben gibt es auch noch die Liste series60-remote-announcements, auf der
Ankündigungen – in etwa über eine neue Version – geschrieben werden. Diese ist
moderiert und es können nur Beiträge von bestimmten Personen veröffentlicht werden.
Die letzte Liste – series60-remote-commits – enthält Kopien aller Änderungen des
Quellcodes aus dem SVN-Versionsverwaltungssystem.
Auf der Webseite ist ein Archiv mit allen E-Mails vorhanden:

Abbildung 5-1: Screenshot des Archivs der Mailing-Liste "series60-remote-devel"

Die Mailing Listen basieren auf dem Open-Source-Programm mailman.

Seite 77
6. Bug Tracker
Auf Sourceforge wird auch noch ein System zur Verfolgung von Fehlern und zum
Schreiben von Verbesserungsvorschlägen betrieben.

Abbildung 6-1: Screenshot des Bug-Tracking-Systems

Seite 78
7. Quellen
Bluetooth
Einführung in Bluetooth - Heiko Holtkamp
http://www.sorex-austria.com/tl_files/news/News_Pdf/bluetooth.pdf

The Use of Bluetooth in Linux and Location Aware Computing by Albert Huang
http://people.csail.mit.edu/albert/pubs/2005-ashuang-sm-thesis.pdf

Bluetooth Device Access Guide – Apple


http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/Bluetoot
h/BT_Intro/BT_Intro.html

Service Discovery in Bluetooth


http://wiki.forum.nokia.com/index.php/Service_Discovery_in_Bluetooth

BLUETOOTH SPECIFICATION Version 4.0

D-Bus
http://www.freedesktop.org/wiki/Software/dbus

http://doc.qt.nokia.com/4.7/intro-to-dbus.html

http://de.wikipedia.org/wiki/DBus

Python
Generatoren:
Python von Peter Kaiser, Johannes Ernesti (Galileo openbook)
http://openbook.galileocomputing.de/python/index.htm

Global Interpreter Lock:


http://docs.python.org/py3k/c-api/init.html

http://wiki.python.org/moin/GlobalInterpreterLock

Seite 79
8. Anhang
8.1. Veröffentlichte News-Beiträge der
Webseite
8.1.1. New architecture of Open Mobile Suite

Dear users and developers of Series60-Remote,

I'd like to show you a preview of the most important changes I've planned for the future:

As you can see in the attached image the project will be renamed to "Open Mobile Suite". This is
because I will also support devices which aren't based on the S60 operating system. Such mobile
operating systems are for example Android and - if Nokia doesn't screw things completely up * -
also MeeGo.

The client application on the PC is going to be splitted in several parts. This makes it possible to
replace the User interface.
I'll try to code an plugin for Telepathy that allows the writing of chat messages over SMS from every
Telepathy-compatible client (for example empathy or the new KDE-Telepathy application).
You can also see your contacts and calendar entries in your KDE PIM suite (kaddressbook and

Seite 80
korganizer), because I'll write an Akonadi backend for it.

The communication of these components relies on the D-Bus inter-process communication


system, which is compatible with Linux, Windows** and Mac OS X.

* Yes, this is a reference to today's announcement from Nokia about the cooperation with Microsoft
and the consequences of it for MeeGo.

** For the time being the Qt mainloop isn't supported in dbus-python, but I hope this will change
soon.

Lukas

8.2. Wichtige Mails der Mailing list series60-


remote-devel
8.2.1. 27.01.2011: Status update [Re: Split series60-remote
in two projects]
From: Lukas Hetzenecker <lukas.hetzenecker@gmail.com>
To: series60-remote-devel@lists.sourceforge.net
Subject: Status update [Re: Split series60-remote in two projects]
Date: Thu, 27 Jan 2011 22:36:01 +0100

Hello,

I have thought about the idea of slitting series60-remote in two seperate project once more, but this
time for a different reason: the Global Interpreter Lock.

The User interface is really sluggish when there is much work in the bluetooth connectrion thread
(this is e.g. when updating the contacts or fetching the messages).
Strangely this lag appears to be even more annoying with my new laptop so I decided to do
something against it!

The lagging is caused by the Global Interpreter Lock [1] of Python and the only solution for it is to
use separate processes.
So I'm going to split the application in a part that is resposible for the bluetooth connections and
the other shows the user interface.

And now I'm looking for a method that these two processes can communicate with each other.
I wanted to use DBus for this, but I couldn't get it to work on Windows (I can't install/use the python
bindings for it [2]).

If anybody wants to start developing for the project this would be the ideal task to begin with! If
you've used inter-process communication (IPC) in Python before and know a cool multi-platform
way of doing it, please also respond to this mail.

Greetings
Lukas

[1] http://en.wikipedia.org/wiki/Global_Interpreter_Lock
[2] http://lists.freedesktop.org/archives/dbus/2011-January/014036.html

Seite 81
8.3. Patches
8.3.1. PyBluez

8.3.1.1. 31.10.2009 – Patch für Compiler VC++08


From: Lukas Hetzenecker <LuHe@gmx.at>
To: pybluez <pybluez@googlegroups.com>
Subject: Patch for Visual C++ 2008 Express Edition
Date: Sat, 31 Oct 2009 17:00:11 +0100

Hello,

i tried to compile PyBluez 0.17 on Windows Vista, but I got some errors:
I used Visual C++ 2008 Express Edition and the latest version of the Widcomm
BTW development kit (BTW 6.1.0.1506 SDK)

1.) The Platform SDK wasn't found, because it is located in C:\Program


Files\Microsoft SDKs\Windows\v6.0A

C:\Users\lukas\Downloads\PyBluez-0.17-old>python setup.py build


Can't find the Windows XP Platform SDK

2.) The file widcomm\util.h isn't included in the source code.

widcomm\inquirer.cpp(7) : fatal error C1083: Cannot open include file:


"util.h": No such file or directory
error: command '"C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe"'
failed with exit status 2

3.) The _T macro was not found. It is declared in tchar.h

widcomm\rfcommif.cpp(31) : error C3861: "_T": identifier not found, even with


argument-dependent lookup
error: command '"C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\cl.exe"'
fa
iled with exit status 2

4.) I needed to link against the library oleaut32 too.

WidcommSdklib.lib(MSUtils.obj) : error LNK2019: error LNK2019: unresolved


external
symbol "__imp__SysAllocString@4" referenced in function "_MSGetDeviceInfo".
build\lib.win32-2.6\bluetooth\_widcomm.pyd : fatal error LNK1120: 1 unresolved
externals
error: command '"C:\Programme\Microsoft Visual Studio 9.0\VC\BIN \link.exe"'
failed with exit status 1120

I attached a patch which makes it compile for me. Could anybody please review
this patch?

Seite 82
diff -r -u PyBluez-0.17-new\/setup.py PyBluez-0.17\/setup.py
--- PyBluez-0.17-new\/setup.py 2009-10-15 19:44:11.000000000 +0200
+++ PyBluez-0.17\/setup.py 2009-10-31 14:18:30.936444700 +0100
@@ -10,8 +10,9 @@
if sys.platform == 'win32':
XP2_PSDK_PATH = os.path.join(os.getenv('ProgramFiles'), r"Microsoft Platform SDK for
Windows XP SP2")
S03_PSDK_PATH = os.path.join(os.getenv('ProgramFiles'), r"Microsoft Platform SDK")
+ S08_PSDK_PATH = os.path.join(os.getenv('ProgramFiles'), r"Microsoft
SDKs\\Windows\\v6.0A")
PSDK_PATH = None
- for p in [ XP2_PSDK_PATH, S03_PSDK_PATH ]:
+ for p in [ XP2_PSDK_PATH, S03_PSDK_PATH, S08_PSDK_PATH ]:
if os.path.exists(p):
PSDK_PATH = p
break
@@ -33,7 +34,7 @@
define_macros = [ ('_BTWLIB', None) ],
library_dirs = [ "%s\\Release" % WC_BASE,
"%s\\Lib" % PSDK_PATH, ],
- libraries = [ "WidcommSdklib", "ws2_32", "version", "user32",
"Advapi32", "Winspool", "ole32" ],
+ libraries = [ "WidcommSdklib", "ws2_32", "version", "user32",
"Advapi32", "Winspool", "ole32", "oleaut32" ],
sources = [ "widcomm\\_widcomm.cpp",
"widcomm\\inquirer.cpp",
"widcomm\\rfcommport.cpp",
diff -r -u PyBluez-0.17-new\/widcomm/l2capif.cpp PyBluez-0.17\/widcomm/l2capif.cpp
--- PyBluez-0.17-new\/widcomm/l2capif.cpp 2009-10-15 19:43:30.000000000 +0200
+++ PyBluez-0.17\/widcomm/l2capif.cpp 2009-10-31 14:16:36.556444700 +0100
@@ -1,5 +1,6 @@
#include <windows.h>
#include <Python.h>
+#include <tchar.h>

#include <BtIfDefinitions.h>
#include <BtIfClasses.h>
diff -r -u PyBluez-0.17-new\/widcomm/rfcommif.cpp PyBluez-0.17\/widcomm/rfcommif.cpp
--- PyBluez-0.17-new\/widcomm/rfcommif.cpp 2009-10-15 19:43:30.000000000 +0200
+++ PyBluez-0.17\/widcomm/rfcommif.cpp 2009-10-31 14:16:05.080444700 +0100
@@ -1,5 +1,6 @@
#include <windows.h>
#include <Python.h>
+#include <tchar.h>

#include <BtIfDefinitions.h>
#include <BtIfClasses.h>
Only in PyBluez-0.17\/widcomm: util.h

8.3.1.2. 03.08.2010 – Geräte-Klasse beim Bluetooth-Scan


zurückgeben
From: Lukas Hetzenecker <LuHe@gmx.at>
To: pybluez@googlegroups.com
Subject: Patch for "Class of device" in
Date: Tue, 3 Aug 2010 04:45:59 +0200
Cc: Albert Huang <albert@csail.mit.edu>

This patch adds the argument lookup_class to discover_devices.


If this argument is true the Class of device as integer is appended to the return tuple.
I've currently only implemented it for msbt and widcomm. For bluez it would be the easiest way to
port discover_devices to use the asynchronous DeviceDiscoverer.

Lukas

Seite 83
===================================================================
--- bluetooth/widcomm.py (Revision 44)
+++ bluetooth/widcomm.py (Arbeitskopie)
@@ -106,7 +106,7 @@

inquirer = WCInquirer ()

-def discover_devices (duration=8, flush_cache=True, lookup_names=False):


+def discover_devices (duration=8, flush_cache=True, lookup_names=False,
lookup_class=False):
inquirer.start_inquiry ()

while inquirer.inquiry_in_progress:
@@ -114,9 +114,9 @@

discovered = inquirer.recently_discovered[:]

- if not lookup_names:
+ if not lookup_names and not lookup_class:
return [ tup[0] for tup in discovered ]
- if lookup_names:
+ if lookup_names and not lookup_class:
result = []
for bdaddr, devClass, bdName, bConnected in discovered:
if bdName:
@@ -124,7 +124,24 @@
else:
result.append ((bdAddr, None))
return result
-
+ if not lookup_names and lookup_class:
+ result = []
+ for bdaddr, devClass, bdName, bConnected in discovered:
+ hex = "%02X%02X%02X" % (ord(devClass[0]), ord(devClass[1]),
ord(devClass[2]))
+ devClass = int(hex, 16)
+ result.append ((bdAddr, devClass))
+ return result
+ if lookup_names and lookup_class:
+ result = []
+ for bdaddr, devClass, bdName, bConnected in discovered:
+ hex = "%02X%02X%02X" % (ord(devClass[0]), ord(devClass[1]),
ord(devClass[2]))
+ devClass = int(hex, 16)
+ if bdName:
+ result.append ((bdaddr, bdName, devClass))
+ else:
+ result.append ((bdAddr, None, devClass))
+ return result
+
def lookup_name (address, timeout=10):
discover_devices ()
for bdaddr, devClass, bdName, bConnected in inquirer.recently_discovered:
Index: bluetooth/msbt.py
===================================================================
--- bluetooth/msbt.py (Revision 44)
+++ bluetooth/msbt.py (Arbeitskopie)
@@ -5,8 +5,8 @@

# ============== SDP service registration and unregistration ============

-def discover_devices (duration=8, flush_cache=True, lookup_names=False):


- return bt.discover_devices (flush_cache, lookup_names)
+def discover_devices (duration=8, flush_cache=True, lookup_names=False,
lookup_class=False):
+ return bt.discover_devices (flush_cache, lookup_names, lookup_class)

def lookup_name (address, timeout=10):

Seite 84
if not is_valid_address (address):
Index: msbt/_msbt.c
===================================================================
--- msbt/_msbt.c (Revision 44)
+++ msbt/_msbt.c (Arbeitskopie)
@@ -392,6 +392,7 @@
{
int flushcache = 0;
int lookupnames = 0;
+ int lookupclass = 0;

// inquiry data structure


DWORD qs_len = sizeof( WSAQUERYSET );
@@ -405,19 +406,20 @@

dbg("msbt_discover_devices\n");

- if(!PyArg_ParseTuple(args, "ii", &flushcache, &lookupnames)) {


+ if(!PyArg_ParseTuple(args, "iii", &flushcache, &lookupnames, &lookupclass)) {
free( qs );
return 0;
}

if (flushcache) flushcache = LUP_FLUSHCACHE;


if (lookupnames) lookupnames = LUP_RETURN_NAME;
+ if (lookupclass) lookupclass = LUP_RETURN_TYPE;

ZeroMemory( qs, qs_len );


qs->dwSize = sizeof(WSAQUERYSET);
qs->dwNameSpace = NS_BTH;

- flags |= flushcache | lookupnames | LUP_RETURN_ADDR;


+ flags |= flushcache | lookupnames | lookupclass | LUP_RETURN_ADDR;

Py_BEGIN_ALLOW_THREADS;
// start the device inquiry
@@ -456,9 +458,13 @@
((SOCKADDR_BTH*)qs->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr;
ba2str( result, buf );

- if( lookupnames ) {
+ if( lookupnames && lookupclass ) {
+ tup = Py_BuildValue( "ssl", buf, qs->lpszServiceInstanceName, qs-
>lpServiceClassId->Data1 );
+ } else if( lookupnames ) {
tup = Py_BuildValue( "ss", buf, qs->lpszServiceInstanceName );
- } else {
+ } else if (lookupclass) {
+ tup = Py_BuildValue( "sl", buf, qs->lpServiceClassId->Data1 );
+ } else {
tup = PyString_FromString (buf);
}
PyList_Append( toreturn, tup );

Seite 85
8.3.1.3. 03.08.2010 – Verhindern einer Blockierung der GUI
From: Lukas Hetzenecker <LuHe@gmx.at>
To: pybluez@googlegroups.com
Subject: Patch for threads in widcomm
Date: Tue, 3 Aug 2010 04:49:17 +0200

The initialization of the inquiry is really slow in widcomm.

Would it be possible to allow threads during the function call?


The attached patch should be self-explanatory ;)

Lukas

Index: widcomm/inquirer.cpp
===================================================================
--- widcomm/inquirer.cpp (Revision 44)
+++ widcomm/inquirer.cpp (Arbeitskopie)
@@ -229,11 +229,16 @@
start_inquiry (PyObject *s)
{
WCInquirerPyObject *self = (WCInquirerPyObject*) s;
- if (self->inq->StartInquiry ()) {
+ bool success = TRUE;
+ Py_BEGIN_ALLOW_THREADS
+ success = self->inq->StartInquiry ();
+ Py_END_ALLOW_THREADS
+ if (success) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
+
}

8.3.1.4. 08.04.2011 – Behebung eines Fehlers beim Geräte-Scan


From: Lukas Hetzenecker <lukas.hetzenecker@gmail.com>
To: pybluez@googlegroups.com, Albert Huang <albert@csail.mit.edu>
Subject: Bugfix for bluez/DeviceDiscoverer: Extended Inquiry Response
Date: Fri, 08 Apr 2011 08:27:39 +0200

Hello Albert,

I found a bug in the asynchronous DeviceDiscoverer for the bluez stack:


The "Extended Inquiry Response" event (see Bluetooth Specification 4.0, PDF
page 1690) is not handled in _process_hci_event. As a result of this some
devices (like my HTC Desire HD smart phone) are not detected.

The attached patch file adds support for this event and checks if the complete
local name is in an EIR Data Structure. If it is then a name request is not
needed.

Could you review this patch?

Lukas

Seite 86
Content-Disposition: attachment; filename="fix_extended_inquiry_response.diff"
Content-Transfer-Encoding: 7Bit
Content-Type: text/x-patch; charset="utf-8";
name="fix_extended_inquiry_response.diff"

Index: bluez/btmodule.c
===================================================================
--- bluez/btmodule.c (Revision 45)
+++ bluez/btmodule.c (Arbeitskopie)
@@ -3190,6 +3190,9 @@
#ifdef EVT_INQUIRY_RESULT_WITH_RSSI
ADD_INT_CONST(m, EVT_INQUIRY_RESULT_WITH_RSSI);
#endif
+#ifdef EVT_EXTENDED_INQUIRY_RESULT
+ ADD_INT_CONST(m, EVT_EXTENDED_INQUIRY_RESULT);
+#endif
#ifdef EVT_TESTING
ADD_INT_CONST(m, EVT_TESTING);
#endif
@@ -3324,6 +3327,14 @@
ADD_INT_CONST(m, MSG_ETAG);
#endif

+ /* Size of inquiry info */


+#ifdef INQUIRY_INFO_WITH_RSSI_SIZE
+ ADD_INT_CONST(m, INQUIRY_INFO_WITH_RSSI_SIZE);
+#endif
+#ifdef EXTENDED_INQUIRY_INFO_SIZE
+ ADD_INT_CONST(m, EXTENDED_INQUIRY_INFO_SIZE);
+#endif
+
/* Protocol level and numbers, usable for [gs]etsockopt */
ADD_INT_CONST(m, SOL_SOCKET);
ADD_INT_CONST(m, SOL_L2CAP);
Index: bluetooth/bluez.py
===================================================================
--- bluetooth/bluez.py (Revision 45)
+++ bluetooth/bluez.py (Arbeitskopie)
@@ -439,7 +439,7 @@

if self.sock is None: return


# voodoo magic!!!
- pkt = self.sock.recv (255)
+ pkt = self.sock.recv (258)
ptype, event, plen = struct.unpack ("BBB", pkt[:3])
pkt = pkt[3:]
if event == _bt.EVT_INQUIRY_RESULT:
@@ -456,7 +456,7 @@
clockoff = pkt[1+12*nrsp+2*i:1+12*nrsp+2*i+2]

self._device_discovered (addr, devclass,


- psrm, pspm, clockoff)
+ psrm, pspm, clockoff, None)
elif event == _bt.EVT_INQUIRY_RESULT_WITH_RSSI:
nrsp = struct.unpack ("B", pkt[0])[0]
for i in range (nrsp):
@@ -474,7 +474,36 @@
rssi = struct.unpack ("b", pkt[1+13*nrsp+i])[0]

self._device_discovered (addr, devclass,


- psrm, pspm, clockoff)
+ psrm, pspm, clockoff, None)
+ elif event == _bt.EVT_EXTENDED_INQUIRY_RESULT:
+ nrsp = struct.unpack ("B", pkt[0])[0]
+ for i in range (nrsp):
+ addr = _bt.ba2str (pkt[1+6*i:1+6*i+6])
+ psrm = pkt[ 1+6*nrsp+i ]
+ pspm = pkt[ 1+7*nrsp+i ]

Seite 87
+ devclass_raw = struct.unpack ("BBB",
+ pkt[1+8*nrsp+3*i:1+8*nrsp+3*i+3])
+ devclass = (devclass_raw[2] << 16) | \
+ (devclass_raw[1] << 8) | \
+ devclass_raw[0]
+ clockoff = pkt[1+11*nrsp+2*i:1+11*nrsp+2*i+2]
+ rssi = struct.unpack ("b", pkt[1+13*nrsp+i])[0]
+
+ data_len = _bt.EXTENDED_INQUIRY_INFO_SIZE -
_bt.INQUIRY_INFO_WITH_RSSI_SIZE
+ data = pkt[1+14*nrsp+i:1+14*nrsp+i+data_len]
+ name = None
+ pos = 0
+ while(pos <= len(data)):
+ struct_len = ord(data[pos])
+ if struct_len == 0:
+ break
+ eir_type = ord(data[pos+1])
+ if eir_type == 0x09: # Complete local name
+ name = data[pos+2:pos+struct_len+1]
+ pos += struct_len + 2
+
+ self._device_discovered (addr, devclass,
+ psrm, pspm, clockoff, name)
elif event == _bt.EVT_INQUIRY_COMPLETE:
self.is_inquiring = False
if len (self.names_to_find) == 0:
@@ -527,9 +556,11 @@
# print "unrecognized packet type 0x%02x" % ptype

def _device_discovered (self, address, device_class,


- psrm, pspm, clockoff):
+ psrm, pspm, clockoff, name):
if self.lookup_names:
- if address not in self.names_found and \
+ if name is not None:
+ self.device_discovered (address, device_class, name)
+ elif address not in self.names_found and \
address not in self.names_to_find:

self.names_to_find[address] = \

Seite 88

Das könnte Ihnen auch gefallen