summaryrefslogtreecommitdiff
path: root/daemon/appmsgmanager.cpp
blob: b6200789dd957ec4a59d48ce9ef3c42305d8114c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include "appmsgmanager.h"
#include "unpacker.h"
#include "packer.h"

// TODO D-Bus server for non JS kit apps!!!!

AppMsgManager::AppMsgManager(AppManager *apps, WatchConnector *watch, QObject *parent)
    : QObject(parent), apps(apps), watch(watch), lastTransactionId(0)
{
    watch->setEndpointHandler(WatchConnector::watchLAUNCHER,
                              [this](const QByteArray &data) {
        if (data.at(0) == WatchConnector::appmsgPUSH) {
            uint transaction;
            QUuid uuid;
            WatchConnector::Dict dict;

            if (!unpackPushMessage(data, &transaction, &uuid, &dict)) {
                // Failed to parse!
                // Since we're the only one handling this endpoint,
                // all messages must be accepted
                logger()->warn() << "Failed to parser LAUNCHER PUSH message";
                return true;
            }
            if (!dict.contains(1)) {
                logger()->warn() << "LAUNCHER message has no item in dict";
                return true;
            }

            switch (dict.value(1).toInt()) {
            case WatchConnector::launcherSTARTED:
                logger()->debug() << "App starting in watch:" << uuid;
                this->watch->sendMessage(WatchConnector::watchLAUNCHER,
                                         buildAckMessage(transaction));
                emit appStarted(uuid);
                break;
            case WatchConnector::launcherSTOPPED:
                logger()->debug() << "App stopping in watch:" << uuid;
                this->watch->sendMessage(WatchConnector::watchLAUNCHER,
                                         buildAckMessage(transaction));
                emit appStopped(uuid);
                break;
            default:
                logger()->warn() << "LAUNCHER pushed unknown message:" << uuid << dict;
                this->watch->sendMessage(WatchConnector::watchLAUNCHER,
                                         buildNackMessage(transaction));
                break;
            }
        }

        return true;
    });

    watch->setEndpointHandler(WatchConnector::watchAPPLICATION_MESSAGE,
                              [this](const QByteArray &data) {
        switch (data.at(0)) {
        case WatchConnector::appmsgPUSH: {
            uint transaction;
            QUuid uuid;
            WatchConnector::Dict dict;

            if (!unpackPushMessage(data, &transaction, &uuid, &dict)) {
                logger()->warn() << "Failed to parse APP_MSG PUSH";
                return true;
            }

            logger()->debug() << "Received appmsg PUSH from" << uuid << "with" << dict;

            QVariantMap data = mapAppKeys(uuid, dict);
            logger()->debug() << "Mapped dict" << data;

            emit messageReceived(uuid, data);
            break;
        }
        default:
            logger()->warn() << "Unknown application message type:" << data.at(0);
            break;
        }

        return true;
    });
}

void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data, const std::function<void ()> &ackCallback, const std::function<void ()> &nackCallback)
{
    WatchConnector::Dict dict = mapAppKeys(uuid, data);
    quint8 transaction = ++lastTransactionId;
    QByteArray msg = buildPushMessage(transaction, uuid, dict);

    logger()->debug() << "Sending appmsg" << transaction << "to" << uuid << "with" << dict;

    WatchConnector::Dict t_dict;
    QUuid t_uuid;
    uint t_trans;
    if (unpackPushMessage(msg, &t_trans, &t_uuid, &t_dict)) {
        logger()->debug() << t_trans << t_uuid << t_dict;
    } else {
        logger()->warn() << "not unpack my own";
    }


    watch->sendMessage(WatchConnector::watchAPPLICATION_MESSAGE, msg,
                       [this, ackCallback, nackCallback, transaction](const QByteArray &reply) {
        if (reply.size() < 2) return false;

        quint8 type = reply[0];
        quint8 recv_transaction = reply[1];

        logger()->debug() << "Got response to transaction" << transaction;

        if (recv_transaction != transaction) return false;

        switch (type) {
        case WatchConnector::appmsgACK:
            logger()->debug() << "Got ACK to transaction" << transaction;
            if (ackCallback) ackCallback();
            return true;
        case WatchConnector::appmsgNACK:
            logger()->info() << "Got NACK to transaction" << transaction;
            if (nackCallback) nackCallback();
            return true;
        default:
            return false;
        }
    });
}

void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data)
{
    std::function<void()> nullCallback;
    send(uuid, data, nullCallback, nullCallback);
}

WatchConnector::Dict AppMsgManager::mapAppKeys(const QUuid &uuid, const QVariantMap &data)
{
    AppInfo info = apps->info(uuid);
    if (info.uuid() != uuid) {
        logger()->warn() << "Unknown app GUID while sending message:" << uuid;
    }

    WatchConnector::Dict d;

    for (QVariantMap::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) {
        if (info.hasAppKey(it.key())) {
            d.insert(info.valueForAppKey(it.key()), it.value());
        } else {
            // Even if we do not know about this appkey, try to see if it's already a numeric key we
            // can send to the watch.
            bool ok = false;
            int num = it.key().toInt(&ok);
            if (ok) {
                d.insert(num, it.value());
            } else {
                logger()->warn() << "Unknown appKey" << it.key() << "for app with GUID" << uuid;
            }
        }
    }

    return d;
}

QVariantMap AppMsgManager::mapAppKeys(const QUuid &uuid, const WatchConnector::Dict &dict)
{
    AppInfo info = apps->info(uuid);
    if (info.uuid() != uuid) {
        logger()->warn() << "Unknown app GUID while sending message:" << uuid;
    }

    QVariantMap data;

    for (WatchConnector::Dict::const_iterator it = dict.constBegin(); it != dict.constEnd(); ++it) {
        if (info.hasAppKeyValue(it.key())) {
            data.insert(info.appKeyForValue(it.key()), it.value());
        } else {
            logger()->warn() << "Unknown appKey value" << it.key() << "for app with GUID" << uuid;
            data.insert(QString::number(it.key()), it.value());
        }
    }

    return data;
}

bool AppMsgManager::unpackPushMessage(const QByteArray &msg, uint *transaction, QUuid *uuid, WatchConnector::Dict *dict)
{
    Unpacker u(msg);
    quint8 code = u.read<quint8>();
    Q_ASSERT(code == WatchConnector::appmsgPUSH);

    *transaction = u.read<quint8>();
    *uuid = u.readUuid();
    *dict = u.readDict();

    if (u.bad()) {
        return false;
    }

    return true;
}

QByteArray AppMsgManager::buildPushMessage(uint transaction, const QUuid &uuid, const WatchConnector::Dict &dict)
{
    QByteArray ba;
    Packer p(&ba);
    p.write<quint8>(WatchConnector::appmsgPUSH);
    p.write<quint8>(transaction);
    p.writeUuid(uuid);
    p.writeDict(dict);

    return ba;
}

QByteArray AppMsgManager::buildAckMessage(uint transaction)
{
    QByteArray ba(2, Qt::Uninitialized);
    ba[0] = WatchConnector::appmsgACK;
    ba[1] = transaction;
    return ba;
}

QByteArray AppMsgManager::buildNackMessage(uint transaction)
{
    QByteArray ba(2, Qt::Uninitialized);
    ba[0] = WatchConnector::appmsgNACK;
    ba[1] = transaction;
    return ba;
}