Nun, wie es aussieht, bekomme ich keine Unterstützung vom Hersteller. Man habe andere Prioritäten als ein SDK zu öffnen. Verstehe ich. Warten möchte ich aber auch nicht. Also rücken wir dem Ding mit üblichen Reversing tools zuleibe: bluez/gattool und einem decompiler.
Schließlich muss die offizielle App ja irgendwie mit dem Ding reden. Dazu hole ich mir als erstes mal das APK von hier. Apks sind erst mal ja nur zips, die man umbenennen und dann einfach auspacken kann. Darin sind tolle Dinge wie Ressoucen (Bilder, sounds, …) und das Programm selbst. Im konkreten Fall als .dex Datei. Außerdem erkennt man sofort, dass diese App mit Phonegap/Cordova entwickelt ist, weil es den Ordner assets\www gibt und darin die ganzen typischen Sachen rumliegen. Schön, das kenne ich, damit kenne ich mich aus. Weiterhin angenehm ist, dass die ganze Applikationslogik als javascript-files quasi im Quellcode rumliegt und ich mir erst mal den tieferen Blick in die .dex Datei (z.B, mit dex2jar und java-decompiler) für später aufheben kann 🙂
Es wäre eigentlich denkbar und der einfachste Weg, diese tolle app einfach auszupacken und erweitert um ein paar LD Funktionen mit Phonegap neu zu builden. Aber das Ergebnis könnte man nicht verteilen, weil es vermutlich Stress mit “geistigem Eigentum” gibt. Also lieber schnell was von Null mit den wichtigsten Infos zusammenbasteln. Wobei… *hust* im 1.1.10 apk ist in der index.js der Vermerk (einfach dringelassen vom phonegap schätze ich), dass das File unter Apache license steht :p
So, man will also laut dem .js code ein Gerät mit folgenden Eigenschaften finden:
serviceUuid = “ffe0”,
charUuid = “ffe1”,
desiredName = “SSV1_00000”
bluetoothle.subscribe(subSuccess, subFail, {“address”:address,”serviceUuid”:serviceUuid,”characteristicUuid”:charUuid, “isNotification”:true });
schauen wir mal…
root@zeopi8-1:/# hcitool lescan
LE Scan …
20:91:48:AA:B8:FA (unknown)
20:91:48:AA:B8:FA SSV1_00000
root@zeopi8-1:/# gatttool -b 20:91:48:AA:B8:FA -I
[20:91:48:AA:B8:FA][LE]> connect
Attempting to connect to 20:91:48:AA:B8:FA
Connection successful
[20:91:48:AA:B8:FA][LE]> primary
attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x000f uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0010, end grp handle: 0xffff uuid: 0000ffe0-0000-1000-8000-00805f9b34fb
yep. Hab ich. Nice.
Jetzt die ganzen Funktionen. Ziemlich wüster Wust, das gehört eigentlich gekapselt und zentralisiert, aber ich bin dankbar über die vielen Kommentare und dass auch einige unbenutzte Sachen noch im Code auskommtentiert drin gelassen wurde. Einige Highlights:
function getVersionNumber(){
var hexArray = [“AA”,”AA”,”03″,”99″,”20″,”00″,””];
function sendCheckDataCommand() var hexArray = [“AA”,”AA”,”03″,”94″,”20″,”00″,””];
function sendGetHeaderCommand()
var hexArray = [“AA”,”AA”,”03″,”93″,”20″,”00″,””];
function sendEraseDataCommand()
var hexArray = [“AA”,”AA”,”03″,”96″,”20″,”00″,””];
function getAccel()
var hexArray = [“AA”,”AA”,”03″,”58″,”20″,”00″,””];
function sendConfirmationCommand()
var hexArray = [“AA”,”AA”,”03″,”9A”,”10″,”01″,””];
function sendDataRecordingCmd()
var hexArray = [“AA”,”AA”,”03″,”95″,”20″,”00″,””];
function sendTurnOffRecordCmd()
var hexArray = [“AA”,”AA”,”03″,”95″,”10″,”00″,””];
function setVolCmd(value)
var hexArray = [“AA”,”AA”,”03″,”84″,”10″,hexValue,””];
function setWakeTime(value){
var hexArray = [“AA”,”AA”,”06″,”34″,”10″];
function changeState(newState)
var hexArray = [“AA”,”AA”,”03″,”24″,”10″,newState.toString(),””];
function sendStartCommand()
var hexArray = [“AA”,”AA”,”0C”,”82″,”10″];
hexArray = hexArray.concat(intToBytes(time,6), intToBytes(sampleRate*1000,4), [“”]);
function sendEndCommand()
var hexArray = [“AA”,”AA”,”08″,”83″,”10″];
hexArray = hexArray.concat(intToBytes(time,6), [“”]);
Weiter unten findet sich in der Bolognesesoße um den Javascript-Spaghettihaufen ab Zeile 2557 (index.js) Information darüber, wie das Protokoll aufgebaut ist, z.B.
// — Received accel data — //
if (returnedBytes[0] == 170 && returnedBytes[1] == 170 && returnedBytes[3] == 88 && returnedBytes[4] == 48){
//alert(“received signal strength of: “+returnedBytes[4]);
var xArray = [returnedBytes[5], returnedBytes[6]];
var yArray = [returnedBytes[7], returnedBytes[8]];
var zArray = [returnedBytes[9], returnedBytes[10]];
// — Received Sleep Data Header Packet — //
if (returnedBytes[0] == 170 && returnedBytes[1] == 170 && returnedBytes[3] == 129 && returnedBytes[4] == 48){
// — Received Sleep Data Stream Packet — //
if (returnedBytes[0] == 170 && returnedBytes[1] == 170 && returnedBytes[2] == 11 /* 16 */){
// — Received Sleep Data Footer Packet — //
if (returnedBytes[0] == 170 && returnedBytes[1] == 170 && returnedBytes[3] == 131 && returnedBytes[4] == 48){
Leider findet sich im Code der App kein Hinweis auf eine Möglichkeit, der Mütze Audio zum Abspielen zu schicken. Das heißt, auch die Firmware der Mütze selbst müsste mal gehackt werden. Dass man so einfach an den Code kommt wie an den der App, glaube ich weniger, als nächstes müsste man mal die Plastikbox vorsichtig öffnen, und den Inhalt begutachten, ein ROM-Dump versuchen usw.